Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: Remove intermediate NodeExecutionsTable row content #75

Merged
merged 11 commits into from
Jun 22, 2020
8 changes: 4 additions & 4 deletions src/components/Cache/createCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ function hasId(value: Object): value is HasIdObject {
* strictly equal (===). The cache provides methods for getting/setting values
* by key and for merging values or arrays of values with any existing items.
*/
export interface ValueCache {
export interface ValueCache<ValueType = object> {
/** Retrieve an item by key */
get(key: EntityKey): object | undefined;
get(key: EntityKey): ValueType | undefined;
/** Check existence of an item by key */
has(key: EntityKey): boolean;
/** Merge an array of values. If the items have an `id` property, its value
Expand All @@ -30,9 +30,9 @@ export interface ValueCache {
* performed. For arrays, the value is _replaced_.
* @returns The merged value
*/
mergeValue(key: EntityKey, value: object): object;
mergeValue(key: EntityKey, value: ValueType): ValueType;
/** Set an item value by key. Replaces any existing value. */
set(key: EntityKey, value: object): object;
set(key: EntityKey, value: ValueType): ValueType;
}

type Cacheable = object | any[];
Expand Down
26 changes: 16 additions & 10 deletions src/components/Executions/ExecutionDetails/ExecutionDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { WaitForData, withRouteParams } from 'components/common';
import {
RefreshConfig,
useDataRefresher,
useWorkflowExecution
} from 'components/hooks';
import { RefreshConfig, useDataRefresher } from 'components/hooks';
import { Execution } from 'models';
import * as React from 'react';
import { executionRefreshIntervalMs } from '../constants';
import { ExecutionContext, ExecutionDataCacheContext } from '../contexts';
import { useExecutionDataCache } from '../useExecutionDataCache';
import { useWorkflowExecution } from '../useWorkflowExecution';
import { executionIsTerminal } from '../utils';
import { ExecutionContext } from './contexts';
import { ExecutionDetailsAppBarContent } from './ExecutionDetailsAppBarContent';
import { ExecutionNodeViews } from './ExecutionNodeViews';

Expand All @@ -35,15 +33,23 @@ export const ExecutionDetailsContainer: React.FC<ExecutionDetailsRouteParams> =
domain: domainId,
name: executionId
};

const { fetchable, terminateExecution } = useWorkflowExecution(id);
const dataCache = useExecutionDataCache();
const { fetchable, terminateExecution } = useWorkflowExecution(
id,
dataCache
);
useDataRefresher(id, fetchable, executionRefreshConfig);
const contextValue = { terminateExecution, execution: fetchable.value };
const contextValue = {
terminateExecution,
execution: fetchable.value
};
return (
<WaitForData {...fetchable}>
<ExecutionContext.Provider value={contextValue}>
<ExecutionDetailsAppBarContent execution={fetchable.value} />
<ExecutionNodeViews execution={fetchable.value} />
<ExecutionDataCacheContext.Provider value={dataCache}>
<ExecutionNodeViews execution={fetchable.value} />
</ExecutionDataCacheContext.Provider>
</ExecutionContext.Provider>
</WaitForData>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { NodeDetailsProps } from 'components/WorkflowGraph';
import { useStyles as useBaseStyles } from 'components/WorkflowGraph/NodeDetails/styles';

import { LiteralMapViewer } from 'components/Literals';
import { ExecutionContext } from '../contexts';
import { ExecutionContext } from '../../contexts';

/** Details panel renderer for the start/input node in a graph. Displays the
* top level `WorkflowExecution` inputs.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { SectionHeader, WaitForData } from 'components/common';
import { useCommonStyles } from 'components/common/styles';
import { useWorkflowExecutionData } from 'components/hooks';
import { LiteralMapViewer, RemoteLiteralMapViewer } from 'components/Literals';
import { NodeDetailsProps } from 'components/WorkflowGraph';
import { useStyles as useBaseStyles } from 'components/WorkflowGraph/NodeDetails/styles';
import { emptyLiteralMapBlob, Execution } from 'models';
import * as React from 'react';
import { ExecutionContext } from '../contexts';
import { ExecutionContext } from '../../contexts';
import { useWorkflowExecutionData } from '../../useWorkflowExecution';

const RemoteExecutionOutputs: React.FC<{ execution: Execution }> = ({
execution
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
TaskNodeDetails
} from 'components/WorkflowGraph';
import { useStyles as useBaseStyles } from 'components/WorkflowGraph/NodeDetails/styles';
import { NodeExecutionsContext } from '../contexts';
import { NodeExecutionsContext } from '../../contexts';
import { NodeExecutionData } from '../NodeExecutionData';
import { NodeExecutionInputs } from '../NodeExecutionInputs';
import { NodeExecutionOutputs } from '../NodeExecutionOutputs';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { WaitForData } from 'components/common';
import { useTabState } from 'components/hooks/useTabState';
import { Execution } from 'models';
import * as React from 'react';
import { NodeExecutionsRequestConfigContext } from '../contexts';
import { ExecutionFilters } from '../ExecutionFilters';
import { useNodeExecutionFiltersState } from '../filters/useExecutionFiltersState';
import { NodeExecutionsTable } from '../Tables/NodeExecutionsTable';
import { useWorkflowExecutionState } from '../useWorkflowExecutionState';
import { NodeExecutionsRequestConfigContext } from './contexts';
import { ExecutionWorkflowGraph } from './ExecutionWorkflowGraph';

const useStyles = makeStyles((theme: Theme) => ({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { DetailsPanel } from 'components/common';
import { WorkflowGraph } from 'components/WorkflowGraph';
import { keyBy } from 'lodash';
import { endNodeId, startNodeId } from 'models';
import { endNodeId, NodeExecution, startNodeId } from 'models';
import { Workflow } from 'models/Workflow';
import * as React from 'react';
import { DetailedNodeExecution } from '../types';
import { NodeExecutionsContext } from './contexts';
import { NodeExecutionsContext } from '../contexts';
import { useDetailedNodeExecutions } from '../useDetailedNodeExecutions';
import { NodeExecutionDetails } from './NodeExecutionDetails';
import { TaskExecutionNodeRenderer } from './TaskExecutionNodeRenderer/TaskExecutionNodeRenderer';

export interface ExecutionWorkflowGraphProps {
nodeExecutions: DetailedNodeExecution[];
nodeExecutions: NodeExecution[];
workflow: Workflow;
}

Expand All @@ -19,9 +19,10 @@ export const ExecutionWorkflowGraph: React.FC<ExecutionWorkflowGraphProps> = ({
nodeExecutions,
workflow
}) => {
const detailedNodeExecutions = useDetailedNodeExecutions(nodeExecutions);
const nodeExecutionsById = React.useMemo(
() => keyBy(nodeExecutions, 'id.nodeId'),
[nodeExecutions]
() => keyBy(detailedNodeExecutions, 'id.nodeId'),
[detailedNodeExecutions]
);
const [selectedNodes, setSelectedNodes] = React.useState<string[]>([]);
const onNodeSelectionChanged = (newSelection: string[]) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { TaskNodeRenderer } from 'components/WorkflowGraph/TaskNodeRenderer';
import { NodeExecutionPhase } from 'models/Execution/enums';
import { DAGNode } from 'models/Graph';

import { NodeExecutionsContext } from '../contexts';
import { NodeExecutionsContext } from '../../contexts';
import { StatusIndicator } from './StatusIndicator';

/** Renders DAGNodes with colors based on their node type, as well as dots to
Expand Down
2 changes: 1 addition & 1 deletion src/components/Executions/ExecutionInputsOutputsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { Dialog, DialogContent, Tab, Tabs } from '@material-ui/core';
import { makeStyles, Theme } from '@material-ui/core/styles';
import { WaitForData } from 'components/common';
import { ClosableDialogTitle } from 'components/common/ClosableDialogTitle';
import { useWorkflowExecutionData } from 'components/hooks';
import { LiteralMapViewer, RemoteLiteralMapViewer } from 'components/Literals';
import { emptyLiteralMapBlob, Execution } from 'models';
import * as React from 'react';
import { useWorkflowExecutionData } from './useWorkflowExecution';

const useStyles = makeStyles((theme: Theme) => ({
content: {
Expand Down
162 changes: 40 additions & 122 deletions src/components/Executions/Tables/NodeExecutionChildren.tsx
Original file line number Diff line number Diff line change
@@ -1,132 +1,50 @@
import { WaitForData } from 'components/common';
import {
RefreshConfig,
useDataRefresher,
useWorkflowExecution
} from 'components/hooks';
import { isEqual } from 'lodash';
import { Execution, NodeExecutionClosure, WorkflowNodeMetadata } from 'models';
import { Typography } from '@material-ui/core';
import * as classnames from 'classnames';
import { useExpandableMonospaceTextStyles } from 'components/common/ExpandableMonospaceText';
import * as React from 'react';
schottra marked this conversation as resolved.
Show resolved Hide resolved
import { executionIsTerminal, executionRefreshIntervalMs } from '..';
import { ExecutionContext } from '../ExecutionDetails/contexts';
import { DetailedNodeExecution } from '../types';
import { useDetailedTaskExecutions } from '../useDetailedTaskExecutions';
import {
useTaskExecutions,
useTaskExecutionsRefresher
} from '../useTaskExecutions';
import { generateRowSkeleton } from './generateRowSkeleton';
import { NoExecutionsContent } from './NoExecutionsContent';
import { useColumnStyles } from './styles';
import { generateColumns as generateTaskExecutionColumns } from './taskExecutionColumns';
import { TaskExecutionRow } from './TaskExecutionRow';
import { generateColumns as generateWorkflowExecutionColumns } from './workflowExecutionColumns';
import { WorkflowExecutionRow } from './WorkflowExecutionRow';
import { DetailedNodeExecutionGroup } from '../types';
import { NodeExecutionRow } from './NodeExecutionRow';
import { useExecutionTableStyles } from './styles';

export interface NodeExecutionChildrenProps {
execution: DetailedNodeExecution;
childGroups: DetailedNodeExecutionGroup[];
}

const TaskNodeExecutionChildren: React.FC<NodeExecutionChildrenProps> = ({
execution: nodeExecution
/** Renders a nested list of row items for children of a NodeExecution */
export const NodeExecutionChildren: React.FC<NodeExecutionChildrenProps> = ({
childGroups
}) => {
const taskExecutions = useDetailedTaskExecutions(
useTaskExecutions(nodeExecution.id)
);
useTaskExecutionsRefresher(nodeExecution, taskExecutions);

const columnStyles = useColumnStyles();
// Memoizing columns so they won't be re-generated unless the styles change
const { columns, Skeleton } = React.useMemo(() => {
const columns = generateTaskExecutionColumns(columnStyles);
return { columns, Skeleton: generateRowSkeleton(columns) };
}, [columnStyles]);
const showNames = childGroups.length > 1;
schottra marked this conversation as resolved.
Show resolved Hide resolved
const tableStyles = useExecutionTableStyles();
const monospaceTextStyles = useExpandableMonospaceTextStyles();
return (
<WaitForData
spinnerVariant="medium"
loadingComponent={Skeleton}
{...taskExecutions}
>
{taskExecutions.value.length ? (
taskExecutions.value.map(taskExecution => (
<TaskExecutionRow
columns={columns}
key={taskExecution.cacheKey}
execution={taskExecution}
nodeExecution={nodeExecution}
<>
{childGroups.map(({ name, nodeExecutions }) => {
const rows = nodeExecutions.map(nodeExecution => (
<NodeExecutionRow
key={nodeExecution.cacheKey}
execution={nodeExecution}
/>
))
) : (
<NoExecutionsContent />
)}
</WaitForData>
);
};

interface WorkflowNodeExecution extends DetailedNodeExecution {
closure: NodeExecutionClosure & {
workflowNodeMetadata: WorkflowNodeMetadata;
};
}
interface WorkflowNodeExecutionChildrenProps
extends NodeExecutionChildrenProps {
execution: WorkflowNodeExecution;
}

const executionRefreshConfig: RefreshConfig<Execution> = {
interval: executionRefreshIntervalMs,
valueIsFinal: executionIsTerminal
};

const WorkflowNodeExecutionChildren: React.FC<WorkflowNodeExecutionChildrenProps> = ({
execution
}) => {
const { executionId } = execution.closure.workflowNodeMetadata;
const workflowExecution = useWorkflowExecution(executionId).fetchable;
useDataRefresher(executionId, workflowExecution, executionRefreshConfig);

const columnStyles = useColumnStyles();
// Memoizing columns so they won't be re-generated unless the styles change
const { columns, Skeleton } = React.useMemo(() => {
const columns = generateWorkflowExecutionColumns(columnStyles);
return { columns, Skeleton: generateRowSkeleton(columns) };
}, [columnStyles]);
return (
<WaitForData
spinnerVariant="medium"
loadingComponent={Skeleton}
{...workflowExecution}
>
{workflowExecution.value ? (
<WorkflowExecutionRow
columns={columns}
execution={workflowExecution.value}
/>
) : (
<NoExecutionsContent />
)}
</WaitForData>
));
const key = `group-${name}`;
return showNames ? (
<div key={key}>
<div className={tableStyles.row}>
<Typography variant="overline">{name}</Typography>
</div>
<div
className={classnames(
tableStyles.childrenContainer,
monospaceTextStyles.nestedParent
)}
>
{rows}
</div>
</div>
) : (
<div key={key}>{rows}</div>
);
})}
</>
);
};

/** Renders a nested list of row items for children of a NodeExecution */
export const NodeExecutionChildren: React.FC<NodeExecutionChildrenProps> = props => {
const { workflowNodeMetadata } = props.execution.closure;
const { execution: topExecution } = React.useContext(ExecutionContext);

// Nested NodeExecutions will sometimes have `workflowNodeMetadata` that
// points to the parent WorkflowExecution. We only want to expand workflow
// nodes that point to a different workflow execution
if (
workflowNodeMetadata &&
!isEqual(workflowNodeMetadata.executionId, topExecution.id)
) {
return (
<WorkflowNodeExecutionChildren
{...props}
execution={props.execution as WorkflowNodeExecution}
/>
);
}
return <TaskNodeExecutionChildren {...props} />;
};
Loading