-> = ({ children, executionId, domainId, projectId }) => {
- const id = {
- project: projectId,
- domain: domainId,
- name: executionId,
- };
-
- const workflowExecutionQuery = useWorkflowExecutionQuery(id);
-
- return (
- // get the workflow execution query to get the current workflow id
-
- {(execution: Execution) => (
-
- {children}
-
- )}
-
- );
-};
-
-export function withExecutionDetails(
- WrappedComponent: React.FC
,
-) {
- return (props: P) => {
- const [localRouteProps, setLocalRouteProps] = React.useState
();
-
- useEffect(() => {
- setLocalRouteProps(prev => {
- if (JSON.stringify(prev) === JSON.stringify(props)) {
- return prev;
- }
-
- return props;
- });
- }, [props]);
-
- if (!localRouteProps) {
- return ;
- }
- return (
-
-
-
- );
- };
-}
-
-export interface ExecutionDetailsRouteParams {
- domainId: string;
- executionId: string;
- projectId: string;
-}
-
-export const ExecutionDetails: React.FunctionComponent<
- RouteComponentProps
-> = withRouteParams(
- withExecutionDetails(ExecutionContainer),
-);
diff --git a/packages/console/src/components/Executions/ExecutionDetails/ExecutionDetailsActions.tsx b/packages/console/src/components/Executions/ExecutionDetails/ExecutionDetailsActions.tsx
deleted file mode 100644
index 88e88acea..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/ExecutionDetailsActions.tsx
+++ /dev/null
@@ -1,246 +0,0 @@
-import React, { useEffect, useState } from 'react';
-import {
- Button,
- Dialog,
- DialogContent,
- Grid,
- IconButton,
-} from '@material-ui/core';
-import { ResourceIdentifier, Identifier } from 'models/Common/types';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import { getTask } from 'models/Task/api';
-import { LaunchFormDialog } from 'components/Launch/LaunchForm/LaunchFormDialog';
-import { NodeExecutionIdentifier } from 'models/Execution/types';
-import {
- useNodeExecution,
- useNodeExecutionData,
-} from 'components/hooks/useNodeExecution';
-import { literalsToLiteralValueMap } from 'components/Launch/LaunchForm/utils';
-import { TaskInitialLaunchParameters } from 'components/Launch/LaunchForm/types';
-import { NodeExecutionPhase } from 'models/Execution/enums';
-import { extractCompiledNodes } from 'components/hooks/utils';
-import Close from '@material-ui/icons/Close';
-import classnames from 'classnames';
-import { Fullscreen, FullscreenExit } from '@material-ui/icons';
-import { useEscapeKey } from 'components/hooks/useKeyListener';
-import { NodeExecutionDetails } from '../types';
-import t from './strings';
-import { ExecutionNodeDeck } from './ExecutionNodeDeck';
-import {
- useNodeExecutionContext,
- useNodeExecutionsById,
-} from '../contextProvider/NodeExecutionDetails';
-
-const useStyles = makeStyles((theme: Theme) => {
- return {
- actionsContainer: {
- borderTop: `1px solid ${theme.palette.divider}`,
- marginTop: theme.spacing(2),
- paddingTop: theme.spacing(2),
- '& button': {
- marginRight: theme.spacing(1),
- },
- },
- dialog: {
- maxWidth: `calc(100% - ${theme.spacing(12)}px)`,
- maxHeight: `calc(100% - ${theme.spacing(12)}px)`,
- height: theme.spacing(90),
- width: theme.spacing(110),
- transition: 'all 0.3s ease',
- },
- fullscreenDialog: {
- maxWidth: '100vw',
- width: '100vw',
- maxHeight: '100svh',
- height: '100svh',
- margin: 0,
- transition: 'all 0.3s ease',
- borderRadius: 0,
- },
- dialogHeader: {
- padding: theme.spacing(2),
- paddingBottom: theme.spacing(0),
- fontFamily: 'Open sans',
- },
- deckTitle: {
- flexGrow: 1,
- textAlign: 'center',
- fontSize: '24px',
- lineHeight: '32px',
- marginBlock: 0,
- paddingTop: theme.spacing(2),
- paddingBottom: theme.spacing(2),
- },
- close: {
- paddingRight: theme.spacing(2),
- },
- };
-});
-interface ExecutionDetailsActionsProps {
- className?: string;
- details?: NodeExecutionDetails;
- nodeExecutionId: NodeExecutionIdentifier;
- phase: NodeExecutionPhase;
- text?: {
- flyteDeckText?: string;
- rerunText?: string;
- resumeText?: string;
- };
-}
-
-export const ExecutionDetailsActions = ({
- className,
- details,
- nodeExecutionId,
- phase,
- text,
-}: ExecutionDetailsActionsProps): JSX.Element => {
- const styles = useStyles();
-
- const [showLaunchForm, setShowLaunchForm] = useState(false);
- const [showResumeForm, setShowResumeForm] = useState(false);
-
- const [initialParameters, setInitialParameters] = useState<
- TaskInitialLaunchParameters | undefined
- >(undefined);
- const { nodeExecutionsById } = useNodeExecutionsById();
- const executionData = useNodeExecutionData(nodeExecutionId);
- const execution = useNodeExecution(nodeExecutionId);
- const { compiledWorkflowClosure } = useNodeExecutionContext();
- const id = details?.taskTemplate?.id;
-
- const compiledNode = extractCompiledNodes(compiledWorkflowClosure).find(
- node =>
- node.id ===
- nodeExecutionsById[nodeExecutionId.nodeId]?.metadata?.specNodeId ||
- node.id === nodeExecutionId.nodeId,
- );
-
- useEffect(() => {
- if (!id) {
- return;
- }
-
- (async () => {
- const task = await getTask(id!);
-
- const literals = executionData.value.fullInputs?.literals;
- const taskInputsTypes =
- task.closure.compiledTask.template?.interface?.inputs?.variables;
-
- const tempInitialParameters: TaskInitialLaunchParameters = {
- values:
- literals &&
- taskInputsTypes &&
- literalsToLiteralValueMap(literals, taskInputsTypes),
- taskId: id as Identifier | undefined,
- };
-
- setInitialParameters(tempInitialParameters);
- })();
- }, [details]);
-
- const [showDeck, setShowDeck] = React.useState(false);
- const onCloseDeck = () => setShowDeck(false);
-
- // Close deck modal on escape key press
- useEscapeKey(onCloseDeck);
-
- const [fullScreen, setSetFullScreen] = React.useState(false);
- const toggleFullScreen = () => {
- setSetFullScreen(!fullScreen);
- };
-
- const rerunOnClick = (e: React.MouseEvent) => {
- e.stopPropagation();
- setShowLaunchForm(true);
- };
-
- const onResumeClick = (e: React.MouseEvent) => {
- e.stopPropagation();
- setShowResumeForm(true);
- };
-
- return (
- <>
-
- {execution?.value?.closure?.deckUri && (
-
- )}
- {id && initialParameters && details && (
-
- )}
- {phase === NodeExecutionPhase.PAUSED && (
-
- )}
-
- {id && initialParameters && (
-
- )}
- {compiledNode && (
-
- )}
- {execution?.value?.closure?.deckUri && (
-
- )}
- >
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/ExecutionDetailsAppBarContent.tsx b/packages/console/src/components/Executions/ExecutionDetails/ExecutionDetailsAppBarContent.tsx
deleted file mode 100644
index 17b512a10..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/ExecutionDetailsAppBarContent.tsx
+++ /dev/null
@@ -1,277 +0,0 @@
-import React from 'react';
-import { Box, Button, Dialog, Grid, Link, Typography } from '@material-ui/core';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import ArrowBack from '@material-ui/icons/ArrowBack';
-import classnames from 'classnames';
-import { navbarGridHeight } from 'common/layout';
-import { ButtonCircularProgress } from 'components/common/ButtonCircularProgress';
-import { MoreOptionsMenu } from 'components/common/MoreOptionsMenu';
-import { useCommonStyles } from 'components/common/styles';
-import { useLocationState } from 'components/hooks/useLocationState';
-import { Link as RouterLink } from 'react-router-dom';
-import { history } from 'routes/history';
-import { Routes } from 'routes/routes';
-import { WorkflowExecutionPhase } from 'models/Execution/enums';
-import { SubNavBarContent } from 'components/Navigation/SubNavBarContent';
-import { useEscapeKey } from 'components/hooks/useKeyListener';
-import { FeatureFlag, useFeatureFlag } from 'basics/FeatureFlags';
-import { BreadcrumbTitleActions } from 'components/Breadcrumbs';
-import { ExecutionInputsOutputsModal } from '../ExecutionInputsOutputsModal';
-import { ExecutionStatusBadge } from '../ExecutionStatusBadge';
-import { TerminateExecutionButton } from '../TerminateExecution/TerminateExecutionButton';
-import { executionIsRunning, executionIsTerminal } from '../utils';
-import { backLinkTitle, executionActionStrings } from './constants';
-import { RelaunchExecutionForm } from './RelaunchExecutionForm';
-import { getExecutionBackLink, getExecutionSourceId } from './utils';
-import { useRecoverExecutionState } from './useRecoverExecutionState';
-import { ExecutionContext } from '../contexts';
-
-const useStyles = makeStyles((theme: Theme) => {
- return {
- actions: {
- alignItems: 'center',
- display: 'flex',
- justifyContent: 'flex-end',
- flex: '1 0 auto',
- height: '100%',
- marginLeft: theme.spacing(2),
- },
- backLink: {
- color: 'inherit',
- marginRight: theme.spacing(1),
- },
- container: {
- alignItems: 'center',
- display: 'flex',
- flex: '1 1 auto',
- maxWidth: '100%',
- },
- inputsOutputsLink: {
- color: theme.palette.primary.main,
- },
- title: {
- flex: '0 1 auto',
- marginLeft: theme.spacing(2),
- },
- titleContainer: {
- alignItems: 'center',
- display: 'flex',
- flex: '0 1 auto',
- flexDirection: 'column',
- maxHeight: theme.spacing(navbarGridHeight),
- overflow: 'hidden',
- },
- version: {
- flex: '0 1 auto',
- overflow: 'hidden',
- },
- };
-});
-
-/** Renders information about a given Execution into the NavBar */
-export const ExecutionDetailsAppBarContentInner: React.FC<{}> = () => {
- const commonStyles = useCommonStyles();
- const styles = useStyles();
-
- const isBreadcrumbFlag = useFeatureFlag(FeatureFlag.breadcrumbs);
-
- const { execution } = React.useContext(ExecutionContext);
- const { domain, name, project } = execution.id;
-
- const [showInputsOutputs, setShowInputsOutputs] = React.useState(false);
- const [showRelaunchForm, setShowRelaunchForm] = React.useState(false);
- const { phase } = execution.closure;
- const sourceId = getExecutionSourceId(execution);
- const { backLink: originalBackLink = getExecutionBackLink(execution) } =
- useLocationState();
-
- const isRunning = executionIsRunning(execution);
- const isTerminal = executionIsTerminal(execution);
- const onClickShowInputsOutputs = () => setShowInputsOutputs(true);
- const onClickRelaunch = () => setShowRelaunchForm(true);
- const onCloseRelaunch = (_?: any) => setShowRelaunchForm(false);
-
- // Close modal on escape key press
- useEscapeKey(onCloseRelaunch);
-
- const fromExecutionNav = new URLSearchParams(history.location.search).get(
- 'fromExecutionNav',
- );
- const backLink = fromExecutionNav
- ? Routes.ProjectDetails.sections.dashboard.makeUrl(project, domain)
- : originalBackLink;
-
- const {
- recoverExecution,
- recoverState: { isLoading: recovering, data: recoveredId },
- } = useRecoverExecutionState();
-
- React.useEffect(() => {
- if (!recovering && recoveredId) {
- history.push(Routes.ExecutionDetails.makeUrl(recoveredId));
- }
- }, [recovering, recoveredId]);
-
- let modalContent: JSX.Element | null = null;
- if (showInputsOutputs) {
- const onClose = () => setShowInputsOutputs(false);
- modalContent = (
-
- );
- }
-
- const onClickRecover = React.useCallback(async () => {
- await recoverExecution();
- }, [recoverExecution]);
-
- const isRecoverVisible = React.useMemo(
- () =>
- [
- WorkflowExecutionPhase.FAILED,
- WorkflowExecutionPhase.ABORTED,
- WorkflowExecutionPhase.TIMED_OUT,
- ].includes(phase),
- [phase],
- );
-
- const actionContent = isRunning ? (
-
- ) : isTerminal ? (
- <>
- {isRecoverVisible && (
-
-
-
- )}
-
-
-
- >
- ) : null;
-
- // For non-terminal executions, add an overflow menu with the ability to clone
- const moreActionsContent = !isTerminal ? (
-
- ) : null;
-
- return (
- <>
- {!isBreadcrumbFlag && (
-
-
-
-
-
-
-
-
- {`${project}/${domain}/${sourceId.name}/`}
- {name}
-
-
-
-
-
-
- View Inputs & Outputs
-
-
- {actionContent}
- {moreActionsContent}
-
-
- )}
- {isBreadcrumbFlag && (
-
-
-
-
-
-
-
- View Inputs & Outputs
-
-
- {actionContent && <>{actionContent}>}
- {moreActionsContent && <>{moreActionsContent}>}
-
-
- )}
-
- {modalContent}
- >
- );
-};
-
-export const ExecutionDetailsAppBarContent: React.FC<{}> = () => {
- return (
-
-
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/ExecutionMetadata.tsx b/packages/console/src/components/Executions/ExecutionDetails/ExecutionMetadata.tsx
deleted file mode 100644
index 84c8f214c..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/ExecutionMetadata.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import * as React from 'react';
-import { Grid, Typography } from '@material-ui/core';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import classnames from 'classnames';
-import { dashedValueString } from 'common/constants';
-import { formatDateUTC, protobufDurationToHMS } from 'common/formatters';
-import { timestampToDate } from 'common/utils';
-import { useCommonStyles } from 'components/common/styles';
-import { secondaryBackgroundColor } from 'components/Theme/constants';
-import { Link as RouterLink } from 'react-router-dom';
-import { Routes } from 'routes/routes';
-import { ExecutionContext } from '../contexts';
-import { ExpandableExecutionError } from '../Tables/ExpandableExecutionError';
-import { ExecutionMetadataLabels } from './constants';
-import { ExecutionMetadataExtra } from './ExecutionMetadataExtra';
-
-const useStyles = makeStyles((theme: Theme) => {
- return {
- container: {
- background: secondaryBackgroundColor,
- width: '100%',
- },
- detailsContainer: {
- display: 'flex',
- paddingTop: theme.spacing(1),
- paddingBottom: theme.spacing(2),
- marginTop: 0,
- },
- detailItem: {
- marginLeft: theme.spacing(4),
- },
- expandCollapseButton: {
- transition: theme.transitions.create('transform'),
- '&.expanded': {
- transform: 'rotate(180deg)',
- },
- },
- expandCollapseContainer: {
- bottom: 0,
- position: 'absolute',
- right: theme.spacing(2),
- transform: 'translateY(100%)',
- zIndex: 1,
- },
- version: {
- flex: '0 1 auto',
- overflow: 'hidden',
- },
- };
-});
-
-interface DetailItem {
- className?: string;
- label: ExecutionMetadataLabels;
- value: React.ReactNode;
-}
-
-/** Renders metadata details about a given Execution */
-export const ExecutionMetadata: React.FC<{}> = () => {
- const commonStyles = useCommonStyles();
- const styles = useStyles();
-
- const { execution } = React.useContext(ExecutionContext);
-
- const { domain } = execution.id;
- const { abortMetadata, duration, error, startedAt, workflowId } =
- execution.closure;
- const { referenceExecution, systemMetadata } = execution.spec.metadata;
- const cluster = systemMetadata?.executionCluster ?? dashedValueString;
-
- const details: DetailItem[] = [
- { label: ExecutionMetadataLabels.domain, value: domain },
- {
- className: styles.version,
- label: ExecutionMetadataLabels.version,
- value: workflowId.version,
- },
- {
- label: ExecutionMetadataLabels.cluster,
- value: cluster,
- },
- {
- label: ExecutionMetadataLabels.time,
- value: startedAt
- ? formatDateUTC(timestampToDate(startedAt))
- : dashedValueString,
- },
- {
- label: ExecutionMetadataLabels.duration,
- value: duration ? protobufDurationToHMS(duration) : dashedValueString,
- },
- ];
-
- if (referenceExecution != null) {
- details.push({
- label: ExecutionMetadataLabels.relatedTo,
- value: (
-
- {referenceExecution.name}
-
- ),
- });
- }
-
- return (
-
-
- {details.map(({ className, label, value }) => (
-
-
- {label}
-
-
- {value}
-
-
- ))}
-
-
-
- {error || abortMetadata ? (
-
- ) : null}
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/ExecutionMetadataExtra.tsx b/packages/console/src/components/Executions/ExecutionDetails/ExecutionMetadataExtra.tsx
deleted file mode 100644
index 52acede52..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/ExecutionMetadataExtra.tsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import { Grid, Typography } from '@material-ui/core';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import classnames from 'classnames';
-import { useCommonStyles } from 'components/common/styles';
-import { Execution } from 'models/Execution/types';
-import * as React from 'react';
-import { getLaunchPlan } from 'models/Launch/api';
-import { LaunchPlanSpec } from 'models/Launch/types';
-import { dashedValueString } from 'common/constants';
-import { ExecutionMetadataLabels } from './constants';
-
-const useStyles = makeStyles((theme: Theme) => ({
- detailItem: {
- flexShrink: 0,
- marginLeft: theme.spacing(4),
- },
-}));
-
-interface DetailItem {
- className?: string;
- label: ExecutionMetadataLabels;
- value: React.ReactNode;
-}
-
-/**
- * Renders extra metadata details about a given Execution
- * @param execution
- * @constructor
- */
-export const ExecutionMetadataExtra: React.FC<{
- execution: Execution;
-}> = ({ execution }) => {
- const commonStyles = useCommonStyles();
- const styles = useStyles();
-
- const {
- launchPlan: launchPlanId,
- maxParallelism,
- rawOutputDataConfig,
- securityContext,
- interruptible,
- overwriteCache,
- } = execution.spec;
-
- const [launchPlanSpec, setLaunchPlanSpec] = React.useState<
- Partial
- >({});
-
- React.useEffect(() => {
- getLaunchPlan(launchPlanId).then(({ spec }) => setLaunchPlanSpec(spec));
- }, [launchPlanId]);
-
- const details: DetailItem[] = [
- {
- label: ExecutionMetadataLabels.iam,
- value:
- securityContext?.runAs?.iamRole ||
- ExecutionMetadataLabels.securityContextDefault,
- },
- {
- label: ExecutionMetadataLabels.serviceAccount,
- value:
- securityContext?.runAs?.k8sServiceAccount ||
- ExecutionMetadataLabels.securityContextDefault,
- },
- {
- label: ExecutionMetadataLabels.rawOutputPrefix,
- value:
- rawOutputDataConfig?.outputLocationPrefix ||
- launchPlanSpec?.rawOutputDataConfig?.outputLocationPrefix ||
- dashedValueString,
- },
- {
- label: ExecutionMetadataLabels.parallelism,
- value: maxParallelism,
- },
- {
- label: ExecutionMetadataLabels.interruptible,
- value: interruptible
- ? interruptible.value
- ? 'true'
- : 'false'
- : dashedValueString,
- },
- {
- label: ExecutionMetadataLabels.overwriteCache,
- value: overwriteCache ? 'true' : 'false',
- },
- ];
-
- return (
- <>
- {details.map(({ className, label, value }) => (
-
-
- {label}
-
-
- {value}
-
-
- ))}
- >
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/ExecutionNodeDeck.tsx b/packages/console/src/components/Executions/ExecutionDetails/ExecutionNodeDeck.tsx
deleted file mode 100644
index a108d025c..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/ExecutionNodeDeck.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react';
-import { useDownloadLink } from 'components/hooks/useDataProxy';
-import { Core } from '@flyteorg/flyteidl-types';
-import { LoadingSpinner, WaitForData } from 'components/common';
-
-/** Fetches and renders the deck data for a given `nodeExecutionId` */
-export const ExecutionNodeDeck: React.FC<{
- nodeExecutionId: Core.NodeExecutionIdentifier;
- className?: string;
-}> = ({ nodeExecutionId, className = '' }) => {
- const downloadLink = useDownloadLink(nodeExecutionId);
- const iFrameSrc = downloadLink?.value?.signedUrl?.[0];
-
- return (
-
-
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/ExecutionNodeURL.tsx b/packages/console/src/components/Executions/ExecutionDetails/ExecutionNodeURL.tsx
deleted file mode 100644
index 5d6577c59..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/ExecutionNodeURL.tsx
+++ /dev/null
@@ -1,188 +0,0 @@
-import * as React from 'react';
-import { Box, Button, SvgIconTypeMap, Typography } from '@material-ui/core';
-import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
-import { docco } from 'react-syntax-highlighter/dist/esm/styles/hljs/docco';
-import FileCopyIcon from '@material-ui/icons/FileCopy';
-import { DefaultComponentProps } from '@material-ui/core/OverridableComponent';
-import copyToClipboard from 'copy-to-clipboard';
-import { Theme, makeStyles } from '@material-ui/core/styles';
-import {
- primaryHighlightColor,
- separatorColor,
- errorBackgroundColor,
- listhoverColor,
-} from 'components/Theme/constants';
-import classNames from 'classnames';
-import { RowExpander } from '../Tables/RowExpander';
-
-const useStyles = makeStyles((theme: Theme) => ({
- container: {
- marginLeft: '-10px',
- },
- codeWrapper: {
- overflow: 'hidden',
- border: `1px solid ${separatorColor}`,
- borderRadius: 4,
- marginLeft: '16px',
- },
-
- hoverWrapper: {
- position: 'relative',
-
- '& .textButton': {
- color: theme.palette.primary.main,
- border: 'none',
- right: '2px',
- top: 0,
- },
-
- '& .copyButton': {
- backgroundColor: theme.palette.common.white,
- border: `1px solid ${primaryHighlightColor}`,
- borderRadius: theme.spacing(1),
- color: theme.palette.text.secondary,
- height: theme.spacing(4),
- minWidth: 0,
- padding: 0,
- position: 'absolute',
- right: theme.spacing(2),
- top: theme.spacing(1),
- width: theme.spacing(4),
- display: 'none',
-
- '&:hover': {
- backgroundColor: listhoverColor,
- },
- },
- '&:hover': {
- '& .copyButton': {
- display: 'flex',
- },
- },
-
- '& pre': {
- margin: '0 !important',
- },
- },
-}));
-
-const CopyButton: React.FC<
- DefaultComponentProps> & {
- onCopyClick: React.MouseEventHandler;
- buttonVariant?: 'text' | 'button';
- }
-> = ({ onCopyClick, buttonVariant, children, ...props }) => {
- return (
-
- );
-};
-
-/** Fetches and renders the deck data for a given `nodeExecutionId` */
-export const ExecutionNodeURL: React.FC<{
- dataSourceURI?: string;
- copyUrlText: string;
-}> = ({ dataSourceURI, copyUrlText }) => {
- const styles = useStyles();
- const [expanded, setExpanded] = React.useState(false);
- const isHttps = /^https:/.test(window.location.href);
-
- const code = isHttps
- ? // https snippet
- `from flytekit.remote.remote import FlyteRemote
-from flytekit.configuration import Config
-remote = FlyteRemote(
- Config.for_endpoint("${window.location.host}"),
-)
-remote.get("${dataSourceURI}")`
- : // http snippet
- `from flytekit.remote.remote import FlyteRemote
-from flytekit.configuration import Config
-remote = FlyteRemote(
- Config.for_endpoint("${window.location.host}", True),
-)
-remote.get("${dataSourceURI}")`;
-
- const toggleExpanded = () => {
- setExpanded(!expanded);
- };
-
- return dataSourceURI ? (
-
-
- {
- event.preventDefault();
-
- copyToClipboard(dataSourceURI);
- }}
- >
-
- {copyUrlText}
-
-
-
-
-
-
- FlyteRemote Usage
-
-
-
-
-
- {code}
-
-
- {
- event.preventDefault();
-
- copyToClipboard(code);
- }}
- />
-
-
-
- ) : null;
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/ExecutionTab.tsx b/packages/console/src/components/Executions/ExecutionDetails/ExecutionTab.tsx
deleted file mode 100644
index 865220744..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/ExecutionTab.tsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import * as React from 'react';
-import { WorkflowGraph } from 'components/WorkflowGraph/WorkflowGraph';
-import { Theme, makeStyles } from '@material-ui/core/styles';
-import { tabs } from './constants';
-import { NodeExecutionsTable } from '../Tables/NodeExecutionsTable';
-import { DetailsPanelContextProvider } from './DetailsPanelContext';
-import { ScaleProvider } from './Timeline/scaleContext';
-import { ExecutionTimelineContainer } from './Timeline/ExecutionTimelineContainer';
-import { useNodeExecutionFiltersState } from '../filters/useExecutionFiltersState';
-
-const useStyles = makeStyles((theme: Theme) => ({
- nodesContainer: {
- borderTop: `1px solid ${theme.palette.divider}`,
- display: 'flex',
- flex: '1 1 100%',
- flexDirection: 'column',
- minHeight: 0,
- },
-}));
-
-interface ExecutionTabProps {
- tabType: string;
-}
-
-/** Contains the available ways to visualize the nodes of a WorkflowExecution */
-export const ExecutionTab: React.FC = ({ tabType }) => {
- const styles = useStyles();
- const filterState = useNodeExecutionFiltersState();
-
- return (
-
-
-
- {tabType === tabs.nodes.id && (
-
- )}
- {tabType === tabs.graph.id && }
- {tabType === tabs.timeline.id && }
-
-
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/ExecutionTabView.tsx b/packages/console/src/components/Executions/ExecutionDetails/ExecutionTabView.tsx
deleted file mode 100644
index 408802537..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/ExecutionTabView.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import { Tab, Tabs } from '@material-ui/core';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import { useTabState } from 'components/hooks/useTabState';
-import { secondaryBackgroundColor } from 'components/Theme/constants';
-import { tabs } from './constants';
-import { ExecutionTab } from './ExecutionTab';
-
-const useStyles = makeStyles((theme: Theme) => ({
- tabs: {
- background: secondaryBackgroundColor,
- paddingLeft: theme.spacing(3.5),
- },
-}));
-
-const DEFAULT_TAB = tabs.nodes.id;
-
-/** Contains the available ways to visualize the nodes of a WorkflowExecution */
-export const ExecutionTabView: React.FC<{}> = () => {
- const styles = useStyles();
- const tabState = useTabState(tabs, DEFAULT_TAB);
-
- return (
- <>
-
-
-
-
-
-
-
- >
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionDetailsPanelContent.tsx b/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionDetailsPanelContent.tsx
deleted file mode 100644
index 9cb04ef71..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionDetailsPanelContent.tsx
+++ /dev/null
@@ -1,487 +0,0 @@
-import React, { useEffect, useMemo, useRef, useState } from 'react';
-import { IconButton, Typography, Tab, Tabs } from '@material-ui/core';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import { ArrowBackIos, Close } from '@material-ui/icons';
-import classnames from 'classnames';
-import { useCommonStyles } from 'components/common/styles';
-import { InfoIcon } from 'components/common/Icons/InfoIcon';
-import { bodyFontFamily, smallFontSize } from 'components/Theme/constants';
-import { ExecutionStatusBadge } from 'components/Executions/ExecutionStatusBadge';
-import { LocationState } from 'components/hooks/useLocationState';
-import { useTabState } from 'components/hooks/useTabState';
-import { LocationDescriptor } from 'history';
-import { Workflow } from 'models/Workflow/types';
-import {
- MapTaskExecution,
- NodeExecution,
- NodeExecutionIdentifier,
-} from 'models/Execution/types';
-import Skeleton from 'react-loading-skeleton';
-import { useQueryClient } from 'react-query';
-import { Link as RouterLink } from 'react-router-dom';
-import { Routes } from 'routes/routes';
-import { NoDataIsAvailable } from 'components/Literals/LiteralMapViewer';
-import { fetchWorkflow } from 'components/Workflow/workflowQueries';
-import { PanelSection } from 'components/common/PanelSection';
-import { DumpJSON } from 'components/common/DumpJSON';
-import { ScrollableMonospaceText } from 'components/common/ScrollableMonospaceText';
-import { dNode } from 'models/Graph/types';
-import { NodeExecutionPhase, TaskExecutionPhase } from 'models/Execution/enums';
-import {
- transformWorkflowToKeyedDag,
- getNodeNameFromDag,
-} from 'components/WorkflowGraph/utils';
-import { TaskVersionDetailsLink } from 'components/Entities/VersionDetails/VersionDetailsLink';
-import { Identifier } from 'models/Common/types';
-import { isEqual, values } from 'lodash';
-import { extractCompiledNodes } from 'components/hooks/utils';
-import { NodeExecutionCacheStatus } from '../NodeExecutionCacheStatus';
-import { getTaskExecutions } from '../nodeExecutionQueries';
-import { NodeExecutionDetails } from '../types';
-import {
- useNodeExecutionContext,
- useNodeExecutionsById,
-} from '../contextProvider/NodeExecutionDetails';
-import { getTaskExecutionDetailReasons } from './utils';
-import { fetchWorkflowExecution } from '../useWorkflowExecution';
-import { NodeExecutionTabs } from './NodeExecutionTabs';
-import { ExecutionDetailsActions } from './ExecutionDetailsActions';
-import { getNodeFrontendPhase, isNodeGateNode } from '../utils';
-import { WorkflowNodeExecution } from '../contexts';
-
-const useStyles = makeStyles((theme: Theme) => {
- const paddingVertical = `${theme.spacing(2)}px`;
- const paddingHorizontal = `${theme.spacing(3)}px`;
- return {
- notRunStatus: {
- alignItems: 'center',
- backgroundColor: 'gray',
- borderRadius: '4px',
- color: theme.palette.text.primary,
- display: 'flex',
- flex: '0 0 auto',
- height: theme.spacing(3),
- fontSize: smallFontSize,
- justifyContent: 'center',
- textTransform: 'uppercase',
- width: theme.spacing(11),
- fontFamily: bodyFontFamily,
- fontWeight: 'bold',
- },
- closeButton: {
- marginLeft: theme.spacing(1),
- },
- container: {
- display: 'flex',
- flexDirection: 'column',
- height: '100%',
- paddingTop: theme.spacing(2),
- width: '100%',
- },
- content: {
- overflowY: 'auto',
- },
- displayId: {
- marginBottom: theme.spacing(1),
- },
- header: {
- borderBottom: `${theme.spacing(1)}px solid ${theme.palette.divider}`,
- },
- headerContent: {
- padding: `0 ${paddingHorizontal} ${paddingVertical} ${paddingHorizontal}`,
- },
- nodeTypeContainer: {
- alignItems: 'flex-end',
- borderTop: `1px solid ${theme.palette.divider}`,
- display: 'flex',
- flexDirection: 'row',
- fontWeight: 'bold',
- justifyContent: 'space-between',
- marginTop: theme.spacing(2),
- paddingTop: theme.spacing(2),
- },
- actionsContainer: {
- borderTop: `1px solid ${theme.palette.divider}`,
- marginTop: theme.spacing(2),
- paddingTop: theme.spacing(2),
- },
- nodeTypeContent: {
- minWidth: theme.spacing(9),
- },
- nodeTypeLink: {
- fontWeight: 'normal',
- },
- tabs: {
- borderBottom: `1px solid ${theme.palette.divider}`,
- },
- title: {
- 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),
- },
- };
-});
-
-const tabIds = {
- executions: 'executions',
- inputs: 'inputs',
- outputs: 'outputs',
- task: 'task',
-};
-
-interface NodeExecutionDetailsProps {
- nodeExecutionId: NodeExecutionIdentifier;
- taskPhase: TaskExecutionPhase;
- onClose?: () => void;
-}
-
-const NodeExecutionLinkContent: React.FC<{
- execution: NodeExecution;
-}> = ({ execution }) => {
- const commonStyles = useCommonStyles();
- const styles = useStyles();
- const { workflowNodeMetadata } = execution.closure;
- if (!workflowNodeMetadata) {
- return null;
- }
- const linkTarget: LocationDescriptor = {
- pathname: Routes.ExecutionDetails.makeUrl(workflowNodeMetadata.executionId),
- state: {
- backLink: Routes.ExecutionDetails.makeUrl(execution.id.executionId),
- },
- };
- return (
-
- View Sub-Workflow
-
- );
-};
-
-const ExecutionTypeDetails: React.FC<{
- details?: NodeExecutionDetails;
- execution: NodeExecution;
-}> = ({ details, execution }) => {
- const styles = useStyles();
- const commonStyles = useCommonStyles();
- return (
-
-
-
- Type
-
-
{details ? details.displayType : }
-
-
-
- );
-};
-
-// TODO FC#393: Check if it could be replaced with tabsContent or simplified further.
-// Check if we need to request task info instead of relying on dag
-// Also check strange setDag pattern
-const WorkflowTabs: React.FC<{
- dagData: dNode;
- nodeId: string;
-}> = ({ dagData, nodeId }) => {
- const styles = useStyles();
- const tabState = useTabState(tabIds, tabIds.inputs);
-
- let tabContent: JSX.Element | null = null;
- const id = nodeId.slice(nodeId.lastIndexOf('-') + 1);
- const taskTemplate = dagData[id]?.value.template;
- switch (tabState.value) {
- case tabIds.inputs: {
- tabContent = taskTemplate ? (
-
-
-
- ) : null;
- break;
- }
- case tabIds.task: {
- tabContent = taskTemplate ? (
-
-
-
-
- ) : null;
- break;
- }
- }
- return (
- <>
-
-
- {!!taskTemplate && }
-
- {tabContent}
- >
- );
-};
-
-/** DetailsPanel content which renders execution information about a given NodeExecution
- */
-export const NodeExecutionDetailsPanelContent: React.FC<
- NodeExecutionDetailsProps
-> = ({ nodeExecutionId, taskPhase, onClose }) => {
- const commonStyles = useCommonStyles();
- const styles = useStyles();
- const queryClient = useQueryClient();
-
- const { nodeExecutionsById, setCurrentNodeExecutionsById } =
- useNodeExecutionsById();
-
- const nodeExecution = useMemo(() => {
- const finalExecution = values(nodeExecutionsById).find(node =>
- isEqual(node.id, nodeExecutionId),
- );
-
- return finalExecution;
- }, [nodeExecutionId, nodeExecutionsById]);
-
- const [isReasonsVisible, setReasonsVisible] = useState(false);
- const [dag, setDag] = useState(null);
- const [details, setDetails] = useState();
- const [selectedTaskExecution, setSelectedTaskExecution] =
- useState();
-
- const { getNodeExecutionDetails, compiledWorkflowClosure } =
- useNodeExecutionContext();
-
- const isGateNode = isNodeGateNode(
- extractCompiledNodes(compiledWorkflowClosure),
- nodeExecutionsById[nodeExecutionId.nodeId]?.metadata?.specNodeId ||
- nodeExecutionId.nodeId,
- );
-
- const [nodeExecutionLoading, setNodeExecutionLoading] =
- useState(false);
-
- const isMounted = useRef(false);
- useEffect(() => {
- isMounted.current = true;
- return () => {
- isMounted.current = false;
- };
- }, []);
-
- useEffect(() => {
- let isCurrent = true;
- getNodeExecutionDetails(nodeExecution).then(res => {
- if (isCurrent) {
- setDetails(res);
- }
- });
-
- return () => {
- isCurrent = false;
- };
- }, [nodeExecution]);
-
- useEffect(() => {
- let isCurrent = true;
-
- async function fetchTasksData(queryClient) {
- setNodeExecutionLoading(true);
- const newNode = await getTaskExecutions(queryClient, nodeExecution!);
-
- if (isCurrent && newNode) {
- const {
- closure: _,
- metadata: __,
- ...parentLight
- } = newNode || ({} as WorkflowNodeExecution);
-
- setCurrentNodeExecutionsById({
- [newNode.scopedId!]: parentLight as WorkflowNodeExecution,
- });
- setNodeExecutionLoading(false);
- }
- }
-
- if (nodeExecution && !nodeExecution?.tasksFetched) {
- fetchTasksData(queryClient);
- } else {
- if (isCurrent) {
- setNodeExecutionLoading(false);
- }
- }
- return () => {
- isCurrent = false;
- };
- }, [nodeExecution]);
-
- useEffect(() => {
- setReasonsVisible(false);
- }, [nodeExecutionId]);
-
- useEffect(() => {
- setSelectedTaskExecution(undefined);
- }, [nodeExecutionId, taskPhase]);
-
- // TODO: needs to be removed
- const getWorkflowDag = async () => {
- const workflowExecution = await fetchWorkflowExecution(
- queryClient,
- nodeExecutionId.executionId,
- );
- const workflowData: Workflow = await fetchWorkflow(
- queryClient,
- workflowExecution.closure.workflowId,
- );
- if (workflowData) {
- const keyedDag = transformWorkflowToKeyedDag(workflowData);
- if (isMounted.current) setDag(keyedDag);
- }
- };
-
- if (!nodeExecution) {
- getWorkflowDag();
- } else {
- if (dag) setDag(null);
- }
-
- const reasons = getTaskExecutionDetailReasons(
- nodeExecution?.taskExecutions ?? [],
- );
-
- const onBackClick = () => {
- setSelectedTaskExecution(undefined);
- };
-
- const headerTitle = useMemo(() => {
- const mapTaskHeader = `${selectedTaskExecution?.taskIndex} of ${nodeExecutionId.nodeId}`;
- const header = selectedTaskExecution
- ? mapTaskHeader
- : nodeExecutionId.nodeId;
-
- return (
-
-
- {!!selectedTaskExecution && (
-
-
-
- )}
- {header}
-
-
-
-
-
- );
- }, [nodeExecutionId, selectedTaskExecution]);
-
- const frontendPhase = useMemo(() => {
- const computedPhase = getNodeFrontendPhase(
- nodeExecution?.closure.phase ?? NodeExecutionPhase.UNDEFINED,
- isGateNode,
- );
- return computedPhase;
- }, [nodeExecution, isGateNode]);
-
- const statusContent = nodeExecution ? (
-
-
-
-
- {reasons?.length ? (
-
-
-
- ) : null}
-
- ) : (
- NOT RUN
- );
-
- let detailsContent: JSX.Element | null = null;
- if (nodeExecution) {
- detailsContent = (
- <>
-
-
- >
- );
- }
-
- const tabsContent: JSX.Element | null = nodeExecution ? (
-
- ) : null;
-
- const emptyName = isGateNode ? <>> : ;
- const displayName = details?.displayName ?? emptyName;
-
- return (
-
-
- {!nodeExecutionLoading && dag ? (
-
- ) : (
- tabsContent
- )}
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/NodeExecutionInputs.tsx b/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/NodeExecutionInputs.tsx
deleted file mode 100644
index 1fb9f702a..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/NodeExecutionInputs.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { PanelSection } from 'components/common/PanelSection';
-import { WaitForData } from 'components/common/WaitForData';
-import { useNodeExecutionData } from 'components/hooks/useNodeExecution';
-import { LiteralMapViewer } from 'components/Literals/LiteralMapViewer';
-import { NodeExecution } from 'models/Execution/types';
-import * as React from 'react';
-import { ExecutionNodeURL } from '../ExecutionNodeURL';
-
-/** Fetches and renders the input data for a given `NodeExecution` */
-export const NodeExecutionInputs: React.FC<{
- execution: NodeExecution;
- taskIndex?: number;
-}> = ({ execution, taskIndex }) => {
- const executionData = useNodeExecutionData(execution.id);
-
- return (
-
-
- {(() => {
- const data = executionData?.value;
- const fullInputs = data?.fullInputs;
- const dataSourceURI = data?.flyteUrls?.inputs;
- const hasInputs = Object.keys(fullInputs?.literals || {}).length > 0;
- return (
- <>
- {hasInputs && taskIndex === undefined ? (
-
- ) : null}
-
- >
- );
- })()}
-
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/NodeExecutionOutputs.tsx b/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/NodeExecutionOutputs.tsx
deleted file mode 100644
index c74318c9a..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/NodeExecutionOutputs.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { PanelSection } from 'components/common/PanelSection';
-import { WaitForData } from 'components/common/WaitForData';
-import { useNodeExecutionData } from 'components/hooks/useNodeExecution';
-import { LiteralMapViewer } from 'components/Literals/LiteralMapViewer';
-import { NodeExecution } from 'models/Execution/types';
-import * as React from 'react';
-import { ExecutionNodeURL } from '../ExecutionNodeURL';
-
-/** Fetches and renders the output data for a given `NodeExecution` */
-export const NodeExecutionOutputs: React.FC<{
- execution: NodeExecution;
- taskIndex?: number;
-}> = ({ execution, taskIndex }) => {
- const executionData = useNodeExecutionData(execution.id);
-
- return (
-
-
- {(() => {
- const data = executionData?.value;
- const fullOutputs = data?.fullOutputs;
- const dataSourceURI = data?.flyteUrls?.outputs;
- const hasOutputs =
- Object.keys(fullOutputs?.literals || {}).length > 0;
- return (
- <>
- {hasOutputs && taskIndex === undefined ? (
-
- ) : null}
-
- >
- );
- })()}
-
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/index.tsx b/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/index.tsx
deleted file mode 100644
index 8d759aadc..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/index.tsx
+++ /dev/null
@@ -1,137 +0,0 @@
-import * as React from 'react';
-import { makeStyles } from '@material-ui/core/styles';
-import { Tab, Tabs } from '@material-ui/core';
-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';
-import { NodeExecutionInputs } from './NodeExecutionInputs';
-import { NodeExecutionOutputs } from './NodeExecutionOutputs';
-
-const useStyles = makeStyles(theme => {
- return {
- content: {
- overflowY: 'auto',
- paddingBottom: '100px', // TODO @FC 454 temporary fix for panel height issue
- },
- tabs: {
- borderBottom: `1px solid ${theme.palette.divider}`,
- '& .c--MuiTab-root': {
- minWidth: 'auto',
- },
- '& .MuiTabs-flexContainer': {
- justifyContent: 'space-around',
- },
- },
- tabItem: {
- margin: theme.spacing(0, 1),
- },
- };
-});
-
-const tabIds = {
- executions: 'executions',
- inputs: 'inputs',
- outputs: 'outputs',
- task: 'task',
-};
-
-const defaultTab = tabIds.executions;
-
-export const NodeExecutionTabs: React.FC<{
- nodeExecution: NodeExecution;
- selectedTaskExecution?: MapTaskExecution;
- onTaskSelected: (val: MapTaskExecution) => void;
- phase?: TaskExecutionPhase;
- taskTemplate?: TaskTemplate | null;
- taskIndex?: number;
-}> = ({
- nodeExecution,
- selectedTaskExecution,
- onTaskSelected,
- taskTemplate,
- phase,
- taskIndex,
-}) => {
- const styles = useStyles();
- const tabState = useTabState(tabIds, defaultTab);
-
- if (tabState.value === tabIds.task && !taskTemplate) {
- // Reset tab value, if task tab is selected, while no taskTemplate is avaible
- // can happen when user switches between nodeExecutions without closing the drawer
- tabState.onChange(() => {
- /* */
- }, defaultTab);
- }
-
- let tabContent: JSX.Element | null = null;
- switch (tabState.value) {
- case tabIds.executions: {
- tabContent = selectedTaskExecution ? (
-
- ) : (
-
- );
- break;
- }
- case tabIds.inputs: {
- tabContent = (
-
- );
- break;
- }
- case tabIds.outputs: {
- tabContent = (
-
- );
- break;
- }
- case tabIds.task: {
- tabContent = taskTemplate ? (
-
-
-
-
- ) : null;
- break;
- }
- }
-
- const executionLabel =
- isMapTaskType(taskTemplate?.type) && !selectedTaskExecution
- ? 'Map Execution'
- : 'Executions';
-
- return (
- <>
-
-
-
-
- {!!taskTemplate && (
-
- )}
-
- {tabContent}
- >
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/test/index.test.tsx b/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/test/index.test.tsx
deleted file mode 100644
index 9dd8fed89..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/NodeExecutionTabs/test/index.test.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import { render } from '@testing-library/react';
-import { useTabState } from 'components/hooks/useTabState';
-import { extractTaskTemplates } from 'components/hooks/utils';
-import { TaskExecutionPhase } from 'models/Execution/enums';
-import { createMockNodeExecutions } from 'models/Execution/__mocks__/mockNodeExecutionsData';
-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];
-const nodeExecution = getMockNodeExecution();
-const workflow = createMockWorkflow('SampleWorkflow');
-const taskTemplate = {
- ...extractTaskTemplates(workflow)[0],
- type: TaskType.ARRAY,
-};
-const phase = TaskExecutionPhase.SUCCEEDED;
-
-jest.mock('components/hooks/useTabState');
-
-describe('NodeExecutionTabs', () => {
- const mockUseTabState = useTabState as jest.Mock;
- 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', () => {
- const { queryByText, queryAllByRole } = render(
- ,
- );
- expect(queryAllByRole('tab')).toHaveLength(4);
- expect(queryByText('Executions')).toBeInTheDocument();
- });
-
- it('should display proper tab name when it was provided and shouldShow is FALSE', () => {
- const { queryByText, queryAllByRole } = render(
- ,
- );
-
- expect(queryAllByRole('tab')).toHaveLength(4);
- expect(queryByText('Map Execution')).toBeInTheDocument();
- });
- });
-
- describe('without map tasks', () => {
- it('should display proper tab name when mapTask was not provided', () => {
- const { queryAllByRole, queryByText } = render(
- ,
- );
-
- expect(queryAllByRole('tab')).toHaveLength(3);
- expect(queryByText('Executions')).toBeInTheDocument();
- });
- });
-});
diff --git a/packages/console/src/components/Executions/ExecutionDetails/RelaunchExecutionForm.tsx b/packages/console/src/components/Executions/ExecutionDetails/RelaunchExecutionForm.tsx
deleted file mode 100644
index 876eeb5d8..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/RelaunchExecutionForm.tsx
+++ /dev/null
@@ -1,161 +0,0 @@
-import { WaitForData } from 'components/common/WaitForData';
-import { useAPIContext } from 'components/data/apiContext';
-import { fetchStates } from 'components/hooks/types';
-import { useFetchableData } from 'components/hooks/useFetchableData';
-import { LaunchForm } from 'components/Launch/LaunchForm/LaunchForm';
-import {
- TaskInitialLaunchParameters,
- WorkflowInitialLaunchParameters,
-} from 'components/Launch/LaunchForm/types';
-import { fetchAndMapExecutionInputValues } from 'components/Launch/LaunchForm/useMappedExecutionInputValues';
-import {
- getTaskInputs,
- getWorkflowInputs,
-} from 'components/Launch/LaunchForm/utils';
-import { Execution } from 'models/Execution/types';
-import * as React from 'react';
-import { isSingleTaskExecution } from './utils';
-
-export interface RelaunchExecutionFormProps {
- execution: Execution;
- onClose(): void;
-}
-
-function useRelaunchWorkflowFormState({
- execution,
-}: RelaunchExecutionFormProps) {
- const apiContext = useAPIContext();
- const initialParameters = useFetchableData<
- WorkflowInitialLaunchParameters,
- Execution
- >(
- {
- defaultValue: {} as WorkflowInitialLaunchParameters,
- doFetch: async execution => {
- const {
- closure: { workflowId },
- spec: {
- launchPlan,
- disableAll,
- maxParallelism,
- rawOutputDataConfig,
- labels,
- annotations,
- authRole,
- securityContext,
- interruptible,
- overwriteCache,
- },
- } = execution;
-
- const workflow = await apiContext.getWorkflow(workflowId);
- const inputDefinitions = getWorkflowInputs(workflow);
- const values = await fetchAndMapExecutionInputValues(
- {
- execution,
- inputDefinitions,
- },
- apiContext,
- );
-
- return {
- values,
- launchPlan,
- workflowId,
- disableAll,
- maxParallelism,
- rawOutputDataConfig,
- labels,
- annotations,
- authRole,
- securityContext,
- interruptible,
- overwriteCache,
- };
- },
- },
- execution,
- );
- return { initialParameters };
-}
-
-function useRelaunchTaskFormState({ execution }: RelaunchExecutionFormProps) {
- const apiContext = useAPIContext();
- const initialParameters = useFetchableData<
- TaskInitialLaunchParameters,
- Execution
- >(
- {
- defaultValue: {} as TaskInitialLaunchParameters,
- doFetch: async execution => {
- const {
- spec: { authRole, launchPlan: taskId, interruptible, overwriteCache },
- } = execution;
- const task = await apiContext.getTask(taskId);
- const inputDefinitions = getTaskInputs(task);
- const values = await fetchAndMapExecutionInputValues(
- {
- execution,
- inputDefinitions,
- },
- apiContext,
- );
- return { authRole, values, taskId, interruptible, overwriteCache };
- },
- },
- execution,
- );
- return { initialParameters };
-}
-
-const RelaunchTaskForm: React.FC = props => {
- const { initialParameters } = useRelaunchTaskFormState(props);
- const {
- spec: { launchPlan: taskId },
- } = props.execution;
- return (
-
- {initialParameters.state.matches(fetchStates.LOADED) ? (
-
- ) : null}
-
- );
-};
-const RelaunchWorkflowForm: React.FC = props => {
- const { initialParameters } = useRelaunchWorkflowFormState(props);
- const {
- closure: { workflowId },
- spec: { securityContext },
- } = props.execution;
-
- return (
-
- {initialParameters.state.matches(fetchStates.LOADED) ? (
-
- ) : null}
-
- );
-};
-
-/** For a given execution, fetches the associated Workflow/Task and renders a
- * `LaunchForm` based on the same source with input values taken from the execution. */
-export const RelaunchExecutionForm: React.FC<
- RelaunchExecutionFormProps
-> = props => {
- return isSingleTaskExecution(props.execution) ? (
-
- ) : (
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/TaskExecutionNodeRenderer/StatusIndicator.tsx b/packages/console/src/components/Executions/ExecutionDetails/TaskExecutionNodeRenderer/StatusIndicator.tsx
deleted file mode 100644
index b13f2cc30..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/TaskExecutionNodeRenderer/StatusIndicator.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import { makeStyles } from '@material-ui/core/styles';
-import classnames from 'classnames';
-import { NodeConfig, Point } from 'components/flytegraph/types';
-import * as React from 'react';
-
-const useStyles = makeStyles(() => ({
- pulse: {
- animation: '1200ms infinite alternate',
- animationName: 'pulse',
- },
- '@keyframes pulse': {
- '0%': {
- opacity: 0.35,
- },
- '80%': {
- opacity: 1,
- },
- },
-}));
-
-const statusSize = 11;
-
-/** Renders an indicator for a node based on execution status */
-export const StatusIndicator: React.FC<{
- color: string;
- config: NodeConfig;
- position: Point;
- pulse: boolean;
-}> = ({ color, config, position, pulse }) => (
-
-
-
-);
diff --git a/packages/console/src/components/Executions/ExecutionDetails/TaskExecutionNodeRenderer/TaskExecutionNode.tsx b/packages/console/src/components/Executions/ExecutionDetails/TaskExecutionNodeRenderer/TaskExecutionNode.tsx
deleted file mode 100644
index d796b38bf..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/TaskExecutionNodeRenderer/TaskExecutionNode.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import React from 'react';
-import { useNodeExecutionsById } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { getNodeExecutionPhaseConstants } from 'components/Executions/utils';
-import { NodeRendererProps, Point } from 'components/flytegraph/types';
-import { TaskNodeRenderer } from 'components/WorkflowGraph/TaskNodeRenderer';
-import { NodeExecutionPhase } from 'models/Execution/enums';
-import { DAGNode } from 'models/Graph/types';
-import { StatusIndicator } from './StatusIndicator';
-
-/** Renders DAGNodes with colors based on their node type, as well as dots to
- * indicate the execution status
- */
-export const TaskExecutionNode: React.FC<
- NodeRendererProps
-> = props => {
- const { node, config, selected } = props;
- const { nodeExecutionsById } = useNodeExecutionsById();
- const nodeExecution = nodeExecutionsById[node.id];
-
- const phase = nodeExecution
- ? nodeExecution.closure.phase
- : NodeExecutionPhase.UNDEFINED;
- const { badgeColor: color } = getNodeExecutionPhaseConstants(phase);
- const renderStatus = phase !== NodeExecutionPhase.UNDEFINED;
-
- const height = config.fontSize + config.textPadding * 2;
- const width = node.textWidth + config.textPadding * 2;
-
- // Position status indicator centered on left border
- const statusPosition: Point = {
- x: -width / 2,
- y: 0,
- };
-
- const noStatusOverlayProps = {
- height,
- width,
- fill: 'white',
- opacity: selected ? 0 : 0.35,
- stroke: 'white',
- strokeWidth: selected ? 0 : config.strokeWidth,
- rx: config.cornerRounding,
- ry: config.cornerRounding,
- x: -width / 2,
- y: -height / 2,
- };
-
- const nodeChildren = renderStatus ? (
-
- ) : (
-
- );
-
- return {nodeChildren};
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/TaskExecutionNodeRenderer/TaskExecutionNodeRenderer.tsx b/packages/console/src/components/Executions/ExecutionDetails/TaskExecutionNodeRenderer/TaskExecutionNodeRenderer.tsx
deleted file mode 100644
index 50ae08531..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/TaskExecutionNodeRenderer/TaskExecutionNodeRenderer.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import { NodeRendererProps } from 'components/flytegraph/types';
-import { TaskNodeRenderer } from 'components/WorkflowGraph/TaskNodeRenderer';
-import { isEndNode, isStartNode } from 'models/Node/utils';
-import { DAGNode } from 'models/Graph/types';
-import * as React from 'react';
-import { TaskExecutionNode } from './TaskExecutionNode';
-
-/** Renders DAGNodes with colors based on their node type, as well as dots to
- * indicate the execution status
- */
-export const TaskExecutionNodeRenderer: React.FC<
- NodeRendererProps
-> = props => {
- if (isStartNode(props.node) || isEndNode(props.node)) {
- return ;
- }
- return ;
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/ChartHeader.tsx b/packages/console/src/components/Executions/ExecutionDetails/Timeline/ChartHeader.tsx
deleted file mode 100644
index d145f0810..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/ChartHeader.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-import * as React from 'react';
-import moment from 'moment-timezone';
-import makeStyles from '@material-ui/core/styles/makeStyles';
-import { COLOR_SPECTRUM } from 'components/Theme/colorSpectrum';
-import { useScaleContext } from './scaleContext';
-import { TimeZone } from './helpers';
-
-interface StyleProps {
- chartWidth: number;
- labelInterval: number;
-}
-
-const useStyles = makeStyles(_theme => ({
- chartHeader: (props: StyleProps) => ({
- height: 41,
- display: 'flex',
- alignItems: 'center',
- width: `${props.chartWidth}px`,
- }),
- taskDurationsLabelItem: (props: StyleProps) => ({
- fontSize: 12,
- fontFamily: 'Open Sans',
- fontWeight: 'bold',
- color: COLOR_SPECTRUM.gray40.color,
- paddingLeft: 10,
- width: `${props.labelInterval}px`,
- }),
-}));
-
-interface HeaderProps extends StyleProps {
- chartTimezone: string;
- totalDurationSec: number;
- startedAt: Date;
-}
-
-export const ChartHeader = (props: HeaderProps) => {
- const styles = useStyles(props);
-
- const { chartInterval: chartTimeInterval, setMaxValue } = useScaleContext();
- const { startedAt, chartTimezone, totalDurationSec } = props;
-
- React.useEffect(() => {
- setMaxValue(props.totalDurationSec);
- }, [props.totalDurationSec, setMaxValue]);
-
- const labels = React.useMemo(() => {
- const len = Math.ceil(totalDurationSec / chartTimeInterval);
- const lbs = len > 0 ? new Array(len).fill('') : [];
- return lbs.map((_, idx) => {
- const time = moment.utc(
- new Date(startedAt.getTime() + idx * chartTimeInterval * 1000),
- );
- return chartTimezone === TimeZone.UTC
- ? time.format('hh:mm:ss A')
- : time.local().format('hh:mm:ss A');
- });
- }, [chartTimezone, startedAt, chartTimeInterval, totalDurationSec]);
-
- return (
-
- {labels.map(label => {
- return (
-
- {label}
-
- );
- })}
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx b/packages/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx
deleted file mode 100644
index 3fd9a32d3..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx
+++ /dev/null
@@ -1,245 +0,0 @@
-import React, {
- createRef,
- useContext,
- useEffect,
- useRef,
- useState,
-} from 'react';
-import { makeStyles, Typography } from '@material-ui/core';
-import { tableHeaderColor } from 'components/Theme/constants';
-import { timestampToDate } from 'common/utils';
-import { dNode } from 'models/Graph/types';
-import {
- fetchChildrenExecutions,
- searchNode,
-} from 'components/Executions/utils';
-import { useQueryClient } from 'react-query';
-import { eq, merge } from 'lodash';
-import { useNodeExecutionsById } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { ExecutionContext } from 'components/Executions/contexts';
-import { useExecutionMetrics } from 'components/Executions/useExecutionMetrics';
-import { convertToPlainNodes } from './helpers';
-import { ChartHeader } from './ChartHeader';
-import { useScaleContext } from './scaleContext';
-import { TaskNames } from './TaskNames';
-import { getChartDurationData } from './TimelineChart/chartData';
-import { TimelineChart } from './TimelineChart';
-import t from '../strings';
-import {
- getExecutionMetricsOperationIds,
- parseSpanData,
-} from './TimelineChart/utils';
-
-interface StyleProps {
- chartWidth: number;
- itemsShown: number;
-}
-
-const useStyles = makeStyles(theme => ({
- chartHeader: (props: StyleProps) => ({
- marginTop: -10,
- marginLeft: -15,
- width: `${props.chartWidth + 20}px`,
- height: `${56 * props.itemsShown + 20}px`,
- }),
- taskNames: {
- display: 'flex',
- flexDirection: 'column',
- borderRight: `1px solid ${theme.palette.divider}`,
- overflowY: 'auto',
- },
- taskNamesHeader: {
- textTransform: 'uppercase',
- fontSize: 12,
- fontWeight: 'bold',
- lineHeight: '16px',
- color: tableHeaderColor,
- height: 45,
- flexBasis: 45,
- display: 'flex',
- alignItems: 'center',
- borderBottom: `4px solid ${theme.palette.divider}`,
- paddingLeft: 30,
- },
- taskDurations: {
- borderLeft: `1px solid ${theme.palette.divider}`,
- marginLeft: 4,
- flex: 1,
- overflow: 'hidden',
- display: 'flex',
- flexDirection: 'column',
- },
- taskDurationsLabelsView: {
- overflow: 'hidden',
- borderBottom: `4px solid ${theme.palette.divider}`,
- },
- taskDurationsView: {
- flex: 1,
- overflowY: 'hidden',
- },
-}));
-
-const INTERVAL_LENGTH = 110;
-
-interface ExProps {
- chartTimezone: string;
- initialNodes: dNode[];
-}
-
-export const ExecutionTimeline: React.FC = ({
- chartTimezone,
- initialNodes,
-}) => {
- const [chartWidth, setChartWidth] = useState(0);
- const [labelInterval, setLabelInterval] = useState(INTERVAL_LENGTH);
- const durationsRef = useRef(null);
- const durationsLabelsRef = useRef(null);
- const taskNamesRef = createRef();
-
- const [originalNodes, setOriginalNodes] = useState(initialNodes);
- const [showNodes, setShowNodes] = useState([]);
- const [startedAt, setStartedAt] = useState(new Date());
- const queryClient = useQueryClient();
- const { nodeExecutionsById, setCurrentNodeExecutionsById } =
- useNodeExecutionsById();
- const { chartInterval: chartTimeInterval } = useScaleContext();
- const { execution } = useContext(ExecutionContext);
- const executionMetricsData = useExecutionMetrics(execution.id, 10);
-
- useEffect(() => {
- setOriginalNodes(ogn => {
- const newNodes = merge(initialNodes, ogn);
-
- if (!eq(newNodes, ogn)) {
- return newNodes;
- }
-
- return ogn;
- });
-
- const plainNodes = convertToPlainNodes(originalNodes);
- const updatedShownNodesMap = plainNodes.map(node => {
- const execution = nodeExecutionsById[node.scopedId];
- return {
- ...node,
- startedAt: execution?.closure.startedAt,
- execution,
- };
- });
- setShowNodes(updatedShownNodesMap);
-
- // set startTime for all timeline offset and duration calculations.
- const firstStartedAt = updatedShownNodesMap[0]?.startedAt;
- if (firstStartedAt) {
- setStartedAt(timestampToDate(firstStartedAt));
- }
- }, [initialNodes, originalNodes, nodeExecutionsById]);
-
- const { items: barItemsData, totalDurationSec } = getChartDurationData(
- showNodes,
- startedAt,
- );
- const styles = useStyles({
- chartWidth: chartWidth,
- itemsShown: showNodes.length,
- });
-
- useEffect(() => {
- // Sync width of elements and intervals of ChartHeader (time labels) and TimelineChart
- const calcWidth =
- Math.ceil(totalDurationSec / chartTimeInterval) * INTERVAL_LENGTH;
- if (durationsRef.current && calcWidth < durationsRef.current.clientWidth) {
- setLabelInterval(
- durationsRef.current.clientWidth /
- Math.ceil(totalDurationSec / chartTimeInterval),
- );
- setChartWidth(durationsRef.current.clientWidth);
- } else {
- setChartWidth(calcWidth);
- setLabelInterval(INTERVAL_LENGTH);
- }
- }, [totalDurationSec, chartTimeInterval, durationsRef]);
-
- const onGraphScroll = () => {
- // cover horizontal scroll only
- const scrollLeft = durationsRef?.current?.scrollLeft ?? 0;
- const labelView = durationsLabelsRef?.current;
- if (labelView) {
- labelView.scrollTo({
- left: scrollLeft,
- });
- }
- };
-
- const onVerticalNodesScroll = () => {
- const scrollTop = taskNamesRef?.current?.scrollTop ?? 0;
- const graphView = durationsRef?.current;
- if (graphView) {
- graphView.scrollTo({
- top: scrollTop,
- });
- }
- };
-
- const toggleNode = async (id: string, scopedId: string, level: number) => {
- await fetchChildrenExecutions(
- queryClient,
- scopedId,
- nodeExecutionsById,
- setCurrentNodeExecutionsById,
- );
- searchNode(originalNodes, 0, id, scopedId, level);
- setOriginalNodes([...originalNodes]);
- };
-
- const operationIds = getExecutionMetricsOperationIds(
- executionMetricsData.value,
- );
-
- const parsedExecutionMetricsData = parseSpanData(executionMetricsData.value);
-
- return (
- <>
-
-
- {t('taskNameColumnHeader')}
-
-
-
-
- >
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimelineContainer.tsx b/packages/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimelineContainer.tsx
deleted file mode 100644
index efcd92e00..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimelineContainer.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import * as React from 'react';
-import { makeStyles } from '@material-ui/core/styles';
-import { useState } from 'react';
-import { useNodeExecutionsById } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { ExecutionTimeline } from './ExecutionTimeline';
-import { ExecutionTimelineFooter } from './ExecutionTimelineFooter';
-import { TimeZone } from './helpers';
-
-const useStyles = makeStyles(() => ({
- wrapper: {
- display: 'flex',
- flexDirection: 'column',
- flex: '1 1 100%',
- },
- container: {
- display: 'flex',
- flex: '1 1 0',
- overflowY: 'auto',
- },
-}));
-
-export const ExecutionTimelineContainer: React.FC<{}> = () => {
- const styles = useStyles();
- const [chartTimezone, setChartTimezone] = useState(TimeZone.Local);
- const handleTimezoneChange = tz => setChartTimezone(tz);
-
- const { initialDNodes: initialNodes } = useNodeExecutionsById();
- return (
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimelineFooter.tsx b/packages/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimelineFooter.tsx
deleted file mode 100644
index c9c356242..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimelineFooter.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import * as React from 'react';
-import { Theme, Radio, RadioGroup, Slider } from '@material-ui/core';
-import { makeStyles, withStyles } from '@material-ui/styles';
-import FormControlLabel from '@material-ui/core/FormControlLabel';
-import { COLOR_SPECTRUM } from 'components/Theme/colorSpectrum';
-import { TimeZone } from './helpers';
-import { useScaleContext } from './scaleContext';
-
-function valueText(value: number) {
- return `${value}s`;
-}
-
-const useStyles = makeStyles((theme: Theme) => ({
- container: {
- borderTop: `1px solid ${theme.palette.divider}`,
- padding: '20px 24px',
- display: 'flex',
- justifyContent: 'space-between',
- alignItems: 'center',
- },
-}));
-
-const CustomSlider = withStyles({
- root: {
- color: COLOR_SPECTRUM.indigo60.color,
- height: 4,
- padding: '15px 0',
- width: 360,
- },
- active: {},
- valueLabel: {
- left: 'calc(-50% + 12px)',
- color: COLOR_SPECTRUM.black.color,
- top: -22,
- '& *': {
- background: 'transparent',
- color: COLOR_SPECTRUM.black.color,
- },
- },
- track: {
- height: 4,
- },
- rail: {
- height: 4,
- opacity: 0.5,
- backgroundColor: COLOR_SPECTRUM.gray20.color,
- },
- mark: {
- backgroundColor: COLOR_SPECTRUM.gray20.color,
- height: 8,
- width: 2,
- marginTop: -2,
- },
- markLabel: {
- top: -6,
- fontSize: 12,
- color: COLOR_SPECTRUM.gray40.color,
- },
- markActive: {
- opacity: 1,
- backgroundColor: 'currentColor',
- },
- marked: {
- marginBottom: 0,
- },
-})(Slider);
-
-interface ExecutionTimelineFooterProps {
- onTimezoneChange?: (timezone: string) => void;
-}
-
-export const ExecutionTimelineFooter: React.FC<
- ExecutionTimelineFooterProps
-> = ({ onTimezoneChange }) => {
- const styles = useStyles();
- const [timezone, setTimezone] = React.useState(TimeZone.Local);
-
- const timeScale = useScaleContext();
-
- const handleTimezoneChange = (event: React.ChangeEvent) => {
- const newTimezone = (event.target as HTMLInputElement).value;
- setTimezone(newTimezone);
- if (onTimezoneChange) {
- onTimezoneChange(newTimezone);
- }
- };
-
- const handleTimeIntervalChange = (event, newValue) => {
- timeScale.setScaleFactor(newValue);
- };
-
- return (
-
- <>{children}>}
- valueLabelDisplay="on"
- getAriaValueText={valueText}
- />
-
- }
- label="Local Time"
- />
- }
- label="UTC"
- />
-
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/NodeExecutionName.tsx b/packages/console/src/components/Executions/ExecutionDetails/Timeline/NodeExecutionName.tsx
deleted file mode 100644
index f3cfc30e4..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/NodeExecutionName.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import { makeStyles, Theme } from '@material-ui/core';
-import Typography from '@material-ui/core/Typography';
-import classNames from 'classnames';
-import { useCommonStyles } from 'components/common/styles';
-import { useNodeExecutionContext } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { SelectNodeExecutionLink } from 'components/Executions/Tables/SelectNodeExecutionLink';
-import { isEqual } from 'lodash';
-import { NodeExecutionPhase } from 'models/Execution/enums';
-import { NodeExecution } from 'models/Execution/types';
-import React, { useEffect, useState } from 'react';
-import { useDetailsPanel } from '../DetailsPanelContext';
-
-interface NodeExecutionTimelineNameData {
- name: string;
- templateName?: string;
- execution?: NodeExecution;
- className?: string;
-}
-
-const useStyles = makeStyles((_theme: Theme) => ({
- selectedExecutionName: {
- fontWeight: 'bold',
- },
- displayName: {
- marginTop: 4,
- textOverflow: 'ellipsis',
- width: '100%',
- overflow: 'hidden',
- },
-}));
-
-export const NodeExecutionName: React.FC = ({
- name,
- templateName,
- execution,
- className,
-}) => {
- const commonStyles = useCommonStyles();
- const styles = useStyles();
-
- const { getNodeExecutionDetails } = useNodeExecutionContext();
- const { selectedExecution, setSelectedExecution } = useDetailsPanel();
- const [displayName, setDisplayName] = useState();
-
- useEffect(() => {
- let isCurrent = true;
- getNodeExecutionDetails(execution).then(res => {
- if (isCurrent) {
- setDisplayName(res?.displayName);
- }
- });
- return () => {
- isCurrent = false;
- };
- });
-
- if (!execution) {
- // to avoid crash - disable items which do not have associated execution.
- // as we won't be able to provide task info for them anyway.
- return {name};
- }
- const isSelected =
- selectedExecution != null && isEqual(execution.id, selectedExecution);
-
- const defaultName = displayName ?? name;
- const truncatedName = defaultName?.split('.').pop() || defaultName;
-
- return (
- <>
- {isSelected ||
- execution.closure.phase === NodeExecutionPhase.UNDEFINED ? (
-
- {truncatedName}
-
- ) : (
-
- )}
- {templateName && (
-
- {templateName}
-
- )}
- >
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx b/packages/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx
deleted file mode 100644
index 32040a6f8..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-import React from 'react';
-import { IconButton, makeStyles, Theme, Tooltip } from '@material-ui/core';
-import { RowExpander } from 'components/Executions/Tables/RowExpander';
-import { getNodeTemplateName } from 'components/WorkflowGraph/utils';
-import { dNode } from 'models/Graph/types';
-import { PlayCircleOutline } from '@material-ui/icons';
-import { isParentNode } from 'components/Executions/utils';
-import { useNodeExecutionsById } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { isExpanded } from 'models/Node/utils';
-import { NodeExecutionName } from './NodeExecutionName';
-import t from '../strings';
-
-const useStyles = makeStyles((theme: Theme) => ({
- taskNamesList: {
- overflowY: 'scroll',
- flex: 1,
- },
- namesContainer: {
- display: 'flex',
- flexDirection: 'row',
- alignItems: 'flex-start',
- justifyContent: 'left',
- padding: '0 10px',
- height: 56,
- width: 256,
- borderBottom: `1px solid ${theme.palette.divider}`,
- whiteSpace: 'nowrap',
- },
- namesContainerExpander: {
- display: 'flex',
- marginTop: 'auto',
- marginBottom: 'auto',
- },
- namesContainerBody: {
- display: 'flex',
- flexDirection: 'column',
- alignItems: 'flex-start',
- justifyContent: 'center',
- whiteSpace: 'nowrap',
- height: '100%',
- overflow: 'hidden',
- },
- leaf: {
- width: 30,
- },
-}));
-
-interface TaskNamesProps {
- nodes: dNode[];
- onToggle: (id: string, scopeId: string, level: number) => void;
- onAction?: (id: string) => void;
- onScroll?: () => void;
-}
-
-export const TaskNames = React.forwardRef(
- ({ nodes, onScroll, onToggle, onAction }, ref) => {
- const styles = useStyles();
- const { nodeExecutionsById } = useNodeExecutionsById();
-
- const expanderRef = React.useRef();
-
- return (
-
- {nodes.map(node => {
- const nodeLevel = node?.level ?? 0;
- const nodeExecution = nodeExecutionsById[node.scopedId];
-
- return (
-
-
-
- {nodeExecution && isParentNode(nodeExecution) ? (
-
}
- expanded={isExpanded(node)}
- onClick={() =>
- onToggle(node.id, node.scopedId, nodeLevel)
- }
- />
- ) : (
-
- )}
-
-
-
-
-
-
- {onAction && (
-
- onAction(node.id)}
- data-testid={`resume-gate-node-${node.id}`}
- >
-
-
-
- )}
-
- );
- })}
-
- );
- },
-);
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/barOptions.ts b/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/barOptions.ts
deleted file mode 100644
index 01a4cfaff..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/barOptions.ts
+++ /dev/null
@@ -1,162 +0,0 @@
-import {
- Chart as ChartJS,
- // registerables
- ArcElement,
- LineElement,
- BarElement,
- PointElement,
- BarController,
- BubbleController,
- DoughnutController,
- LineController,
- PieController,
- PolarAreaController,
- RadarController,
- ScatterController,
- CategoryScale,
- LinearScale,
- LogarithmicScale,
- RadialLinearScale,
- TimeScale,
- TimeSeriesScale,
- Decimation,
- Filler,
- Legend,
- Title,
- Tooltip,
- SubTitle,
-} from 'chart.js';
-import ChartDataLabels from 'chartjs-plugin-datalabels';
-import { isEqual, isNil } from 'lodash';
-
-const registerables = [
- ArcElement,
- LineElement,
- BarElement,
- PointElement,
- BarController,
- BubbleController,
- DoughnutController,
- LineController,
- PieController,
- PolarAreaController,
- RadarController,
- ScatterController,
- CategoryScale,
- LinearScale,
- LogarithmicScale,
- RadialLinearScale,
- TimeScale,
- TimeSeriesScale,
- Decimation,
- Filler,
- Legend,
- Title,
- Tooltip,
- SubTitle,
-];
-
-// "registerables" helper was failing tests
-registerables.forEach(plugin => ChartJS.register(plugin));
-ChartJS.register(ChartDataLabels);
-
-// Create positioner to put tooltip at cursor position
-Tooltip.positioners.cursor = function (_chartElements, coordinates) {
- return coordinates;
-};
-
-export const getBarOptions = (
- chartTimeIntervalSec: number,
- tooltipLabels: string[][],
- chartRef: React.MutableRefObject,
- tooltip: any,
- setTooltip: any,
-) => {
- return {
- animation: false as const,
- indexAxis: 'y' as const,
- elements: {
- bar: {
- borderWidth: 2,
- },
- },
- responsive: true,
- maintainAspectRatio: false,
- plugins: {
- legend: {
- display: false,
- },
- title: {
- display: false,
- },
- tooltip: {
- // Setting up tooltip: https://www.chartjs.org/docs/latest/configuration/tooltip.html
- enabled: false,
- position: 'cursor',
- filter: function (tooltipItem) {
- // no tooltip for offsets
- return tooltipItem.datasetIndex !== 0;
- },
- callbacks: {
- label: function (context) {
- const index = context.dataIndex;
-
- return tooltipLabels ? [`${tooltipLabels[index]}`] : '';
- },
- labelColor: function () {
- return {
- fontColor: 'white',
- };
- },
- },
- external: context => {
- const tooltipModel = context.tooltip;
-
- if (!chartRef || !chartRef.current) {
- return;
- }
-
- if (tooltipModel.opacity === 0) {
- if (tooltip.opacity !== 0)
- setTooltip(prev => ({ ...prev, opacity: 0 }));
- return;
- }
-
- const position = context.chart.canvas.getBoundingClientRect();
-
- const dataIndex = tooltipModel.dataPoints[0]?.dataIndex;
-
- if (isNil(dataIndex)) {
- return;
- }
-
- const newTooltipData = {
- opacity: 1,
- left: position.left + tooltipModel.caretX,
- top: position.top + tooltipModel.caretY,
- dataIndex: dataIndex,
- };
-
- if (!isEqual(tooltip, newTooltipData)) {
- setTooltip(newTooltipData);
- }
- },
- },
- },
- scales: {
- x: {
- format: Intl.DateTimeFormat,
- position: 'top' as const,
- ticks: {
- display: false,
- autoSkip: false,
- stepSize: chartTimeIntervalSec,
- },
- stacked: true,
- },
- y: {
- stacked: true,
- },
- },
- };
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/chartData.ts b/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/chartData.ts
deleted file mode 100644
index 8963e7848..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/chartData.ts
+++ /dev/null
@@ -1,87 +0,0 @@
-import { timestampToDate } from 'common/utils';
-import { CatalogCacheStatus, NodeExecutionPhase } from 'models/Execution/enums';
-import { dNode } from 'models/Graph/types';
-import { isMapTaskType } from 'models/Task/utils';
-import { BarItemData } from './utils';
-
-const WEEK_DURATION_SEC = 7 * 24 * 3600;
-
-const EMPTY_BAR_ITEM: BarItemData = {
- phase: NodeExecutionPhase.UNDEFINED,
- startOffsetSec: 0,
- durationSec: 0,
- isFromCache: false,
- isMapTaskCache: false,
-};
-
-export const getChartDurationData = (
- nodes: dNode[],
- startedAt: Date,
-): { items: BarItemData[]; totalDurationSec: number } => {
- if (nodes.length === 0) return { items: [], totalDurationSec: 0 };
-
- let totalDurationSec = 0;
- const initialStartTime = startedAt.getTime();
-
- const result: BarItemData[] = nodes.map(({ execution, value }) => {
- if (!execution) {
- return EMPTY_BAR_ITEM;
- }
-
- let phase = execution.closure.phase;
- const isFromCache =
- execution.closure.taskNodeMetadata?.cacheStatus ===
- CatalogCacheStatus.CACHE_HIT;
-
- const isMapTaskCache =
- isMapTaskType(value?.template?.type) &&
- value?.template?.metadata?.cacheSerializable;
-
- // Offset values
- let startOffset = 0;
- const startedAt = execution.closure.startedAt;
- if (isFromCache) {
- if (execution.closure.createdAt) {
- startOffset =
- timestampToDate(execution.closure.createdAt).getTime() -
- initialStartTime;
- }
- } else if (startedAt) {
- startOffset = timestampToDate(startedAt).getTime() - initialStartTime;
- }
-
- // duration
- let durationSec = 0;
- if (isFromCache) {
- const updatedAt = execution.closure.updatedAt?.seconds?.toNumber() ?? 0;
- const createdAt = execution.closure.createdAt?.seconds?.toNumber() ?? 0;
- durationSec = updatedAt - createdAt;
- durationSec = durationSec === 0 ? 2 : durationSec;
- } else if (phase === NodeExecutionPhase.RUNNING) {
- if (startedAt) {
- const duration = Date.now() - timestampToDate(startedAt).getTime();
- durationSec = duration / 1000;
- if (durationSec > WEEK_DURATION_SEC) {
- // TODO: https://github.com/flyteorg/flyteconsole/issues/332
- // In some cases tasks which were needed to be ABORTED are stuck in running state,
- // In case if task is still running after a week - we assume it should have been aborted.
- // The proper fix should be covered by isue: flyteconsole#332
- phase = NodeExecutionPhase.ABORTED;
- const allegedDurationSec = Math.trunc(
- totalDurationSec - startOffset / 1000,
- );
- durationSec = allegedDurationSec > 0 ? allegedDurationSec : 10;
- }
- }
- } else {
- durationSec = execution.closure.duration?.seconds?.toNumber() ?? 0;
- }
-
- const startOffsetSec = Math.trunc(startOffset / 1000);
- totalDurationSec = Math.max(totalDurationSec, startOffsetSec + durationSec);
- return { phase, startOffsetSec, durationSec, isFromCache, isMapTaskCache };
- });
-
- // Do we want to get initialStartTime from different place, to avoid recalculating it.
- return { items: result, totalDurationSec };
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/index.tsx b/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/index.tsx
deleted file mode 100644
index 23b747a85..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/index.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import * as React from 'react';
-import { Bar } from 'react-chartjs-2';
-import { dNode } from 'models/Graph/types';
-import { Box, Theme, makeStyles } from '@material-ui/core';
-
-import { NodeExecutionPhase } from 'models';
-import { getNodeExecutionPhaseConstants } from 'components/Executions/utils';
-import {
- BarItemData,
- formatSecondsToHmsFormat,
- generateChartData,
- getChartData,
- getDuration,
- parseSpanData,
-} from './utils';
-import { getBarOptions } from './barOptions';
-
-interface TimelineChartProps {
- items: BarItemData[];
- nodes: dNode[];
- chartTimeIntervalSec: number;
- operationIds: string[];
- parsedExecutionMetricsData: ReturnType;
-}
-
-interface StyleProps {
- opacity: number;
- top: number;
- left: number;
- phaseColor: string;
-}
-
-const useStyles = makeStyles(theme => ({
- tooltipContainer: {
- position: 'absolute',
- background: theme.palette.grey[100],
- color: theme.palette.common.black,
- padding: theme.spacing(2),
- borderRadius: 8,
- width: 'fit-content',
- maxContent: 'fit-content',
- top: ({ top }) => top + 10,
- left: ({ left }) => left + 10,
- display: ({ opacity }) => (opacity ? 'block' : 'none'),
- },
- phaseText: {
- width: 'fit-content',
- marginBlockEnd: theme.spacing(1),
- },
- tooltipText: {
- minWidth: '50px',
- },
- tooltipTextContainer: {
- display: 'flex',
- gap: 1,
- color: theme.palette.grey[700],
- },
- operationIdContainer: {
- textAlign: 'left',
- flex: 1,
- },
-}));
-
-export const TimelineChart = (props: TimelineChartProps) => {
- const [tooltip, setTooltip] = React.useState({
- opacity: 0,
- top: 0,
- left: 0,
- dataIndex: -1,
- });
- const chartRef = React.useRef(null);
- const phaseData = generateChartData(props.items);
-
- const options = getBarOptions(
- props.chartTimeIntervalSec,
- phaseData.tooltipLabel,
- chartRef,
- tooltip,
- setTooltip,
- ) as any;
-
- const data = getChartData(phaseData);
- const node = props.nodes[tooltip.dataIndex];
- const phase = node?.execution?.closure.phase ?? NodeExecutionPhase.UNDEFINED;
- const phaseConstant = getNodeExecutionPhaseConstants(phase);
- const spans = (node && props.parsedExecutionMetricsData[node.scopedId]) || [];
-
- const styles = useStyles({
- opacity: tooltip.opacity,
- top: tooltip.top,
- left: tooltip.left,
- phaseColor: phaseConstant.badgeColor,
- });
-
- return (
- <>
-
-
- {phase && {phaseConstant.text}}
- {spans?.map(span => (
-
-
- {formatSecondsToHmsFormat(
- Math.round(
- (getDuration(span.startTime, span.endTime) / 1000) * 100,
- ) / 100,
- )}
-
-
- {span.operationId}
-
-
- ))}
-
- >
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/utils.ts b/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/utils.ts
deleted file mode 100644
index ed745fbd1..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/TimelineChart/utils.ts
+++ /dev/null
@@ -1,238 +0,0 @@
-import { getNodeExecutionPhaseConstants } from 'components/Executions/utils';
-import { primaryTextColor } from 'components/Theme/constants';
-import { NodeExecutionPhase } from 'models/Execution/enums';
-import t from 'components/Executions/strings';
-import { Admin, Core, Protobuf } from '@flyteorg/flyteidl-types';
-import { get, uniq } from 'lodash';
-import { timestampToDate } from 'common';
-import traverse from 'traverse';
-
-export const CASHED_GREEN = 'rgba(74,227,174,0.25)'; // statusColors.SUCCESS (Mint20) with 25% opacity
-export const TRANSPARENT = 'rgba(0, 0, 0, 0)';
-
-export interface BarItemData {
- phase: NodeExecutionPhase;
- startOffsetSec: number;
- durationSec: number;
- isFromCache: boolean;
- isMapTaskCache: boolean;
-}
-
-interface ChartDataInput {
- elementsNumber: number;
- durations: number[];
- startOffset: number[];
- offsetColor: string[];
- tooltipLabel: string[][];
- barLabel: string[];
- barColor: string[];
-}
-
-/**
- * Recursively traverses span data and returns a map of nodeId/taskId to span data.
- * Example return:
- * {
- * "n0": [span, span, span],
- * "n1": [span, span]
- * }
- */
-export const parseSpanData = (
- data: Admin.WorkflowExecutionGetMetricsResponse,
-) => {
- const results: Record = {};
- const workflowSpans = data?.span ?? {};
-
- const traverseSpanData = (rootSpan: Core.Span) => {
- const spanNodeId =
- rootSpan.nodeId?.nodeId ||
- rootSpan.taskId?.nodeExecutionId?.nodeId ||
- rootSpan.workflowId?.name ||
- '';
-
- if (!results[spanNodeId]) {
- results[spanNodeId] = [];
- }
-
- if (rootSpan.spans?.length > 0) {
- rootSpan.spans.forEach(span => {
- /* Recurse if taskId/nodeId; else add to record */
- if (span.nodeId?.nodeId || span.taskId?.nodeExecutionId?.nodeId) {
- traverseSpanData(span as Core.Span);
- } else {
- results[spanNodeId].push(span);
- }
- });
- }
- };
- traverseSpanData(workflowSpans as Core.Span);
- return results;
-};
-
-export const getOperationsFromWorkflowExecutionMetrics = (
- data: Admin.WorkflowExecutionGetMetricsResponse,
-): string[] => {
- const operationIds = uniq(
- traverse(data)
- .paths()
- .filter(path => path[path.length - 1] === 'operationId')
- .map(path => get(data, path)),
- );
-
- return operationIds;
-};
-/**
- * Depending on amounf of second provided shows data in
- * XhXmXs or XmXs or Xs format
- */
-export const formatSecondsToHmsFormat = (seconds: number) => {
- const hours = Math.floor(seconds / 3600);
- seconds %= 3600;
- const minutes = Math.floor(seconds / 60);
- seconds = seconds % 60;
- /**
- * Note:
- * if we're showing hours or minutes, round seconds
- * if we're (only) showing seconds, round to nearest 1/100
- */
- if (hours > 0) {
- return `${hours}h ${minutes}m ${Math.round(seconds)}s`;
- } else if (minutes > 0) {
- return `${minutes}m ${Math.round(seconds)}s`;
- } else {
- seconds = Math.round(seconds * 100) / 100;
- return `${seconds}s`;
- }
-};
-
-export const getDurationString = (element: BarItemData): string => {
- return formatSecondsToHmsFormat(element.durationSec);
-};
-
-/**
- * Generates chart data maps per each BarItemData ("node") section
- */
-export const generateChartData = (data: BarItemData[]): ChartDataInput => {
- const durations: number[] = [];
- const startOffset: number[] = [];
- const offsetColor: string[] = [];
- const tooltipLabel: string[][] = [];
- const barLabel: string[] = [];
- const barColor: string[] = [];
-
- data.forEach(element => {
- const phaseConstant = getNodeExecutionPhaseConstants(
- element.phase ?? NodeExecutionPhase.UNDEFINED,
- );
-
- const durationString = formatSecondsToHmsFormat(element.durationSec);
- const tooltipString = `${phaseConstant.text}: ${durationString}`;
- // don't show Label if there is now duration yet.
- const labelString = element.durationSec > 0 ? durationString : '';
-
- const generateTooltipLabelText = (element: BarItemData): string[] => {
- if (element.isMapTaskCache) return [tooltipString, t('mapCacheMessage')];
- if (element.isFromCache) return [tooltipString, t('readFromCache')];
-
- return [tooltipString];
- };
-
- const generateBarLabelText = (element: BarItemData): string => {
- if (element.isMapTaskCache) return '\u229A ' + t('mapCacheMessage');
- if (element.isFromCache) return '\u229A ' + t('fromCache');
- return labelString;
- };
-
- durations.push(element.durationSec);
- startOffset.push(element.startOffsetSec);
- offsetColor.push(element.isFromCache ? CASHED_GREEN : TRANSPARENT);
- tooltipLabel.push(generateTooltipLabelText(element));
- barLabel.push(generateBarLabelText(element));
- barColor.push(phaseConstant.badgeColor);
- });
-
- return {
- elementsNumber: data.length,
- durations,
- startOffset,
- offsetColor,
- tooltipLabel,
- barLabel,
- barColor,
- };
-};
-
-export const getDuration = (
- startTime: Protobuf.ITimestamp,
- endTime?: Protobuf.ITimestamp,
-) => {
- const endTimeInMS = endTime ? timestampToDate(endTime).getTime() : Date.now();
- const duration = endTimeInMS - timestampToDate(startTime).getTime();
- return duration;
-};
-
-export const getExecutionMetricsOperationIds = (
- data: Admin.WorkflowExecutionGetMetricsResponse,
-): string[] => {
- const operationIds = uniq(
- traverse(data)
- .paths()
- .filter(path => path[path.length - 1] === 'operationId')
- .map(path => get(data, path)),
- );
-
- return operationIds;
-};
-
-/**
- * Generates chart data format suitable for Chart.js Bar. Each bar consists of two data items:
- * |-----------|XXXXXXXXXXXXXXXX|
- * |-|XXXXXX|
- * |------|XXXXXXXXXXXXX|
- * Where |---| is offset - usually transparent part to give user a feeling that timeline wasn't started from ZERO time position
- * Where |XXX| is duration of the operation, colored per step Phase status.
- */
-export const getChartData = (data: ChartDataInput) => {
- const defaultStyle = {
- barPercentage: 1,
- borderWidth: 0,
- };
-
- return {
- labels: Array(data.elementsNumber).fill(''), // clear up Chart Bar default labels
- datasets: [
- // fill-in offsets
- {
- ...defaultStyle,
- data: data.startOffset,
- backgroundColor: data.offsetColor,
- datalabels: {
- labels: {
- title: null,
- },
- },
- },
- // fill in duration bars
- {
- ...defaultStyle,
- data: data.durations,
- backgroundColor: data.barColor,
- borderColor: 'rgba(0, 0, 0, 0.55)',
- borderWidth: {
- top: 0,
- left: 0,
- right: 1,
- bottom: 0,
- },
- datalabels: {
- // Positioning info - https://chartjs-plugin-datalabels.netlify.app/guide/positioning.html
- color: primaryTextColor,
- align: 'end' as const, // related to text
- anchor: 'start' as const, // related to bar
- formatter: function (value, context) {
- return data.barLabel[context.dataIndex] ?? '';
- },
- },
- },
- ],
- };
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/helpers.ts b/packages/console/src/components/Executions/ExecutionDetails/Timeline/helpers.ts
deleted file mode 100644
index 53bbb6c97..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/helpers.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { ignoredNodeIds } from 'models/Node/constants';
-import { dNode } from 'models/Graph/types';
-import { isExpanded } from 'models/Node/utils';
-
-export const TimeZone = {
- Local: 'local',
- UTC: 'utc',
-};
-
-export function isTransitionNode(node: dNode) {
- // In case of branchNode childs, start and end nodes could be present as 'n0-start-node' etc.
- return ignoredNodeIds.includes(node?.id);
-}
-
-export function convertToPlainNodes(nodes: dNode[], level = 0): dNode[] {
- const result: dNode[] = [];
- if (!nodes || nodes.length === 0) {
- return result;
- }
- nodes?.forEach(node => {
- if (isTransitionNode(node)) {
- return;
- }
- result.push({ ...node, level });
- if (node?.nodes?.length > 0 && isExpanded(node)) {
- result.push(...convertToPlainNodes(node.nodes, level + 1));
- }
- });
- return result;
-}
diff --git a/packages/console/src/components/Executions/ExecutionDetails/Timeline/scaleContext.tsx b/packages/console/src/components/Executions/ExecutionDetails/Timeline/scaleContext.tsx
deleted file mode 100644
index 6389e40bc..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/Timeline/scaleContext.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-import { Mark } from '@material-ui/core/Slider';
-import { log } from 'common/log';
-import * as React from 'react';
-import { createContext, useContext } from 'react';
-import { formatSecondsToHmsFormat } from './TimelineChart/utils';
-
-const MIN_SCALE_VALUE = 60; // 1 min
-const MAX_SCALE_VALUE = 3600; // 1h
-
-const DEFAULT_MAX = MIN_SCALE_VALUE;
-const DEFAULT_SCALE_FACTOR = 1;
-
-const percentage = [0.02, 0.1, 0.4, 0.6, 0.8, 1];
-const maxScaleValue = percentage.length - 1;
-
-interface TimelineScaleState {
- scaleFactor: number;
- chartInterval: number; // value in seconds for one tic of an interval
- marks: Mark[];
- setMaxValue: (newMax: number) => void;
- setScaleFactor: (newScale: number) => void;
-}
-
-/** Use this Context to redefine Provider returns in storybooks */
-export const ScaleContext = createContext({
- /** Default values used if ContextProvider wasn't initialized. */
- scaleFactor: DEFAULT_SCALE_FACTOR,
- chartInterval: 20,
- marks: [],
- setMaxValue: () => {
- log.error('ERROR: No ScaleContextProvider was found in parent components.');
- },
- setScaleFactor: () => {
- log.error('ERROR: No ScaleContextProvider was found in parent components.');
- },
-});
-
-/** Could be used to access the whole TimelineScaleState */
-export const useScaleContext = (): TimelineScaleState =>
- useContext(ScaleContext);
-
-interface ScaleProviderProps {
- children?: React.ReactNode;
-}
-
-/** Should wrap "top level" component in Execution view, will build a nodeExecutions tree for specific workflow */
-export const ScaleProvider = (props: ScaleProviderProps) => {
- const [maxValue, setMaxValue] = React.useState(DEFAULT_MAX);
- const [scaleFactor, setScaleFactor] =
- React.useState(DEFAULT_SCALE_FACTOR);
- const [marks, setMarks] = React.useState([]);
- const [chartInterval, setChartInterval] = React.useState(1);
-
- React.useEffect(() => {
- const getIntervalValue = (scale: number): number => {
- const intervalValue = Math.ceil(maxValue * percentage[scale]);
- return intervalValue > 0 ? intervalValue : 1;
- };
-
- setChartInterval(getIntervalValue(scaleFactor));
-
- const newMarks: Mark[] = [];
- for (let i = 0; i < percentage.length; ++i) {
- newMarks.push({
- value: i,
- label: formatSecondsToHmsFormat(getIntervalValue(i)),
- });
- }
- setMarks(newMarks);
- }, [maxValue, scaleFactor]);
-
- const setMaxTimeValue = (newMax: number) => {
- // use min and max caps
- let newValue =
- newMax < MIN_SCALE_VALUE
- ? MIN_SCALE_VALUE
- : newMax > MAX_SCALE_VALUE
- ? MAX_SCALE_VALUE
- : newMax;
- // round a value to have full amount of minutes:
- newValue = Math.ceil(newValue / 60) * 60;
- setMaxValue(newValue);
- };
-
- const setNewScaleFactor = (newScale: number) => {
- const applyScale =
- newScale < 0 ? 0 : newScale > maxScaleValue ? maxScaleValue : newScale;
- setScaleFactor(applyScale);
- };
-
- return (
-
- {props.children}
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/constants.ts b/packages/console/src/components/Executions/ExecutionDetails/constants.ts
deleted file mode 100644
index 67238889d..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/constants.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-export enum ExecutionMetadataLabels {
- cluster = 'Cluster',
- domain = 'Domain',
- duration = 'Duration',
- time = 'Time',
- relatedTo = 'Related to',
- version = 'Version',
- serviceAccount = 'Service Account',
- iam = 'IAM Role',
- rawOutputPrefix = 'Raw Output Prefix',
- parallelism = 'Parallelism',
- securityContextDefault = 'default',
- interruptible = 'Interruptible override',
- overwriteCache = 'Overwrite cached outputs',
-}
-
-export const tabs = {
- nodes: {
- id: 'nodes',
- label: 'Nodes',
- },
- graph: {
- id: 'graph',
- label: 'Graph',
- },
- timeline: {
- id: 'timeline',
- label: 'Timeline',
- },
-};
-
-export const executionActionStrings = {
- clone: 'Clone Execution',
-};
-
-export const backLinkTitle = 'Back to parent';
diff --git a/packages/console/src/components/Executions/ExecutionDetails/index.ts b/packages/console/src/components/Executions/ExecutionDetails/index.ts
deleted file mode 100644
index 27be36577..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './ExecutionDetails';
-export * from './ExecutionDetailsActions';
-export * from './useExecutionNodeViewsState';
-export * from './ExecutionNodeDeck';
diff --git a/packages/console/src/components/Executions/ExecutionDetails/strings.tsx b/packages/console/src/components/Executions/ExecutionDetails/strings.tsx
deleted file mode 100644
index 5e8409bfc..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/strings.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import { createLocalizedString } from '@flyteorg/locale';
-
-const str = {
- rerun: 'Rerun Task',
- flyteDeck: 'Flyte Deck',
- resume: 'Resume',
- taskNameColumnHeader: 'Task Name',
-};
-
-export { patternKey } from '@flyteorg/locale';
-export default createLocalizedString(str);
diff --git a/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionDetailsAppBarContent.test.tsx b/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionDetailsAppBarContent.test.tsx
deleted file mode 100644
index 1e002f615..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionDetailsAppBarContent.test.tsx
+++ /dev/null
@@ -1,97 +0,0 @@
-import {
- fireEvent,
- render,
- RenderResult,
- waitFor,
-} from '@testing-library/react';
-import { labels as commonLabels } from 'components/common/constants';
-import {
- ExecutionContext,
- ExecutionContextData,
-} from 'components/Executions/contexts';
-import { Identifier } from 'models/Common/types';
-import { WorkflowExecutionPhase } from 'models/Execution/enums';
-import { Execution } from 'models/Execution/types';
-import { createMockExecution } from 'models/__mocks__/executionsData';
-import * as React from 'react';
-import { MemoryRouter } from 'react-router';
-import { QueryClient, QueryClientProvider } from 'react-query';
-import { createTestQueryClient } from 'test/utils';
-import { executionActionStrings } from '../constants';
-import { ExecutionDetailsAppBarContentInner } from '../ExecutionDetailsAppBarContent';
-
-jest.mock('components/Navigation/SubNavBarContent', () => ({
- SubNavBarContent: ({ children }: React.Props) => children,
-}));
-
-describe('ExecutionDetailsAppBarContent', () => {
- let execution: Execution;
- let executionContext: ExecutionContextData;
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- let sourceId: Identifier;
- let queryClient: QueryClient;
-
- beforeEach(() => {
- execution = createMockExecution();
- sourceId = execution.closure.workflowId;
-
- executionContext = {
- execution,
- };
-
- queryClient = createTestQueryClient();
- });
-
- const renderContent = () =>
- render(
-
-
-
-
-
-
- ,
- );
-
- describe('for running executions', () => {
- beforeEach(() => {
- execution.closure.phase = WorkflowExecutionPhase.RUNNING;
- });
-
- it('renders an overflow menu', async () => {
- const { getByLabelText } = renderContent();
- expect(getByLabelText(commonLabels.moreOptionsButton)).toBeTruthy();
- });
-
- describe('in overflow menu', () => {
- let renderResult: RenderResult;
- let buttonEl: HTMLElement;
-
- beforeEach(async () => {
- renderResult = renderContent();
- const { getByLabelText } = renderResult;
- buttonEl = await waitFor(() =>
- getByLabelText(commonLabels.moreOptionsButton),
- );
- await fireEvent.click(buttonEl);
- await waitFor(() => getByLabelText(commonLabels.moreOptionsMenu));
- });
-
- it('renders a clone option', () => {
- const { getByText } = renderResult;
- expect(getByText(executionActionStrings.clone)).toBeInTheDocument();
- });
- });
- });
-
- describe('for terminal executions', () => {
- beforeEach(() => {
- execution.closure.phase = WorkflowExecutionPhase.SUCCEEDED;
- });
-
- it('does not render an overflow menu', async () => {
- const { queryByLabelText } = renderContent();
- expect(queryByLabelText(commonLabels.moreOptionsButton)).toBeNull();
- });
- });
-});
diff --git a/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionNodeViews.test.tsx b/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionNodeViews.test.tsx
deleted file mode 100644
index 9502e0702..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionNodeViews.test.tsx
+++ /dev/null
@@ -1,227 +0,0 @@
-import * as React from 'react';
-import { fireEvent, render, waitFor, screen } from '@testing-library/react';
-import { filterLabels } from 'components/Executions/filters/constants';
-import { nodeExecutionStatusFilters } from 'components/Executions/filters/statusFilters';
-import { oneFailedTaskWorkflow } from 'mocks/data/fixtures/oneFailedTaskWorkflow';
-import { insertFixture } from 'mocks/data/insertFixture';
-import { mockServer } from 'mocks/server';
-import { Execution, NodeExecution } from 'models/Execution/types';
-import { QueryClient, QueryClientProvider } from 'react-query';
-import { createTestQueryClient } from 'test/utils';
-import { ExecutionContext } from 'components/Executions/contexts';
-import { listNodeExecutions, listTaskExecutions } from 'models/Execution/api';
-import { NodeExecutionPhase } from 'models';
-import { mockWorkflowId } from 'mocks/data/fixtures/types';
-import {
- NodeExecutionDetailsContext,
- WorkflowNodeExecutionsProvider,
-} from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { transformerWorkflowToDag } from 'components/WorkflowGraph/transformerWorkflowToDag';
-import { ExecutionNodeViews } from '../ExecutionNodeViews';
-import { tabs } from '../constants';
-
-jest.mock('components/Executions/Tables/NodeExecutionRow', () => ({
- NodeExecutionRow: jest.fn(({ node }) => (
-
- {node?.execution?.id?.nodeId}
-
- )),
-}));
-
-jest.mock(
- 'components/Executions/ExecutionDetails/Timeline/ExecutionTimelineFooter',
- () => ({
- ExecutionTimelineFooter: jest.fn(() => ),
- }),
-);
-
-jest.mock(
- 'components/Executions/ExecutionDetails/Timeline/TimelineChart/index',
- () => ({
- TimelineChart: jest.fn(() => ),
- }),
-);
-
-jest.mock(
- 'components/Executions/ExecutionDetails/Timeline/NodeExecutionName',
- () => ({
- NodeExecutionName: jest.fn(({ name }) => {name}
),
- }),
-);
-jest.mock('models/Execution/api', () => ({
- listNodeExecutions: jest.fn(),
- listTaskExecutions: jest.fn(),
-}));
-
-jest.mock('components/WorkflowGraph/transformerWorkflowToDag', () => ({
- transformerWorkflowToDag: jest.fn(),
-}));
-
-describe('ExecutionNodeViews', () => {
- let queryClient: QueryClient;
- let execution: Execution;
- let fixture: ReturnType;
- let nodeExecutionsArray: NodeExecution[];
- beforeEach(() => {
- fixture = oneFailedTaskWorkflow.generate();
- execution = fixture.workflowExecutions.top.data;
- insertFixture(mockServer, fixture);
- const nodeExecutions = fixture.workflowExecutions.top.nodeExecutions;
- nodeExecutionsArray = Object.values(nodeExecutions).map(({ data }) => data);
-
- transformerWorkflowToDag.mockImplementation(_ => {
- const nodes = nodeExecutionsArray.map(n => ({
- id: n.id.nodeId,
- scopedId: n.scopedId,
- execution: n,
- // type: dTypes.gateNode,
- name: n.id.nodeId,
- type: 3,
- nodes: [],
- edges: [],
- }));
- return {
- dag: {
- id: 'start-node',
- scopedId: 'start-node',
- value: {
- id: 'start-node',
- },
- type: 4,
- name: 'start',
- nodes: [
- {
- id: 'start-node',
- scopedId: 'start-node',
- value: {
- inputs: [],
- upstreamNodeIds: [],
- outputAliases: [],
- id: 'start-node',
- },
- type: 4,
- name: 'start',
- nodes: [],
- edges: [],
- },
- {
- id: 'end-node',
- scopedId: 'end-node',
- value: {
- inputs: [],
- upstreamNodeIds: [],
- outputAliases: [],
- id: 'end-node',
- },
- type: 5,
- name: 'end',
- nodes: [],
- edges: [],
- },
- ...nodes,
- ],
- },
- staticExecutionIdsMap: {},
- };
- });
- listNodeExecutions.mockImplementation((_, filters) => {
- let finalNodes = nodeExecutionsArray;
- if (filters?.filter?.length) {
- const phases = filters?.filter
- ?.filter(f => f.key === 'phase')?.[0]
- .value?.map(f => NodeExecutionPhase[f]);
- finalNodes = finalNodes.filter(n => {
- return phases.includes(n.closure.phase);
- });
- }
- return Promise.resolve({
- entities: Object.values(finalNodes),
- });
- });
- listTaskExecutions.mockImplementation(() => {
- return Promise.resolve({
- entities: [],
- });
- });
- queryClient = createTestQueryClient();
- });
-
- const renderViews = () =>
- render(
-
-
-
-
-
-
-
-
- ,
- );
-
- it('maintains filter when switching back to nodes tab', async () => {
- const { nodeExecutions } = fixture.workflowExecutions.top;
- const failedNodeName = nodeExecutions.failedNode.data.id.nodeId;
- const succeededNodeName = nodeExecutions.pythonNode.data.id.nodeId;
-
- const { getByText, queryByText, queryAllByTestId } = renderViews();
-
- await waitFor(() => getByText(tabs.nodes.label));
-
- const nodesTab = getByText(tabs.nodes.label);
- const timelineTab = getByText(tabs.timeline.label);
-
- // Ensure we are on Nodes tab
- await fireEvent.click(nodesTab);
- await waitFor(() => {
- const nodes = queryAllByTestId('node-execution-row');
- return nodes?.length === 2;
- });
-
- await waitFor(() => queryByText(succeededNodeName));
-
- const statusButton = await waitFor(() => getByText(filterLabels.status));
-
- // Apply 'Failed' filter and wait for list to include only the failed item
- await fireEvent.click(statusButton);
-
- const failedFilter = await waitFor(() =>
- screen.getByLabelText(nodeExecutionStatusFilters.failed.label),
- );
-
- // Wait for succeeded task to disappear and ensure failed task remains
- await fireEvent.click(failedFilter);
-
- await waitFor(() => {
- const nodes = queryAllByTestId('node-execution-row');
- return nodes?.length === 1;
- });
-
- expect(queryByText(succeededNodeName)).not.toBeInTheDocument();
-
- expect(queryByText(failedNodeName)).toBeInTheDocument();
-
- // Switch to the Graph tab
- await fireEvent.click(statusButton);
- await fireEvent.click(timelineTab);
- await waitFor(() => queryByText(succeededNodeName));
-
- // expect all initital nodes to be rendered
- expect(queryByText(succeededNodeName)).toBeInTheDocument();
- expect(queryByText(failedNodeName)).toBeInTheDocument();
-
- // Switch back to Nodes Tab and verify filter still applied
- await fireEvent.click(nodesTab);
- await waitFor(() => queryByText(failedNodeName));
- expect(queryByText(succeededNodeName)).not.toBeInTheDocument();
- expect(queryByText(failedNodeName)).toBeInTheDocument();
- });
-});
diff --git a/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionTabContent.test.tsx b/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionTabContent.test.tsx
deleted file mode 100644
index 63ddae854..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionTabContent.test.tsx
+++ /dev/null
@@ -1,104 +0,0 @@
-import { render, waitFor } from '@testing-library/react';
-import { NodeExecutionDetailsContextProvider } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { WorkflowNodeExecutionsContext } from 'components/Executions/contexts';
-import { basicPythonWorkflow } from 'mocks/data/fixtures/basicPythonWorkflow';
-import { mockWorkflowId } from 'mocks/data/fixtures/types';
-import { insertFixture } from 'mocks/data/insertFixture';
-import { mockServer } from 'mocks/server';
-import * as React from 'react';
-import { QueryClient, QueryClientProvider } from 'react-query';
-import { createTestQueryClient } from 'test/utils';
-import { ExecutionTab } from '../ExecutionTab';
-import { tabs } from '../constants';
-
-jest.mock('components/Workflow/workflowQueries');
-const { fetchWorkflow } = require('components/Workflow/workflowQueries');
-
-jest.mock('components/common/DetailsPanel', () => ({
- DetailsPanel: jest.fn(({ children }) => (
- {children}
- )),
-}));
-
-jest.mock('components/Executions/Tables/NodeExecutionsTable', () => ({
- NodeExecutionsTable: jest.fn(({ children }) => (
- {children}
- )),
-}));
-jest.mock(
- 'components/Executions/ExecutionDetails/Timeline/ExecutionTimeline',
- () => ({
- ExecutionTimeline: jest.fn(({ children }) => (
- {children}
- )),
- }),
-);
-jest.mock(
- 'components/Executions/ExecutionDetails/Timeline/ExecutionTimelineFooter',
- () => ({
- ExecutionTimelineFooter: jest.fn(({ children }) => (
- {children}
- )),
- }),
-);
-jest.mock('components/WorkflowGraph/WorkflowGraph', () => ({
- WorkflowGraph: jest.fn(({ children }) => (
- {children}
- )),
-}));
-
-describe('Executions > ExecutionDetails > ExecutionTabContent', () => {
- let queryClient: QueryClient;
- let fixture: ReturnType;
-
- beforeEach(() => {
- queryClient = createTestQueryClient();
- fixture = basicPythonWorkflow.generate();
- insertFixture(mockServer, fixture);
- fetchWorkflow.mockImplementation(() =>
- Promise.resolve(fixture.workflows.top),
- );
- });
-
- const renderTabContent = ({ tabType, nodeExecutionsById }) => {
- return render(
-
-
-
-
-
-
- ,
- );
- };
-
- it('renders NodeExecutionsTable when the Nodes tab is selected', async () => {
- const { queryByTestId } = renderTabContent({
- tabType: tabs.nodes.id,
- nodeExecutionsById: {},
- });
-
- await waitFor(() => queryByTestId('node-executions-table'));
- expect(queryByTestId('node-executions-table')).toBeInTheDocument();
- });
-
- it('renders WorkflowGraph when the Graph tab is selected', async () => {
- const { queryByTestId } = renderTabContent({
- tabType: tabs.graph.id,
- nodeExecutionsById: {},
- });
-
- await waitFor(() => queryByTestId('workflow-graph'));
- expect(queryByTestId('workflow-graph')).toBeInTheDocument();
- });
-
- it('renders ExecutionTimeline when the Timeline tab is selected', async () => {
- const { queryByTestId } = renderTabContent({
- tabType: tabs.timeline.id,
- nodeExecutionsById: {},
- });
-
- await waitFor(() => queryByTestId('execution-timeline'));
- expect(queryByTestId('execution-timeline')).toBeInTheDocument();
- });
-});
diff --git a/packages/console/src/components/Executions/ExecutionDetails/test/NodeExecutionDetailsPanelContent.test.tsx b/packages/console/src/components/Executions/ExecutionDetails/test/NodeExecutionDetailsPanelContent.test.tsx
deleted file mode 100644
index 4d41a184b..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/test/NodeExecutionDetailsPanelContent.test.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-import * as React from 'react';
-import { render, waitFor } from '@testing-library/react';
-import { NodeExecutionDetailsContextProvider } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { basicPythonWorkflow } from 'mocks/data/fixtures/basicPythonWorkflow';
-import { mockWorkflowId } from 'mocks/data/fixtures/types';
-import { insertFixture } from 'mocks/data/insertFixture';
-import { mockServer } from 'mocks/server';
-import { TaskExecutionPhase } from 'models/Execution/enums';
-import { NodeExecution } from 'models/Execution/types';
-import { QueryClient, QueryClientProvider } from 'react-query';
-import { MemoryRouter } from 'react-router';
-import { createTestQueryClient } from 'test/utils';
-import { NodeExecutionDetailsPanelContent } from '../NodeExecutionDetailsPanelContent';
-
-jest.mock(
- 'components/Executions/ExecutionDetails/ExecutionDetailsActions',
- () => ({
- ExecutionDetailsActions: jest.fn(() => (
-
- )),
- }),
-);
-jest.mock('components/Workflow/workflowQueries');
-const { fetchWorkflow } = require('components/Workflow/workflowQueries');
-
-describe('NodeExecutionDetailsPanelContent', () => {
- let fixture: ReturnType;
- let execution: NodeExecution;
- let queryClient: QueryClient;
-
- beforeEach(() => {
- fixture = basicPythonWorkflow.generate();
- execution = fixture.workflowExecutions.top.nodeExecutions.pythonNode.data;
- insertFixture(mockServer, fixture);
- fetchWorkflow.mockImplementation(() =>
- Promise.resolve(fixture.workflows.top),
- );
- queryClient = createTestQueryClient();
- });
-
- const renderComponent = () =>
- render(
-
-
-
-
-
-
- ,
- );
-
- it('renders name for task nodes', async () => {
- const { name } = fixture.tasks.python.id;
- const { getByText } = renderComponent();
- await waitFor(() => expect(getByText(name)).toBeInTheDocument());
- });
-});
diff --git a/packages/console/src/components/Executions/ExecutionDetails/test/NodeExecutionName.test.tsx b/packages/console/src/components/Executions/ExecutionDetails/test/NodeExecutionName.test.tsx
deleted file mode 100644
index 7165a89c9..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/test/NodeExecutionName.test.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import * as React from 'react';
-import { render, waitFor } from '@testing-library/react';
-import { NodeExecutionDetailsContextProvider } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { mockWorkflowId } from 'mocks/data/fixtures/types';
-import { QueryClient, QueryClientProvider } from 'react-query';
-import { createTestQueryClient } from 'test/utils';
-import { insertFixture } from 'mocks/data/insertFixture';
-import { mockServer } from 'mocks/server';
-import { basicPythonWorkflow } from 'mocks/data/fixtures/basicPythonWorkflow';
-import { NodeExecution } from 'models/Execution/types';
-import { NodeExecutionName } from '../Timeline/NodeExecutionName';
-
-jest.mock('components/Workflow/workflowQueries');
-const { fetchWorkflow } = require('components/Workflow/workflowQueries');
-
-const name = 'Test';
-const templateName = 'TemplateTest';
-
-describe('Executions > ExecutionDetails > NodeExecutionName', () => {
- let queryClient: QueryClient;
- let fixture: ReturnType;
- let execution: NodeExecution;
-
- beforeEach(() => {
- fixture = basicPythonWorkflow.generate();
- execution = fixture.workflowExecutions.top.nodeExecutions.pythonNode.data;
- queryClient = createTestQueryClient();
- insertFixture(mockServer, fixture);
- fetchWorkflow.mockImplementation(() =>
- Promise.resolve(fixture.workflows.top),
- );
- });
-
- const renderComponent = props =>
- render(
-
-
-
-
- ,
- );
-
- it('should only display title if execution is not provided', async () => {
- const { queryByText } = renderComponent({ name, templateName });
- await waitFor(() => queryByText(name));
-
- expect(queryByText(name)).toBeInTheDocument();
- expect(queryByText(templateName)).not.toBeInTheDocument();
- });
-
- it('should only display title if template name is not provided', async () => {
- const resultName = 'PythonTask';
- const { queryByText } = renderComponent({ name, execution });
- await waitFor(() => queryByText(resultName));
-
- expect(queryByText(resultName)).toBeInTheDocument();
- expect(queryByText(templateName)).not.toBeInTheDocument();
- });
-
- it('should display title and subtitle if template name is provided', async () => {
- const resultName = 'PythonTask';
- const { queryByText } = renderComponent({ name, templateName, execution });
- await waitFor(() => queryByText(resultName));
-
- expect(queryByText(resultName)).toBeInTheDocument();
- expect(queryByText(templateName)).toBeInTheDocument();
- });
-});
diff --git a/packages/console/src/components/Executions/ExecutionDetails/test/TaskNames.test.tsx b/packages/console/src/components/Executions/ExecutionDetails/test/TaskNames.test.tsx
deleted file mode 100644
index 0bc2d0f16..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/test/TaskNames.test.tsx
+++ /dev/null
@@ -1,135 +0,0 @@
-import * as React from 'react';
-import { render } from '@testing-library/react';
-import { dTypes } from 'models/Graph/types';
-import { NodeExecutionDetailsContextProvider } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { QueryClient, QueryClientProvider } from 'react-query';
-import { WorkflowNodeExecutionsContext } from 'components/Executions/contexts';
-import { mockWorkflowId } from 'mocks/data/fixtures/types';
-import { createTestQueryClient } from 'test/utils';
-import { dateToTimestamp } from 'common/utils';
-import { TaskNames } from '../Timeline/TaskNames';
-
-const onToggle = jest.fn();
-const onAction = jest.fn();
-
-jest.mock('models/Workflow/api', () => {
- const originalModule = jest.requireActual('models/Workflow/api');
- return {
- __esModule: true,
- ...originalModule,
- getWorkflow: jest.fn().mockResolvedValue({}),
- };
-});
-
-const node1 = {
- id: 'n1',
- scopedId: 'n1',
- type: dTypes.staticNode,
- name: 'node1',
- nodes: [],
- edges: [],
-};
-
-const node2 = {
- id: 'n2',
- scopedId: 'n2',
- type: dTypes.gateNode,
- name: 'node2',
- nodes: [],
- edges: [],
-};
-
-describe('ExecutionDetails > Timeline > TaskNames', () => {
- let queryClient: QueryClient;
- beforeEach(() => {
- queryClient = createTestQueryClient();
- });
-
- const renderComponent = props => {
- const nodeExecutionsById = props.nodes.reduce(
- (accumulator, currentValue) => {
- accumulator[currentValue.id] = {
- closure: {
- createdAt: dateToTimestamp(new Date()),
- outputUri: '',
- phase: 1,
- },
- metadata: currentValue.metadata,
- id: {
- executionId: {
- domain: 'development',
- name: 'MyWorkflow',
- project: 'flytetest',
- },
- nodeId: currentValue.id,
- },
- inputUri: '',
- scopedId: currentValue.scopedId,
- };
- return accumulator;
- },
- {},
- );
- return render(
-
-
- {},
- setShouldUpdate: () => {},
- shouldUpdate: false,
- }}
- >
-
-
-
- ,
- );
- };
-
- it('should render task names list', () => {
- const nodes = [node1, node2];
- const { getAllByTestId } = renderComponent({ nodes, onToggle });
- expect(getAllByTestId('task-name-item').length).toEqual(nodes.length);
- });
-
- it('should render task names list with resume buttons if onAction prop is passed', () => {
- const nodes = [node1, node2];
- const { getAllByTestId, getAllByTitle } = renderComponent({
- nodes,
- onToggle,
- onAction,
- });
- expect(getAllByTestId('task-name-item').length).toEqual(nodes.length);
- expect(getAllByTitle('Resume').length).toEqual(nodes.length);
- });
-
- it('should render task names list with expanders if nodes contain nested nodes list', () => {
- const nestedNodes = [
- {
- id: 't1',
- scopedId: 'n1',
- type: dTypes.task,
- name: 'task1',
- nodes: [],
- edges: [],
- },
- ];
- const nodes = [
- { ...node1, metadata: { isParentNode: true }, nodes: nestedNodes },
- { ...node2, metadata: { isParentNode: true }, nodes: nestedNodes },
- ];
- const { getAllByTestId, getAllByTitle } = renderComponent({
- nodes,
- onToggle,
- });
- expect(getAllByTestId('task-name-item').length).toEqual(nodes.length);
- expect(getAllByTitle('Expand row').length).toEqual(nodes.length);
- });
-});
diff --git a/packages/console/src/components/Executions/ExecutionDetails/test/__mocks__/NodeExecution.mock.ts b/packages/console/src/components/Executions/ExecutionDetails/test/__mocks__/NodeExecution.mock.ts
deleted file mode 100644
index 6a533fc00..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/test/__mocks__/NodeExecution.mock.ts
+++ /dev/null
@@ -1,144 +0,0 @@
-import { CatalogCacheStatus, NodeExecutionPhase } from 'models/Execution/enums';
-
-const dNodeBasicExecution = {
- id: 'other-root-n0',
- scopedId: 'other-root-n0',
- execution: {
- id: {
- nodeId: 'other-root-n0',
- executionId: {
- project: 'flytesnacks',
- domain: 'development',
- name: 'rnktdb3skr',
- },
- },
- closure: {
- phase: 3,
- startedAt: {
- seconds: { low: 1642627611, high: 0, unsigned: false },
- nanos: 0,
- },
- duration: { seconds: { low: 55, high: 0, unsigned: false }, nanos: 0 },
- createdAt: {
- seconds: { low: 1642627611, high: 0, unsigned: false },
- nanos: 0,
- },
- updatedAt: {
- seconds: { low: 1642627666, high: 0, unsigned: false },
- nanos: 0,
- },
- outputUri:
- 's3://flyte-demo/metadata/propeller/flytesnacks-development-rnktdb3skr/other-root-n0/data/0/outputs.pb',
- },
- metadata: { isParentNode: true, specNodeId: 'other-root-n0' },
- scopedId: 'other-root-n0',
- },
-};
-
-const getMockNodeExecution = (
- initialStartSec: number,
- phase: NodeExecutionPhase,
- startOffsetSec: number,
- durationSec: number,
- cacheStatus?: CatalogCacheStatus,
-) => {
- const node = { ...dNodeBasicExecution } as any;
- node.execution.closure.phase = phase;
- if (cacheStatus) {
- node.execution.closure = {
- ...node.execution.closure,
- taskNodeMetadata: {
- cacheStatus: cacheStatus,
- },
- };
- if (cacheStatus === CatalogCacheStatus.CACHE_HIT) {
- node.execution.closure.createdAt.seconds.low =
- initialStartSec + startOffsetSec;
- node.execution.closure.updatedAt.seconds.low =
- initialStartSec + startOffsetSec + durationSec;
- return {
- ...node,
- execution: {
- ...node.execution,
- closure: {
- ...node.execution.closure,
- startedAt: undefined,
- duration: undefined,
- },
- },
- };
- }
- }
- node.execution.closure.startedAt.seconds.low =
- initialStartSec + startOffsetSec;
- node.execution.closure.duration.seconds.low =
- initialStartSec + startOffsetSec + durationSec;
- return node;
-};
-
-export const mockbarItems = [
- {
- phase: NodeExecutionPhase.FAILED,
- startOffsetSec: 0,
- durationSec: 15,
- isFromCache: false,
- isMapTaskCache: false,
- },
- {
- phase: NodeExecutionPhase.SUCCEEDED,
- startOffsetSec: 5,
- durationSec: 11,
- isFromCache: true,
- isMapTaskCache: false,
- },
- {
- phase: NodeExecutionPhase.RUNNING,
- startOffsetSec: 17,
- durationSec: 23,
- isFromCache: false,
- isMapTaskCache: false,
- },
- {
- phase: NodeExecutionPhase.QUEUED,
- startOffsetSec: 39,
- durationSec: 0,
- isFromCache: false,
- isMapTaskCache: false,
- },
- {
- phase: NodeExecutionPhase.SUCCEEDED,
- startOffsetSec: 5,
- durationSec: 11,
- isFromCache: false,
- isMapTaskCache: true,
- },
-];
-
-export const getMockExecutionsForBarChart = (startTimeSec: number) => {
- const start = startTimeSec;
- return [
- getMockNodeExecution(start, NodeExecutionPhase.FAILED, 0, 15),
- getMockNodeExecution(
- start,
- NodeExecutionPhase.SUCCEEDED,
- 5,
- 11,
- CatalogCacheStatus.CACHE_HIT,
- ),
- getMockNodeExecution(
- start,
- NodeExecutionPhase.RUNNING,
- 17,
- 23,
- CatalogCacheStatus.CACHE_MISS,
- ),
- getMockNodeExecution(start, NodeExecutionPhase.QUEUED, 39, 0),
- getMockNodeExecution(
- start,
- NodeExecutionPhase.SUCCEEDED,
- 5,
- 11,
- CatalogCacheStatus.MAP_CACHE,
- ),
- ];
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/useExecutionNodeViewsState.ts b/packages/console/src/components/Executions/ExecutionDetails/useExecutionNodeViewsState.ts
deleted file mode 100644
index b913e1936..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/useExecutionNodeViewsState.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { useConditionalQuery } from 'components/hooks/useConditionalQuery';
-import { limits } from 'models/AdminEntity/constants';
-import { FilterOperation, SortDirection } from 'models/AdminEntity/types';
-import { executionSortFields } from 'models/Execution/constants';
-import { Execution, NodeExecution } from 'models/Execution/types';
-import { useQueryClient, UseQueryResult } from 'react-query';
-import { executionRefreshIntervalMs } from '../constants';
-import { makeNodeExecutionListQuery } from '../nodeExecutionQueries';
-import { executionIsTerminal, nodeExecutionIsTerminal } from '../utils';
-
-export interface UseExecutionNodeViewsState {
- nodeExecutionsQuery: UseQueryResult;
- nodeExecutionsRequestConfig: {
- filter: FilterOperation[];
- sort: {
- key: string;
- direction: any;
- };
- limit: number;
- };
-}
-const sort = {
- key: executionSortFields.createdAt,
- direction: SortDirection.ASCENDING,
-};
-
-export function useExecutionNodeViewsStatePoll(
- execution: Execution,
- filter: FilterOperation[] = [],
-): UseExecutionNodeViewsState {
- const nodeExecutionsRequestConfig = {
- filter,
- sort,
- limit: limits.NONE,
- };
-
- const shouldEnableQuery = (executions: NodeExecution[]) => {
- const shouldEnable =
- !executionIsTerminal(execution) ||
- executions.some(ne => !nodeExecutionIsTerminal(ne));
- return shouldEnable;
- };
-
- const nodeExecutionsQuery = useConditionalQuery(
- {
- ...makeNodeExecutionListQuery(
- useQueryClient(),
- execution.id,
- nodeExecutionsRequestConfig,
- ),
- refetchInterval: executionRefreshIntervalMs,
- },
- shouldEnableQuery,
- );
-
- return { nodeExecutionsQuery, nodeExecutionsRequestConfig };
-}
diff --git a/packages/console/src/components/Executions/ExecutionDetails/useNodeExecutionRow.ts b/packages/console/src/components/Executions/ExecutionDetails/useNodeExecutionRow.ts
deleted file mode 100644
index b714131ff..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/useNodeExecutionRow.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { useConditionalQuery } from 'components/hooks/useConditionalQuery';
-import { NodeExecution } from 'models/Execution/types';
-
-import { QueryClient, UseQueryResult } from 'react-query';
-import { nodeExecutionRefreshIntervalMs } from '../constants';
-import { makeNodeExecutionQueryEnhanced } from '../nodeExecutionQueries';
-
-export const useNodeExecutionRow = (
- queryClient: QueryClient,
- execution: NodeExecution,
- shouldEnableQuery: (data: NodeExecution[]) => boolean,
-): {
- nodeExecutionRowQuery: UseQueryResult;
-} => {
- const nodeExecutionRowQuery = useConditionalQuery(
- {
- ...makeNodeExecutionQueryEnhanced(execution, queryClient),
- refetchInterval: nodeExecutionRefreshIntervalMs,
- },
- shouldEnableQuery,
- );
-
- return { nodeExecutionRowQuery };
-};
diff --git a/packages/console/src/components/Executions/ExecutionDetails/useRecoverExecutionState.ts b/packages/console/src/components/Executions/ExecutionDetails/useRecoverExecutionState.ts
deleted file mode 100644
index 3daecd3a3..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/useRecoverExecutionState.ts
+++ /dev/null
@@ -1,30 +0,0 @@
-import { useAPIContext } from 'components/data/apiContext';
-import { useContext } from 'react';
-import { useMutation } from 'react-query';
-import { WorkflowExecutionIdentifier } from 'models/Execution/types';
-import { ExecutionContext } from '../contexts';
-
-export function useRecoverExecutionState() {
- const { recoverWorkflowExecution } = useAPIContext();
- const {
- execution: { id },
- } = useContext(ExecutionContext);
-
- const { mutate, ...recoverState } = useMutation<
- WorkflowExecutionIdentifier,
- Error
- >(async () => {
- const { id: recoveredId } = await recoverWorkflowExecution({ id });
- if (!recoveredId) {
- throw new Error('API Response did not include new execution id');
- }
- return recoveredId as WorkflowExecutionIdentifier;
- });
-
- const recoverExecution = () => mutate();
-
- return {
- recoverState,
- recoverExecution,
- };
-}
diff --git a/packages/console/src/components/Executions/ExecutionDetails/utils.ts b/packages/console/src/components/Executions/ExecutionDetails/utils.ts
deleted file mode 100644
index a6b1dc4fc..000000000
--- a/packages/console/src/components/Executions/ExecutionDetails/utils.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { Identifier, ResourceType } from 'models/Common/types';
-import {
- Execution,
- NodeExecution,
- TaskExecution,
-} from 'models/Execution/types';
-import { Routes } from 'routes/routes';
-import { timestampToDate } from 'common';
-import { formatDateUTC } from 'common/formatters';
-
-export function isSingleTaskExecution(execution: Execution) {
- return execution.spec.launchPlan.resourceType === ResourceType.TASK;
-}
-
-export function getExecutionSourceId(execution: Execution): Identifier {
- return isSingleTaskExecution(execution)
- ? execution.spec.launchPlan
- : execution.closure.workflowId;
-}
-
-export function getExecutionBackLink(execution: Execution): string {
- const { project, domain, name } = getExecutionSourceId(execution);
- return isSingleTaskExecution(execution)
- ? Routes.TaskDetails.makeUrl(project, domain, name)
- : Routes.WorkflowDetails.makeUrl(project, domain, name);
-}
-
-export function getTaskExecutionDetailReasons(
- taskExecutionDetails?: TaskExecution[],
-): (string | null | undefined)[] {
- let reasons: string[] = [];
- taskExecutionDetails?.forEach?.(taskExecution => {
- const finalReasons = (
- taskExecution.closure.reasons?.length
- ? taskExecution.closure.reasons
- : [{ message: taskExecution.closure.reason }]
- ).filter(r => !!r);
- if (
- finalReasons &&
- finalReasons.some(eachReason => eachReason?.message?.trim() !== '')
- ) {
- reasons = reasons.concat(
- finalReasons.map(
- reason =>
- (reason.occurredAt
- ? `${formatDateUTC(timestampToDate(reason.occurredAt))} `
- : '') + reason.message,
- ),
- );
- }
- });
- return reasons;
-}
-
-export function isChildGroupsFetched(
- scopedId: string,
- nodeExecutionsById: Dictionary,
-): boolean {
- return Object.values(nodeExecutionsById).some(
- v => v?.fromUniqueParentId === scopedId,
- );
-}
diff --git a/packages/console/src/components/Executions/ExecutionFilters.tsx b/packages/console/src/components/Executions/ExecutionFilters.tsx
deleted file mode 100644
index 43b494117..000000000
--- a/packages/console/src/components/Executions/ExecutionFilters.tsx
+++ /dev/null
@@ -1,187 +0,0 @@
-import * as React from 'react';
-import { FormControlLabel, Checkbox, FormGroup, Grid } from '@material-ui/core';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import { MultiSelectForm } from 'components/common/MultiSelectForm';
-import { SearchInputForm } from 'components/common/SearchInputForm';
-import { SingleSelectForm } from 'components/common/SingleSelectForm';
-import { FilterPopoverButton } from 'components/Tables/filters/FilterPopoverButton';
-import {
- FilterState,
- MultiFilterState,
- SearchFilterState,
- SingleFilterState,
- BooleanFilterState,
-} from './filters/types';
-
-const useStyles = makeStyles((theme: Theme) => ({
- container: {
- alignItems: 'center',
- display: 'flex',
- flexDirection: 'row',
- minHeight: theme.spacing(7),
- paddingLeft: theme.spacing(1),
- paddingTop: theme.spacing(2),
- paddingBottom: theme.spacing(1),
- width: '100%',
- },
- filterButton: {
- marginLeft: theme.spacing(1),
- },
- checkbox: {
- marginLeft: theme.spacing(1),
- },
-}));
-
-interface OnlyMyExecutionsFilterState {
- onlyMyExecutionsValue: boolean;
- isFilterDisabled: boolean;
- onOnlyMyExecutionsFilterChange: (filterOnlyMyExecutions: boolean) => void;
-}
-
-export interface ExecutionFiltersProps {
- filters: (FilterState | BooleanFilterState)[];
- chartIds?: string[];
- clearCharts?: () => void;
- showArchived?: boolean;
- onArchiveFilterChange?: (showArchievedItems: boolean) => void;
- onlyMyExecutionsFilterState?: OnlyMyExecutionsFilterState;
-}
-
-const RenderFilter: React.FC<{ filter: FilterState }> = ({ filter }) => {
- const searchFilterState = filter as SearchFilterState;
- switch (filter.type) {
- case 'single':
- return )} />;
- case 'multi':
- return )} />;
- case 'search':
- return (
-
- );
- default:
- return null;
- }
-};
-
-/** Renders the set of filter buttons relevant to a table of WorkflowExecutions:
- * Status, Version, Start Time, Duration
- * The state for this component is generated externally by `useExecutionFiltersState` and passed in.
- * This allows for the consuming code to have direct access to the
- * current filters without relying on complicated callback arrangements
- */
-export const ExecutionFilters: React.FC = ({
- filters,
- chartIds,
- clearCharts,
- showArchived,
- onArchiveFilterChange,
- onlyMyExecutionsFilterState,
-}) => {
- const styles = useStyles();
-
- filters = filters.map(filter => {
- const onChangeFunc = filter.onChange;
- filter.onChange = value => {
- if (clearCharts) {
- clearCharts();
- }
- if (onChangeFunc) {
- onChangeFunc(value);
- }
- };
- return filter;
- });
-
- return (
-
- {filters.map((filter: any) => {
- if (filter.hidden) {
- return null;
- }
- if (filter.type === 'boolean') {
- const handleChange = (event: React.ChangeEvent) =>
- filter.setActive(event.target.checked);
-
- return (
-
-
- }
- className={styles.checkbox}
- label={filter.label}
- />
-
- );
- }
- return (
-
- }
- />
-
- );
- })}
- {chartIds && chartIds.length > 0 && (
- <>>}
- className={styles.filterButton}
- buttonText="Clear Manually Selected Executions"
- onReset={clearCharts}
- key="charts"
- data-testId="clear-charts"
- />
- )}
- {!!onlyMyExecutionsFilterState && (
-
-
-
- onlyMyExecutionsFilterState.onOnlyMyExecutionsFilterChange(
- checked,
- )
- }
- />
- }
- className={styles.checkbox}
- label="Only my executions"
- />
-
-
- )}
- {!!onArchiveFilterChange && (
-
-
- onArchiveFilterChange(checked)}
- />
- }
- className={styles.checkbox}
- label="Show archived executions"
- />
-
-
- )}
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionInputsOutputsModal.tsx b/packages/console/src/components/Executions/ExecutionInputsOutputsModal.tsx
deleted file mode 100644
index e67d24ede..000000000
--- a/packages/console/src/components/Executions/ExecutionInputsOutputsModal.tsx
+++ /dev/null
@@ -1,145 +0,0 @@
-import { Dialog, DialogContent, Tab, Tabs } from '@material-ui/core';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import { ClosableDialogTitle } from 'components/common/ClosableDialogTitle';
-import { WaitForData } from 'components/common/WaitForData';
-import { LiteralMapViewer } from 'components/Literals/LiteralMapViewer';
-import { emptyLiteralMapBlob } from 'models/Common/constants';
-import { Execution } from 'models/Execution/types';
-import * as React from 'react';
-import { useEscapeKey } from 'components/hooks/useKeyListener';
-import { useWorkflowExecutionData } from './useWorkflowExecution';
-
-const useStyles = makeStyles((theme: Theme) => ({
- content: {
- paddingTop: theme.spacing(2),
- },
- dialog: {
- // Attempt to fill the window up to a max width/height
- // This will normally result in the dialog being maxWidth X maxHeight
- // except when the viewport is smaller, in which case we will take as
- // much room as possible while leaving consistent margins (enforced
- // by the MUI Dialog component)
- maxWidth: `calc(100% - ${theme.spacing(12)}px)`,
- maxHeight: `calc(100% - ${theme.spacing(12)}px)`,
- height: theme.spacing(90),
- width: theme.spacing(100),
- },
- tabs: {
- padding: `0 ${theme.spacing(2)}px`,
- },
- title: {
- display: 'flex',
- flexDirection: 'row',
- },
-}));
-
-enum TabId {
- INPUTS = 'inputs',
- OUTPUTS = 'outputs',
-}
-
-const RemoteExecutionInputs: React.FC<{ execution: Execution }> = ({
- execution,
-}) => {
- const executionData = useWorkflowExecutionData(execution.id);
- return (
-
-
-
- );
-};
-
-const RemoteExecutionOutputs: React.FC<{ execution: Execution }> = ({
- execution,
-}) => {
- const executionData = useWorkflowExecutionData(execution.id);
- return (
-
-
-
- );
-};
-
-const RenderInputs: React.FC<{ execution: Execution }> = ({ execution }) => {
- // computedInputs is deprecated, but older data may still use that field.
- // For new records, the inputs will always need to be fetched separately
- return execution.closure.computedInputs ? (
-
- ) : (
-
- );
-};
-
-const RenderOutputs: React.FC<{ execution: Execution }> = ({ execution }) => {
- const outputs = execution.closure.outputs || emptyLiteralMapBlob;
-
- // Small outputs will be stored directly in the execution.
- // For larger outputs, we need to fetch them using the /data endpoint
- return outputs.uri ? (
-
- ) : (
-
- );
-};
-
-interface RenderDialogProps {
- execution: Execution;
- onClose: () => void;
-}
-
-const RenderDialog: React.FC = ({ execution, onClose }) => {
- const styles = useStyles();
- const [selectedTab, setSelectedTab] = React.useState(TabId.INPUTS);
- const handleTabChange = (event: React.ChangeEvent<{}>, tabId: TabId) =>
- setSelectedTab(tabId);
- return (
- <>
-
- {execution.id.name}
-
-
-
-
-
-
- {selectedTab === TabId.INPUTS && }
- {selectedTab === TabId.OUTPUTS && (
-
- )}
-
- >
- );
-};
-
-interface ExecutionInputsOutputsModalProps {
- execution: Execution | null;
- onClose(): void;
-}
-
-/** Renders a Modal that will load/display the inputs/outputs for a given
- * Execution in a tabbed/scrollable container
- */
-export const ExecutionInputsOutputsModal: React.FC<
- ExecutionInputsOutputsModalProps
-> = ({ execution, onClose }) => {
- const styles = useStyles();
- useEscapeKey(onClose);
- return (
-
- );
-};
diff --git a/packages/console/src/components/Executions/ExecutionStatusBadge.tsx b/packages/console/src/components/Executions/ExecutionStatusBadge.tsx
deleted file mode 100644
index df10152b1..000000000
--- a/packages/console/src/components/Executions/ExecutionStatusBadge.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import classnames from 'classnames';
-import {
- bodyFontFamily,
- smallFontSize,
- statusColors,
-} from 'components/Theme/constants';
-import {
- NodeExecutionPhase,
- TaskExecutionPhase,
- WorkflowExecutionPhase,
-} from 'models/Execution/enums';
-import * as React from 'react';
-import {
- getNodeExecutionPhaseConstants,
- getTaskExecutionPhaseConstants,
- getWorkflowExecutionPhaseConstants,
-} from './utils';
-
-const useStyles = makeStyles((theme: Theme) => ({
- default: {
- alignItems: 'center',
- backgroundColor: theme.palette.common.white,
- borderRadius: '4px',
- color: theme.palette.text.primary,
- display: 'flex',
- flex: '0 0 auto',
- height: theme.spacing(3),
- fontSize: smallFontSize,
- justifyContent: 'center',
- textTransform: 'uppercase',
- width: theme.spacing(11),
- },
- root: {
- fontFamily: bodyFontFamily,
- fontWeight: 'normal',
- },
- text: {
- backgroundColor: 'inherit',
- border: 'none',
- marginTop: theme.spacing(1),
- textTransform: 'lowercase',
- },
-}));
-
-interface BaseProps {
- variant?: 'default' | 'text';
- disabled?: boolean;
-}
-
-interface WorkflowExecutionStatusBadgeProps extends BaseProps {
- phase: WorkflowExecutionPhase;
- type: 'workflow';
-}
-
-interface NodeExecutionStatusBadgeProps extends BaseProps {
- phase: NodeExecutionPhase;
- type: 'node';
-}
-
-interface TaskExecutionStatusBadgeProps extends BaseProps {
- phase: TaskExecutionPhase;
- type: 'task';
-}
-
-type ExecutionStatusBadgeProps =
- | WorkflowExecutionStatusBadgeProps
- | NodeExecutionStatusBadgeProps
- | TaskExecutionStatusBadgeProps;
-
-export function getPhaseConstants(
- type: 'workflow' | 'node' | 'task',
- phase: WorkflowExecutionPhase | NodeExecutionPhase | TaskExecutionPhase,
-) {
- if (type === 'task') {
- return getTaskExecutionPhaseConstants(phase as TaskExecutionPhase);
- }
- if (type === 'node') {
- return getNodeExecutionPhaseConstants(phase as NodeExecutionPhase);
- }
- return getWorkflowExecutionPhaseConstants(phase as WorkflowExecutionPhase);
-}
-
-/** Given a `closure.phase` value for a Workflow/Task/NodeExecution, will render
- * a badge with the proper text and styling to indicate the status (succeeded/
- * failed etc.)
- */
-export const ExecutionStatusBadge: React.FC<
- ExecutionStatusBadgeProps &
- React.DetailedHTMLProps<
- React.HTMLAttributes,
- HTMLDivElement
- >
-> = ({
- phase,
- type,
- variant = 'default',
- disabled = false,
- className,
- ...htmlProps
-}) => {
- const styles = useStyles();
- const style: React.CSSProperties = {};
- const { badgeColor, text, textColor } = getPhaseConstants(type, phase);
-
- if (variant === 'text') {
- style.color = textColor;
- } else {
- style.backgroundColor = disabled ? statusColors.UNKNOWN : badgeColor;
- }
-
- return (
-
- {text}
-
- );
-};
diff --git a/packages/console/src/components/Executions/NodeExecutionCacheStatus.tsx b/packages/console/src/components/Executions/NodeExecutionCacheStatus.tsx
deleted file mode 100644
index 3407ef71a..000000000
--- a/packages/console/src/components/Executions/NodeExecutionCacheStatus.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import { NodeExecutionDetails } from 'components/Executions/types';
-import { useNodeExecutionContext } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { CatalogCacheStatus } from 'models/Execution/enums';
-import { MapTaskExecution, NodeExecution } from 'models/Execution/types';
-import * as React from 'react';
-import { isMapTaskType } from 'models/Task/utils';
-import { useEffect, useState } from 'react';
-import { CacheStatus } from './CacheStatus';
-import { getTaskRetryAtemptsForIndex } from './TaskExecutionsList';
-
-interface NodeExecutionCacheStatusProps {
- execution: NodeExecution;
- selectedTaskExecution?: MapTaskExecution;
- /** `normal` will render an icon with description message beside it
- * `iconOnly` will render just the icon with the description as a tooltip
- */
- variant?: 'normal' | 'iconOnly';
- className?: string;
-}
-/** For a given `NodeExecution.closure.taskNodeMetadata` object, will render
- * the cache status with a descriptive message. For `Core.CacheCatalogStatus.CACHE_HIT`,
- * it will also attempt to render a link to the source `WorkflowExecution` (normal
- * variant only).
- *
- * For Map Tasks, we will check the NodeExecutionDetail for the cache status instead. Since map tasks
- * cotains multiple tasks, the logic of the cache status is different.
- */
-export const NodeExecutionCacheStatus: React.FC<
- NodeExecutionCacheStatusProps
-> = ({ execution, selectedTaskExecution, variant = 'normal', className }) => {
- const taskNodeMetadata = execution.closure?.taskNodeMetadata;
- const { getNodeExecutionDetails } = useNodeExecutionContext();
- const [nodeDetails, setNodeDetails] = useState<
- NodeExecutionDetails | undefined
- >();
- const isMapTask = isMapTaskType(nodeDetails?.taskTemplate?.type);
-
- useEffect(() => {
- let isCurrent = true;
- getNodeExecutionDetails(execution).then(res => {
- if (isCurrent) {
- setNodeDetails(res);
- }
- });
- return () => {
- isCurrent = false;
- };
- });
-
- if (isMapTask) {
- if (nodeDetails?.taskTemplate?.metadata?.discoverable) {
- if (selectedTaskExecution) {
- const filteredResources = getTaskRetryAtemptsForIndex(
- selectedTaskExecution?.closure?.metadata?.externalResources ?? [],
- selectedTaskExecution.taskIndex,
- );
- const cacheStatus = filteredResources?.[0]?.cacheStatus;
-
- return cacheStatus !== null ? (
-
- ) : null;
- }
-
- return (
-
- );
- }
-
- return (
-
- );
- }
-
- // cachestatus can be 0
- if (taskNodeMetadata?.cacheStatus === null) {
- return null;
- }
-
- return (
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/EntityVersionsTable.tsx b/packages/console/src/components/Executions/Tables/EntityVersionsTable.tsx
deleted file mode 100644
index e0f347515..000000000
--- a/packages/console/src/components/Executions/Tables/EntityVersionsTable.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import classnames from 'classnames';
-import { noVersionsFoundString } from 'common/constants';
-import { useCommonStyles } from 'components/common/styles';
-import { ListProps } from 'components/common/types';
-import PaginatedDataList from 'components/Tables/PaginatedDataList';
-import { Workflow } from 'models/Workflow/types';
-import { Identifier, ResourceType } from 'models/Common/types';
-import * as React from 'react';
-import { useParams } from 'react-router';
-import { history } from 'routes/history';
-import { Routes } from 'routes/routes';
-import { entityStrings } from 'components/Entities/constants';
-import { useExecutionTableStyles } from './styles';
-import { useWorkflowExecutionsTableState } from './useWorkflowExecutionTableState';
-import { useWorkflowVersionsTableColumns } from './useWorkflowVersionsTableColumns';
-import { WorkflowVersionRow } from './WorkflowVersionRow';
-
-interface EntityVersionsTableProps extends ListProps {
- versionView?: boolean;
- resourceType: ResourceType;
-}
-
-interface EntityVersionRouteParams {
- entityVersion: string;
-}
-
-/**
- * Renders a table of WorkflowVersion records.
- * @param props
- * @constructor
- */
-export const EntityVersionsTable: React.FC<
- EntityVersionsTableProps
-> = props => {
- const { value: versions, versionView, resourceType } = props;
- const state = useWorkflowExecutionsTableState();
- const commonStyles = useCommonStyles();
- const tableStyles = useExecutionTableStyles();
- const { entityVersion } = useParams();
-
- const columns = useWorkflowVersionsTableColumns();
-
- const handleClickRow = React.useCallback(
- ({
- project,
- name,
- domain,
- version,
- resourceType = ResourceType.UNSPECIFIED,
- }: Identifier) =>
- () => {
- history.push(
- Routes.EntityVersionDetails.makeUrl(
- project,
- domain,
- name,
- entityStrings[resourceType],
- version,
- ),
- );
- },
- [],
- );
-
- const rowRenderer = (row: Workflow) => (
-
- );
-
- return (
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/ExecutionsTableHeader.tsx b/packages/console/src/components/Executions/Tables/ExecutionsTableHeader.tsx
deleted file mode 100644
index 0a1481098..000000000
--- a/packages/console/src/components/Executions/Tables/ExecutionsTableHeader.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Typography } from '@material-ui/core';
-import classnames from 'classnames';
-import { isFunction } from 'common/typeCheckers';
-import * as React from 'react';
-import { useExecutionTableStyles } from './styles';
-import { ColumnDefinition } from './types';
-
-/** Layout/rendering logic for the header row of an ExecutionsTable */
-export const ExecutionsTableHeader: React.FC<{
- columns: ColumnDefinition[];
- scrollbarPadding?: number;
- versionView?: boolean;
-}> = ({ columns, scrollbarPadding = 0, versionView = false }) => {
- const tableStyles = useExecutionTableStyles();
- const scrollbarSpacer =
- scrollbarPadding > 0 ? : null;
- return (
-
- {versionView && (
-
-
-
- )}
- {columns.map(({ key, label, className }) => {
- const labelContent = isFunction(label) ? (
- React.createElement(label)
- ) : (
-
{label}
- );
- return (
-
- {labelContent}
-
- );
- })}
- {scrollbarSpacer}
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/ExpandableExecutionError.tsx b/packages/console/src/components/Executions/Tables/ExpandableExecutionError.tsx
deleted file mode 100644
index 30525d600..000000000
--- a/packages/console/src/components/Executions/Tables/ExpandableExecutionError.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { Admin } from '@flyteorg/flyteidl-types';
-import { ExpandableMonospaceText } from 'components/common/ExpandableMonospaceText';
-import { ExecutionError } from 'models/Execution/types';
-import * as React from 'react';
-import { useExecutionTableStyles } from './styles';
-
-/** Renders an expandable/collapsible container for an ExecutionErorr, along with
- * a button for copying the error string.
- */
-export const ExpandableExecutionError: React.FC<{
- abortMetadata?: Admin.IAbortMetadata;
- error?: ExecutionError;
- initialExpansionState?: boolean;
- onExpandCollapse?(expanded: boolean): void;
-}> = ({
- abortMetadata,
- error,
- initialExpansionState = false,
- onExpandCollapse,
-}) => {
- const styles = useExecutionTableStyles();
- return (
-
-
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/NoExecutionsContent.tsx b/packages/console/src/components/Executions/Tables/NoExecutionsContent.tsx
deleted file mode 100644
index 3ad69b85f..000000000
--- a/packages/console/src/components/Executions/Tables/NoExecutionsContent.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Typography } from '@material-ui/core';
-import { noExecutionsFoundString } from 'common/constants';
-import * as React from 'react';
-import { useExecutionTableStyles } from './styles';
-
-type SizeVariant = 'small' | 'large';
-
-/** A message to show as a placeholder when we have an empty list of executions */
-export const NoExecutionsContent: React.FC<{ size?: SizeVariant }> = ({
- size = 'small',
-}) => (
-
-
- {noExecutionsFoundString}
-
-
-);
diff --git a/packages/console/src/components/Executions/Tables/NodeExecutionActions.tsx b/packages/console/src/components/Executions/Tables/NodeExecutionActions.tsx
deleted file mode 100644
index 79cfd5967..000000000
--- a/packages/console/src/components/Executions/Tables/NodeExecutionActions.tsx
+++ /dev/null
@@ -1,153 +0,0 @@
-import { IconButton, Tooltip } from '@material-ui/core';
-import { NodeExecution, NodeExecutionIdentifier } from 'models/Execution/types';
-import * as React from 'react';
-import InputsAndOutputsIcon from '@material-ui/icons/Tv';
-import PlayCircleOutlineIcon from '@material-ui/icons/PlayCircleOutline';
-import { RerunIcon } from '@flyteorg/ui-atoms';
-import { Identifier, ResourceIdentifier } from 'models/Common/types';
-import { LaunchFormDialog } from 'components/Launch/LaunchForm/LaunchFormDialog';
-import { getTask } from 'models/Task/api';
-import { useNodeExecutionData } from 'components/hooks/useNodeExecution';
-import { TaskInitialLaunchParameters } from 'components/Launch/LaunchForm/types';
-import { literalsToLiteralValueMap } from 'components/Launch/LaunchForm/utils';
-import { useEffect, useState } from 'react';
-import { NodeExecutionPhase } from 'models/Execution/enums';
-import { extractCompiledNodes } from 'components/hooks/utils';
-import { useNodeExecutionContext } from '../contextProvider/NodeExecutionDetails';
-import { NodeExecutionDetails } from '../types';
-import t from './strings';
-import { getNodeFrontendPhase, isNodeGateNode } from '../utils';
-import { useDetailsPanel } from '../ExecutionDetails/DetailsPanelContext';
-
-interface NodeExecutionActionsProps {
- execution: NodeExecution;
- className?: string;
-}
-
-export const NodeExecutionActions = ({
- execution,
- className,
-}: NodeExecutionActionsProps): JSX.Element => {
- const { compiledWorkflowClosure, getNodeExecutionDetails } =
- useNodeExecutionContext();
- const { setSelectedExecution } = useDetailsPanel();
-
- const [showLaunchForm, setShowLaunchForm] = useState(false);
- const [showResumeForm, setShowResumeForm] = useState(false);
- const [nodeExecutionDetails, setNodeExecutionDetails] = useState<
- NodeExecutionDetails | undefined
- >(undefined);
- const [initialParameters, setInitialParameters] = useState<
- TaskInitialLaunchParameters | undefined
- >(undefined);
-
- const executionData = useNodeExecutionData(execution.id);
- const id = nodeExecutionDetails?.taskTemplate?.id;
-
- const isGateNode = isNodeGateNode(
- extractCompiledNodes(compiledWorkflowClosure),
- execution.metadata?.specNodeId || execution.id.nodeId,
- );
-
- const phase = getNodeFrontendPhase(execution.closure.phase, isGateNode);
- const compiledNode = extractCompiledNodes(compiledWorkflowClosure)?.find(
- node =>
- node.id === execution.metadata?.specNodeId ||
- node.id === execution.id.nodeId,
- );
-
- useEffect(() => {
- let isCurrent = true;
- getNodeExecutionDetails(execution).then(res => {
- if (isCurrent) {
- setNodeExecutionDetails(res);
- }
- });
- return () => {
- isCurrent = false;
- };
- });
-
- useEffect(() => {
- if (!id) {
- return;
- }
-
- (async () => {
- const task = await getTask(id as Identifier);
-
- const literals = executionData.value.fullInputs?.literals;
- const taskInputsTypes =
- task.closure.compiledTask.template?.interface?.inputs?.variables;
-
- const tempInitialParameters: TaskInitialLaunchParameters = {
- values:
- literals &&
- taskInputsTypes &&
- literalsToLiteralValueMap(literals, taskInputsTypes),
- taskId: id as Identifier | undefined,
- };
-
- setInitialParameters(tempInitialParameters);
- })();
- }, [id]);
-
- // open the side panel for selected execution's detail
- const inputsAndOutputsIconOnClick = (e: React.MouseEvent) => {
- // prevent the parent row body onClick event trigger
- e.stopPropagation();
- // use null in case if there is no execution provided - when it is null will close panel
- setSelectedExecution(execution?.id ?? null);
- };
-
- const rerunIconOnClick = (e: React.MouseEvent) => {
- e.stopPropagation();
- setShowLaunchForm(true);
- };
-
- const onResumeClick = (e: React.MouseEvent) => {
- e.stopPropagation();
- setShowResumeForm(true);
- };
-
- return (
-
- {phase === NodeExecutionPhase.PAUSED && (
-
-
-
-
-
- )}
-
-
-
-
-
- {id && initialParameters ? (
- <>
-
-
-
-
-
-
- >
- ) : null}
- {compiledNode && (
-
- )}
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/NodeExecutionRow.tsx b/packages/console/src/components/Executions/Tables/NodeExecutionRow.tsx
deleted file mode 100644
index 03be656a6..000000000
--- a/packages/console/src/components/Executions/Tables/NodeExecutionRow.tsx
+++ /dev/null
@@ -1,160 +0,0 @@
-import React from 'react';
-import classnames from 'classnames';
-import { dNode } from 'models/Graph/types';
-import { NodeExecutionPhase } from 'models/Execution/enums';
-import { isEqual } from 'lodash';
-import { useTheme } from 'components/Theme/useTheme';
-import { makeStyles } from '@material-ui/core';
-import { isExpanded } from 'models/Node/utils';
-import { dateToTimestamp } from 'common/utils';
-import {
- grayedClassName,
- selectedClassName,
- useExecutionTableStyles,
-} from './styles';
-import { NodeExecutionColumnDefinition } from './types';
-import { useDetailsPanel } from '../ExecutionDetails/DetailsPanelContext';
-import { RowExpander } from './RowExpander';
-import { calculateNodeExecutionRowLeftSpacing } from './utils';
-import { isParentNode } from '../utils';
-import { useNodeExecutionDynamicContext } from '../contextProvider/NodeExecutionDetails/NodeExecutionDynamicProvider';
-
-const useStyles = makeStyles(theme => ({
- [`${grayedClassName}`]: {
- color: `${theme.palette.grey[300]} !important`,
- },
- namesContainerExpander: {
- display: 'flex',
- marginTop: 'auto',
- marginBottom: 'auto',
- },
- leaf: {
- width: 30,
- },
-}));
-
-interface NodeExecutionRowProps {
- columns: NodeExecutionColumnDefinition[];
- level?: number;
- style?: React.CSSProperties;
- node: dNode;
- onToggle: (id: string, scopeId: string, level: number) => void;
-}
-
-/** Renders a NodeExecution as a row inside a `NodeExecutionsTable` */
-export const NodeExecutionRow: React.FC = ({
- columns,
- node,
- style,
- onToggle,
-}) => {
- const styles = useStyles();
- const theme = useTheme();
- const tableStyles = useExecutionTableStyles();
- const { childCount, componentProps } = useNodeExecutionDynamicContext();
- const nodeLevel = node?.level ?? 0;
-
- // For the first level, we want the borders to span the entire table,
- // so we'll use padding to space the content. For nested rows, we want the
- // border to start where the content does, so we'll use margin.
- const spacingProp = nodeLevel === 0 ? 'paddingLeft' : 'marginLeft';
- const rowContentStyle = {
- [spacingProp]: `${calculateNodeExecutionRowLeftSpacing(
- nodeLevel,
- theme.spacing,
- )}px`,
- };
-
- const expanderRef = React.useRef();
-
- const { selectedExecution, setSelectedExecution } = useDetailsPanel();
-
- const selected = selectedExecution
- ? isEqual(selectedExecution, node.execution?.id)
- : false;
-
- const expanderContent = React.useMemo(() => {
- const isParent = node?.execution ? isParentNode(node.execution) : false;
- const isExpandedVal = isExpanded(node);
-
- return isParent ? (
- }
- expanded={isExpandedVal}
- onClick={() => {
- onToggle(node.id, node.scopedId, nodeLevel);
- }}
- disabled={!childCount}
- />
- ) : (
-
- );
- }, [node, nodeLevel, node.execution, childCount]);
-
- // open the side panel for selected execution's detail
- // use null in case if there is no execution provided - when it is null, will close side panel
- const onClickRow = () =>
- node?.execution?.closure.phase !== NodeExecutionPhase.UNDEFINED &&
- setSelectedExecution(node.execution?.id ?? null);
-
- return (
-
-
-
-
-
- {expanderContent}
-
-
- {columns.map(({ className, key: columnKey, cellRenderer }) => (
-
- {cellRenderer({
- node,
- execution: node.execution || {
- closure: {
- createdAt: dateToTimestamp(new Date()),
- outputUri: '',
- phase: NodeExecutionPhase.UNDEFINED,
- },
- id: {
- executionId: {
- domain: node.value?.taskNode?.referenceId?.domain,
- name: node.value?.taskNode?.referenceId?.name,
- project: node.value?.taskNode?.referenceId?.project,
- },
- nodeId: node.id,
- },
- inputUri: '',
- scopedId: node.scopedId,
- },
- className: node.grayedOut ? tableStyles.grayed : '',
- })}
-
- ))}
-
-
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/NodeExecutionsTable.tsx b/packages/console/src/components/Executions/Tables/NodeExecutionsTable.tsx
deleted file mode 100644
index ec32c6e2a..000000000
--- a/packages/console/src/components/Executions/Tables/NodeExecutionsTable.tsx
+++ /dev/null
@@ -1,296 +0,0 @@
-import React, { useMemo, useEffect, useState, useContext } from 'react';
-import classnames from 'classnames';
-import { useCommonStyles } from 'components/common/styles';
-import scrollbarSize from 'dom-helpers/scrollbarSize';
-import { NodeExecution } from 'models/Execution/types';
-import { merge, isEqual, cloneDeep } from 'lodash';
-import { extractCompiledNodes } from 'components/hooks/utils';
-import {
- FilterOperation,
- FilterOperationName,
- FilterOperationValueList,
-} from 'models';
-import { dNode } from 'models/Graph/types';
-import { ExecutionsTableHeader } from './ExecutionsTableHeader';
-import { generateColumns } from './nodeExecutionColumns';
-import { NoExecutionsContent } from './NoExecutionsContent';
-import { useColumnStyles, useExecutionTableStyles } from './styles';
-import { convertToPlainNodes } from '../ExecutionDetails/Timeline/helpers';
-import {
- useNodeExecutionContext,
- useNodeExecutionsById,
-} from '../contextProvider/NodeExecutionDetails';
-import { NodeExecutionRow } from './NodeExecutionRow';
-import { ExecutionFiltersState } from '../filters/useExecutionFiltersState';
-import { searchNode } from '../utils';
-import { nodeExecutionPhaseConstants } from '../constants';
-import { NodeExecutionDynamicProvider } from '../contextProvider/NodeExecutionDetails/NodeExecutionDynamicProvider';
-import { ExecutionFilters } from '../ExecutionFilters';
-import {
- ExecutionContext,
- FilteredNodeExecutions,
- NodeExecutionsById,
-} from '../contexts';
-import { useExecutionNodeViewsStatePoll } from '../ExecutionDetails/useExecutionNodeViewsState';
-import { stringifyIsEqual } from '../contextProvider/NodeExecutionDetails/utils';
-
-const scrollbarPadding = scrollbarSize();
-
-const mergeOriginIntoNodes = (target: dNode[], origin: dNode[]) => {
- if (!target?.length) {
- return target;
- }
- const originClone = cloneDeep(origin);
- const newTarget = cloneDeep(target);
- newTarget?.forEach(value => {
- const originalNode = originClone.find(
- og => og.id === value.id && og.scopedId === value.scopedId,
- );
- const newNodes = mergeOriginIntoNodes(
- value.nodes,
- originalNode?.nodes || [],
- );
-
- value = merge(value, originalNode);
- value.nodes = newNodes;
- return value;
- });
-
- return newTarget;
-};
-
-const executionMatchesPhaseFilter = (
- nodeExecution: NodeExecution,
- { key, value, operation }: FilterOperation,
-) => {
- if (key === 'phase' && operation === FilterOperationName.VALUE_IN) {
- // default to UNKNOWN phase if the field does not exist on a closure
- const itemValue =
- nodeExecutionPhaseConstants()[nodeExecution?.closure[key]]?.value ??
- nodeExecutionPhaseConstants()[0].value;
- // phase check filters always return values in an array
- const valuesArray = value as FilterOperationValueList;
- return valuesArray.includes(itemValue);
- }
- return false;
-};
-
-const filterNodes = (
- initialNodes: dNode[],
- nodeExecutionsById: NodeExecutionsById,
- appliedFilters: FilterOperation[],
-) => {
- if (!initialNodes?.length) {
- return [];
- }
-
- let initialClone = cloneDeep(initialNodes);
-
- for (const n of initialClone) {
- n.nodes = filterNodes(n.nodes, nodeExecutionsById, appliedFilters);
- }
-
- initialClone = initialClone.filter(node => {
- const hasFilteredChildren = !!node.nodes?.length;
- const shouldBeIncluded = executionMatchesPhaseFilter(
- nodeExecutionsById[node.scopedId],
- appliedFilters[0],
- );
- const result = hasFilteredChildren || shouldBeIncluded;
-
- if (hasFilteredChildren && !shouldBeIncluded) {
- node.grayedOut = true;
- }
-
- return result;
- });
-
- return initialClone;
-};
-
-const isPhaseFilter = (appliedFilters: FilterOperation[] = []) => {
- if (appliedFilters.length === 1 && appliedFilters[0].key === 'phase') {
- return true;
- }
- return false;
-};
-
-/** Renders a table of NodeExecution records. Executions with errors will
- * have an expanadable container rendered as part of the table row.
- * NodeExecutions are expandable and will potentially render a list of child
- * TaskExecutions
- */
-export const NodeExecutionsTable: React.FC<{
- filterState: ExecutionFiltersState;
-}> = ({ filterState }) => {
- const commonStyles = useCommonStyles();
- const tableStyles = useExecutionTableStyles();
- const columnStyles = useColumnStyles();
-
- const { execution } = useContext(ExecutionContext);
-
- const { appliedFilters } = filterState;
- const [filteredNodeExecutions, setFilteredNodeExecutions] =
- useState();
- const { nodeExecutionsById, initialDNodes: initialNodes } =
- useNodeExecutionsById();
-
- const [filters, setFilters] = useState([]);
- const [originalNodes, setOriginalNodes] = useState([]);
-
- // query to get filtered data to narrow down Table outputs
- const { nodeExecutionsQuery: filteredNodeExecutionsQuery } =
- useExecutionNodeViewsStatePoll(execution, filters);
-
- const { compiledWorkflowClosure } = useNodeExecutionContext();
- const [showNodes, setShowNodes] = useState([]);
-
- const [initialFilteredNodes, setInitialFilteredNodes] = useState<
- dNode[] | undefined
- >(undefined);
-
- useEffect(() => {
- // keep original nodes as a record of the nodes' toggle status
- setOriginalNodes(prev => {
- const newOgNodes = merge(initialNodes, prev);
- if (stringifyIsEqual(prev, newOgNodes)) {
- return prev;
- }
- return newOgNodes;
- });
- }, [initialNodes]);
-
- // wait for changes to filtered node executions
- useEffect(() => {
- if (filteredNodeExecutionsQuery.isFetching) {
- return;
- }
-
- const newFilteredNodeExecutions = isPhaseFilter(filters)
- ? undefined
- : filteredNodeExecutionsQuery.data;
-
- setFilteredNodeExecutions(prev => {
- if (isEqual(prev, newFilteredNodeExecutions)) {
- return prev;
- }
-
- return newFilteredNodeExecutions;
- });
- }, [filteredNodeExecutionsQuery]);
-
- useEffect(() => {
- const newShownNodes =
- filters.length > 0 && initialFilteredNodes
- ? // if there are filtered nodes, merge original ones into them to preserve toggle status
- mergeOriginIntoNodes(
- cloneDeep(initialFilteredNodes),
- cloneDeep(originalNodes),
- )
- : // else, merge originalNodes into initialNodes to preserve toggle status
- mergeOriginIntoNodes(
- cloneDeep(initialNodes),
- cloneDeep(originalNodes),
- );
-
- const plainNodes = convertToPlainNodes(newShownNodes || []);
- const updatedShownNodesMap = plainNodes.map(node => {
- const execution = nodeExecutionsById?.[node?.scopedId];
- return {
- ...node,
- startedAt: execution?.closure.startedAt,
- execution,
- };
- });
- setShowNodes(updatedShownNodesMap);
- }, [
- initialNodes,
- initialFilteredNodes,
- originalNodes,
- nodeExecutionsById,
- filters,
- ]);
-
- useEffect(() => {
- setFilters(prev => {
- if (isEqual(prev, appliedFilters)) {
- return prev;
- }
- return JSON.parse(JSON.stringify(appliedFilters));
- });
- }, [appliedFilters]);
-
- // Memoizing columns so they won't be re-generated unless the styles change
- const compiledNodes = extractCompiledNodes(compiledWorkflowClosure);
- const columns = useMemo(
- () => generateColumns(columnStyles, compiledNodes),
- [columnStyles, compiledNodes],
- );
-
- useEffect(() => {
- if (filters.length > 0) {
- // if filter was apllied, but filteredNodeExecutions is empty, we only appliied Phase filter,
- // and need to clear out items manually
- if (!filteredNodeExecutions) {
- // top level
- const filteredNodes = filterNodes(
- initialNodes,
- nodeExecutionsById,
- filters,
- );
-
- setInitialFilteredNodes(filteredNodes);
- } else {
- const filteredNodes = initialNodes.filter((node: dNode) =>
- filteredNodeExecutions.find(
- (execution: NodeExecution) => execution.scopedId === node.scopedId,
- ),
- );
- setInitialFilteredNodes(filteredNodes);
- }
- }
- }, [initialNodes, filteredNodeExecutions, filters]);
-
- const toggleNode = async (id: string, scopedId: string, level: number) => {
- searchNode(originalNodes, 0, id, scopedId, level);
- setOriginalNodes([...originalNodes]);
- };
-
- return (
- <>
-
-
-
-
-
-
- {showNodes.length > 0 ? (
- showNodes.map(node => {
- return (
-
-
-
- );
- })
- ) : (
-
- )}
-
-
- >
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/RowExpander.tsx b/packages/console/src/components/Executions/Tables/RowExpander.tsx
deleted file mode 100644
index 42f8cdb70..000000000
--- a/packages/console/src/components/Executions/Tables/RowExpander.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import * as React from 'react';
-import { IconButton } from '@material-ui/core';
-import ChevronRight from '@material-ui/icons/ChevronRight';
-import ExpandMore from '@material-ui/icons/ExpandMore';
-import t from './strings';
-
-interface RowExpanderProps {
- expanded: boolean;
- disabled?: boolean;
- key?: string;
- onClick: () => void;
-}
-/** A simple expand/collapse arrow to be rendered next to row items. */
-export const RowExpander = React.forwardRef<
- HTMLButtonElement,
- RowExpanderProps
->(({ disabled, expanded, key, onClick }, ref) => {
- return (
- ) => {
- // prevent the parent row body onClick event trigger
- e.stopPropagation();
- onClick();
- }}
- disabled={disabled}
- >
- {expanded ? : }
-
- );
-});
diff --git a/packages/console/src/components/Executions/Tables/SelectNodeExecutionLink.tsx b/packages/console/src/components/Executions/Tables/SelectNodeExecutionLink.tsx
deleted file mode 100644
index bbc3b5ff5..000000000
--- a/packages/console/src/components/Executions/Tables/SelectNodeExecutionLink.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import { Link } from '@material-ui/core';
-import { NodeExecution, NodeExecutionIdentifier } from 'models/Execution/types';
-import * as React from 'react';
-
-/** Renders a link that, when clicked, will trigger selection of the
- * given NodeExecution.
- */
-export const SelectNodeExecutionLink: React.FC<{
- className?: string;
- execution: NodeExecution;
- linkText: string;
- setSelectedExecution: (
- selectedExecutionId: NodeExecutionIdentifier | null,
- ) => void;
-}> = ({ className, execution, linkText, setSelectedExecution }) => {
- // open the side panel for selected execution's detail
- const onClick = (e: React.MouseEvent) => {
- // prevent the parent row body onClick event trigger
- e.stopPropagation();
- // use null in case if there is no execution provided - when it is null will close panel
- setSelectedExecution(execution?.id ?? null);
- };
-
- return (
-
- {linkText}
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/WorkflowExecutionLink.tsx b/packages/console/src/components/Executions/Tables/WorkflowExecutionLink.tsx
deleted file mode 100644
index b9e7029dd..000000000
--- a/packages/console/src/components/Executions/Tables/WorkflowExecutionLink.tsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import React from 'react';
-import classnames from 'classnames';
-import { useCommonStyles } from 'components/common/styles';
-import { WorkflowExecutionIdentifier } from 'models/Execution/types';
-import { Link as RouterLink } from 'react-router-dom';
-import { Routes } from 'routes/routes';
-import { history } from 'routes/history';
-
-/** A simple component to render a link to a specific WorkflowExecution */
-export const WorkflowExecutionLink: React.FC<{
- className?: string;
- color?: 'primary' | 'disabled';
- id: WorkflowExecutionIdentifier;
-}> = ({ className, color = 'primary', id }) => {
- const commonStyles = useCommonStyles();
- const {
- location: { pathname, hash, search },
- } = history;
- const fromExecutionNav = pathname.split('/').pop() === 'executions';
-
- const linkColor =
- color === 'disabled'
- ? commonStyles.secondaryLink
- : commonStyles.primaryLink;
-
- // preserve router deep link state
- const backLink = pathname + search + hash;
-
- return (
-
- {id.name}
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/WorkflowExecutionRow.tsx b/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/WorkflowExecutionRow.tsx
deleted file mode 100644
index 64a9d9ea0..000000000
--- a/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/WorkflowExecutionRow.tsx
+++ /dev/null
@@ -1,162 +0,0 @@
-import * as React from 'react';
-import { useState } from 'react';
-import { useMutation } from 'react-query';
-import { makeStyles, Theme } from '@material-ui/core';
-import classnames from 'classnames';
-import { useSnackbar } from 'notistack';
-import { Execution } from 'models/Execution/types';
-import { ExecutionState } from 'models/Execution/enums';
-import { updateExecution } from 'models/Execution/api';
-import { ListRowProps } from 'react-virtualized';
-import { isExecutionArchived } from '../../utils';
-import { ExpandableExecutionError } from '../ExpandableExecutionError';
-import { useExecutionTableStyles } from '../styles';
-import {
- WorkflowExecutionColumnDefinition,
- WorkflowExecutionsTableState,
-} from '../types';
-import { showOnHoverClass } from './cells';
-import {
- useConfirmationSection,
- useWorkflowExecutionsTableColumns,
-} from './useWorkflowExecutionsTableColumns';
-import t from './strings';
-
-const useStyles = makeStyles((theme: Theme) => ({
- row: {
- paddingLeft: theme.spacing(2),
- // All children using the showOnHover class will be hidden until
- // the mouse enters the container
- [`& .${showOnHoverClass}`]: {
- opacity: 0,
- },
- [`&:hover .${showOnHoverClass}`]: {
- opacity: 1,
- },
- },
-}));
-
-export interface WorkflowExecutionRowProps extends Partial {
- showWorkflowName: boolean;
- errorExpanded?: boolean;
- execution: Execution;
- onExpandCollapseError?(expanded: boolean): void;
- state: WorkflowExecutionsTableState;
-}
-
-/** Renders a single `Execution` record as a row. Designed to be used as a child
- * of `WorkflowExecutionTable`.
- */
-export const WorkflowExecutionRow: React.FC<
- WorkflowExecutionRowProps & {
- style?: React.CSSProperties;
- }
-> = ({
- showWorkflowName,
- errorExpanded,
- execution,
- onExpandCollapseError,
- state,
- style,
-}) => {
- const { enqueueSnackbar } = useSnackbar();
- const tableStyles = useExecutionTableStyles();
- const styles = useStyles();
-
- const isArchived = isExecutionArchived(execution);
- const [hideItem, setHideItem] = useState(false);
- const [isUpdating, setIsUpdating] = useState(false);
- const [showConfirmation, setShowConfirmation] = useState(false);
-
- const mutation = useMutation(
- (newState: ExecutionState) => updateExecution(execution.id, newState),
- {
- onMutate: () => setIsUpdating(true),
- onSuccess: () => {
- enqueueSnackbar(t('archiveSuccess', !isArchived), {
- variant: 'success',
- });
- setHideItem(true);
- // ensure to collapse error info and re-calculate rows positions.
- onExpandCollapseError?.(false);
- },
- onError: () => {
- enqueueSnackbar(`${mutation.error ?? t('archiveError', !isArchived)}`, {
- variant: 'error',
- });
- },
- onSettled: () => {
- setShowConfirmation(false);
- setIsUpdating(false);
- },
- },
- );
-
- const onArchiveConfirmClick = () => {
- mutation.mutate(
- isArchived
- ? ExecutionState.EXECUTION_ACTIVE
- : ExecutionState.EXECUTION_ARCHIVED,
- );
- };
-
- const columns = useWorkflowExecutionsTableColumns({
- showWorkflowName,
- onArchiveClick: () => setShowConfirmation(true),
- });
- const confirmation = useConfirmationSection({
- isArchived,
- isLoading: isUpdating,
- onCancel: () => setShowConfirmation(false),
- onConfirmClick: onArchiveConfirmClick,
- });
- // To hide the onHover action buttons,
- // we take off the last column which is onHover actions buttons
- const columnsWithApproval = [...columns.slice(0, -1), confirmation];
-
- // we show error info only on active items
- const { abortMetadata, error } = execution.closure;
- const showErrorInfo = !isArchived && (error || abortMetadata);
-
- const renderCell = ({
- className,
- key: columnKey,
- cellRenderer,
- }: WorkflowExecutionColumnDefinition): JSX.Element => (
-
- {cellRenderer({ execution, state })}
-
- );
-
- if (hideItem) {
- return null;
- }
-
- return (
-
-
- {!showConfirmation
- ? columns.map(renderCell)
- : columnsWithApproval.map(renderCell)}
-
- {showErrorInfo ? (
-
- ) : null}
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/cells.tsx b/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/cells.tsx
deleted file mode 100644
index 0a0e68718..000000000
--- a/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/cells.tsx
+++ /dev/null
@@ -1,227 +0,0 @@
-import * as React from 'react';
-import {
- Typography,
- IconButton,
- Button,
- CircularProgress,
-} from '@material-ui/core';
-import ArchiveOutlined from '@material-ui/icons/ArchiveOutlined';
-import UnarchiveOutline from '@material-ui/icons/UnarchiveOutlined';
-import LaunchPlanIcon from '@material-ui/icons/AssignmentOutlined';
-import InputOutputIcon from '@material-ui/icons/Tv';
-import {
- formatDateLocalTimezone,
- formatDateUTC,
- millisecondsToHMS,
-} from 'common/formatters';
-import { timestampToDate } from 'common/utils';
-
-import { ExecutionStatusBadge } from 'components/Executions/ExecutionStatusBadge';
-import { Execution } from 'models/Execution/types';
-import { ExecutionState, WorkflowExecutionPhase } from 'models/Execution/enums';
-import classnames from 'classnames';
-import { LaunchPlanLink } from 'components/LaunchPlan/LaunchPlanLink';
-import { WorkflowExecutionsTableState } from '../types';
-import { WorkflowExecutionLink } from '../WorkflowExecutionLink';
-import { getWorkflowExecutionTimingMS, isExecutionArchived } from '../../utils';
-import { useStyles } from './styles';
-import t from './strings';
-
-export function getExecutionIdCell(
- execution: Execution,
- className: string,
- showWorkflowName?: boolean,
-): React.ReactNode {
- const { startedAt, workflowId } = execution.closure;
- const isArchived = isExecutionArchived(execution);
-
- return (
- <>
-
-
- {showWorkflowName ? workflowId.name : t('lastRunStartedAt', startedAt)}
-
- >
- );
-}
-
-export function getStatusCell(execution: Execution): React.ReactNode {
- const isArchived = isExecutionArchived(execution);
- const phase = execution.closure.phase ?? WorkflowExecutionPhase.UNDEFINED;
-
- return (
-
- );
-}
-
-export function getStartTimeCell(execution: Execution): React.ReactNode {
- const { startedAt } = execution.closure;
-
- if (!startedAt) {
- return null;
- }
-
- const startedAtDate = timestampToDate(startedAt);
- const isArchived = isExecutionArchived(execution);
-
- return (
- <>
-
- {formatDateUTC(startedAtDate)}
-
-
- {formatDateLocalTimezone(startedAtDate)}
-
- >
- );
-}
-
-export function getDurationCell(execution: Execution): React.ReactNode {
- const isArchived = isExecutionArchived(execution);
- const timing = getWorkflowExecutionTimingMS(execution);
-
- return (
-
- {timing !== null ? millisecondsToHMS(timing.duration) : ''}
-
- );
-}
-
-export function getLaunchPlanCell(
- execution: Execution,
- className: string,
-): React.ReactNode {
- const isArchived = isExecutionArchived(execution);
- const lp = execution.spec.launchPlan;
- const version = execution.spec.launchPlan.version;
-
- return (
- <>
-
-
- {version}
-
- >
- );
-}
-
-export const showOnHoverClass = 'showOnHover';
-export function getActionsCell(
- execution: Execution,
- state: WorkflowExecutionsTableState,
- showLaunchPlan: boolean,
- wrapperClassName: string,
- iconClassName: string,
- onArchiveClick?: () => void, // (newState: ExecutionState) => void,
-): React.ReactNode {
- const isArchived = isExecutionArchived(execution);
- const onClick = () => state.setSelectedIOExecution(execution);
-
- const getArchiveIcon = (isArchived: boolean) =>
- isArchived ? : ;
-
- return (
-
-
-
-
- {showLaunchPlan && (
- {
- /* Not implemented */
- }}
- >
-
-
- )}
- {!!onArchiveClick && (
-
- {getArchiveIcon(isArchived)}
-
- )}
-
- );
-}
-
-/**
- * ApprovalDooubleCell - represents approval request to Archive/Cancel operation on specific execution
- */
-export interface ApprovalDoubleCellProps {
- isArchived: boolean;
- isLoading: boolean;
- onCancel: () => void;
- onConfirmClick: (newState: ExecutionState) => void;
-}
-
-export function ApprovalDoubleCell(props: ApprovalDoubleCellProps) {
- const { isArchived, isLoading, onCancel, onConfirmClick } = props;
- const styles = useStyles();
-
- if (isLoading) {
- return (
-
-
-
- );
- }
-
- return (
- <>
-
-
- >
- );
-}
diff --git a/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/strings.ts b/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/strings.ts
deleted file mode 100644
index 36885a578..000000000
--- a/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/strings.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { createLocalizedString } from '@flyteorg/locale';
-import { dateFromNow } from 'common/formatters';
-import { timestampToDate } from 'common/utils';
-import { Protobuf } from '@flyteorg/flyteidl-types';
-
-const str = {
- tableLabel_name: 'execution id',
- tableLabel_launchPlan: 'launch plan',
- tableLabel_phase: 'status',
- tableLabel_startedAt: 'start time',
- tableLabel_duration: 'duration',
- tableLabel_actions: '',
- cancelAction: 'Cancel',
- inputOutputTooltip: 'View Inputs & Outputs',
- launchPlanTooltip: 'View Launch Plan',
- archiveAction: (isArchived: boolean) =>
- isArchived ? 'Unarchive' : 'Archive',
- archiveSuccess: (isArchived: boolean) =>
- `Item was successfully ${isArchived ? 'archived' : 'unarchived'}`,
- archiveError: (isArchived: boolean) =>
- `Error: Something went wrong, we can not ${
- isArchived ? 'archive' : 'unarchive'
- } item`,
- lastRunStartedAt: (startedAt?: Protobuf.ITimestamp) => {
- return startedAt
- ? `Last run ${dateFromNow(timestampToDate(startedAt))}`
- : '';
- },
-};
-
-export { patternKey } from '@flyteorg/locale';
-export default createLocalizedString(str);
diff --git a/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/styles.ts b/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/styles.ts
deleted file mode 100644
index e5b01992a..000000000
--- a/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/styles.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { makeStyles, Theme } from '@material-ui/core';
-import { workflowExecutionsTableColumnWidths } from '../constants';
-
-export const useStyles = makeStyles((theme: Theme) => ({
- cellName: {
- paddingLeft: theme.spacing(1),
- },
- columnName: {
- flexGrow: 1,
- flexBasis: workflowExecutionsTableColumnWidths.name,
- whiteSpace: 'normal',
- },
- columnLaunchPlan: {
- flexGrow: 1,
- flexBasis: workflowExecutionsTableColumnWidths.launchPlan,
- overflow: 'hidden',
- },
- columnLastRun: {
- flexBasis: workflowExecutionsTableColumnWidths.lastRun,
- },
- columnStatus: {
- flexBasis: workflowExecutionsTableColumnWidths.phase,
- },
- columnStartedAt: {
- flexBasis: workflowExecutionsTableColumnWidths.startedAt,
- },
- columnDuration: {
- flexBasis: workflowExecutionsTableColumnWidths.duration,
- textAlign: 'right',
- },
- columnActions: {
- flexBasis: workflowExecutionsTableColumnWidths.actions,
- marginLeft: theme.spacing(2),
- marginRight: theme.spacing(2),
- textAlign: 'right',
- },
- rightMargin: {
- marginRight: theme.spacing(1),
- },
- confirmationButton: {
- borderRadius: 0,
- // make the button responsive, so the button won't overflow
- width: '50%',
- minHeight: '53px',
- // cancel margins that are coming from table row style
- marginTop: theme.spacing(-1),
- marginBottom: theme.spacing(-1),
- },
- actionContainer: {
- transition: theme.transitions.create('opacity', {
- duration: theme.transitions.duration.shorter,
- easing: theme.transitions.easing.easeInOut,
- }),
- },
- actionProgress: {
- width: '100px', // same as confirmationButton size
- textAlign: 'center',
- },
-}));
diff --git a/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/useWorkflowExecutionsTableColumns.tsx b/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/useWorkflowExecutionsTableColumns.tsx
deleted file mode 100644
index 25d378987..000000000
--- a/packages/console/src/components/Executions/Tables/WorkflowExecutionTable/useWorkflowExecutionsTableColumns.tsx
+++ /dev/null
@@ -1,116 +0,0 @@
-import * as React from 'react';
-
-import { FeatureFlag, useFeatureFlag } from 'basics/FeatureFlags';
-import { useCommonStyles } from 'components/common/styles';
-import { WorkflowExecutionColumnDefinition } from '../types';
-import {
- ApprovalDoubleCell,
- ApprovalDoubleCellProps,
- getActionsCell,
- getDurationCell,
- getExecutionIdCell,
- getStartTimeCell,
- getStatusCell,
- getLaunchPlanCell,
-} from './cells';
-import { useStyles } from './styles';
-import t, { patternKey } from './strings';
-
-interface WorkflowExecutionColumnOptions {
- showWorkflowName?: boolean;
- onArchiveClick?: () => void;
-}
-
-/** Returns a memoized list of column definitions to use when rendering a
- * `WorkflowExecutionRow`. Memoization is based on common/column style objects
- * and any fields in the incoming `WorkflowExecutionColumnOptions` object.
- */
-export function useWorkflowExecutionsTableColumns(
- props: WorkflowExecutionColumnOptions,
-): WorkflowExecutionColumnDefinition[] {
- const styles = useStyles();
- const commonStyles = useCommonStyles();
- const isLaunchPlanEnabled = useFeatureFlag(FeatureFlag.LaunchPlan);
-
- return React.useMemo(() => {
- const arr: WorkflowExecutionColumnDefinition[] = [
- {
- cellRenderer: ({ execution }) =>
- getExecutionIdCell(
- execution,
- commonStyles.textWrapped,
- props.showWorkflowName,
- ),
- className: styles.columnName,
- key: 'name',
- label: t(patternKey('tableLabel', 'name')),
- },
- {
- cellRenderer: ({ execution }) =>
- getLaunchPlanCell(execution, commonStyles.textWrapped),
- className: styles.columnLaunchPlan,
- key: 'launchPlan',
- label: t(patternKey('tableLabel', 'launchPlan')),
- },
- {
- cellRenderer: ({ execution }) => getStatusCell(execution),
- className: styles.columnStatus,
- key: 'phase',
- label: t(patternKey('tableLabel', 'phase')),
- },
- {
- cellRenderer: ({ execution }) => getStartTimeCell(execution),
- className: styles.columnStartedAt,
- key: 'startedAt',
- label: t(patternKey('tableLabel', 'startedAt')),
- },
- {
- cellRenderer: ({ execution }) => getDurationCell(execution),
- className: styles.columnDuration,
- key: 'duration',
- label: t(patternKey('tableLabel', 'duration')),
- },
- {
- cellRenderer: ({ execution, state }) =>
- getActionsCell(
- execution,
- state,
- isLaunchPlanEnabled,
- styles.actionContainer,
- styles.rightMargin,
- props.onArchiveClick,
- ),
- className: styles.columnActions,
- key: 'actions',
- label: t(patternKey('tableLabel', 'actions')),
- },
- ];
-
- return arr;
- }, [
- styles,
- commonStyles,
- props.showWorkflowName,
- isLaunchPlanEnabled,
- props.onArchiveClick,
- ]);
-}
-
-/**
- * Returns a confirmation section to be used in a row instead of regular column cells
- * when an action confrimation is required from user
- */
-export function useConfirmationSection(
- props: ApprovalDoubleCellProps,
-): WorkflowExecutionColumnDefinition {
- const styles = useStyles();
-
- const approve: WorkflowExecutionColumnDefinition = {
- cellRenderer: () => ,
- className: styles.columnActions,
- key: 'actions',
- // Label is used for headers only, this item doesn't have header as shown temporarily in the row
- label: '',
- };
- return approve;
-}
diff --git a/packages/console/src/components/Executions/Tables/WorkflowExecutionsTable.tsx b/packages/console/src/components/Executions/Tables/WorkflowExecutionsTable.tsx
deleted file mode 100644
index f3ad23e1c..000000000
--- a/packages/console/src/components/Executions/Tables/WorkflowExecutionsTable.tsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import classnames from 'classnames';
-import * as React from 'react';
-import { ListRowRenderer } from 'react-virtualized';
-import { noExecutionsFoundString } from 'common/constants';
-import { getCacheKey } from 'components/Cache/utils';
-import { useCommonStyles } from 'components/common/styles';
-import { ListProps } from 'components/common/types';
-import { DataList, DataListRef } from 'components/Tables/DataList';
-import { Execution } from 'models/Execution/types';
-import { ExecutionInputsOutputsModal } from '../ExecutionInputsOutputsModal';
-import { ExecutionsTableHeader } from './ExecutionsTableHeader';
-import { useExecutionTableStyles } from './styles';
-import { useWorkflowExecutionsTableColumns } from './WorkflowExecutionTable/useWorkflowExecutionsTableColumns';
-import { useWorkflowExecutionsTableState } from './useWorkflowExecutionTableState';
-import { WorkflowExecutionRow } from './WorkflowExecutionTable/WorkflowExecutionRow';
-
-export interface WorkflowExecutionsTableProps extends ListProps {
- showWorkflowName?: boolean;
-}
-
-/** Renders a table of WorkflowExecution records. Executions with errors will
- * have an expanadable container rendered as part of the table row.
- */
-export const WorkflowExecutionsTable: React.FC<
- WorkflowExecutionsTableProps
-> = props => {
- const { value: executions, showWorkflowName = false } = props;
- const [expandedErrors, setExpandedErrors] = React.useState<
- Dictionary
- >({});
- const state = useWorkflowExecutionsTableState();
- const commonStyles = useCommonStyles();
- const tableStyles = useExecutionTableStyles();
- const listRef = React.useRef(null);
-
- // Reset error expansion states whenever list changes
- React.useLayoutEffect(() => {
- setExpandedErrors({});
- }, [executions]);
-
- // passing an empty property list, as we only use it for table headers info here
- const columns = useWorkflowExecutionsTableColumns({});
-
- const retry = () => props.fetch();
- const onCloseIOModal = () => state.setSelectedIOExecution(null);
- const recomputeRow = (rowIndex: number) => {
- if (listRef.current !== null) {
- listRef.current.recomputeRowHeights(rowIndex);
- }
- };
-
- // Custom renderer to allow us to append error content to executions which
- // are in a failed state
- const rowRenderer: ListRowRenderer = rowProps => {
- const execution = executions[rowProps.index];
- const cacheKey = getCacheKey(execution.id);
- const onExpandCollapseError = (expanded: boolean) => {
- setExpandedErrors(currentExpandedErrors => ({
- ...currentExpandedErrors,
- [cacheKey]: expanded,
- }));
- recomputeRow(rowProps.index);
- };
- return (
-
- );
- };
-
- return (
-
-
-
-
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/WorkflowVersionRow.tsx b/packages/console/src/components/Executions/Tables/WorkflowVersionRow.tsx
deleted file mode 100644
index 2c1898a54..000000000
--- a/packages/console/src/components/Executions/Tables/WorkflowVersionRow.tsx
+++ /dev/null
@@ -1,126 +0,0 @@
-import { makeStyles, TableCell, Theme } from '@material-ui/core';
-import Radio from '@material-ui/core/Radio';
-import classnames from 'classnames';
-import * as React from 'react';
-import { ListRowProps } from 'react-virtualized';
-import { Workflow } from 'models/Workflow/types';
-import TableRow from '@material-ui/core/TableRow';
-import { useWorkflowExecutions } from 'components/hooks/useWorkflowExecutions';
-import { executionSortFields } from 'models/Execution/constants';
-import { SortDirection } from 'models/AdminEntity/types';
-import { executionFilterGenerator } from 'components/Entities/generators';
-import { ResourceIdentifier } from 'models/Common/types';
-import { useWorkflowVersionsColumnStyles } from './styles';
-import {
- WorkflowExecutionsTableState,
- WorkflowVersionColumnDefinition,
-} from './types';
-
-const useStyles = makeStyles((theme: Theme) => ({
- row: {
- cursor: 'pointer',
- height: theme.spacing(8),
- },
- cell: {
- padding: theme.spacing(1),
- },
-}));
-
-export interface WorkflowVersionRowProps extends Partial {
- columns: WorkflowVersionColumnDefinition[];
- workflow: Workflow;
- state: WorkflowExecutionsTableState;
- onClick: (() => void) | undefined;
- versionView?: boolean;
- isChecked?: boolean;
-}
-
-/**
- * Renders a single `Workflow` record as a row. Designed to be used as a child
- * of `WorkflowVersionsTable`.
- * @param columns
- * @param workflow
- * @param state
- * @param style
- * @param onClick
- * @param versionView
- * @param isChecked
- * @constructor
- */
-export const WorkflowVersionRow: React.FC<
- WorkflowVersionRowProps & { style?: React.CSSProperties }
-> = ({
- columns,
- workflow,
- state,
- style,
- onClick,
- versionView = false,
- isChecked = false,
-}) => {
- const versionTableStyles = useWorkflowVersionsColumnStyles();
- const styles = useStyles();
-
- const sort = {
- key: executionSortFields.createdAt,
- direction: SortDirection.DESCENDING,
- };
-
- const baseFilters = React.useMemo(
- () =>
- workflow.id.resourceType
- ? executionFilterGenerator[workflow.id.resourceType](
- workflow.id as ResourceIdentifier,
- workflow.id.version,
- )
- : [],
- [workflow.id.version],
- );
-
- const executions = useWorkflowExecutions(
- {
- domain: workflow.id.domain,
- project: workflow.id.project,
- version: workflow.id.version,
- },
- {
- sort,
- filter: baseFilters,
- limit: 10,
- },
- );
-
- return (
-
- {versionView && (
-
-
-
- )}
- {columns.map(({ className, key: columnKey, cellRenderer }) => (
-
- {cellRenderer({
- workflow,
- state,
- executions,
- })}
-
- ))}
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/__mocks__/WorkflowExecutionsTable.tsx b/packages/console/src/components/Executions/Tables/__mocks__/WorkflowExecutionsTable.tsx
deleted file mode 100644
index 88487098c..000000000
--- a/packages/console/src/components/Executions/Tables/__mocks__/WorkflowExecutionsTable.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import classnames from 'classnames';
-import { useCommonStyles } from 'components/common/styles';
-import * as React from 'react';
-import { ExecutionsTableHeader } from '../ExecutionsTableHeader';
-import { useExecutionTableStyles } from '../styles';
-import { useWorkflowExecutionsTableColumns } from '../WorkflowExecutionTable/useWorkflowExecutionsTableColumns';
-import { useWorkflowExecutionsTableState } from '../useWorkflowExecutionTableState';
-import { WorkflowExecutionRow } from '../WorkflowExecutionTable/WorkflowExecutionRow';
-import { WorkflowExecutionsTableProps } from '../WorkflowExecutionsTable';
-
-/** Mocked, simpler version of WorkflowExecutionsTable which does not use a DataList since
- * that will not work in a test environment.
- */
-export const WorkflowExecutionsTable: React.FC<
- WorkflowExecutionsTableProps
-> = props => {
- const { value: executions, showWorkflowName = false } = props;
- const state = useWorkflowExecutionsTableState();
- const commonStyles = useCommonStyles();
- const tableStyles = useExecutionTableStyles();
- const columns = useWorkflowExecutionsTableColumns({});
-
- return (
-
-
- {executions.map(execution => (
-
- ))}
-
- );
-};
diff --git a/packages/console/src/components/Executions/Tables/__stories__/NodeExecutionsTable.stories.tsx b/packages/console/src/components/Executions/Tables/__stories__/NodeExecutionsTable.stories.tsx
deleted file mode 100644
index d8f177724..000000000
--- a/packages/console/src/components/Executions/Tables/__stories__/NodeExecutionsTable.stories.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import { storiesOf } from '@storybook/react';
-import { NodeExecutionDetailsContext } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { makeNodeExecutionListQuery } from 'components/Executions/nodeExecutionQueries';
-import { NodeExecutionDisplayType } from 'components/Executions/types';
-import { basicPythonWorkflow } from 'mocks/data/fixtures/basicPythonWorkflow';
-import * as React from 'react';
-import { useQuery, useQueryClient } from 'react-query';
-import { NodeExecutionsTable } from '../NodeExecutionsTable';
-
-const useStyles = makeStyles((theme: Theme) => ({
- container: {
- borderLeft: `1px solid ${theme.palette.grey[400]}`,
- display: 'flex',
- height: '100vh',
- padding: `${theme.spacing(2)}px 0`,
- width: '100vw',
- },
-}));
-
-const fixture = basicPythonWorkflow.generate();
-const workflowExecution = fixture.workflowExecutions.top.data;
-const workflowId = {
- ...fixture.workflowExecutions.top.data.id,
- version: '0.1',
-};
-const compiledWorkflowClosure = null;
-
-const getNodeExecutionDetails = async () => {
- return {
- displayId: 'node0',
- displayName: 'basic.byton.workflow.unique.task_name',
- displayType: NodeExecutionDisplayType.PythonTask,
- };
-};
-
-const stories = storiesOf('Tables/NodeExecutionsTable', module);
-stories.addDecorator(story => {
- return {story()}
;
-});
-stories.add('Basic', () => {
- const query = useQuery(
- makeNodeExecutionListQuery(useQueryClient(), workflowExecution.id),
- );
- return query.data ? (
-
-
-
- ) : (
-
- );
-});
-stories.add('With no items', () => {
- return (
-
-
-
- );
-});
diff --git a/packages/console/src/components/Executions/Tables/__stories__/WorkflowExecutionsTable.stories.tsx b/packages/console/src/components/Executions/Tables/__stories__/WorkflowExecutionsTable.stories.tsx
deleted file mode 100644
index 4bbd96a0f..000000000
--- a/packages/console/src/components/Executions/Tables/__stories__/WorkflowExecutionsTable.stories.tsx
+++ /dev/null
@@ -1,59 +0,0 @@
-import * as React from 'react';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import { action } from '@storybook/addon-actions';
-import { storiesOf } from '@storybook/react';
-import { ExecutionState } from 'models/Execution/enums';
-import { createMockWorkflowExecutionsListResponse } from 'models/Execution/__mocks__/mockWorkflowExecutionsData';
-import {
- WorkflowExecutionsTable,
- WorkflowExecutionsTableProps,
-} from '../WorkflowExecutionsTable';
-
-const useStyles = makeStyles((theme: Theme) => ({
- container: {
- borderLeft: `1px solid ${theme.palette.grey[400]}`,
- display: 'flex',
- height: '100vh',
- padding: `${theme.spacing(2)}px 0`,
- width: '100vw',
- },
-}));
-
-const fetchAction = action('fetch');
-
-const propsArchived: WorkflowExecutionsTableProps = {
- value: createMockWorkflowExecutionsListResponse(
- 10,
- ExecutionState.EXECUTION_ARCHIVED,
- ).executions,
- lastError: null,
- isFetching: false,
- moreItemsAvailable: false,
- fetch: () => Promise.resolve(() => fetchAction() as unknown),
-};
-
-const props: WorkflowExecutionsTableProps = {
- value: createMockWorkflowExecutionsListResponse(
- 10,
- ExecutionState.EXECUTION_ACTIVE,
- ).executions,
- lastError: null,
- isFetching: false,
- moreItemsAvailable: false,
- fetch: () => Promise.resolve(() => fetchAction() as unknown),
-};
-
-const stories = storiesOf('Tables/WorkflowExecutionsTable', module);
-stories.addDecorator(story => (
- {story()}
-));
-stories.add('Basic', () => );
-stories.add('Only archived items', () => (
-
-));
-stories.add('With more items available', () => (
-
-));
-stories.add('With no items', () => (
-
-));
diff --git a/packages/console/src/components/Executions/Tables/constants.ts b/packages/console/src/components/Executions/Tables/constants.ts
deleted file mode 100644
index 19e428e79..000000000
--- a/packages/console/src/components/Executions/Tables/constants.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-export const workflowExecutionsTableColumnWidths = {
- duration: 100,
- actions: 130,
- lastRun: 130,
- name: 240,
- launchPlan: 120,
- phase: 120,
- startedAt: 200,
-};
-
-export const nodeExecutionsTableColumnWidths = {
- duration: 100,
- logs: 138,
- type: 144,
- nodeId: 144,
- name: 380,
- phase: 150,
- startedAt: 200,
-};
-
-export const workflowVersionsTableColumnWidths = {
- radio: 40,
- name: 380,
- release: 150,
- lastRun: 175,
- createdAt: 260,
- recentRun: 160,
-};
diff --git a/packages/console/src/components/Executions/Tables/nodeExecutionColumns.tsx b/packages/console/src/components/Executions/Tables/nodeExecutionColumns.tsx
deleted file mode 100644
index 23a1795c7..000000000
--- a/packages/console/src/components/Executions/Tables/nodeExecutionColumns.tsx
+++ /dev/null
@@ -1,212 +0,0 @@
-import { Tooltip, Typography } from '@material-ui/core';
-import {
- formatDateLocalTimezone,
- formatDateUTC,
- millisecondsToHMS,
-} from 'common/formatters';
-import { timestampToDate } from 'common/utils';
-import { useCommonStyles } from 'components/common/styles';
-import * as React from 'react';
-import { useEffect, useState } from 'react';
-import { CompiledNode } from 'models/Node/types';
-import { NodeExecutionPhase } from 'models/Execution/enums';
-import { getNodeTemplateName } from 'components/WorkflowGraph/utils';
-import classnames from 'classnames';
-import { useNodeExecutionContext } from '../contextProvider/NodeExecutionDetails';
-import { ExecutionStatusBadge } from '../ExecutionStatusBadge';
-import { NodeExecutionCacheStatus } from '../NodeExecutionCacheStatus';
-import {
- getNodeExecutionTimingMS,
- getNodeFrontendPhase,
- isNodeGateNode,
-} from '../utils';
-import { NodeExecutionActions } from './NodeExecutionActions';
-import { useColumnStyles } from './styles';
-import {
- NodeExecutionCellRendererData,
- NodeExecutionColumnDefinition,
-} from './types';
-import t from '../strings';
-import { NodeExecutionName } from '../ExecutionDetails/Timeline/NodeExecutionName';
-
-const DisplayId: React.FC = ({
- execution,
- className,
-}) => {
- const commonStyles = useCommonStyles();
- const { getNodeExecutionDetails } = useNodeExecutionContext();
- const [displayId, setDisplayId] = useState();
-
- useEffect(() => {
- let isCurrent = true;
- getNodeExecutionDetails(execution).then(res => {
- if (isCurrent) {
- setDisplayId(res?.displayId);
- }
- });
- return () => {
- isCurrent = false;
- };
- });
-
- const nodeId = displayId ?? execution.id.nodeId;
- return (
-
-
- {nodeId}
-
-
- );
-};
-
-const DisplayType: React.FC = ({
- execution,
- className,
-}) => {
- const { getNodeExecutionDetails } = useNodeExecutionContext();
- const [type, setType] = useState();
-
- useEffect(() => {
- let isCurrent = true;
- getNodeExecutionDetails(execution).then(res => {
- if (isCurrent) {
- setType(res?.displayType);
- }
- });
- return () => {
- isCurrent = false;
- };
- });
-
- return (
-
- {type}
-
- );
-};
-
-export function generateColumns(
- styles: ReturnType,
- nodes: CompiledNode[],
-): NodeExecutionColumnDefinition[] {
- return [
- {
- cellRenderer: ({ node, className }) => (
-
- ),
- className: styles.columnName,
- key: 'name',
- label: t('nameLabel'),
- },
- {
- cellRenderer: props => ,
- className: styles.columnNodeId,
- key: 'nodeId',
- label: t('nodeIdLabel'),
- },
- {
- cellRenderer: props => ,
- className: styles.columnType,
- key: 'type',
- label: t('typeLabel'),
- },
- {
- cellRenderer: ({ execution, className }) => {
- const isGateNode = isNodeGateNode(
- nodes,
- execution.metadata?.specNodeId || execution.id.nodeId,
- );
-
- const phase = getNodeFrontendPhase(
- execution.closure?.phase ?? NodeExecutionPhase.UNDEFINED,
- isGateNode,
- );
-
- return (
- <>
-
-
- >
- );
- },
- className: styles.columnStatus,
- key: 'phase',
- label: t('phaseLabel'),
- },
- {
- cellRenderer: ({ execution: { closure }, className }) => {
- const { startedAt } = closure;
- if (!startedAt) {
- return '';
- }
- const startedAtDate = timestampToDate(startedAt);
- return (
- <>
-
- {formatDateUTC(startedAtDate)}
-
-
- {formatDateLocalTimezone(startedAtDate)}
-
- >
- );
- },
- className: styles.columnStartedAt,
- key: 'startedAt',
- label: t('startedAtLabel'),
- },
- {
- cellRenderer: ({ execution, className }) => {
- const timing = getNodeExecutionTimingMS(execution);
- if (timing === null) {
- return '';
- }
- return (
- <>
-
- {millisecondsToHMS(timing.duration)}
-
- >
- );
- },
- className: styles.columnDuration,
- key: 'duration',
- label: () => (
- <>
-
- {t('durationLabel')}
-
-
- {t('queuedTimeLabel')}
-
- >
- ),
- },
- {
- cellRenderer: ({ execution, className }) =>
- execution.closure.phase === NodeExecutionPhase.UNDEFINED ? null : (
-
- ),
- className: styles.columnLogs,
- key: 'actions',
- label: '',
- },
- ];
-}
diff --git a/packages/console/src/components/Executions/Tables/strings.tsx b/packages/console/src/components/Executions/Tables/strings.tsx
deleted file mode 100644
index fe705a48e..000000000
--- a/packages/console/src/components/Executions/Tables/strings.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { createLocalizedString } from '@flyteorg/locale';
-
-const str = {
- durationLabel: 'duration',
- inputsAndOutputsTooltip: 'View Inputs & Outputs',
- nameLabel: 'task name',
- nodeIdLabel: 'node id',
- phaseLabel: 'status',
- queuedTimeLabel: 'queued time',
- rerunTooltip: 'Rerun',
- resumeTooltip: 'Resume',
- startedAtLabel: 'start time',
- typeLabel: 'type',
- loadMoreButton: 'Load More',
- expanderTitle: (expanded: boolean) =>
- expanded ? 'Collapse row' : 'Expand row',
-};
-
-export { patternKey } from '@flyteorg/locale';
-export default createLocalizedString(str);
diff --git a/packages/console/src/components/Executions/Tables/styles.ts b/packages/console/src/components/Executions/Tables/styles.ts
deleted file mode 100644
index b76fa4f79..000000000
--- a/packages/console/src/components/Executions/Tables/styles.ts
+++ /dev/null
@@ -1,188 +0,0 @@
-import { makeStyles, Theme } from '@material-ui/core';
-import { headerGridHeight } from 'components/Tables/constants';
-import {
- headerFontFamily,
- listhoverColor,
- tableHeaderColor,
- tablePlaceholderColor,
-} from 'components/Theme/constants';
-import {
- nodeExecutionsTableColumnWidths,
- workflowVersionsTableColumnWidths,
-} from './constants';
-
-export const selectedClassName = 'selected';
-export const grayedClassName = 'grayed';
-
-// NOTE: The order of these `makeStyles` calls is important, as it determines
-// specificity in the browser. The execution table styles are overridden by
-// the columns styles in some cases. So the column styles should be defined
-// last.
-export const useExecutionTableStyles = makeStyles((theme: Theme) => ({
- filters: {
- paddingLeft: theme.spacing(3),
- },
- [grayedClassName]: {
- color: theme.palette.grey[300],
- },
- borderBottom: {
- borderBottom: `1px solid ${theme.palette.divider}`,
- },
- errorContainer: {
- padding: `0 ${theme.spacing(8)}px ${theme.spacing(2)}px`,
- },
- expander: {
- alignItems: 'center',
- display: 'flex',
- justifyContent: 'center',
- marginLeft: theme.spacing(-4),
- marginRight: theme.spacing(1),
- width: theme.spacing(3),
- },
- headerColumn: {
- marginRight: theme.spacing(1),
- minWidth: 0,
- '&:first-of-type': {
- marginLeft: theme.spacing(2),
- },
- },
- headerColumnVersion: {
- width: theme.spacing(4),
- },
- headerRow: {
- alignItems: 'center',
- borderBottom: `4px solid ${theme.palette.divider}`,
- borderTop: `1px solid ${theme.palette.divider}`,
- color: tableHeaderColor,
- display: 'flex',
- fontFamily: headerFontFamily,
- flexDirection: 'row',
- height: theme.spacing(headerGridHeight),
- },
- noRowsContent: {
- color: tablePlaceholderColor,
- margin: `${theme.spacing(5)}px auto`,
- textAlign: 'center',
- },
- row: {
- display: 'flex',
- flexDirection: 'column',
- justifyContent: 'center',
- '&:hover': {
- backgroundColor: listhoverColor,
- },
- [`&.${selectedClassName}`]: {
- backgroundColor: listhoverColor,
- },
- [`&.${grayedClassName}`]: {
- color: theme.palette.grey[300],
- },
- },
- clickableRow: {
- cursor: 'pointer',
- },
- rowColumns: {
- alignItems: 'center',
- display: 'flex',
- flexDirection: 'row',
- },
- rowColumn: {
- marginRight: theme.spacing(1),
- minWidth: 0,
- paddingBottom: theme.spacing(1),
- paddingTop: theme.spacing(1),
- overflow: 'hidden',
- textOverflow: 'ellipsis',
- whiteSpace: 'nowrap',
- },
- scrollContainer: {
- flex: '1 1 0',
- overflowY: 'scroll',
- paddingBottom: theme.spacing(3),
- },
- tableContainer: {
- display: 'flex',
- flexDirection: 'column',
- },
-}));
-
-export const nameColumnLeftMarginGridWidth = 6;
-export const useColumnStyles = makeStyles((theme: Theme) => ({
- [`&.${grayedClassName}`]: {
- color: theme.palette.grey[400],
- },
- columnName: {
- flexGrow: 1,
- // We want this to fluidly expand into whatever available space,
- // so no minimum width.
- flexBasis: 0,
- overflow: 'hidden',
- '&:first-of-type': {
- marginLeft: theme.spacing(nameColumnLeftMarginGridWidth),
- },
- [`&.${grayedClassName}`]: {
- color: theme.palette.grey[400],
- },
- },
- columnNodeId: {
- flexBasis: nodeExecutionsTableColumnWidths.nodeId,
- [`&.${grayedClassName}`]: {
- color: theme.palette.grey[400],
- },
- },
- columnType: {
- flexBasis: nodeExecutionsTableColumnWidths.type,
- textTransform: 'capitalize',
- [`&.${grayedClassName}`]: {
- color: theme.palette.grey[400],
- },
- },
- columnStatus: {
- display: 'flex',
- flexBasis: nodeExecutionsTableColumnWidths.phase,
- },
- columnStartedAt: {
- flexBasis: nodeExecutionsTableColumnWidths.startedAt,
- [`&.${grayedClassName}`]: {
- color: theme.palette.grey[400],
- },
- },
- columnDuration: {
- flexBasis: nodeExecutionsTableColumnWidths.duration,
- textAlign: 'right',
- [`&.${grayedClassName}`]: {
- color: theme.palette.grey[400],
- },
- },
- columnLogs: {
- flexBasis: nodeExecutionsTableColumnWidths.logs,
- marginLeft: theme.spacing(4),
- marginRight: theme.spacing(2),
- [`&.${grayedClassName}`]: {
- color: theme.palette.grey[400],
- },
- },
- selectedExecutionName: {
- fontWeight: 'bold',
- },
-}));
-
-export const useWorkflowVersionsColumnStyles = makeStyles(() => ({
- columnRadioButton: {
- width: workflowVersionsTableColumnWidths.radio,
- },
- columnName: {
- flexBasis: workflowVersionsTableColumnWidths.name,
- whiteSpace: 'normal',
- flexGrow: 1,
- },
- columnCreatedAt: {
- flexBasis: workflowVersionsTableColumnWidths.createdAt,
- },
- columnLastRun: {
- flexBasis: workflowVersionsTableColumnWidths.lastRun,
- },
- columnRecentRun: {
- flexBasis: workflowVersionsTableColumnWidths.recentRun,
- },
-}));
diff --git a/packages/console/src/components/Executions/Tables/test/NodeExecutionActions.test.tsx b/packages/console/src/components/Executions/Tables/test/NodeExecutionActions.test.tsx
deleted file mode 100644
index 71f501b37..000000000
--- a/packages/console/src/components/Executions/Tables/test/NodeExecutionActions.test.tsx
+++ /dev/null
@@ -1,99 +0,0 @@
-import * as React from 'react';
-import { act, fireEvent, render, waitFor } from '@testing-library/react';
-import { NodeExecutionDetailsContextProvider } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { mockWorkflowId } from 'mocks/data/fixtures/types';
-import { QueryClient, QueryClientProvider } from 'react-query';
-import { createTestQueryClient } from 'test/utils';
-import { insertFixture } from 'mocks/data/insertFixture';
-import { mockServer } from 'mocks/server';
-import { basicPythonWorkflow } from 'mocks/data/fixtures/basicPythonWorkflow';
-import { NodeExecution } from 'models/Execution/types';
-import { cloneDeep } from 'lodash';
-import { DetailsPanelContextProvider } from 'components/Executions/ExecutionDetails/DetailsPanelContext';
-import { NodeExecutionActions } from '../NodeExecutionActions';
-
-jest.mock('components/Workflow/workflowQueries');
-jest.mock('components/Launch/LaunchForm/ResumeForm', () => ({
- ResumeForm: jest.fn(({ children }) => (
- {children}
- )),
-}));
-
-const { fetchWorkflow } = require('components/Workflow/workflowQueries');
-
-const state = { selectedExecution: null, setSelectedExeccution: jest.fn() };
-
-describe('Executions > Tables > NodeExecutionActions', () => {
- let queryClient: QueryClient;
- let fixture: ReturnType;
- let execution: NodeExecution;
-
- beforeEach(() => {
- fixture = basicPythonWorkflow.generate();
- execution = cloneDeep(
- fixture.workflowExecutions.top.nodeExecutions.pythonNode.data,
- );
- queryClient = createTestQueryClient();
- insertFixture(mockServer, fixture);
- fetchWorkflow.mockImplementation(() =>
- Promise.resolve(fixture.workflows.top),
- );
- });
-
- const renderComponent = props =>
- render(
-
-
-
-
-
-
- ,
- );
-
- it('should render rerun action, if id can be determined', async () => {
- let queryByTitle;
- await act(() => {
- const component = renderComponent({ execution, state });
- queryByTitle = component.queryByTitle;
- });
- await waitFor(() => queryByTitle('View Inputs & Outputs'));
-
- expect(queryByTitle('View Inputs & Outputs')).toBeInTheDocument();
- expect(queryByTitle('Resume')).not.toBeInTheDocument();
- expect(queryByTitle('Rerun')).toBeInTheDocument();
- });
-
- it('should render resume action, if the status is PAUSED', async () => {
- const mockExecution = { ...execution, closure: { phase: 100 } };
- let queryByTitle;
- await act(() => {
- const component = renderComponent({ execution: mockExecution, state });
- queryByTitle = component.queryByTitle;
- });
- await waitFor(() => queryByTitle('Resume'));
-
- expect(queryByTitle('View Inputs & Outputs')).toBeInTheDocument();
- expect(queryByTitle('Rerun')).toBeInTheDocument();
- expect(queryByTitle('Resume')).toBeInTheDocument();
- });
-
- it('should render ResumeForm on resume button click', async () => {
- const mockExecution = { ...execution, closure: { phase: 100 } };
- let queryByTitle, getByTitle, queryByTestId;
- await act(() => {
- const component = renderComponent({ execution: mockExecution, state });
- queryByTitle = component.queryByTitle;
- getByTitle = component.getByTitle;
- queryByTestId = component.queryByTestId;
- });
- await waitFor(() => queryByTitle('Resume'));
-
- expect(queryByTitle('Resume')).toBeInTheDocument();
-
- const resumeButton = getByTitle('Resume');
- await fireEvent.click(resumeButton);
-
- expect(queryByTestId('resume-form')).toBeInTheDocument();
- });
-});
diff --git a/packages/console/src/components/Executions/Tables/test/NodeExecutionRow.test.tsx b/packages/console/src/components/Executions/Tables/test/NodeExecutionRow.test.tsx
deleted file mode 100644
index a6ebf92bc..000000000
--- a/packages/console/src/components/Executions/Tables/test/NodeExecutionRow.test.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import * as React from 'react';
-import { render, waitFor } from '@testing-library/react';
-import { NodeExecutionDetailsContextProvider } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import { mockWorkflowId } from 'mocks/data/fixtures/types';
-import { QueryClient, QueryClientProvider } from 'react-query';
-import { createTestQueryClient } from 'test/utils';
-import { insertFixture } from 'mocks/data/insertFixture';
-import { mockServer } from 'mocks/server';
-import { basicPythonWorkflow } from 'mocks/data/fixtures/basicPythonWorkflow';
-import { NodeExecution } from 'models/Execution/types';
-import { dNode, dTypes } from 'models/Graph/types';
-import { NodeExecutionDynamicContext } from 'components/Executions/contextProvider/NodeExecutionDetails/NodeExecutionDynamicProvider';
-import { cloneDeep } from 'lodash';
-import { NodeExecutionRow } from '../NodeExecutionRow';
-
-jest.mock('components/Workflow/workflowQueries');
-const { fetchWorkflow } = require('components/Workflow/workflowQueries');
-
-const columns = [];
-const node: dNode = {
- id: 'n1',
- scopedId: 'n1',
- type: dTypes.start,
- name: 'node1',
- nodes: [],
- edges: [],
-};
-const onToggle = jest.fn();
-
-describe('Executions > Tables > NodeExecutionRow', () => {
- let queryClient: QueryClient;
- let fixture: ReturnType;
- let execution: NodeExecution;
-
- beforeEach(() => {
- fixture = basicPythonWorkflow.generate();
- execution = fixture.workflowExecutions.top.nodeExecutions.pythonNode.data;
- node.execution = cloneDeep(execution);
- queryClient = createTestQueryClient();
- insertFixture(mockServer, fixture);
- fetchWorkflow.mockImplementation(() =>
- Promise.resolve(fixture.workflows.top),
- );
- });
-
- const renderComponent = props => {
- const { node } = props;
- return render(
-
-
- n.execution),
- componentProps: {
- ref: null,
- },
- inView: false,
- }}
- >
-
-
-
- ,
- );
- };
- it('should not render expander if node is a leaf', async () => {
- const { queryByRole, queryByTestId } = renderComponent({
- columns,
- node,
- onToggle,
- });
- await waitFor(() => queryByRole('listitem'));
-
- expect(queryByRole('listitem')).toBeInTheDocument();
- expect(queryByTestId('expander')).not.toBeInTheDocument();
- });
-
- it('should render expander if node contains list of nodes', async () => {
- node.execution!.metadata!.isParentNode = true;
- const mockNode = {
- ...node,
- nodes: [node, node],
- };
-
- const { queryByRole, queryByTitle } = renderComponent({
- columns,
- node: mockNode,
- nodeExecution: execution,
- onToggle,
- });
- await waitFor(() => queryByRole('listitem'));
-
- expect(queryByRole('listitem')).toBeInTheDocument();
- expect(queryByTitle('Expand row')).toBeInTheDocument();
- });
-});
diff --git a/packages/console/src/components/Executions/Tables/test/NodeExecutionsTable.test.tsx b/packages/console/src/components/Executions/Tables/test/NodeExecutionsTable.test.tsx
deleted file mode 100644
index 5652401bd..000000000
--- a/packages/console/src/components/Executions/Tables/test/NodeExecutionsTable.test.tsx
+++ /dev/null
@@ -1,336 +0,0 @@
-import { render, waitFor } from '@testing-library/react';
-import { NodeExecutionDetailsContextProvider } from 'components/Executions/contextProvider/NodeExecutionDetails';
-import {
- ExecutionContext,
- WorkflowNodeExecutionsContext,
-} from 'components/Executions/contexts';
-import { basicPythonWorkflow } from 'mocks/data/fixtures/basicPythonWorkflow';
-import { noExecutionsFoundString } from 'common/constants';
-import { mockWorkflowId } from 'mocks/data/fixtures/types';
-import { insertFixture } from 'mocks/data/insertFixture';
-import { mockServer } from 'mocks/server';
-import { NodeExecutionPhase } from 'models/Execution/enums';
-import * as React from 'react';
-import { dateToTimestamp } from 'common/utils';
-import { QueryClient, QueryClientProvider } from 'react-query';
-import { createTestQueryClient } from 'test/utils';
-import { dNode } from 'models/Graph/types';
-import { useNodeExecutionFiltersState } from 'components/Executions/filters/useExecutionFiltersState';
-import { Execution, NodeExecution } from 'models';
-import { listNodeExecutions, listTaskExecutions } from 'models/Execution/api';
-import { NodeExecutionsTable } from '../NodeExecutionsTable';
-
-jest.mock('components/Workflow/workflowQueries');
-const { fetchWorkflow } = require('components/Workflow/workflowQueries');
-
-jest.mock('components/Executions/filters/useExecutionFiltersState');
-const mockUseNodeExecutionFiltersState =
- useNodeExecutionFiltersState as jest.Mock;
-mockUseNodeExecutionFiltersState.mockReturnValue({
- filters: [],
- appliedFilters: [],
-});
-
-jest.mock('components/Executions/Tables/NodeExecutionRow', () => ({
- NodeExecutionRow: jest.fn(({ node }) => (
-
-
- {node?.execution?.id?.nodeId}
-
-
- {node?.execution?.closure?.phase}
-
-
- )),
-}));
-
-jest.mock('models/Execution/api', () => ({
- listNodeExecutions: jest.fn(),
- listTaskExecutions: jest.fn(),
-}));
-
-const mockNodes = (n: number): dNode[] => {
- const nodes: dNode[] = [];
- for (let i = 1; i <= n; i++) {
- nodes.push({
- id: `node${i}`,
- scopedId: `n${i}`,
- type: 4,
- name: `Node ${i}`,
- nodes: [],
- edges: [],
- });
- }
- return nodes;
-};
-const executionId = { domain: 'domain', name: 'name', project: 'project' };
-
-const mockExecutionsById = (n: number, phases: NodeExecutionPhase[]) => {
- const nodeExecutionsById = {};
-
- for (let i = 1; i <= n; i++) {
- nodeExecutionsById[`n${i}`] = {
- closure: {
- createdAt: dateToTimestamp(new Date()),
- outputUri: '',
- phase: phases[i - 1],
- },
- id: {
- executionId,
- nodeId: `node${i}`,
- },
- inputUri: '',
- scopedId: `n${i}`,
- };
- }
- return nodeExecutionsById;
-};
-
-describe('NodeExecutionsTableExecutions > Tables > NodeExecutionsTable', () => {
- let queryClient: QueryClient;
- let fixture: ReturnType;
- const initialNodes = mockNodes(2);
-
- beforeEach(() => {
- queryClient = createTestQueryClient();
- fixture = basicPythonWorkflow.generate();
- insertFixture(mockServer, fixture);
- fetchWorkflow.mockImplementation(() =>
- Promise.resolve(fixture.workflows.top),
- );
-
- listNodeExecutions.mockImplementation(() => {
- return Promise.resolve({
- entities: Object.values([]),
- });
- });
- listTaskExecutions.mockImplementation(() => {
- return Promise.resolve({
- entities: Object.values([]),
- });
- });
- });
-
- const renderTable = ({ nodeExecutionsById, initialNodes, filterState }) =>
- render(
-
-
-
- {},
- setShouldUpdate: () => {},
- shouldUpdate: false,
- }}
- >
-
-
-
-
- ,
- );
-
- it('renders empty content when there are no nodes', async () => {
- const filterState = {
- filters: [],
- appliedFilters: [],
- };
- const { queryByText, queryByTestId } = renderTable({
- initialNodes: [],
- nodeExecutionsById: {},
- filterState,
- });
-
- await waitFor(() => queryByText(noExecutionsFoundString));
-
- expect(queryByText(noExecutionsFoundString)).toBeInTheDocument();
- expect(queryByTestId('node-execution-row')).not.toBeInTheDocument();
- });
-
- it('renders NodeExecutionRows with initialNodes when no filteredNodes were provided', async () => {
- const phases = [NodeExecutionPhase.FAILED, NodeExecutionPhase.SUCCEEDED];
- const nodeExecutionsById = mockExecutionsById(2, phases);
- const filterState = {
- filters: [],
- appliedFilters: [],
- };
- const { queryAllByTestId } = renderTable({
- initialNodes,
- nodeExecutionsById,
- filterState,
- });
-
- await waitFor(() => {
- const nodes = queryAllByTestId('node-execution-row');
- expect(nodes).toHaveLength(initialNodes.length);
- return nodes;
- });
- const ids = queryAllByTestId('node-execution-col-id');
- expect(ids).toHaveLength(initialNodes.length);
- const renderedPhases = queryAllByTestId('node-execution-col-phase');
- expect(renderedPhases).toHaveLength(initialNodes.length);
- for (const i in initialNodes) {
- expect(ids[i]).toHaveTextContent(initialNodes[i].id);
- expect(renderedPhases[i]).toHaveTextContent(phases[i].toString());
- }
- });
-
- it('renders NodeExecutionRows with initialNodes even when filterNodes were provided, if appliedFilters is empty', async () => {
- const phases = [NodeExecutionPhase.FAILED, NodeExecutionPhase.SUCCEEDED];
- const nodeExecutionsById = mockExecutionsById(2, phases);
- const filteredNodeExecutions = nodeExecutionsById['n1'];
- const filterState = {
- filters: [],
- appliedFilters: [],
- };
- listNodeExecutions.mockImplementation(() => {
- return Promise.resolve({
- entities: [filteredNodeExecutions],
- });
- });
-
- const { queryAllByTestId } = renderTable({
- initialNodes,
- nodeExecutionsById,
- filterState,
- });
-
- await waitFor(() =>
- expect(listNodeExecutions).toHaveBeenCalledWith(
- expect.objectContaining(executionId),
- expect.objectContaining({
- filter: [],
- }),
- ),
- );
- await waitFor(() => queryAllByTestId('node-execution-row'));
-
- expect(queryAllByTestId('node-execution-row')).toHaveLength(
- initialNodes.length,
- );
- const ids = queryAllByTestId('node-execution-col-id');
- expect(ids).toHaveLength(initialNodes.length);
- const renderedPhases = queryAllByTestId('node-execution-col-phase');
- expect(renderedPhases).toHaveLength(initialNodes.length);
- for (const i in initialNodes) {
- expect(ids[i]).toHaveTextContent(initialNodes[i].id);
- expect(renderedPhases[i]).toHaveTextContent(phases[i].toString());
- }
- });
-
- it('renders NodeExecutionRows with filterNodes if appliedFilters are less than original filters', async () => {
- const appliedFilters = [
- { key: 'phase', operation: 'value_in', value: ['FAILED'] },
- ];
- const filterState = {
- filters: [],
- appliedFilters,
- };
- const phases = [NodeExecutionPhase.FAILED, NodeExecutionPhase.SUCCEEDED];
- const nodeExecutionsById = mockExecutionsById(2, phases);
- const filteredNodeExecutions = [nodeExecutionsById['n1']];
- listNodeExecutions.mockImplementation(() => {
- return Promise.resolve({
- entities: filteredNodeExecutions,
- });
- });
-
- const { queryAllByTestId, debug, container } = renderTable({
- initialNodes,
- nodeExecutionsById,
- filterState,
- });
-
- await waitFor(() =>
- expect(listNodeExecutions).toHaveBeenCalledWith(
- expect.objectContaining(executionId),
- expect.objectContaining({
- filter: appliedFilters,
- }),
- ),
- );
-
- debug(container);
-
- await waitFor(() => {
- const rows = queryAllByTestId('node-execution-row');
- expect(rows).toHaveLength(filteredNodeExecutions.length);
- });
-
- const ids = queryAllByTestId('node-execution-col-id');
- expect(ids).toHaveLength(filteredNodeExecutions.length);
- const renderedPhases = queryAllByTestId('node-execution-col-phase');
- expect(renderedPhases).toHaveLength(filteredNodeExecutions.length);
-
- for (const i in filteredNodeExecutions) {
- expect(ids[i]).toHaveTextContent(filteredNodeExecutions[i].id?.nodeId);
- expect(renderedPhases[i]).toHaveTextContent(phases[i].toString());
- }
- });
-
- it('renders NodeExecutionRows with filterNodes if appliedFilters are the same as original filters', async () => {
- const phases = [NodeExecutionPhase.FAILED, NodeExecutionPhase.SUCCEEDED];
- const appliedFilters = [
- { key: 'phase', operation: 'value_in', value: ['FAILED', 'SUCCEEDED'] },
- ];
- const filterState = {
- filters: [],
- appliedFilters,
- };
-
- const nodeExecutionsById = mockExecutionsById(2, phases);
- const filteredNodeExecutions: NodeExecution[] =
- Object.values(nodeExecutionsById);
- listNodeExecutions.mockImplementation(() => {
- return Promise.resolve({
- entities: filteredNodeExecutions,
- });
- });
-
- const { queryAllByTestId } = renderTable({
- initialNodes,
- nodeExecutionsById,
- filterState,
- });
-
- await waitFor(() =>
- expect(listNodeExecutions).toHaveBeenCalledWith(
- expect.objectContaining(executionId),
- expect.objectContaining({
- filter: appliedFilters,
- }),
- ),
- );
-
- await waitFor(() => {
- const rows = queryAllByTestId('node-execution-row');
- return rows.length === filteredNodeExecutions.length;
- });
-
- expect(queryAllByTestId('node-execution-row')).toHaveLength(
- filteredNodeExecutions.length,
- );
-
- const ids = queryAllByTestId('node-execution-col-id');
- expect(ids).toHaveLength(filteredNodeExecutions.length);
- const renderedPhases = queryAllByTestId('node-execution-col-phase');
- expect(renderedPhases).toHaveLength(filteredNodeExecutions.length);
-
- for (const i in filteredNodeExecutions) {
- expect(ids[i]).toHaveTextContent(filteredNodeExecutions[i].id?.nodeId);
- expect(renderedPhases[i]).toHaveTextContent(phases[i].toString());
- }
- });
-});
diff --git a/packages/console/src/components/Executions/Tables/types.ts b/packages/console/src/components/Executions/Tables/types.ts
deleted file mode 100644
index ffa7eb4e1..000000000
--- a/packages/console/src/components/Executions/Tables/types.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-import { PaginatedFetchableData } from 'components/hooks/types';
-import {
- Execution,
- NodeExecution,
- NodeExecutionIdentifier,
-} from 'models/Execution/types';
-import { dNode } from 'models/Graph/types';
-import { Workflow } from 'models/Workflow/types';
-
-export interface WorkflowExecutionsTableState {
- selectedIOExecution: Execution | null;
- setSelectedIOExecution(execution: Execution | null): void;
-}
-export interface NodeExecutionsTableState {
- selectedExecution?: NodeExecutionIdentifier | null;
- setSelectedExecution: (
- selectedExecutionId: NodeExecutionIdentifier | null,
- ) => void;
-}
-
-export interface ColumnDefinition {
- cellRenderer(data: CellRendererData): React.ReactNode;
- className?: string;
- key: string;
- label: string | React.FC;
-}
-
-export interface NodeExecutionCellRendererData {
- execution: NodeExecution;
- node: dNode;
- className: string;
-}
-export type NodeExecutionColumnDefinition =
- ColumnDefinition;
-
-export interface WorkflowExecutionCellRendererData {
- execution: Execution;
- state: WorkflowExecutionsTableState;
-}
-export type WorkflowExecutionColumnDefinition =
- ColumnDefinition;
-
-export interface WorkflowVersionCellRendererData {
- workflow: Workflow;
- state: WorkflowExecutionsTableState;
- executions: PaginatedFetchableData;
-}
-
-export type WorkflowVersionColumnDefinition =
- ColumnDefinition;
diff --git a/packages/console/src/components/Executions/Tables/useWorkflowExecutionTableState.ts b/packages/console/src/components/Executions/Tables/useWorkflowExecutionTableState.ts
deleted file mode 100644
index 8ee6cd215..000000000
--- a/packages/console/src/components/Executions/Tables/useWorkflowExecutionTableState.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Execution } from 'models/Execution/types';
-import { useState } from 'react';
-import { WorkflowExecutionsTableState } from './types';
-
-export function useWorkflowExecutionsTableState(): WorkflowExecutionsTableState {
- const [selectedIOExecution, setSelectedIOExecution] =
- useState(null);
- return {
- selectedIOExecution,
- setSelectedIOExecution,
- };
-}
diff --git a/packages/console/src/components/Executions/Tables/useWorkflowVersionsTableColumns.tsx b/packages/console/src/components/Executions/Tables/useWorkflowVersionsTableColumns.tsx
deleted file mode 100644
index 5bbc29496..000000000
--- a/packages/console/src/components/Executions/Tables/useWorkflowVersionsTableColumns.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import { Typography } from '@material-ui/core';
-import { formatDateUTC } from 'common/formatters';
-import {
- padExecutionPaths,
- padExecutions,
- timestampToDate,
-} from 'common/utils';
-import { WaitForData } from 'components/common/WaitForData';
-import ProjectStatusBar from 'components/Project/ProjectStatusBar';
-import moment from 'moment';
-import * as React from 'react';
-import { useWorkflowVersionsColumnStyles } from './styles';
-import { WorkflowVersionColumnDefinition } from './types';
-
-/**
- * Returns a memoized list of column definitions to use when rendering a
- * `WorkflowVersionRow`. Memoization is based on common/column style objects
- * and any fields in the incoming `WorkflowExecutionColumnOptions` object.
- */
-export function useWorkflowVersionsTableColumns(): WorkflowVersionColumnDefinition[] {
- const styles = useWorkflowVersionsColumnStyles();
- return React.useMemo(
- () => [
- {
- cellRenderer: ({
- workflow: {
- id: { version },
- },
- }) => {version},
- className: styles.columnName,
- key: 'name',
- label: 'version id',
- },
- {
- cellRenderer: ({ workflow: { closure } }) => {
- if (!closure?.createdAt) {
- return '';
- }
- const createdAtDate = timestampToDate(closure.createdAt);
- return (
-
- {formatDateUTC(createdAtDate)}
-
- );
- },
- className: styles.columnCreatedAt,
- key: 'createdAt',
- label: 'time created',
- },
- {
- cellRenderer: ({ executions }) => {
- return (
-
-
- {executions.value.length
- ? moment(
- timestampToDate(executions.value[0].closure.createdAt),
- ).fromNow()
- : ''}
-
-
- );
- },
- className: styles.columnLastRun,
- key: 'lastExecution',
- label: 'last execution',
- },
- {
- cellRenderer: ({ executions }) => {
- return (
-
- execution.closure.phase) ||
- [],
- )}
- paths={padExecutionPaths(
- executions.value.map(execution => execution.id) || [],
- )}
- />
-
- );
- },
- className: styles.columnRecentRun,
- key: 'recentRun',
- label: 'recent run',
- },
- ],
- [styles],
- );
-}
diff --git a/packages/console/src/components/Executions/Tables/utils.ts b/packages/console/src/components/Executions/Tables/utils.ts
deleted file mode 100644
index d90568b17..000000000
--- a/packages/console/src/components/Executions/Tables/utils.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Spacing } from '@material-ui/core/styles/createSpacing';
-import { nameColumnLeftMarginGridWidth } from './styles';
-
-export function calculateNodeExecutionRowLeftSpacing(
- level: number,
- spacing: Spacing,
-) {
- return spacing(nameColumnLeftMarginGridWidth + 3 * level);
-}
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/MapTaskExecutionDetails.tsx b/packages/console/src/components/Executions/TaskExecutionsList/MapTaskExecutionDetails.tsx
deleted file mode 100644
index 0acffa816..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/MapTaskExecutionDetails.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-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 (
-
- {filteredResources.map(item => {
- const attempt = item.retryAttempt ?? 0;
- const headerText = formatRetryAttempt(attempt);
- return (
-
-
-
- );
- })}
-
- );
-};
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/MapTaskExecutionListItem.tsx b/packages/console/src/components/Executions/TaskExecutionsList/MapTaskExecutionListItem.tsx
deleted file mode 100644
index 7cd22a560..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/MapTaskExecutionListItem.tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-import * as React from 'react';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import Typography from '@material-ui/core/Typography';
-import classnames from 'classnames';
-import { PanelSection } from 'components/common/PanelSection';
-import { useCommonStyles } from 'components/common/styles';
-import { TaskExecutionPhase } from 'models/Execution/enums';
-import { MapTaskExecution, TaskExecution } from 'models/Execution/types';
-import { MapTaskStatusInfo } from 'components/common/MapTaskExecutionsList/MapTaskStatusInfo';
-import { isMapTaskV1 } from 'models';
-import { TaskExecutionDetails } from './TaskExecutionDetails';
-import { TaskExecutionError } from './TaskExecutionError';
-import { TaskExecutionLogs } from './TaskExecutionLogs';
-import { formatRetryAttempt, getGroupedLogs } from './utils';
-import { RENDER_ORDER } from './constants';
-
-const useStyles = makeStyles((theme: Theme) => ({
- detailsLink: {
- fontWeight: 'normal',
- },
- header: {
- marginBottom: theme.spacing(1),
- },
- title: {
- marginBottom: theme.spacing(1),
- },
- showDetailsButton: {
- marginTop: theme.spacing(1),
- },
- section: {
- marginBottom: theme.spacing(2),
- },
-}));
-
-interface MapTaskExecutionsListItemProps {
- taskExecution: TaskExecution;
- showAttempts: boolean;
- onTaskSelected: (val: MapTaskExecution) => void;
- selectedPhase?: TaskExecutionPhase;
-}
-
-/** Renders an individual `TaskExecution` record as part of a list */
-export const MapTaskExecutionsListItem: React.FC<
- MapTaskExecutionsListItemProps
-> = ({ taskExecution, showAttempts, onTaskSelected, selectedPhase }) => {
- const commonStyles = useCommonStyles();
- const styles = useStyles();
-
- const {
- closure: {
- error,
- startedAt,
- updatedAt,
- duration,
- phase,
- logs,
- metadata,
- eventVersion,
- taskType,
- },
- id: { retryAttempt },
- } = taskExecution;
- const taskHasStarted = phase >= TaskExecutionPhase.QUEUED;
- const headerText = formatRetryAttempt(retryAttempt);
- const logsByPhase = getGroupedLogs(metadata?.externalResources ?? []);
-
- const isMapTask = isMapTaskV1(
- eventVersion!,
- metadata?.externalResources?.length ?? 0,
- taskType ?? undefined,
- );
-
- return (
-
- {/* Attempts header is shown only if there is more than one attempt */}
- {showAttempts ? (
-
- ) : null}
- {/* Error info is shown only if there is an error present for this map task */}
- {error ? (
-
- ) : null}
- {/* If main map task has log attached - show it here */}
- {logs && logs.length > 0 ? (
-
- ) : null}
- {/* child/array logs separated by subtasks phase */}
- {RENDER_ORDER.map((phase, id) => {
- const logs = logsByPhase.get(phase);
- if (!logs) {
- return null;
- }
- const key = `${id}-${phase}`;
- return (
-
- );
- })}
- {/* If map task is actively started - show 'started' and 'run time' details */}
- {taskHasStarted && !isMapTask && (
-
- )}
-
- );
-};
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionDetails.tsx b/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionDetails.tsx
deleted file mode 100644
index 6b0813e3d..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionDetails.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { unknownValueString } from 'common/constants';
-import { dateWithFromNow, protobufDurationToHMS } from 'common/formatters';
-import { timestampToDate } from 'common/utils';
-import { DetailsGroup } from 'components/common/DetailsGroup';
-import * as React from 'react';
-import { Protobuf } from '@flyteorg/flyteidl-types';
-
-/** Renders the less important details for a `TaskExecution` as a `DetailsGroup`
- */
-export const TaskExecutionDetails: React.FC<{
- startedAt?: Protobuf.ITimestamp;
- updatedAt?: Protobuf.ITimestamp | null;
- duration?: Protobuf.Duration;
-}> = ({ startedAt, duration, updatedAt }) => {
- const labelWidthGridUnits = startedAt ? 7 : 10;
- const detailItems = React.useMemo(() => {
- if (startedAt) {
- return [
- {
- name: 'started',
- content: dateWithFromNow(timestampToDate(startedAt)),
- },
- {
- name: 'run time',
- content: duration
- ? protobufDurationToHMS(duration)
- : unknownValueString,
- },
- ];
- } else {
- return [
- {
- name: 'last updated',
- content: updatedAt
- ? dateWithFromNow(timestampToDate(updatedAt))
- : unknownValueString,
- },
- ];
- }
- }, [startedAt, duration, updatedAt]);
-
- return (
-
- );
-};
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionError.tsx b/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionError.tsx
deleted file mode 100644
index 43496af23..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionError.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { ExpandableContentLink } from 'components/common/ExpandableContentLink';
-import { useCommonStyles } from 'components/common/styles';
-import { ExecutionError } from 'models/Execution/types';
-import * as React from 'react';
-
-/** Renders an expandable error for a `TaskExecution` */
-export const TaskExecutionError: React.FC<{ error: ExecutionError }> = ({
- error,
-}) => {
- const commonStyles = useCommonStyles();
- const renderContent = () => (
- {error.message}
- );
- return (
-
- );
-};
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionLogs.tsx b/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionLogs.tsx
deleted file mode 100644
index 5a81e2fd5..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionLogs.tsx
+++ /dev/null
@@ -1,66 +0,0 @@
-import * as React from 'react';
-import { Typography } from '@material-ui/core';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import { Core } from '@flyteorg/flyteidl-types';
-import { NewTargetLink } from 'components/common/NewTargetLink';
-import { useCommonStyles } from 'components/common/styles';
-import { noLogsFoundString } from '../constants';
-
-const useStyles = makeStyles((theme: Theme) => ({
- logLink: {
- margin: `${theme.spacing(0.5)} 0`,
- },
- sectionHeader: {
- marginTop: theme.spacing(1),
- },
- logName: {
- fontWeight: 'lighter',
- },
-}));
-
-export const TaskLogList: React.FC<{ logs: Core.ITaskLog[] }> = ({ logs }) => {
- const styles = useStyles();
- const commonStyles = useCommonStyles();
- if (!(logs && logs.length > 0)) {
- return {noLogsFoundString};
- }
- return (
- <>
- {logs.map(({ name, uri }) =>
- uri ? (
-
- {name}
-
- ) : (
- // If there is no url, show item a a name string only, as it's not really clickable
-
- {name}
-
- ),
- )}
- >
- );
-};
-
-/** Renders log links from a `taskLogs`(aka taskExecution.closure.logs), if they exist.
- * Otherwise renders a message indicating that no logs are available.
- */
-export const TaskExecutionLogs: React.FC<{
- taskLogs: Core.ITaskLog[];
- title?: string;
-}> = ({ taskLogs, title }) => {
- const styles = useStyles();
- return (
-
- );
-};
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionLogsCard.tsx b/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionLogsCard.tsx
deleted file mode 100644
index e3f0cb4b7..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionLogsCard.tsx
+++ /dev/null
@@ -1,125 +0,0 @@
-import * as React from 'react';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import Typography from '@material-ui/core/Typography';
-import classnames from 'classnames';
-import { useCommonStyles } from 'components/common/styles';
-import { TaskExecutionPhase } from 'models/Execution/enums';
-import { MapTaskExecution, TaskExecution } from 'models/Execution/types';
-import { Core } from '@flyteorg/flyteidl-types';
-import { ExternalConfigHoc } from 'basics/ExternalConfigHoc';
-import { useExternalConfigurationContext } from 'basics/ExternalConfigurationProvider';
-import { isMapTaskV1 } from 'models';
-import { ExecutionStatusBadge } from '../ExecutionStatusBadge';
-import { TaskExecutionDetails } from './TaskExecutionDetails';
-import { TaskExecutionError } from './TaskExecutionError';
-import { TaskExecutionLogs } from './TaskExecutionLogs';
-
-const useStyles = makeStyles((theme: Theme) => ({
- detailsLink: {
- fontWeight: 'normal',
- },
- header: {
- marginBottom: theme.spacing(1),
- },
- title: {
- marginBottom: theme.spacing(1),
-
- '& > svg': {
- verticalAlign: 'middle',
- },
- },
- showDetailsButton: {
- marginTop: theme.spacing(1),
- },
- section: {
- marginBottom: theme.spacing(2),
- },
-}));
-
-interface TaskExecutionLogsCardProps {
- taskExecution: TaskExecution | MapTaskExecution;
- headerText: string;
- phase: TaskExecutionPhase;
- logs: Core.ITaskLog[];
- mappedItem?: any;
-}
-
-export const TaskExecutionLogsCard: React.FC<
- TaskExecutionLogsCardProps
-> = props => {
- const { taskExecution, headerText, phase, logs } = props;
- const commonStyles = useCommonStyles();
- const styles = useStyles();
- const { registry } = useExternalConfigurationContext();
-
- const {
- closure: {
- error,
- startedAt,
- updatedAt,
- duration,
- metadata,
- eventVersion,
- taskType,
- },
- } = taskExecution;
-
- const taskHasStarted = phase >= TaskExecutionPhase.QUEUED;
- const defaultHeader = (
-
- {headerText}
-
- );
-
- const externalHeader = registry?.taskExecutionAttemps && (
-
- );
-
- const isMapTask = isMapTaskV1(
- eventVersion!,
- metadata?.externalResources?.length ?? 0,
- taskType ?? undefined,
- );
- return (
- <>
-
-
- {externalHeader || defaultHeader}
-
-
-
- {!!error && (
-
- )}
- {taskHasStarted && (
- <>
-
- {!isMapTask && (
-
- )}
- >
- )}
- >
- );
-};
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionsList.tsx b/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionsList.tsx
deleted file mode 100644
index fadc20a4a..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/TaskExecutionsList.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import * as React from 'react';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import { noExecutionsFoundString } from 'common/constants';
-import { NonIdealState } from 'components/common/NonIdealState';
-import { MapTaskExecution, TaskExecution } from 'models/Execution/types';
-import { isMapTaskV1 } from 'models/Task/utils';
-import { TaskExecutionPhase } from 'models/Execution/enums';
-import { MapTaskExecutionsListItem } from './MapTaskExecutionListItem';
-import { TaskExecutionsListItem } from './TaskExecutionsListItem';
-import { getUniqueTaskExecutionName } from './utils';
-import { WorkflowNodeExecution } from '../contexts';
-
-const useStyles = makeStyles((theme: Theme) => ({
- noExecutionsMessage: {
- paddingTop: theme.spacing(2),
- },
-}));
-
-export const TaskExecutionsListContent: React.FC<{
- taskExecutions: TaskExecution[];
- onTaskSelected: (val: MapTaskExecution) => void;
- phase?: TaskExecutionPhase;
-}> = ({ taskExecutions, onTaskSelected, phase }) => {
- const styles = useStyles();
- if (!taskExecutions?.length) {
- return (
-
- );
- }
-
- return (
- <>
- {taskExecutions.map(taskExecution => {
- const {
- closure: { taskType, metadata, eventVersion = 0 },
- } = taskExecution;
- const useNewMapTaskView = isMapTaskV1(
- eventVersion,
- metadata?.externalResources?.length ?? 0,
- taskType ?? undefined,
- );
- return useNewMapTaskView ? (
- 1}
- selectedPhase={phase}
- onTaskSelected={onTaskSelected}
- />
- ) : (
-
- );
- })}
- >
- );
-};
-
-interface TaskExecutionsListProps {
- nodeExecution: WorkflowNodeExecution;
- onTaskSelected: (val: MapTaskExecution) => void;
- phase?: TaskExecutionPhase;
-}
-
-/** Renders a vertical list of task execution records with horizontal separators
- */
-export const TaskExecutionsList: React.FC = ({
- nodeExecution,
- onTaskSelected,
- phase,
-}) => {
- return (
-
- );
-};
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/constants.ts b/packages/console/src/components/Executions/TaskExecutionsList/constants.ts
deleted file mode 100644
index 82c26c290..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/constants.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-import { TaskExecutionPhase } from 'models/Execution/enums';
-
-export const RENDER_ORDER: TaskExecutionPhase[] = [
- TaskExecutionPhase.UNDEFINED,
- TaskExecutionPhase.INITIALIZING,
- TaskExecutionPhase.WAITING_FOR_RESOURCES,
- TaskExecutionPhase.QUEUED,
- TaskExecutionPhase.RUNNING,
- TaskExecutionPhase.SUCCEEDED,
- TaskExecutionPhase.ABORTED,
- TaskExecutionPhase.FAILED,
-];
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/index.ts b/packages/console/src/components/Executions/TaskExecutionsList/index.ts
deleted file mode 100644
index 59a46cbd9..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './TaskExecutionDetails';
-export * from './utils';
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/test/MapTaskExecutionDetails.test.tsx b/packages/console/src/components/Executions/TaskExecutionsList/test/MapTaskExecutionDetails.test.tsx
deleted file mode 100644
index 8ce52b763..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/test/MapTaskExecutionDetails.test.tsx
+++ /dev/null
@@ -1,38 +0,0 @@
-import { render } from '@testing-library/react';
-import * as React from 'react';
-import { MapTaskExecutionDetails } from '../MapTaskExecutionDetails';
-import { MockMapTaskExecution } from '../TaskExecutions.mocks';
-
-jest.mock('../TaskExecutionLogsCard.tsx', () => ({
- TaskExecutionLogsCard: jest.fn(({ children }) => (
- {children}
- )),
-}));
-
-describe('MapTaskExecutionDetails', () => {
- it('should render list with 1 execution attempt', () => {
- const { queryAllByTestId } = render(
- ,
- );
- const logsCards = queryAllByTestId('logs-card');
- expect(logsCards).toHaveLength(1);
- logsCards.forEach(card => {
- expect(card).toBeInTheDocument();
- });
- });
-
- it('should render list with 2 execution attempts', () => {
- const { queryAllByTestId } = render(
- ,
- );
- const logsCards = queryAllByTestId('logs-card');
- expect(logsCards).toHaveLength(2);
- logsCards.forEach(card => {
- expect(card).toBeInTheDocument();
- });
- });
-});
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/test/TaskExecutionDetails.test.tsx b/packages/console/src/components/Executions/TaskExecutionsList/test/TaskExecutionDetails.test.tsx
deleted file mode 100644
index 48ac752c3..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/test/TaskExecutionDetails.test.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { render, waitFor } from '@testing-library/react';
-import { unknownValueString } from 'common/constants';
-import * as React from 'react';
-import { long } from 'test/utils';
-import { TaskExecutionDetails } from '../TaskExecutionDetails';
-
-const date = { seconds: long(5), nanos: 0 };
-const duration = { seconds: long(0), nanos: 0 };
-
-const dateContent = '1/1/1970 12:00:05 AM UTC';
-
-describe('TaskExecutionDetails', () => {
- it('should render details with task started info and duration', () => {
- const { queryByText } = render(
- ,
- );
-
- expect(queryByText('started')).toBeInTheDocument();
- expect(queryByText('last updated')).not.toBeInTheDocument();
- expect(queryByText(dateContent, { exact: false })).toBeInTheDocument();
- expect(queryByText('run time')).toBeInTheDocument();
- expect(queryByText('0s')).toBeInTheDocument();
- });
-
- it('should render details with task started info without duration', () => {
- const { queryByText } = render();
-
- expect(queryByText('started')).toBeInTheDocument();
- expect(queryByText('last updated')).not.toBeInTheDocument();
- expect(queryByText(dateContent, { exact: false })).toBeInTheDocument();
- expect(queryByText('run time')).toBeInTheDocument();
- expect(queryByText(unknownValueString)).toBeInTheDocument();
- });
-
- it('should render details with task updated info and duration', () => {
- const { queryByText } = render(
- ,
- );
-
- expect(queryByText('started')).not.toBeInTheDocument();
- expect(queryByText('last updated')).toBeInTheDocument();
- expect(queryByText(dateContent, { exact: false })).toBeInTheDocument();
- expect(queryByText('run time')).not.toBeInTheDocument();
- expect(queryByText('0s')).not.toBeInTheDocument();
- });
-
- it('should render details with task updated info without duration', async () => {
- const { queryByText } = await render(
- ,
- );
-
- expect(queryByText('started')).not.toBeInTheDocument();
- expect(queryByText('last updated')).toBeInTheDocument();
- expect(queryByText(dateContent, { exact: false })).toBeInTheDocument();
- expect(queryByText('run time')).not.toBeInTheDocument();
- expect(queryByText(unknownValueString)).not.toBeInTheDocument();
- });
-});
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/test/TaskExecutionLogsCard.test.tsx b/packages/console/src/components/Executions/TaskExecutionsList/test/TaskExecutionLogsCard.test.tsx
deleted file mode 100644
index 8d5e7b6a5..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/test/TaskExecutionLogsCard.test.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { render } from '@testing-library/react';
-import * as React from 'react';
-import { mockExecution as mockTaskExecution } from 'models/Execution/__mocks__/mockTaskExecutionsData';
-import { TaskExecutionPhase } from 'models/Execution/enums';
-import { noLogsFoundString } from 'components/Executions/constants';
-import { getPhaseConstants } from 'components/Executions/ExecutionStatusBadge';
-import { TaskExecutionLogsCard } from '../TaskExecutionLogsCard';
-import { formatRetryAttempt } from '../utils';
-
-const headerText = formatRetryAttempt(0);
-const taskLogs = [{ uri: '#', name: 'Kubernetes Logs #0-0' }];
-const phase = TaskExecutionPhase.SUCCEEDED;
-
-describe('TaskExecutionLogsCard', () => {
- it('should render card with logs provided', () => {
- const { queryByText } = render(
- ,
- );
- const { text } = getPhaseConstants('task', phase);
-
- expect(queryByText(headerText)).toBeInTheDocument();
- expect(queryByText(text)).toBeInTheDocument();
- expect(queryByText(taskLogs[0].name)).toBeInTheDocument();
- });
-
- it('should render card with no logs found string', () => {
- const { queryByText } = render(
- ,
- );
-
- const { text } = getPhaseConstants('task', phase);
-
- expect(queryByText(headerText)).toBeInTheDocument();
- expect(queryByText(text)).toBeInTheDocument();
- expect(queryByText(noLogsFoundString)).toBeInTheDocument();
- });
-});
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/test/TaskExecutionsList.test.tsx b/packages/console/src/components/Executions/TaskExecutionsList/test/TaskExecutionsList.test.tsx
deleted file mode 100644
index fcd206f41..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/test/TaskExecutionsList.test.tsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import * as React from 'react';
-import { render, waitFor } from '@testing-library/react';
-import { noExecutionsFoundString } from 'common/constants';
-import { APIContext } from 'components/data/apiContext';
-import { mockAPIContextValue } from 'components/data/__mocks__/apiContext';
-import { listTaskExecutions } from 'models/Execution/api';
-import { NodeExecution } from 'models/Execution/types';
-import { mockNodeExecutionResponse } from 'models/Execution/__mocks__/mockNodeExecutionsData';
-import { TaskExecutionsList } from '../TaskExecutionsList';
-import { MockPythonTaskExecution } from '../TaskExecutions.mocks';
-
-describe('TaskExecutionsList', () => {
- let nodeExecution: NodeExecution;
- let mockListTaskExecutions: jest.Mock>;
-
- const renderList = () =>
- render(
-
-
- ,
- );
- beforeEach(() => {
- nodeExecution = { ...mockNodeExecutionResponse } as NodeExecution;
- mockListTaskExecutions = jest.fn().mockResolvedValue({ entities: [] });
- });
-
- it('Renders message when no task executions exist', async () => {
- const { queryByText } = renderList();
- await waitFor(() => {});
- expect(queryByText(noExecutionsFoundString)).toBeInTheDocument();
- });
-
- it('Renders tasks when task executions exist', async () => {
- nodeExecution = {
- ...mockNodeExecutionResponse,
- startedAt: '2021-01-01T00:00:00Z',
- taskExecutions: [MockPythonTaskExecution],
- } as NodeExecution;
-
- const { queryByText } = renderList();
- await waitFor(() => {});
- expect(queryByText('Attempt 01')).toBeInTheDocument();
- expect(queryByText('Succeeded')).toBeInTheDocument();
- });
-});
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/test/utils.spec.ts b/packages/console/src/components/Executions/TaskExecutionsList/test/utils.spec.ts
deleted file mode 100644
index b517133bd..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/test/utils.spec.ts
+++ /dev/null
@@ -1,133 +0,0 @@
-import { getTaskLogName } from 'components/Executions/TaskExecutionsList/utils';
-import { Event } from '@flyteorg/flyteidl-types';
-import { TaskExecutionPhase } from 'models/Execution/enums';
-import { obj } from 'test/utils';
-import {
- getTaskRetryAtemptsForIndex,
- formatRetryAttempt,
- getGroupedLogs,
- getUniqueTaskExecutionName,
-} from '../utils';
-import {
- getMockMapTaskLogItem,
- MockMapTaskExecution,
-} from '../TaskExecutions.mocks';
-
-describe('getUniqueTaskExecutionName', () => {
- const cases: [{ name: string; retryAttempt: number }, string][] = [
- [{ name: 'task1', retryAttempt: 0 }, 'task1'],
- [{ name: 'task1', retryAttempt: 1 }, 'task1 (2)'],
- [{ name: 'task1', retryAttempt: 2 }, 'task1 (3)'],
- ];
-
- cases.forEach(([input, expected]) =>
- it(`should return ${expected} for ${obj(input)}`, () =>
- expect(
- getUniqueTaskExecutionName({
- id: {
- retryAttempt: input.retryAttempt,
- taskId: { name: input.name },
- },
- } as any),
- ).toEqual(expected)),
- );
-});
-
-describe('formatRetryAttempt', () => {
- const cases: [number, string][] = [
- [0, 'Attempt 01'],
- [1, 'Attempt 02'],
- [2, 'Attempt 03'],
- ];
-
- cases.forEach(([input, expected]) =>
- it(`should return ${expected} for ${input}`, () =>
- expect(formatRetryAttempt(input)).toEqual(expected)),
- );
-});
-
-describe('getGroupedLogs', () => {
- const resources: Event.IExternalResourceInfo[] = [
- getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true),
- getMockMapTaskLogItem(TaskExecutionPhase.FAILED, true, 1),
- getMockMapTaskLogItem(TaskExecutionPhase.FAILED, true, 1, 1),
- getMockMapTaskLogItem(TaskExecutionPhase.SUCCEEDED, true, 1, 2),
- getMockMapTaskLogItem(TaskExecutionPhase.FAILED, false, 2),
- ];
-
- it('should properly group to Success and Failed', () => {
- const logs = getGroupedLogs(resources);
- // Do not have key which was not in the logs
- expect(logs.get(TaskExecutionPhase.QUEUED)).toBeUndefined();
-
- // To have keys which were in the logs
- expect(logs.get(TaskExecutionPhase.SUCCEEDED)).not.toBeUndefined();
- expect(logs.get(TaskExecutionPhase.FAILED)).not.toBeUndefined();
-
- // to include all items with last retry iterations
- expect(logs.get(TaskExecutionPhase.SUCCEEDED)?.length).toEqual(2);
-
- // to filter our previous retry attempt
- expect(logs.get(TaskExecutionPhase.FAILED)?.length).toEqual(1);
- });
-});
-
-describe('getTaskRetryAttemptsForIndex', () => {
- it('should return 2 filtered attempts for provided index', () => {
- const index = 3;
- // '?? []' -> TS check, mock contains externalResources
- const result = getTaskRetryAtemptsForIndex(
- MockMapTaskExecution.closure.metadata?.externalResources ?? [],
- index,
- );
- expect(result).toHaveLength(2);
- });
-
- it('should return 1 filtered attempt for provided index', () => {
- const index = 0;
- // '?? []' -> TS check, mock contains externalResources
- const result = getTaskRetryAtemptsForIndex(
- MockMapTaskExecution.closure.metadata?.externalResources ?? [],
- index,
- );
- expect(result).toHaveLength(1);
- });
-
- it('should return empty array when null index provided', () => {
- const index = null;
- // '?? []' -> TS check, mock contains externalResources
- const result = getTaskRetryAtemptsForIndex(
- MockMapTaskExecution.closure.metadata?.externalResources ?? [],
- index,
- );
- expect(result).toHaveLength(0);
- });
-});
-
-describe('getTaskLogName', () => {
- it('should return correct names', () => {
- const taskName1 = 'task_name_1';
- const taskName2 = 'task.task_name_1';
- const taskLogName1 = 'abc';
- const taskLogName2 = 'abc-1';
- const taskLogName3 = 'abc-1-1';
-
- const result1 = getTaskLogName(taskName1, taskLogName1);
- expect(result1).toStrictEqual(taskName1);
-
- const result2 = getTaskLogName(taskName1, taskLogName2);
- expect(result2).toStrictEqual('task_name_1-1');
-
- const result3 = getTaskLogName(taskName1, taskLogName3);
- expect(result3).toStrictEqual('task_name_1-1-1');
-
- const result4 = getTaskLogName(taskName2, taskLogName1);
- expect(result4).toStrictEqual(taskName1);
-
- const result5 = getTaskLogName(taskName2, taskLogName2);
- expect(result5).toStrictEqual('task_name_1-1');
-
- const result6 = getTaskLogName(taskName2, taskLogName3);
- expect(result6).toStrictEqual('task_name_1-1-1');
- });
-});
diff --git a/packages/console/src/components/Executions/TaskExecutionsList/utils.ts b/packages/console/src/components/Executions/TaskExecutionsList/utils.ts
deleted file mode 100644
index e56951146..000000000
--- a/packages/console/src/components/Executions/TaskExecutionsList/utils.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-import {
- ExternalResource,
- LogsByPhase,
- TaskExecution,
-} from 'models/Execution/types';
-import { leftPaddedNumber } from 'common/formatters';
-import { Event } from '@flyteorg/flyteidl-types';
-import { TaskExecutionPhase } from 'models/Execution/enums';
-
-/** Generates a unique name for a task execution, suitable for display in a
- * header and use as a child component key. The name is a combination of task
- * name and retry attempt (if it is not the first attempt).
- * Note: Names are not *globally* unique, just unique within a given `NodeExecution`
- */
-export function getUniqueTaskExecutionName({ id }: TaskExecution) {
- const { name } = id.taskId;
- const { retryAttempt } = id;
- const suffix =
- retryAttempt && retryAttempt > 0 ? ` (${retryAttempt + 1})` : '';
- return `${name}${suffix}`;
-}
-
-export function formatRetryAttempt(
- attempt: number | string | undefined,
-): string {
- let parsed =
- typeof attempt === 'number' ? attempt : Number.parseInt(`${attempt}`, 10);
- if (Number.isNaN(parsed)) {
- parsed = 0;
- }
-
- // Retry attempts are zero-based, so incrementing before formatting
- return `Attempt ${leftPaddedNumber(parsed + 1, 2)}`;
-}
-
-export const getGroupedLogs = (
- resources: Event.IExternalResourceInfo[],
-): LogsByPhase => {
- const logsByPhase: LogsByPhase = new Map();
-
- // sort output sample [0-2, 0-1, 0, 1, 2], where 0-1 means index = 0 retry = 1
- resources.sort((a, b) => {
- const aIndex = a.index ?? 0;
- const bIndex = b.index ?? 0;
- if (aIndex !== bIndex) {
- // return smaller index first
- return aIndex - bIndex;
- }
-
- const aRetry = a.retryAttempt ?? 0;
- const bRetry = b.retryAttempt ?? 0;
- return bRetry - aRetry;
- });
-
- let lastIndex = -1;
- for (const item of resources) {
- if (item.index === lastIndex) {
- // skip, as we already added final retry to data
- continue;
- }
- const phase = item.phase ?? TaskExecutionPhase.UNDEFINED;
- const currentValue = logsByPhase.get(phase);
- lastIndex = item.index ?? 0;
- if (item.logs) {
- // if there is no log with active url, just create an item with externalId,
- // for user to understand which array items are in this state
- const newLogs =
- item.logs.length > 0
- ? item.logs.map(l => ({ ...l, index: item.index || 0 }))
- : [{ name: item.externalId }];
- logsByPhase.set(
- phase,
- currentValue ? [...currentValue, ...newLogs] : [...newLogs],
- );
- }
- }
-
- return logsByPhase;
-};
-
-export const getTaskRetryAtemptsForIndex = (
- resources: ExternalResource[],
- taskIndex: number | null,
-): ExternalResource[] => {
- // check spesifically for null values, to make sure we're not skipping logs for 0 index
- if (taskIndex === null) {
- return [];
- }
-
- const filtered = resources.filter(a => {
- const index = a.index ?? 0;
- return index === taskIndex;
- });
-
- // sort output sample [0-2, 0-1, 0, 1, 2], where 0-1 means index = 0 retry = 1
- filtered.sort((a, b) => {
- const aIndex = a.index ?? 0;
- const bIndex = b.index ?? 0;
- if (aIndex !== bIndex) {
- // return smaller index first
- return aIndex - bIndex;
- }
-
- const aRetry = a.retryAttempt ?? 0;
- const bRetry = b.retryAttempt ?? 0;
- return bRetry - aRetry;
- });
- return filtered;
-};
-
-export function getTaskLogName(taskName: string, taskLogName: string): string {
- const lastDotIndex = taskName.lastIndexOf('.');
- const prefix =
- lastDotIndex !== -1 ? taskName.slice(lastDotIndex + 1) : taskName;
- const firstDahIndex = taskLogName.indexOf('-');
- const suffix = firstDahIndex !== -1 ? taskLogName.slice(firstDahIndex) : '';
- return `${prefix}${suffix}`;
-}
diff --git a/packages/console/src/components/Executions/TerminateExecution/TerminateExecutionButton.tsx b/packages/console/src/components/Executions/TerminateExecution/TerminateExecutionButton.tsx
deleted file mode 100644
index 7609bee75..000000000
--- a/packages/console/src/components/Executions/TerminateExecution/TerminateExecutionButton.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import {
- DropDownWindowButton,
- FormCloseHandler,
-} from 'components/common/DropDownWindowButton';
-import * as React from 'react';
-import { TerminateExecutionForm } from './TerminateExecutionForm';
-
-interface TerminateExecutionButtonProps {
- className?: string;
-}
-
-/** A button which manages the state and popover interaction used by a
- * `TerminateExecutionForm`
- */
-export const TerminateExecutionButton: React.FC<
- TerminateExecutionButtonProps
-> = ({ className }) => {
- const renderContent = (onClose: FormCloseHandler) => (
-
- );
- return (
-
- );
-};
diff --git a/packages/console/src/components/Executions/TerminateExecution/TerminateExecutionForm.tsx b/packages/console/src/components/Executions/TerminateExecution/TerminateExecutionForm.tsx
deleted file mode 100644
index bf3b1af43..000000000
--- a/packages/console/src/components/Executions/TerminateExecution/TerminateExecutionForm.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import { FormControl, FormLabel, OutlinedInput } from '@material-ui/core';
-import Button from '@material-ui/core/Button';
-import { makeStyles, Theme } from '@material-ui/core/styles';
-import { ButtonCircularProgress } from 'components/common/ButtonCircularProgress';
-import { useCommonStyles } from 'components/common/styles';
-import { smallFontSize } from 'components/Theme/constants';
-import * as React from 'react';
-import { useTerminateExecutionState } from './useTerminateExecutionState';
-
-const useStyles = makeStyles((theme: Theme) => ({
- buttonGroup: {
- justifyContent: 'center',
- },
- input: {
- fontSize: smallFontSize,
- },
- root: {
- width: theme.spacing(30),
- padding: theme.spacing(2),
- },
- title: {
- marginBottom: theme.spacing(1),
- textTransform: 'uppercase',
- color: theme.palette.text.secondary,
- },
-}));
-
-// This corresponds to the maximum length allowed by the API.
-const defaultCauseString = 'Terminated from UI';
-const placeholderString = 'Reason for termination (optional)';
-
-/** A small form for creating and submitting a request to terminate a workflow
- * execution. This includes error/retry logic in the case of an API failure.
- */
-export const TerminateExecutionForm: React.FC<{
- onClose: (...args: any) => void;
-}> = ({ onClose }) => {
- const commonStyles = useCommonStyles();
- const styles = useStyles();
- const {
- cause,
- setCause,
- terminationState: { error, isLoading: terminating },
- terminateExecution,
- } = useTerminateExecutionState(onClose);
-
- const onChange: React.ChangeEventHandler = ({
- target: { value },
- }) => setCause(value);
-
- const submit: React.FormEventHandler = event => {
- event.preventDefault();
- terminateExecution(cause || defaultCauseString);
- };
-
- return (
-
- );
-};
diff --git a/packages/console/src/components/Executions/TerminateExecution/useTerminateExecutionState.ts b/packages/console/src/components/Executions/TerminateExecution/useTerminateExecutionState.ts
deleted file mode 100644
index d6a6e8355..000000000
--- a/packages/console/src/components/Executions/TerminateExecution/useTerminateExecutionState.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import { useAPIContext } from 'components/data/apiContext';
-import { waitForQueryState } from 'components/data/queryUtils';
-import { QueryType } from 'components/data/types';
-import { Execution } from 'models/Execution/types';
-import { useContext, useState } from 'react';
-import { useMutation, useQueryClient } from 'react-query';
-import { ExecutionContext } from '../contexts';
-import { executionIsTerminal } from '../utils';
-
-interface TerminateExecutionVariables {
- cause: string;
-}
-
-/** Holds state for `TerminateExecutionForm` */
-export function useTerminateExecutionState(onClose: () => void) {
- const { getExecution, terminateWorkflowExecution } = useAPIContext();
- const [cause, setCause] = useState('');
- const {
- execution: { id },
- } = useContext(ExecutionContext);
- const queryClient = useQueryClient();
-
- const { mutate, ...terminationState } = useMutation<
- Execution,
- Error,
- TerminateExecutionVariables
- >(
- async ({ cause }: TerminateExecutionVariables) => {
- await terminateWorkflowExecution(id, cause);
- return await waitForQueryState({
- queryClient,
- queryKey: [QueryType.WorkflowExecution, id],
- queryFn: () => getExecution(id),
- valueCheckFn: executionIsTerminal,
- });
- },
- {
- onSuccess: updatedExecution => {
- queryClient.setQueryData(
- [QueryType.WorkflowExecution, id],
- updatedExecution,
- );
- onClose();
- },
- },
- );
-
- const terminateExecution = async (cause: string) => await mutate({ cause });
-
- return {
- cause,
- setCause,
- terminationState,
- terminateExecution,
- };
-}
diff --git a/packages/console/src/components/Executions/constants.ts b/packages/console/src/components/Executions/constants.ts
deleted file mode 100644
index e586872e7..000000000
--- a/packages/console/src/components/Executions/constants.ts
+++ /dev/null
@@ -1,279 +0,0 @@
-import {
- graphStatusColors,
- negativeTextColor,
- positiveTextColor,
- secondaryTextColor,
- statusColors,
-} from 'components/Theme/constants';
-import {
- CatalogCacheStatus,
- NodeExecutionPhase,
- TaskExecutionPhase,
- WorkflowExecutionPhase,
-} from 'models/Execution/enums';
-import { TaskType } from 'models/Task/constants';
-import t from './strings';
-import { ExecutionPhaseConstants, NodeExecutionDisplayType } from './types';
-
-export const executionRefreshIntervalMs = 10000;
-export const nodeExecutionRefreshIntervalMs = 3000;
-export const noLogsFoundString = t('noLogsFoundString');
-
-/** Shared values for color/text/etc for each execution phase */
-export const workflowExecutionPhaseConstants = (): {
- [key in WorkflowExecutionPhase]: ExecutionPhaseConstants;
-} => ({
- [WorkflowExecutionPhase.ABORTED]: {
- text: t('aborted'),
- value: 'ABORTED',
- badgeColor: statusColors.SKIPPED,
- nodeColor: graphStatusColors.ABORTED,
- textColor: negativeTextColor,
- },
- [WorkflowExecutionPhase.ABORTING]: {
- text: t('aborting'),
- value: 'ABORTING',
- badgeColor: statusColors.SKIPPED,
- nodeColor: graphStatusColors.ABORTED,
- textColor: negativeTextColor,
- },
- [WorkflowExecutionPhase.FAILING]: {
- text: t('failing'),
- value: 'FAILING',
- badgeColor: statusColors.FAILURE,
- nodeColor: graphStatusColors.FAILING,
- textColor: negativeTextColor,
- },
- [WorkflowExecutionPhase.FAILED]: {
- text: t('failed'),
- value: 'FAILED',
- badgeColor: statusColors.FAILURE,
- nodeColor: graphStatusColors.FAILED,
- textColor: negativeTextColor,
- },
- [WorkflowExecutionPhase.QUEUED]: {
- text: t('queued'),
- value: 'QUEUED',
- badgeColor: statusColors.QUEUED,
- nodeColor: graphStatusColors.QUEUED,
- textColor: secondaryTextColor,
- },
- [WorkflowExecutionPhase.RUNNING]: {
- text: t('running'),
- value: 'RUNNING',
- badgeColor: statusColors.RUNNING,
- nodeColor: graphStatusColors.RUNNING,
- textColor: secondaryTextColor,
- },
- [WorkflowExecutionPhase.SUCCEEDED]: {
- text: t('succeeded'),
- value: 'SUCCEEDED',
- badgeColor: statusColors.SUCCESS,
- nodeColor: graphStatusColors.SUCCEEDED,
- textColor: positiveTextColor,
- },
- [WorkflowExecutionPhase.SUCCEEDING]: {
- text: t('succeeding'),
- value: 'SUCCEEDING',
- badgeColor: statusColors.SUCCESS,
- nodeColor: graphStatusColors.SUCCEEDED,
- textColor: positiveTextColor,
- },
- [WorkflowExecutionPhase.TIMED_OUT]: {
- text: t('timedOut'),
- value: 'TIMED_OUT',
- badgeColor: statusColors.FAILURE,
- nodeColor: graphStatusColors.FAILED,
- textColor: negativeTextColor,
- },
- [WorkflowExecutionPhase.UNDEFINED]: {
- text: t('unknown'),
- value: 'UNKNOWN',
- badgeColor: statusColors.UNKNOWN,
- nodeColor: graphStatusColors.UNDEFINED,
- textColor: secondaryTextColor,
- },
-});
-
-/** Shared values for color/text/etc for each node execution phase */
-export const nodeExecutionPhaseConstants = (): {
- [key in NodeExecutionPhase]: ExecutionPhaseConstants;
-} => ({
- [NodeExecutionPhase.ABORTED]: {
- text: t('aborted'),
- value: 'ABORTED',
- badgeColor: statusColors.FAILURE,
- nodeColor: graphStatusColors.ABORTED,
- textColor: negativeTextColor,
- },
- [NodeExecutionPhase.FAILING]: {
- text: t('failing'),
- value: 'FAILING',
- badgeColor: statusColors.FAILURE,
- nodeColor: graphStatusColors.FAILING,
- textColor: negativeTextColor,
- },
- [NodeExecutionPhase.FAILED]: {
- text: t('failed'),
- value: 'FAILED',
- badgeColor: statusColors.FAILURE,
- nodeColor: graphStatusColors.FAILED,
- textColor: negativeTextColor,
- },
- [NodeExecutionPhase.QUEUED]: {
- text: t('queued'),
- value: 'QUEUED',
- badgeColor: statusColors.RUNNING,
- nodeColor: graphStatusColors.QUEUED,
- textColor: secondaryTextColor,
- },
- [NodeExecutionPhase.RUNNING]: {
- text: t('running'),
- value: 'RUNNING',
- badgeColor: statusColors.RUNNING,
- nodeColor: graphStatusColors.RUNNING,
- textColor: secondaryTextColor,
- },
- [NodeExecutionPhase.DYNAMIC_RUNNING]: {
- text: t('running'),
- value: 'RUNNING',
- badgeColor: statusColors.RUNNING,
- nodeColor: graphStatusColors.RUNNING,
- textColor: secondaryTextColor,
- },
- [NodeExecutionPhase.SUCCEEDED]: {
- text: t('succeeded'),
- value: 'SUCCEEDED',
- badgeColor: statusColors.SUCCESS,
- nodeColor: graphStatusColors.SUCCEEDED,
- textColor: positiveTextColor,
- },
- [NodeExecutionPhase.TIMED_OUT]: {
- text: t('timedOut'),
- value: 'TIMED_OUT',
- badgeColor: statusColors.FAILURE,
- nodeColor: graphStatusColors.FAILED,
- textColor: negativeTextColor,
- },
- [NodeExecutionPhase.SKIPPED]: {
- text: t('skipped'),
- value: 'SKIPPED',
- badgeColor: statusColors.UNKNOWN,
- nodeColor: graphStatusColors.UNDEFINED,
- textColor: secondaryTextColor,
- },
- [NodeExecutionPhase.RECOVERED]: {
- text: t('recovered'),
- value: 'RECOVERED',
- badgeColor: statusColors.SUCCESS,
- nodeColor: graphStatusColors.SUCCEEDED,
- textColor: positiveTextColor,
- },
- [NodeExecutionPhase.PAUSED]: {
- text: t('paused'),
- value: 'PAUSED',
- badgeColor: statusColors.PAUSED,
- nodeColor: graphStatusColors.PAUSED,
- textColor: secondaryTextColor,
- },
- [NodeExecutionPhase.UNDEFINED]: {
- text: t('unknown'),
- value: 'UNKNOWN',
- badgeColor: statusColors.UNKNOWN,
- nodeColor: graphStatusColors.UNDEFINED,
- textColor: secondaryTextColor,
- },
-});
-
-/** Shared values for color/text/etc for each node execution phase */
-export const taskExecutionPhaseConstants = (): {
- [key in TaskExecutionPhase]: ExecutionPhaseConstants;
-} => ({
- [TaskExecutionPhase.ABORTED]: {
- text: t('aborted'),
- value: 'ABORTED',
- badgeColor: statusColors.FAILURE,
- nodeColor: graphStatusColors.ABORTED,
- textColor: negativeTextColor,
- },
- [TaskExecutionPhase.FAILED]: {
- text: t('failed'),
- value: 'FAILED',
- badgeColor: statusColors.FAILURE,
- nodeColor: graphStatusColors.FAILED,
- textColor: negativeTextColor,
- },
- [TaskExecutionPhase.WAITING_FOR_RESOURCES]: {
- text: t('waiting'),
- value: 'WAITING',
- badgeColor: statusColors.RUNNING,
- nodeColor: graphStatusColors.RUNNING,
- textColor: secondaryTextColor,
- },
- [TaskExecutionPhase.QUEUED]: {
- text: t('queued'),
- value: 'QUEUED',
- badgeColor: statusColors.RUNNING,
- nodeColor: graphStatusColors.QUEUED,
- textColor: secondaryTextColor,
- },
- [TaskExecutionPhase.INITIALIZING]: {
- text: t('initializing'),
- value: 'INITIALIZING',
- badgeColor: statusColors.RUNNING,
- nodeColor: graphStatusColors.RUNNING,
- textColor: secondaryTextColor,
- },
- [TaskExecutionPhase.RUNNING]: {
- text: t('running'),
- value: 'RUNNING',
- badgeColor: statusColors.RUNNING,
- nodeColor: graphStatusColors.RUNNING,
- textColor: secondaryTextColor,
- },
- [TaskExecutionPhase.SUCCEEDED]: {
- text: t('succeeded'),
- value: 'SUCCEEDED',
- badgeColor: statusColors.SUCCESS,
- nodeColor: graphStatusColors.SUCCEEDED,
- textColor: positiveTextColor,
- },
- [TaskExecutionPhase.UNDEFINED]: {
- text: t('unknown'),
- value: 'UNKNOWN',
- badgeColor: statusColors.UNKNOWN,
- nodeColor: graphStatusColors.UNDEFINED,
- textColor: secondaryTextColor,
- },
-});
-
-export const taskTypeToNodeExecutionDisplayType: {
- [k in TaskType]: NodeExecutionDisplayType;
-} = {
- [TaskType.ARRAY]: NodeExecutionDisplayType.MapTask,
- [TaskType.BATCH_HIVE]: NodeExecutionDisplayType.BatchHiveTask,
- [TaskType.DYNAMIC]: NodeExecutionDisplayType.DynamicTask,
- [TaskType.HIVE]: NodeExecutionDisplayType.HiveTask,
- [TaskType.PYTHON]: NodeExecutionDisplayType.PythonTask,
- [TaskType.SIDECAR]: NodeExecutionDisplayType.SidecarTask,
- [TaskType.SPARK]: NodeExecutionDisplayType.SparkTask,
- [TaskType.UNKNOWN]: NodeExecutionDisplayType.UnknownTask,
- [TaskType.WAITABLE]: NodeExecutionDisplayType.WaitableTask,
- [TaskType.MPI]: NodeExecutionDisplayType.MpiTask,
- [TaskType.ARRAY_AWS]: NodeExecutionDisplayType.ARRAY_AWS,
- [TaskType.ARRAY_K8S]: NodeExecutionDisplayType.ARRAY_K8S,
- [TaskType.BRANCH]: NodeExecutionDisplayType.BranchNode,
-};
-
-export const cacheStatusMessages: { [k in CatalogCacheStatus]: string } = {
- [CatalogCacheStatus.CACHE_DISABLED]: t('cacheDisabledMessage'),
- [CatalogCacheStatus.CACHE_HIT]: t('cacheHitMessage'),
- [CatalogCacheStatus.CACHE_LOOKUP_FAILURE]: t('cacheLookupFailureMessage'),
- [CatalogCacheStatus.CACHE_MISS]: t('cacheMissMessage'),
- [CatalogCacheStatus.CACHE_POPULATED]: t('cachePopulatedMessage'),
- [CatalogCacheStatus.CACHE_PUT_FAILURE]: t('cachePutFailure'),
- [CatalogCacheStatus.MAP_CACHE]: t('mapCacheMessage'),
- [CatalogCacheStatus.CACHE_SKIPPED]: t('cacheSkippedMessage'),
-};
-export const unknownCacheStatusString = t('unknownCacheStatusString');
-export const viewSourceExecutionString = t('viewSourceExecutionString');
diff --git a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/NodeExecutionDetailsContextProvider.tsx b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/NodeExecutionDetailsContextProvider.tsx
deleted file mode 100644
index ed8f7bf98..000000000
--- a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/NodeExecutionDetailsContextProvider.tsx
+++ /dev/null
@@ -1,193 +0,0 @@
-import React, {
- PropsWithChildren,
- createContext,
- useContext,
- useEffect,
- useRef,
- useState,
-} from 'react';
-import { log } from 'common/log';
-import { Identifier } from 'models/Common/types';
-import { NodeExecution } from 'models/Execution/types';
-import { CompiledWorkflowClosure } from 'models/Workflow/types';
-import { useQueryClient } from 'react-query';
-import { fetchWorkflow } from 'components/Workflow/workflowQueries';
-import { NodeExecutionDetails } from '../../types';
-import { UNKNOWN_DETAILS } from './types';
-import {
- createExecutionDetails,
- CurrentExecutionDetails,
-} from './createExecutionArray';
-import { getTaskThroughExecution } from './getTaskThroughExecution';
-
-interface NodeExecutionDetailsState {
- getNodeExecutionDetails: (
- nodeExecution?: NodeExecution,
- ) => Promise;
- workflowId: Identifier;
- compiledWorkflowClosure: CompiledWorkflowClosure | null;
-}
-
-const NOT_AVAILABLE = 'NotAvailable';
-/** Use this Context to redefine Provider returns in storybooks */
-export const NodeExecutionDetailsContext =
- createContext({
- /** Default values used if ContextProvider wasn't initialized. */
- getNodeExecutionDetails: async () => {
- log.error(
- 'ERROR: No NodeExecutionDetailsContextProvider was found in parent components.',
- );
- return UNKNOWN_DETAILS;
- },
- workflowId: {
- project: NOT_AVAILABLE,
- domain: NOT_AVAILABLE,
- name: NOT_AVAILABLE,
- version: NOT_AVAILABLE,
- },
- compiledWorkflowClosure: null,
- });
-
-/** Should be used to get NodeExecutionDetails for a specific nodeExecution. */
-export const useNodeExecutionDetails = (nodeExecution?: NodeExecution) =>
- useContext(NodeExecutionDetailsContext).getNodeExecutionDetails(
- nodeExecution,
- );
-
-/** Could be used to access the whole NodeExecutionDetailsState */
-export const useNodeExecutionContext = (): NodeExecutionDetailsState =>
- useContext(NodeExecutionDetailsContext);
-
-export type ProviderProps = PropsWithChildren<{
- workflowId: Identifier;
-}>;
-
-/** Should wrap "top level" component in Execution view, will build a nodeExecutions tree for specific workflow */
-export const NodeExecutionDetailsContextProvider = ({
- workflowId,
- children,
-}: ProviderProps) => {
- // workflow Identifier - separated to parameters, to minimize re-render count
- // as useEffect doesn't know how to do deep comparison
- const { resourceType, project, domain, name, version } = workflowId;
-
- const [executionTree, setExecutionTree] = useState(
- {} as CurrentExecutionDetails,
- );
- const [tasks, setTasks] = useState(new Map());
- const [closure, setClosure] = useState(
- {} as CompiledWorkflowClosure,
- );
-
- const resetState = () => {
- setExecutionTree({} as CurrentExecutionDetails);
- setClosure({} as CompiledWorkflowClosure);
- };
-
- const queryClient = useQueryClient();
- const isMounted = useRef(false);
- useEffect(() => {
- isMounted.current = true;
- return () => {
- isMounted.current = false;
- };
- }, []);
-
- useEffect(() => {
- let isCurrent = true;
- async function fetchData() {
- const workflowId: Identifier = {
- resourceType,
- project,
- domain,
- name,
- version,
- };
- const result = await fetchWorkflow(queryClient, workflowId);
- if (!result) {
- resetState();
- return;
- }
- const fetchedWorkflow = JSON.parse(JSON.stringify(result));
- const tree = createExecutionDetails(fetchedWorkflow);
- if (isCurrent) {
- setClosure(fetchedWorkflow.closure?.compiledWorkflow ?? null);
- setExecutionTree(tree);
- }
- }
-
- fetchData();
-
- // This handles the unmount case
- return () => {
- isCurrent = false;
- resetState();
- };
- }, [queryClient, resourceType, project, domain, name, version]);
-
- const getDynamicTasks = async (nodeExecution: NodeExecution) => {
- const taskDetails = await getTaskThroughExecution(
- queryClient,
- nodeExecution,
- closure,
- );
-
- const tasksMap = tasks;
- tasksMap.set(nodeExecution.id.nodeId, taskDetails);
- if (isMounted.current) {
- setTasks(tasksMap);
- }
-
- return taskDetails;
- };
-
- const getDetails = async (
- nodeExecution?: NodeExecution,
- ): Promise => {
- if (!executionTree || !nodeExecution) {
- return UNKNOWN_DETAILS;
- }
-
- const specId =
- nodeExecution.scopedId ||
- nodeExecution.metadata?.specNodeId ||
- nodeExecution.id.nodeId;
- const nodeDetail = executionTree.nodes?.filter(n => n.scopedId === specId);
- if (nodeDetail?.length === 0) {
- let details = tasks.get(nodeExecution.id.nodeId);
- if (details) {
- // we already have looked for it and found
- return details;
- }
-
- // look for specific task by nodeId in current execution
- if (
- nodeExecution.metadata?.isDynamic ||
- nodeExecution.dynamicParentNodeId
- ) {
- details = await getDynamicTasks(nodeExecution);
- }
- return details;
- }
-
- return nodeDetail?.[0] ?? UNKNOWN_DETAILS;
- };
-
- return (
-
- {children}
-
- );
-};
diff --git a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/NodeExecutionDynamicProvider.tsx b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/NodeExecutionDynamicProvider.tsx
deleted file mode 100644
index 7456454e3..000000000
--- a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/NodeExecutionDynamicProvider.tsx
+++ /dev/null
@@ -1,172 +0,0 @@
-import React, {
- createContext,
- PropsWithChildren,
- useContext,
- useEffect,
- useMemo,
- Ref,
- useState,
-} from 'react';
-import { WorkflowNodeExecution } from 'components/Executions/contexts';
-import { useNodeExecutionRow } from 'components/Executions/ExecutionDetails/useNodeExecutionRow';
-import {
- isParentNode,
- nodeExecutionIsTerminal,
-} from 'components/Executions/utils';
-import { keyBy, values } from 'lodash';
-import { useInView } from 'react-intersection-observer';
-import { useQueryClient } from 'react-query';
-import { dNode } from 'models/Graph/types';
-import { useNodeExecutionsById } from './WorkflowNodeExecutionsProvider';
-
-export type RefType = Ref;
-export interface INodeExecutionDynamicContext {
- node: dNode;
- childExecutions: WorkflowNodeExecution[];
- childCount: number;
- inView: boolean;
- componentProps: React.DetailedHTMLProps<
- React.HTMLAttributes,
- HTMLDivElement
- >;
-}
-
-export const NodeExecutionDynamicContext =
- createContext({
- node: {} as dNode,
- childExecutions: [],
- childCount: 0,
- inView: false,
- componentProps: {
- ref: null,
- },
- });
-
-const checkEnableChildQuery = (
- childExecutions: WorkflowNodeExecution[],
- nodeExecution: WorkflowNodeExecution,
- inView: boolean,
-) => {
- // check that we fetched all children otherwise force fetch
- const missingChildren =
- isParentNode(nodeExecution) && !childExecutions.length;
-
- const childrenStillRunning = childExecutions?.some(
- c => !nodeExecutionIsTerminal(c),
- );
-
- const executionRunning = !nodeExecutionIsTerminal(nodeExecution);
-
- const tasksFetched = nodeExecution.tasksFetched;
-
- const forceRefetch =
- inView &&
- (!tasksFetched ||
- missingChildren ||
- childrenStillRunning ||
- executionRunning);
-
- // force fetch:
- // if parent's children haven't been fetched
- // if parent is still running or
- // if any childExecutions are still running
- return forceRefetch;
-};
-
-export type NodeExecutionDynamicProviderProps = PropsWithChildren<{
- node: dNode;
- overrideInViewValue?: boolean;
-}>;
-/** Should wrap "top level" component in Execution view, will build a nodeExecutions tree for specific workflow */
-export const NodeExecutionDynamicProvider = ({
- node,
- overrideInViewValue,
- children,
-}: NodeExecutionDynamicProviderProps) => {
- const queryClient = useQueryClient();
- const { ref, inView } = useInView();
- const [overloadedInView, setOverloadedInView] = useState(false);
- const [fetchedChildCount, setFetchedChildCount] = useState(0);
-
- useEffect(() => {
- setOverloadedInView(prev => {
- const newVal =
- overrideInViewValue === undefined ? inView : overrideInViewValue;
- if (newVal === prev) {
- return prev;
- }
-
- return newVal;
- });
- }, [inView, overrideInViewValue]);
- // get running data
- const { setCurrentNodeExecutionsById, nodeExecutionsById } =
- useNodeExecutionsById();
-
- const childExecutions = useMemo(() => {
- const children = values(nodeExecutionsById).filter(execution => {
- return execution.fromUniqueParentId === node?.scopedId;
- });
-
- return children;
- }, [nodeExecutionsById]);
-
- const { nodeExecutionRowQuery } = useNodeExecutionRow(
- queryClient,
- node?.execution!,
- () => {
- const shouldRun = checkEnableChildQuery(
- childExecutions,
- node?.execution!,
- !!overloadedInView,
- );
-
- return shouldRun;
- },
- );
-
- useEffect(() => {
- // don't update if still fetching
- if (nodeExecutionRowQuery.isFetching || !nodeExecutionRowQuery.data) {
- return;
- }
-
- const parentAndChildren = nodeExecutionRowQuery.data;
-
- // update parent context with tnew executions data
- const parentAndChildrenById = keyBy(parentAndChildren, 'scopedId');
- setCurrentNodeExecutionsById(parentAndChildrenById, true);
-
- const newChildCount = (parentAndChildren?.length || 1) - 1;
-
- // update known children count
- setFetchedChildCount(prev => {
- if (prev === newChildCount) {
- return prev;
- }
- return newChildCount;
- });
- }, [nodeExecutionRowQuery]);
-
- return (
-
- {children}
-
- );
-};
-
-export const useNodeExecutionDynamicContext =
- (): INodeExecutionDynamicContext => {
- return useContext(NodeExecutionDynamicContext);
- };
diff --git a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/WorkflowNodeExecutionsProvider.tsx b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/WorkflowNodeExecutionsProvider.tsx
deleted file mode 100644
index a2f08d31d..000000000
--- a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/WorkflowNodeExecutionsProvider.tsx
+++ /dev/null
@@ -1,236 +0,0 @@
-import React, {
- PropsWithChildren,
- useContext,
- useEffect,
- useState,
-} from 'react';
-import { NodeExecution } from 'models/Execution/types';
-import {
- IWorkflowNodeExecutionsContext,
- NodeExecutionsById,
- WorkflowNodeExecutionsContext,
-} from 'components/Executions/contexts';
-import { isEqual, keyBy, merge, mergeWith, cloneDeep } from 'lodash';
-import { dNode } from 'models/Graph/types';
-import {
- NodeExecutionDynamicWorkflowQueryResult,
- makeNodeExecutionDynamicWorkflowQuery,
-} from 'components/Workflow/workflowQueries';
-import { transformerWorkflowToDag } from 'components/WorkflowGraph/transformerWorkflowToDag';
-import { checkForDynamicExecutions } from 'components/common/utils';
-import { useQuery } from 'react-query';
-import { convertToPlainNodes } from 'components/Executions/ExecutionDetails/Timeline/helpers';
-import { useNodeExecutionContext } from './NodeExecutionDetailsContextProvider';
-import {
- mapStringifyReplacer,
- mergeNodeExecutions,
- stringifyIsEqual,
-} from './utils';
-
-export type WorkflowNodeExecutionsProviderProps = PropsWithChildren<{
- initialNodeExecutions?: NodeExecution[];
-}>;
-
-/** Should wrap "top level" component in Execution view, will build a nodeExecutions tree for specific workflow */
-export const WorkflowNodeExecutionsProvider = ({
- initialNodeExecutions,
- children,
-}: WorkflowNodeExecutionsProviderProps) => {
- const [shouldUpdate, setShouldUpdate] = useState(false);
- const { compiledWorkflowClosure } = useNodeExecutionContext();
-
- const [nodeExecutionsById, setNodeExecutionsById] =
- useState({});
-
- const [dagError, setDagError] = useState();
- const [mergedDag, setMergedDag] = useState({});
- const [initialDNodes, setInitialDNodes] = useState([]);
-
- const [dynamicWorkflows, setDynamicWorkflows] =
- useState({});
- const [staticExecutionIdsMap, setstaticExecutionIdsMap] = useState({});
-
- const [dynamicParents, setDynamicParents] = useState({});
-
- const nodeExecutionDynamicWorkflowQuery = useQuery(
- makeNodeExecutionDynamicWorkflowQuery(dynamicParents),
- );
-
- useEffect(() => {
- const initialNodeExecutionsById = keyBy(initialNodeExecutions, 'scopedId');
-
- setCurrentNodeExecutionsById(initialNodeExecutionsById, true);
- }, [initialNodeExecutions]);
-
- useEffect(() => {
- const { staticExecutionIdsMap: newstaticExecutionIdsMap } =
- compiledWorkflowClosure
- ? transformerWorkflowToDag(compiledWorkflowClosure)
- : { staticExecutionIdsMap: {} };
-
- setstaticExecutionIdsMap(prev => {
- if (isEqual(prev, newstaticExecutionIdsMap)) {
- return prev;
- }
-
- return newstaticExecutionIdsMap;
- });
- }, [compiledWorkflowClosure]);
-
- useEffect(() => {
- const newdynamicParents = checkForDynamicExecutions(
- nodeExecutionsById,
- staticExecutionIdsMap,
- );
- setDynamicParents(prev => {
- if (isEqual(prev, newdynamicParents)) {
- return prev;
- }
-
- return newdynamicParents;
- });
- }, [nodeExecutionsById]);
-
- useEffect(() => {
- const dagData = compiledWorkflowClosure
- ? transformerWorkflowToDag(
- compiledWorkflowClosure,
- dynamicWorkflows,
- nodeExecutionsById,
- )
- : { dag: {} as dNode, staticExecutionIdsMap: {}, error: undefined };
-
- const { dag, staticExecutionIdsMap, error } = dagData;
-
- if (error) {
- // if an error occured, stop processing
- setDagError(error);
- return;
- }
-
- const nodes = dag?.nodes ?? [];
-
- let newMergedDag = dag;
-
- for (const dynamicId in dynamicWorkflows) {
- if (staticExecutionIdsMap[dynamicId]) {
- if (compiledWorkflowClosure) {
- const dynamicWorkflow = transformerWorkflowToDag(
- compiledWorkflowClosure,
- dynamicWorkflows,
- nodeExecutionsById,
- );
- newMergedDag = dynamicWorkflow.dag;
- }
- }
- }
- setMergedDag(prev => {
- if (stringifyIsEqual(prev, newMergedDag)) {
- return prev;
- }
- return newMergedDag;
- });
-
- // we remove start/end node info in the root dNode list during first assignment
- const plainNodes = convertToPlainNodes(nodes);
- plainNodes.map(node => {
- const initialNode = initialDNodes.find(n => n.scopedId === node.scopedId);
- if (initialNode) {
- node.expanded = initialNode.expanded;
- }
- });
- setInitialDNodes(prev => {
- if (stringifyIsEqual(prev, plainNodes)) {
- return prev;
- }
- return plainNodes;
- });
- }, [
- compiledWorkflowClosure,
- dynamicWorkflows,
- dynamicParents,
- nodeExecutionsById,
- ]);
-
- useEffect(() => {
- if (nodeExecutionDynamicWorkflowQuery.isFetching) {
- return;
- }
- setDynamicWorkflows(prev => {
- const newDynamicWorkflows = merge(
- { ...(prev || {}) },
- nodeExecutionDynamicWorkflowQuery.data,
- );
- if (isEqual(prev, newDynamicWorkflows)) {
- return prev;
- }
-
- return newDynamicWorkflows;
- });
- }, [nodeExecutionDynamicWorkflowQuery]);
-
- useEffect(() => {
- if (shouldUpdate) {
- const newDynamicParents = checkForDynamicExecutions(
- nodeExecutionsById,
- staticExecutionIdsMap,
- );
- setDynamicParents(prev => {
- if (isEqual(prev, newDynamicParents)) {
- return prev;
- }
-
- return newDynamicParents;
- });
- setShouldUpdate(false);
- }
- }, [shouldUpdate]);
-
- const setCurrentNodeExecutionsById = (
- newNodeExecutionsById: NodeExecutionsById,
- checkForDynamicParents?: boolean,
- ): void => {
- const mergedNodes = mergeWith(
- cloneDeep(nodeExecutionsById),
- cloneDeep(newNodeExecutionsById),
- mergeNodeExecutions,
- );
-
- setNodeExecutionsById(prev => {
- if (
- JSON.stringify(prev, mapStringifyReplacer) ===
- JSON.stringify(mergedNodes, mapStringifyReplacer)
- ) {
- return prev;
- }
-
- if (checkForDynamicParents) {
- setShouldUpdate(true);
- }
-
- return mergedNodes;
- });
- };
-
- return (
-
- {children}
-
- );
-};
-
-export const useNodeExecutionsById = (): IWorkflowNodeExecutionsContext => {
- return useContext(WorkflowNodeExecutionsContext);
-};
diff --git a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/createExecutionArray.tsx b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/createExecutionArray.tsx
deleted file mode 100644
index 751b07864..000000000
--- a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/createExecutionArray.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-import { transformerWorkflowToDag } from 'components/WorkflowGraph/transformerWorkflowToDag';
-import { getTaskDisplayType } from 'components/Executions/utils';
-import {
- NodeExecutionDetails,
- NodeExecutionDisplayType,
-} from 'components/Executions/types';
-import { Workflow } from 'models/Workflow/types';
-import { Identifier } from 'models/Common/types';
-import { CompiledTask } from 'models/Task/types';
-import { dNode } from 'models/Graph/types';
-import { isEndNode, isStartNode } from 'models/Node/utils';
-import { UNKNOWN_DETAILS } from './types';
-
-interface NodeExecutionInfo extends NodeExecutionDetails {
- scopedId?: string;
-}
-
-export interface CurrentExecutionDetails {
- executionId: Identifier;
- nodes: NodeExecutionInfo[];
-}
-
-function convertToPlainNodes(nodes: dNode[], level = 0): dNode[] {
- const result: dNode[] = [];
- if (!nodes || nodes.length === 0) {
- return result;
- }
- nodes.forEach(node => {
- if (isStartNode(node) || isEndNode(node)) {
- return;
- }
- result.push({ ...node, level });
- if (node.nodes.length > 0) {
- result.push(...convertToPlainNodes(node.nodes, level + 1));
- }
- });
- return result;
-}
-
-const getNodeDetails = (
- node: dNode,
- tasks: CompiledTask[],
-): NodeExecutionInfo => {
- if (node.value.taskNode) {
- const templateName = node.value.taskNode.referenceId.name ?? node.name;
- const task = tasks.find(t => t.template.id.name === templateName);
- const taskType = getTaskDisplayType(task?.template.type);
-
- return {
- scopedId: node.scopedId,
- displayId: node.value.id ?? node.id,
- displayName: templateName,
- displayType: taskType,
- taskTemplate: task?.template,
- };
- }
-
- if (node.value.workflowNode) {
- const workflowNode = node.value.workflowNode;
- const info = workflowNode.launchplanRef ?? workflowNode.subWorkflowRef;
- return {
- scopedId: node.scopedId,
- displayId: node.value.id ?? node.id,
- displayName: node.name ?? info?.name ?? 'N/A',
- displayType: NodeExecutionDisplayType.Workflow,
- };
- }
-
- // TODO: https://github.com/flyteorg/flyteconsole/issues/274
- if (node.value.branchNode) {
- return {
- scopedId: node.scopedId,
- displayId: node.value.id ?? node.id,
- displayName: 'branchNode',
- displayType: NodeExecutionDisplayType.BranchNode,
- };
- }
-
- if (node.value.gateNode) {
- const templateName = node.name;
- const task = tasks.find(t => t.template.id.name === templateName);
- const taskType = getTaskDisplayType(task?.template.type);
- return {
- scopedId: node.scopedId,
- displayId: node.value.id ?? node.id,
- displayName: 'gateNode',
- displayType: taskType,
- taskTemplate: task?.template,
- };
- }
-
- return UNKNOWN_DETAILS;
-};
-
-export function createExecutionDetails(
- workflow: Workflow,
-): CurrentExecutionDetails {
- const result: CurrentExecutionDetails = {
- executionId: workflow.id,
- nodes: [],
- };
-
- if (!workflow.closure?.compiledWorkflow) {
- return result;
- }
-
- const compiledWorkflow = workflow.closure?.compiledWorkflow;
- const { tasks = [] } = compiledWorkflow;
-
- let dNodes = transformerWorkflowToDag(compiledWorkflow).dag.nodes ?? [];
- dNodes = convertToPlainNodes(dNodes);
-
- dNodes.forEach(n => {
- const details = getNodeDetails(n, tasks);
- result.nodes.push({
- ...details,
- });
- });
-
- return result;
-}
diff --git a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/getTaskThroughExecution.ts b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/getTaskThroughExecution.ts
deleted file mode 100644
index b97730df9..000000000
--- a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/getTaskThroughExecution.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import { getTaskDisplayType } from 'components/Executions/utils';
-import { fetchTaskExecutionList } from 'components/Executions/taskExecutionQueries';
-import { NodeExecutionDetails } from 'components/Executions/types';
-import { fetchTaskTemplate } from 'components/Task/taskQueries';
-import { TaskTemplate } from 'models/Task/types';
-import { QueryClient } from 'react-query/types/core/queryClient';
-import { WorkflowNodeExecution } from 'components/Executions/contexts';
-import { CompiledWorkflowClosure } from 'models';
-import { isEqual } from 'lodash';
-
-export const getTaskThroughExecution = async (
- queryClient: QueryClient,
- nodeExecution: WorkflowNodeExecution,
- closure: CompiledWorkflowClosure,
-): Promise => {
- const taskExecutions = await (nodeExecution?.tasksFetched
- ? // if the nodeExecution tasks were already fetched, use them
- Promise.resolve(nodeExecution.taskExecutions || [])
- : // otherwise, fetch them
- fetchTaskExecutionList(queryClient, nodeExecution.id));
-
- let taskTemplate: TaskTemplate = closure?.tasks?.find(task =>
- isEqual(task.template.id, taskExecutions[0].id.taskId),
- )?.template as TaskTemplate;
-
- if (
- // skip request if the template was found
- !taskTemplate &&
- // skip request if the node has a dynamic parent
- !nodeExecution.dynamicParentNodeId &&
- taskExecutions &&
- taskExecutions.length > 0
- ) {
- taskTemplate = await fetchTaskTemplate(
- queryClient,
- taskExecutions[0].id.taskId,
- );
-
- if (!taskTemplate) {
- // eslint-disable-next-line no-console
- console.error(
- `ERROR: Unexpected missing task template while fetching NodeExecution details: ${JSON.stringify(
- taskExecutions[0].id.taskId,
- )}`,
- );
- }
- }
-
- const taskDetails: NodeExecutionDetails = {
- displayId: nodeExecution.id.nodeId,
- displayName: taskExecutions?.[0]?.id.taskId.name,
- displayType: getTaskDisplayType(taskTemplate?.type),
- taskTemplate: taskTemplate,
- };
-
- return taskDetails;
-};
diff --git a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/index.ts b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/index.ts
deleted file mode 100644
index 1211650c2..000000000
--- a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export * from './NodeExecutionDetailsContextProvider';
-export * from './WorkflowNodeExecutionsProvider';
-export * from './NodeExecutionDynamicProvider';
-export * from './utils';
diff --git a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/types.ts b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/types.ts
deleted file mode 100644
index dae3e1afb..000000000
--- a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/types.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import { NodeExecutionDisplayType } from 'components/Executions/types';
-import { Core } from '@flyteorg/flyteidl-types';
-
-export const UNKNOWN_DETAILS = {
- displayId: 'unknownNode',
- displayType: NodeExecutionDisplayType.Unknown,
-};
-
-export function isIdEqual(lhs: Core.IIdentifier, rhs: Core.IIdentifier) {
- return (
- lhs.resourceType === rhs.resourceType &&
- lhs.project === rhs.project &&
- lhs.domain === rhs.domain &&
- lhs.name === rhs.name &&
- lhs.version === rhs.version
- );
-}
diff --git a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/utils.ts b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/utils.ts
deleted file mode 100644
index c954576e4..000000000
--- a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/utils.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { cloneDeep, merge, mergeWith } from 'lodash';
-
-export const mapStringifyReplacer = (key: string, value: any) => {
- if (value instanceof Map) {
- return {
- dataType: 'Map',
- value: Array.from(value.entries()), // or with spread: value: [...value]
- };
- } else {
- return value;
- }
-};
-
-export const stringifyIsEqual = (a: any, b: any) => {
- return (
- JSON.stringify(a, mapStringifyReplacer) ===
- JSON.stringify(b, mapStringifyReplacer)
- );
-};
-
-export const mergeNodeExecutions = (val, srcVal, _topkey) => {
- const retVal = mergeWith(val, srcVal, (target, src, _key) => {
- if (!target) {
- return src;
- }
- if (src instanceof Map) {
- return src;
- }
- const finaVal = typeof src === 'object' ? merge(target, src) : src;
- return finaVal;
- });
- return retVal;
-};
diff --git a/packages/console/src/components/Executions/contextProvider/index.ts b/packages/console/src/components/Executions/contextProvider/index.ts
deleted file mode 100644
index 05a2a8bf9..000000000
--- a/packages/console/src/components/Executions/contextProvider/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export * from './NodeExecutionDetails';
diff --git a/packages/console/src/components/Executions/contexts.ts b/packages/console/src/components/Executions/contexts.ts
deleted file mode 100644
index 9a8ea77e3..000000000
--- a/packages/console/src/components/Executions/contexts.ts
+++ /dev/null
@@ -1,66 +0,0 @@
-import { Task } from 'models';
-import {
- Execution,
- ExecutionData,
- LogsByPhase,
- MapTaskExecution,
- NodeExecution,
-} from 'models/Execution/types';
-import { dNode } from 'models/Graph/types';
-import { createContext } from 'react';
-
-export interface ExecutionContextData {
- execution: Execution;
-}
-
-export type WorkflowTaskExecution = MapTaskExecution & {
- task?: Task;
- taskData?: ExecutionData;
-};
-export interface WorkflowNodeExecution extends NodeExecution {
- tasksFetched?: boolean;
- logsByPhase?: LogsByPhase;
- taskExecutions?: WorkflowTaskExecution[];
- nodeExecutionData?: ExecutionData;
-}
-
-export const ExecutionContext = createContext(
- {} as ExecutionContextData,
-);
-
-export type NodeExecutionsById = Dictionary;
-export type FilteredNodeExecutions = WorkflowNodeExecution[] | undefined;
-export type SetCurrentNodeExecutionsById = (
- currentNodeExecutionsById: Dictionary,
- checkForDynamicParents?: boolean,
-) => void;
-
-export interface IWorkflowNodeExecutionsContext {
- nodeExecutionsById: NodeExecutionsById;
- setCurrentNodeExecutionsById: SetCurrentNodeExecutionsById;
- shouldUpdate: boolean;
- setShouldUpdate: (val: boolean) => void;
- // Tabs
- initialDNodes: dNode[];
- dagData: {
- mergedDag: any;
- dagError: any;
- };
-}
-
-export const WorkflowNodeExecutionsContext =
- createContext({
- nodeExecutionsById: {},
- setCurrentNodeExecutionsById: () => {
- throw new Error('Must use NodeExecutionsByIdContextProvider');
- },
- shouldUpdate: false,
- setShouldUpdate: _val => {
- throw new Error('Must use NodeExecutionsByIdContextProvider');
- },
- initialDNodes: [],
- dagData: {
- mergedDag: {},
- dagError: null,
- },
- });
diff --git a/packages/console/src/components/Executions/filters/types.ts b/packages/console/src/components/Executions/filters/types.ts
deleted file mode 100644
index 9242f478a..000000000
--- a/packages/console/src/components/Executions/filters/types.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import { FilterOperation, FilterOperationList } from 'models/AdminEntity/types';
-
-export interface FilterValue<
- FilterKey extends string,
- DataType = FilterOperationList,
-> {
- label: string;
- value: FilterKey;
- data: DataType;
-}
-
-export type FilterMap<
- FilterKey extends string,
- DataType = FilterOperationList,
-> = Record>;
-
-export interface FilterButtonState {
- open: boolean;
- setOpen: (open: boolean) => void;
- onClick: () => void;
-}
-
-export type FilterStateType = 'single' | 'multi' | 'search' | 'boolean';
-
-export interface FilterState {
- active: boolean;
- button: FilterButtonState;
- label: string;
- type: FilterStateType;
- getFilter: () => FilterOperation[];
- onReset: () => void;
- onChange?: (value) => void;
-}
-
-export interface SingleFilterState
- extends FilterState {
- onChange: (newValue: string) => void;
- selectedValue: FilterKey;
- type: 'single';
- values: FilterValue[];
-}
-
-export interface SearchFilterState extends FilterState {
- onChange: (newValue: string) => void;
- placeholder: string;
- type: 'search';
- value: string;
-}
-
-export interface MultiFilterState
- extends FilterState {
- listHeader: string;
- onChange: (selectedStates: Record) => void;
- selectedStates: Record;
- type: 'multi';
- values: FilterValue[];
-}
-
-export interface BooleanFilterState extends FilterState {
- setActive: (active: boolean) => void;
- type: 'boolean';
-}
diff --git a/packages/console/src/components/Executions/filters/useMultiFilterState.ts b/packages/console/src/components/Executions/filters/useMultiFilterState.ts
deleted file mode 100644
index 1fb3b4bc4..000000000
--- a/packages/console/src/components/Executions/filters/useMultiFilterState.ts
+++ /dev/null
@@ -1,128 +0,0 @@
-import { useQueryState } from 'components/hooks/useQueryState';
-import { isEqual, mapValues, pickBy } from 'lodash';
-import {
- FilterOperationName,
- FilterOperationValue,
-} from 'models/AdminEntity/types';
-import { useEffect, useState } from 'react';
-import { FilterValue, MultiFilterState } from './types';
-import { useFilterButtonState } from './useFilterButtonState';
-
-function serializeForQueryState(values: any[]) {
- return values.join(';');
-}
-function deserializeFromQueryState(stateValue = '') {
- return stateValue.split(';');
-}
-
-interface MultiFilterStateArgs<
- FilterKey extends string,
- DataType extends FilterOperationValue,
-> {
- options: Record>;
- defaultValue: FilterKey[];
- filterKey: string;
- filterOperation?: FilterOperationName;
- label: string;
- listHeader: string;
- queryStateKey: string;
-}
-
-/** Maintains the state for a generic MultiSelectForm filter.
- * The generated `FilterOperation` will use the provided `key` and `operation` (
- * defaults to `VALUE_IN`).
- * The current state of the filter will be synced with the query string using the provided `queryStateKey` value.
- */
-export function useMultiFilterState<
- FilterKey extends string,
- DataType extends FilterOperationValue,
->({
- options,
- defaultValue,
- filterKey,
- filterOperation = FilterOperationName.VALUE_IN,
- label,
- listHeader,
- queryStateKey,
-}: MultiFilterStateArgs): MultiFilterState<
- FilterKey,
- DataType
-> {
- const defaultMappedOptions = mapValues(options, (_, key) =>
- defaultValue.includes(key as FilterKey),
- ) as Record;
- const { params, setQueryStateValue } =
- useQueryState>();
- const queryStateValue = params[queryStateKey];
- const parsedQueryState = deserializeFromQueryState(queryStateValue);
- const queryStateSelected = mapValues(options, (_, key) =>
- parsedQueryState.includes(key),
- ) as Record;
-
- const [selected, setSelected] = useState({
- ...defaultMappedOptions,
- ...queryStateSelected,
- });
- const button = useFilterButtonState();
-
- const selectedKeys = Object.keys(pickBy(selected));
- const active = !isEqual(selectedKeys, defaultValue);
-
- const onChange = (selectedStates: Record) => {
- setSelected(selectedStates);
- };
-
- const onReset = () => {
- setSelected(defaultMappedOptions);
- button.setOpen(false);
- };
-
- // Sync changes to selected value with the query state
- useEffect(() => {
- const queryValue = active
- ? serializeForQueryState(selectedKeys)
- : undefined;
- setQueryStateValue(queryStateKey, queryValue);
- }, [selectedKeys.join(), queryStateKey]);
-
- // Watch for external changes to query string and sync our value if
- // it's different
- useEffect(() => {
- if (queryStateValue) {
- if (!isEqual(selected, queryStateSelected)) {
- setSelected(queryStateSelected);
- }
- }
- }, [queryStateValue]);
-
- const getFilter = () => {
- // Collect the `data` property from all options for which we have
- // an active selected state
- const value = (Object.keys(options) as FilterKey[]).reduce(
- (out, key) => (selected[key] ? [...out, options[key].data] : out),
- [],
- );
- return value.length
- ? [
- {
- value,
- key: filterKey,
- operation: filterOperation,
- },
- ]
- : [];
- };
-
- return {
- active,
- button,
- getFilter,
- label,
- listHeader,
- onChange,
- onReset,
- type: 'multi',
- values: Object.values(options),
- selectedStates: selected,
- };
-}
diff --git a/packages/console/src/components/Executions/filters/useSearchFilterState.ts b/packages/console/src/components/Executions/filters/useSearchFilterState.ts
deleted file mode 100644
index f2e137485..000000000
--- a/packages/console/src/components/Executions/filters/useSearchFilterState.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { useQueryState } from 'components/hooks/useQueryState';
-import { FilterOperationName } from 'models/AdminEntity/types';
-import { useEffect, useState } from 'react';
-import { SearchFilterState } from './types';
-import { useFilterButtonState } from './useFilterButtonState';
-
-interface SearchFilterStateArgs {
- defaultValue?: string;
- filterKey: string;
- filterOperation?: FilterOperationName;
- label: string;
- placeholder: string;
- queryStateKey: string;
-}
-
-/** Maintains the state for a `SearchInputForm` filter.
- * The generated `FilterOperation` will use the provided `key` and `operation`
- * (defaults to `EQ`)
- * The current search value will be synced to the query string using the
- * provided `queryStateKey` value.
- */
-export function useSearchFilterState({
- defaultValue = '',
- filterKey,
- filterOperation = FilterOperationName.EQ,
- label,
- placeholder,
- queryStateKey,
-}: SearchFilterStateArgs): SearchFilterState {
- const { params, setQueryStateValue } =
- useQueryState>();
- const queryStateValue = params[queryStateKey];
-
- const [value, setValue] = useState(defaultValue);
- const active = value !== defaultValue;
-
- const button = useFilterButtonState();
- const onChange = (newValue: string) => {
- setValue(newValue);
- // Automatically hide the form on submission of a new value
- button.setOpen(false);
- };
-
- const onReset = () => {
- setValue(defaultValue);
- button.setOpen(false);
- };
-
- useEffect(() => {
- const queryValue = value === defaultValue ? undefined : value;
- setQueryStateValue(queryStateKey, queryValue);
- }, [value, queryStateKey]);
-
- useEffect(() => {
- if (queryStateValue) {
- setValue(queryStateValue);
- }
- }, [queryStateValue]);
-
- const getFilter = () =>
- value
- ? [
- {
- value,
- key: filterKey,
- operation: filterOperation,
- },
- ]
- : [];
-
- return {
- active,
- button,
- getFilter,
- onChange,
- onReset,
- label,
- placeholder,
- value,
- type: 'search',
- };
-}
diff --git a/packages/console/src/components/Executions/filters/useSingleFilterState.ts b/packages/console/src/components/Executions/filters/useSingleFilterState.ts
deleted file mode 100644
index 64654527e..000000000
--- a/packages/console/src/components/Executions/filters/useSingleFilterState.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import { log } from 'common/log';
-import { useQueryState } from 'components/hooks/useQueryState';
-import { useEffect, useState } from 'react';
-import { FilterMap, FilterValue, SingleFilterState } from './types';
-import { useFilterButtonState } from './useFilterButtonState';
-
-interface SingleFilterStateArgs {
- options: FilterMap;
- defaultValue: FilterValue;
- label: string;
- queryStateKey: string;
-}
-
-function valueOrDefault(
- options: FilterMap,
- newValue: string | undefined,
- defaultValue: FilterValue,
-) {
- if (newValue === undefined) {
- return defaultValue;
- }
-
- if (!{}.hasOwnProperty.call(options, newValue)) {
- log.warn(`Filter has no option ${newValue}, using default`);
- return defaultValue;
- }
- return options[newValue as FilterKey];
-}
-
-/** Maintains the state for a generic SingleSelectForm filter.
- * The generated filter depends on the `data` of the provided items.
- * The selection state for this filter will be synced with the query string using
- * the provided `queryStateKey`.
- */
-export function useSingleFilterState({
- options,
- defaultValue,
- label,
- queryStateKey,
-}: SingleFilterStateArgs): SingleFilterState {
- const { params, setQueryStateValue } =
- useQueryState>();
- const queryStateValue = params[queryStateKey];
- const [selectedOption, setSelectedOption] = useState(
- valueOrDefault(options, queryStateValue, defaultValue),
- );
- const active = selectedOption.value !== defaultValue.value;
- const button = useFilterButtonState();
-
- const onReset = () => {
- setSelectedOption(defaultValue);
- button.setOpen(false);
- };
-
- const onChange = (newValue: string) => {
- setSelectedOption(valueOrDefault(options, newValue, defaultValue));
- // Automatically hide the form on selection of a new option
- button.setOpen(false);
- };
-
- useEffect(() => {
- const { value } = selectedOption;
- const queryValue = value;
- setQueryStateValue(queryStateKey, queryValue);
- }, [selectedOption, queryStateKey]);
-
- useEffect(() => {
- if (queryStateValue) {
- setSelectedOption(valueOrDefault(options, queryStateValue, defaultValue));
- }
- }, [queryStateValue]);
-
- const getFilter = () => selectedOption.data;
-
- return {
- active,
- button,
- getFilter,
- label,
- onChange,
- onReset,
- selectedValue: selectedOption.value,
- type: 'single',
- values: Object.values(options),
- };
-}
diff --git a/packages/console/src/components/Executions/index.ts b/packages/console/src/components/Executions/index.ts
deleted file mode 100644
index 9058bf7a3..000000000
--- a/packages/console/src/components/Executions/index.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-export * from './types';
-export * from './contexts';
-export * from './contextProvider';
-export * from './ExecutionDetails';
-export * from './nodeExecutionQueries';
-export * from './utils';
-export * from './ExecutionStatusBadge';
-export * from './TaskExecutionsList';
-export * from './taskExecutionQueries';
-export * from './useWorkflowExecution';
-export * from './useWorkflowExecution';
-export * from './nodeExecutionQueries';
diff --git a/packages/console/src/components/Executions/nodeExecutionQueries.ts b/packages/console/src/components/Executions/nodeExecutionQueries.ts
deleted file mode 100644
index 41adff1a4..000000000
--- a/packages/console/src/components/Executions/nodeExecutionQueries.ts
+++ /dev/null
@@ -1,532 +0,0 @@
-import { QueryInput, QueryType } from 'components/data/types';
-import { retriesToZero } from 'components/flytegraph/ReactFlow/utils';
-import { cloneDeep, isEqual } from 'lodash';
-import {
- PaginatedEntityResponse,
- RequestConfig,
-} from 'models/AdminEntity/types';
-import {
- getNodeExecution,
- getNodeExecutionData,
- getTaskExecutionData,
- listNodeExecutions,
- listTaskExecutionChildren,
- listTaskExecutions,
-} from 'models/Execution/api';
-import { nodeExecutionQueryParams } from 'models/Execution/constants';
-import {
- ExternalResource,
- LogsByPhase,
- NodeExecution,
- NodeExecutionIdentifier,
- TaskExecution,
- TaskExecutionIdentifier,
- WorkflowExecutionIdentifier,
-} from 'models/Execution/types';
-import { ignoredNodeIds } from 'models/Node/constants';
-import { isMapTaskV1 } from 'models/Task/utils';
-import { QueryClient } from 'react-query';
-import { getTask } from 'models';
-import { createDebugLogger } from 'common/log';
-import { WorkflowNodeExecution, WorkflowTaskExecution } from './contexts';
-import { fetchTaskExecutionList } from './taskExecutionQueries';
-import { formatRetryAttempt, getGroupedLogs } from './TaskExecutionsList/utils';
-import { NodeExecutionGroup } from './types';
-import { isDynamicNode, isParentNode, nodeExecutionIsTerminal } from './utils';
-
-const debug = createDebugLogger('@nodeExecutionQueries');
-
-function removeSystemNodes(nodeExecutions: NodeExecution[]): NodeExecution[] {
- return nodeExecutions.filter(ne => {
- if (ignoredNodeIds.includes(ne.id.nodeId)) {
- return false;
- }
- const specId = ne.metadata?.specNodeId;
- if (specId != null && ignoredNodeIds.includes(specId)) {
- return false;
- }
- return true;
- });
-}
-
-/** A query for fetching a single `NodeExecution` by id. */
-export function makeNodeExecutionQuery(
- id: NodeExecutionIdentifier,
-): QueryInput {
- return {
- queryKey: [QueryType.NodeExecution, id],
- queryFn: () => getNodeExecution(id),
- };
-}
-
-/** A query for fetching a single `NodeExecution` by id. */
-export function makeNodeExecutionAndTasksQuery(
- id: NodeExecutionIdentifier,
- queryClient: QueryClient,
- dynamicParentNodeId?: string,
-) {
- return {
- queryKey: [QueryType.NodeExecutionAndTasks, id],
- queryFn: async () => {
- // step 1: Fetch the Node execution
- const nodeExecutionPure = await getNodeExecution(id);
-
- const dynamicParent = dynamicParentNodeId
- ? await getNodeExecution({ ...id, nodeId: dynamicParentNodeId })
- : null;
-
- // step 2: Fetch the task executions and attach them to the node execution
- const workflowNodeExecution = (await getTaskExecutions(
- queryClient,
- nodeExecutionPure,
- )) as WorkflowNodeExecution;
-
- if (!workflowNodeExecution) {
- return [{} as WorkflowNodeExecution];
- }
-
- workflowNodeExecution.scopedId = workflowNodeExecution?.id?.nodeId;
- // step 3: get the node executiondata
- const nodeExecutionData = await getNodeExecutionData(id);
-
- // step 4: get the compiled task closure
- // -- only one request is made as it is constant across all attempts
- const taskExecutions = workflowNodeExecution?.taskExecutions || [];
- const taskId = taskExecutions?.[0]?.id?.taskId;
-
- // don't issue a task compiled closure request if the node has a dynamic parent
- // TODO: fetch dynamic parent to get the compiled closure
- const compiledTaskClosure = await (taskId && !dynamicParentNodeId
- ? getTask(taskId!).catch(() => null)
- : Promise.resolve(null));
-
- // step 5: get each task's executions data
- const tasksExecutionData = await Promise.all(
- taskExecutions?.map(te =>
- getTaskExecutionData(te.id).then(executionData => {
- const finalTask: WorkflowTaskExecution = {
- ...te,
- // append data to each task individually
- task: compiledTaskClosure!,
- taskData: executionData,
- };
- return finalTask;
- }),
- ),
- );
-
- const final = [
- {
- ...workflowNodeExecution,
- taskExecutions: tasksExecutionData,
- nodeExecutionData,
- } as WorkflowNodeExecution,
- ];
-
- if (dynamicParent) {
- final.push({
- ...(dynamicParent as WorkflowNodeExecution),
- scopedId: dynamicParentNodeId,
- });
- }
-
- return final;
- },
- };
-}
-
-export const getTaskExecutions = async (
- queryClient: QueryClient,
- nodeExecution: WorkflowNodeExecution,
-): Promise => {
- const isTerminal = nodeExecutionIsTerminal(nodeExecution);
- const isDynamic = isDynamicNode(nodeExecution);
-
- return await fetchTaskExecutionList(
- queryClient,
- nodeExecution.id as any,
- ).then(taskExecutions => {
- const finalTaskExecutions = cloneDeep(taskExecutions)?.map(
- taskExecution =>
- ({
- ...taskExecution,
- dynamicParentNodeId: nodeExecution.dynamicParentNodeId,
- } as WorkflowTaskExecution),
- );
-
- const useNewMapTaskView = finalTaskExecutions?.every(taskExecution => {
- const {
- closure: { taskType, metadata, eventVersion = 0 },
- } = taskExecution;
- return isMapTaskV1(
- eventVersion,
- metadata?.externalResources?.length ?? 0,
- taskType ?? undefined,
- );
- });
-
- const externalResources: ExternalResource[] = finalTaskExecutions
- .map(taskExecution => taskExecution.closure.metadata?.externalResources)
- .flat()
- .filter((resource): resource is ExternalResource => !!resource);
-
- const logsByPhase: LogsByPhase = getGroupedLogs(externalResources);
-
- const appendTasksFetched = !isDynamic || (isDynamic && isTerminal);
-
- return {
- ...nodeExecution,
- taskExecutions: finalTaskExecutions,
- ...(useNewMapTaskView && logsByPhase.size > 0 && { logsByPhase }),
- ...((appendTasksFetched && { tasksFetched: true }) || {}),
- } as any as WorkflowNodeExecution;
- });
-};
-
-/** A query for fetching a single `NodeExecution` by id. */
-export function makeNodeExecutionQueryEnhanced(
- nodeExecution: WorkflowNodeExecution,
- queryClient: QueryClient,
-): QueryInput {
- const { id } = nodeExecution || {};
-
- return {
- enabled: !!id,
- queryKey: [QueryType.NodeExecutionEnhanced, id],
- queryFn: async () => {
- // complexity:
- // +1 for parent node tasks
- // +1 for node execution list
- // +n= executionList.length
- const parentExecution = cloneDeep(nodeExecution);
- const isParent = isParentNode(parentExecution);
- const fromUniqueParentId = parentExecution.id.nodeId;
- const parentScopeId =
- parentExecution.scopedId ?? parentExecution.metadata?.specNodeId;
- parentExecution.scopedId = parentScopeId;
- const dynamicParentNodeId = isDynamicNode(parentExecution)
- ? fromUniqueParentId
- : parentExecution.dynamicParentNodeId;
-
- // if the node is a parent, force refetch its children
- // called by NodeExecutionDynamicProvider
- const parentNodeExecutions = isParent
- ? () =>
- fetchNodeExecutionList(
- // requests listNodeExecutions
- queryClient,
- id.executionId,
- {
- params: {
- [nodeExecutionQueryParams.parentNodeId]: fromUniqueParentId,
- },
- },
- ).then(childExecutions => {
- const children = childExecutions.map(e => {
- const scopedId = e.metadata?.specNodeId
- ? retriesToZero(e?.metadata?.specNodeId)
- : retriesToZero(e?.id?.nodeId);
-
- return {
- ...e,
- scopedId: `${parentScopeId}-0-${scopedId}`,
- fromUniqueParentId,
- ...(dynamicParentNodeId ? { dynamicParentNodeId } : {}),
- };
- });
- return children;
- })
- : () => Promise.resolve([]);
-
- const parentNodeAndTaskExecutions = await Promise.all([
- getTaskExecutions(queryClient, parentExecution),
- parentNodeExecutions(),
- ]).then(([parent, children]) => {
- // strip closure and metadata to avoid overwriting data from queries that handle status updates
- const {
- closure: _,
- metadata: __,
- ...parentLight
- } = parent || ({} as WorkflowNodeExecution);
- return [parentLight, ...children].filter(n => !!n);
- });
-
- return parentNodeAndTaskExecutions as NodeExecution[];
- },
- };
-}
-
-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,
- id: NodeExecutionIdentifier,
-) {
- return queryClient.fetchQuery(makeNodeExecutionQuery(id));
-}
-
-// On successful node execution list queries, extract and store all
-// executions so they are individually fetchable from the cache.
-function cacheNodeExecutions(
- queryClient: QueryClient,
- nodeExecutions: NodeExecution[],
-) {
- nodeExecutions.forEach(ne =>
- queryClient.setQueryData([QueryType.NodeExecution, ne.id], ne),
- );
-}
-
-/** A query for fetching a list of `NodeExecution`s which are children of a given
- * `Execution`.
- */
-export function makeNodeExecutionListQuery(
- queryClient: QueryClient,
- id: WorkflowExecutionIdentifier,
- config?: RequestConfig,
-): QueryInput {
- /**
- * Note on scopedId:
- * We use scopedId as a key between various UI elements built from static data
- * (eg, CompiledWorkflowClosure for the graph) that need to be mapped to runtime
- * values like nodeExecutions; rendering from a static entity has no way to know
- * the actual retry value so we use '0' for this key -- the actual value of retries
- * remains as the nodeId.
- */
- return {
- queryKey: [QueryType.NodeExecutionList, id, config],
- queryFn: async () => {
- const promise = (await listNodeExecutions(id, config)).entities;
- const nodeExecutions = removeSystemNodes(promise);
- nodeExecutions.map(exe => {
- if (exe.metadata?.specNodeId) {
- return (exe.scopedId = retriesToZero(exe.metadata.specNodeId));
- } else {
- return (exe.scopedId = retriesToZero(exe.id.nodeId));
- }
- });
- cacheNodeExecutions(queryClient, nodeExecutions);
-
- return nodeExecutions;
- },
- };
-}
-
-/** Composable fetch function which wraps `makeNodeExecutionListQuery`. */
-export function fetchNodeExecutionList(
- queryClient: QueryClient,
- id: WorkflowExecutionIdentifier,
- config?: RequestConfig,
-) {
- return queryClient.fetchQuery(
- makeNodeExecutionListQuery(queryClient, id, config),
- );
-}
-
-/** A query for fetching a list of `NodeExecution`s which are children of a given
- * `TaskExecution`.
- */
-export function makeTaskExecutionChildListQuery(
- queryClient: QueryClient,
- id: TaskExecutionIdentifier,
- config?: RequestConfig,
-): QueryInput {
- return {
- queryKey: [QueryType.TaskExecutionChildList, id, config],
- queryFn: async () => {
- const nodeExecutions = removeSystemNodes(
- (await listTaskExecutionChildren(id, config)).entities,
- );
- cacheNodeExecutions(queryClient, nodeExecutions);
- return nodeExecutions;
- },
- onSuccess: nodeExecutions => {
- nodeExecutions.forEach(ne =>
- queryClient.setQueryData([QueryType.NodeExecution, ne.id], ne),
- );
- },
- };
-}
-
-/** Composable fetch function which wraps `makeTaskExecutionChildListQuery`. */
-export function fetchTaskExecutionChildList(
- queryClient: QueryClient,
- id: TaskExecutionIdentifier,
- config?: RequestConfig,
-) {
- return queryClient.fetchQuery(
- makeTaskExecutionChildListQuery(queryClient, id, config),
- );
-}
-
-/** --- Queries for fetching children of a NodeExecution --- */
-
-async function fetchGroupForTaskExecution(
- queryClient: QueryClient,
- taskExecutionId: TaskExecutionIdentifier,
- config: RequestConfig,
-): Promise {
- return {
- // NodeExecutions created by a TaskExecution are grouped
- // by the retry attempt of the task.
- name: formatRetryAttempt(taskExecutionId.retryAttempt),
- nodeExecutions: await fetchTaskExecutionChildList(
- queryClient,
- taskExecutionId,
- config,
- ),
- };
-}
-
-async function fetchGroupForWorkflowExecution(
- queryClient: QueryClient,
- executionId: WorkflowExecutionIdentifier,
- config: RequestConfig,
-): Promise {
- return {
- // NodeExecutions created by a workflow execution are grouped
- // by the execution id, since workflow executions are not retryable.
- name: executionId.name,
- nodeExecutions: await fetchNodeExecutionList(
- queryClient,
- executionId,
- config,
- ),
- };
-}
-
-async function fetchGroupsForTaskExecutionNode(
- queryClient: QueryClient,
- nodeExecution: NodeExecution,
- config: RequestConfig,
-): Promise {
- const taskExecutions = await fetchTaskExecutionList(
- queryClient,
- nodeExecution.id,
- config,
- );
-
- // For TaskExecutions marked as parents, fetch its children and create a group.
- // Otherwise, return null and we will filter it out later.
- const groups = await Promise.all(
- taskExecutions.map(execution =>
- execution.isParent
- ? fetchGroupForTaskExecution(queryClient, execution.id, config)
- : Promise.resolve(null),
- ),
- );
-
- // Remove any empty groups
- return groups.filter(
- group => group !== null && group.nodeExecutions.length > 0,
- ) as NodeExecutionGroup[];
-}
-
-async function fetchGroupsForWorkflowExecutionNode(
- queryClient: QueryClient,
- nodeExecution: NodeExecution,
- config: RequestConfig,
-): Promise {
- if (!nodeExecution.closure.workflowNodeMetadata) {
- throw new Error('Unexpected empty `workflowNodeMetadata`');
- }
- const { executionId } = nodeExecution.closure.workflowNodeMetadata;
- // We can only have one WorkflowExecution (no retries), so there is only
- // one group to return. But calling code expects it as an array.
- const group = await fetchGroupForWorkflowExecution(
- queryClient,
- executionId,
- config,
- );
- return group.nodeExecutions.length > 0 ? [group] : [];
-}
-
-async function fetchGroupsForParentNodeExecution(
- queryClient: QueryClient,
- nodeExecution: NodeExecution,
- config: RequestConfig,
-): Promise {
- const finalConfig = {
- ...config,
- params: {
- ...config.params,
- [nodeExecutionQueryParams.parentNodeId]: nodeExecution.id.nodeId,
- },
- };
-
- const parentScopeId =
- nodeExecution.scopedId ?? nodeExecution.metadata?.specNodeId;
- nodeExecution.scopedId = parentScopeId;
-
- const children = await fetchNodeExecutionList(
- queryClient,
- nodeExecution.id.executionId,
- finalConfig,
- );
-
- const groupsByName = children.reduce