Skip to content

Commit

Permalink
fix: execution view children fetch on demand refactor (#676)
Browse files Browse the repository at this point in the history
* fix: execution view children fetch on demand refactor

Signed-off-by: Olga Nad <[email protected]>
Signed-off-by: Jason Porter <[email protected]>

* fix: incorrect import

Signed-off-by: Olga Nad <[email protected]>
Signed-off-by: Jason Porter <[email protected]>

* chore: fix expander bug (#677)

* chore: fix expander bug

Signed-off-by: Carina Ursu <[email protected]>

* chore: add await everywhere

Signed-off-by: Carina Ursu <[email protected]>

---------

Signed-off-by: Carina Ursu <[email protected]>
Signed-off-by: Jason Porter <[email protected]>

* fix: toggle in timeline

Signed-off-by: Olga Nad <[email protected]>
Signed-off-by: Jason Porter <[email protected]>

* fix: checkForDynamicExecutions

Signed-off-by: Jason Porter <[email protected]>

* Revert "fix: checkForDynamicExecutions"

This reverts commit 450d144.

Signed-off-by: Jason Porter <[email protected]>

* Perf Refactor: Some parent nodes don't load children (#680)

fix: checkForDynamicExecutions
Signed-off-by: Jason Porter <[email protected]>

* chore: simplify graph code

Signed-off-by: Carina Ursu <[email protected]>
Signed-off-by: Jason Porter <[email protected]>

* adding util

Signed-off-by: Jason Porter <[email protected]>

* Changing type

Signed-off-by: Jason Porter <[email protected]>

* Updated click handlers

Signed-off-by: Jason Porter <[email protected]>

* Fixed graph dynamic node issue

Signed-off-by: Jason Porter <[email protected]>

* Key was not a valid prop in this case, removed

Signed-off-by: Jason Porter <[email protected]>

* chore: fix tests

Signed-off-by: Carina Ursu <[email protected]>
Signed-off-by: Jason Porter <[email protected]>

---------

Signed-off-by: Olga Nad <[email protected]>
Signed-off-by: Jason Porter <[email protected]>
Signed-off-by: Carina Ursu <[email protected]>
Co-authored-by: Carina Ursu <[email protected]>
Co-authored-by: James <[email protected]>
Co-authored-by: james-union <[email protected]>
Co-authored-by: Jason Porter <[email protected]>
  • Loading branch information
5 people authored Feb 14, 2023
1 parent bed9c1b commit 27d4344
Show file tree
Hide file tree
Showing 33 changed files with 756 additions and 553 deletions.
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
import * as React from 'react';
import React, { useEffect } from 'react';
import { Tab, Tabs } from '@material-ui/core';
import { makeStyles, Theme } from '@material-ui/core/styles';
import { WaitForQuery } from 'components/common/WaitForQuery';
import { DataError } from 'components/Errors/DataError';
import { useTabState } from 'components/hooks/useTabState';
import { secondaryBackgroundColor } from 'components/Theme/constants';
import {
Execution,
ExternalResource,
LogsByPhase,
NodeExecution,
} from 'models/Execution/types';
import { useEffect, useMemo, useState } from 'react';
import { Execution } from 'models/Execution/types';
import { keyBy } from 'lodash';
import { isMapTaskV1 } from 'models/Task/utils';
import { useQueryClient } from 'react-query';
import { LargeLoadingSpinner } from 'components/common/LoadingSpinner';
import { FilterOperation } from 'models/AdminEntity/types';
import { NodeExecutionDetailsContextProvider } from '../contextProvider/NodeExecutionDetails';
Expand All @@ -23,10 +15,8 @@ import { ExecutionFilters } from '../ExecutionFilters';
import { useNodeExecutionFiltersState } from '../filters/useExecutionFiltersState';
import { tabs } from './constants';
import { useExecutionNodeViewsState } from './useExecutionNodeViewsState';
import { fetchTaskExecutionList } from '../taskExecutionQueries';
import { getGroupedLogs } from '../TaskExecutionsList/utils';
import { useAllTreeNodeExecutionGroupsQuery } from '../nodeExecutionQueries';
import { ExecutionTab } from './ExecutionTab';
import { useNodeExecutionsById } from '../useNodeExecutionsById';

const useStyles = makeStyles((theme: Theme) => ({
filters: {
Expand Down Expand Up @@ -55,10 +45,6 @@ const isPhaseFilter = (appliedFilters: FilterOperation[]) => {
return false;
};

interface WorkflowNodeExecution extends NodeExecution {
logsByPhase?: LogsByPhase;
}

interface ExecutionNodeViewsProps {
execution: Execution;
}
Expand All @@ -71,96 +57,28 @@ export const ExecutionNodeViews: React.FC<ExecutionNodeViewsProps> = ({
const styles = useStyles();
const filterState = useNodeExecutionFiltersState();
const tabState = useTabState(tabs, defaultTab);
const queryClient = useQueryClient();
const [nodeExecutionsLoading, setNodeExecutionsLoading] =
useState<boolean>(true);

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

const [nodeExecutions, setNodeExecutions] = useState<NodeExecution[]>([]);
const [nodeExecutionsWithResources, setNodeExecutionsWithResources] =
useState<WorkflowNodeExecution[]>([]);

const nodeExecutionsById = useMemo(() => {
return keyBy(nodeExecutionsWithResources, 'scopedId');
}, [nodeExecutionsWithResources]);

// query to get all data to build Graph and Timeline
const { nodeExecutionsQuery } = useExecutionNodeViewsState(execution);
// query to get filtered data to narrow down Table outputs
const {
nodeExecutionsQuery: { data: filteredNodeExecutions },
} = useExecutionNodeViewsState(execution, filterState.appliedFilters);

useEffect(() => {
let isCurrent = true;

async function fetchData(baseNodeExecutions, queryClient) {
setNodeExecutionsLoading(true);
const newValue = await Promise.all(
baseNodeExecutions.map(async baseNodeExecution => {
const taskExecutions = await fetchTaskExecutionList(
queryClient,
baseNodeExecution.id,
);

const useNewMapTaskView = taskExecutions.every(taskExecution => {
const {
closure: { taskType, metadata, eventVersion = 0 },
} = taskExecution;
return isMapTaskV1(
eventVersion,
metadata?.externalResources?.length ?? 0,
taskType ?? undefined,
);
});
const externalResources: ExternalResource[] = taskExecutions
.map(
taskExecution =>
taskExecution.closure.metadata?.externalResources,
)
.flat()
.filter((resource): resource is ExternalResource => !!resource);

const logsByPhase: LogsByPhase = getGroupedLogs(externalResources);

return {
...baseNodeExecution,
...(useNewMapTaskView && logsByPhase.size > 0 && { logsByPhase }),
};
}),
);

if (isCurrent) {
setNodeExecutionsWithResources(newValue);
setNodeExecutionsLoading(false);
}
}

if (nodeExecutions.length > 0) {
fetchData(nodeExecutions, queryClient);
} else {
if (isCurrent) {
setNodeExecutionsLoading(false);
}
}
return () => {
isCurrent = false;
};
}, [nodeExecutions]);

const childGroupsQuery = useAllTreeNodeExecutionGroupsQuery(
nodeExecutionsQuery.data ?? [],
{},
);
const { nodeExecutionsById, setCurrentNodeExecutionsById } =
useNodeExecutionsById();

useEffect(() => {
if (!childGroupsQuery.isLoading && childGroupsQuery.data) {
setNodeExecutions(childGroupsQuery.data);
}
}, [childGroupsQuery.data]);
const currentNodeExecutionsById = keyBy(
nodeExecutionsQuery.data,
'scopedId',
);
setCurrentNodeExecutionsById(currentNodeExecutionsById);
}, [nodeExecutionsQuery.data]);

const LoadingComponent = () => {
return (
Expand All @@ -171,27 +89,16 @@ export const ExecutionNodeViews: React.FC<ExecutionNodeViewsProps> = ({
};

const renderTab = tabType => {
if (nodeExecutionsLoading) {
return <LoadingComponent />;
}
return (
<WaitForQuery
errorComponent={DataError}
query={childGroupsQuery}
loadingComponent={LoadingComponent}
>
{() => (
<ExecutionTab
tabType={tabType}
// if only phase filter was applied, ignore request response, and filter out nodes via frontend filter
filteredNodeExecutions={
isPhaseFilter(filterState.appliedFilters)
? undefined
: filteredNodeExecutions
}
/>
)}
</WaitForQuery>
<ExecutionTab
tabType={tabType}
// if only phase filter was applied, ignore request response, and filter out nodes via frontend filter
filteredNodeExecutions={
isPhaseFilter(filterState.appliedFilters)
? undefined
: filteredNodeExecutions
}
/>
);
};

Expand All @@ -203,7 +110,9 @@ export const ExecutionNodeViews: React.FC<ExecutionNodeViewsProps> = ({
<Tab value={tabs.timeline.id} label={tabs.timeline.label} />
</Tabs>
<NodeExecutionDetailsContextProvider workflowId={workflowId}>
<NodeExecutionsByIdContext.Provider value={nodeExecutionsById}>
<NodeExecutionsByIdContext.Provider
value={{ nodeExecutionsById, setCurrentNodeExecutionsById }}
>
<div className={styles.nodesContainer}>
{tabState.value === tabs.nodes.id && (
<div className={styles.filters}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import { WorkflowGraph } from 'components/WorkflowGraph/WorkflowGraph';
import { TaskExecutionPhase } from 'models/Execution/enums';
import { NodeExecution, NodeExecutionIdentifier } from 'models/Execution/types';
import { startNodeId, endNodeId } from 'models/Node/constants';
import * as React from 'react';
import React, { useContext, useEffect, useMemo, useState } from 'react';
import { transformerWorkflowToDag } from 'components/WorkflowGraph/transformerWorkflowToDag';
import { checkForDynamicExecutions } from 'components/common/utils';
import { dNode } from 'models/Graph/types';
import { useContext, useEffect, useMemo, useState } from 'react';
import { useQuery } from 'react-query';
import {
FilterOperation,
Expand Down Expand Up @@ -70,31 +69,50 @@ export const ExecutionTabContent: React.FC<ExecutionTabContentProps> = ({
const styles = useStyles();
const { compiledWorkflowClosure } = useNodeExecutionContext();
const { appliedFilters } = useNodeExecutionFiltersState();
const nodeExecutionsById = useContext(NodeExecutionsByIdContext);

const { dag, staticExecutionIdsMap, error } = compiledWorkflowClosure
const { nodeExecutionsById } = useContext(NodeExecutionsByIdContext);
const { staticExecutionIdsMap } = compiledWorkflowClosure
? transformerWorkflowToDag(compiledWorkflowClosure)
: { dag: {}, staticExecutionIdsMap: {}, error: null };
const dynamicParents = checkForDynamicExecutions(
nodeExecutionsById,
staticExecutionIdsMap,
: { staticExecutionIdsMap: {} };
const [dynamicParents, setDynamicParents] = useState(
checkForDynamicExecutions(nodeExecutionsById, staticExecutionIdsMap),
);
const { data: dynamicWorkflows } = useQuery(
const { data: dynamicWorkflows, refetch } = useQuery(
makeNodeExecutionDynamicWorkflowQuery(dynamicParents),
);

const [initialNodes, setInitialNodes] = useState<dNode[]>([]);
const [initialFilteredNodes, setInitialFilteredNodes] = useState<
dNode[] | undefined
>(undefined);
const [dagError, setDagError] = useState(null);
const [mergedDag, setMergedDag] = useState(null);
const [filters, setFilters] = useState<FilterOperation[]>(appliedFilters);
const [isFiltersChanged, setIsFiltersChanged] = useState<boolean>(false);
const [shouldUpdate, setShouldUpdate] = useState<boolean>(false);

useEffect(() => {
if (shouldUpdate) {
const newDynamicParents = checkForDynamicExecutions(
nodeExecutionsById,
staticExecutionIdsMap,
);
setDynamicParents(newDynamicParents);
refetch();
setShouldUpdate(false);
}
}, [shouldUpdate]);

useEffect(() => {
const nodes: dNode[] = compiledWorkflowClosure
? transformerWorkflowToDag(compiledWorkflowClosure, dynamicWorkflows).dag
.nodes
: [];
const { dag, staticExecutionIdsMap, error } = compiledWorkflowClosure
? transformerWorkflowToDag(
compiledWorkflowClosure,
dynamicWorkflows,
nodeExecutionsById,
)
: { dag: {}, staticExecutionIdsMap: {}, error: null };

const nodes = dag.nodes ?? [];

// we remove start/end node info in the root dNode list during first assignment
const plainNodes = convertToPlainNodes(nodes);

Expand All @@ -106,14 +124,27 @@ export const ExecutionTabContent: React.FC<ExecutionTabContentProps> = ({
const dynamicWorkflow = transformerWorkflowToDag(
compiledWorkflowClosure,
dynamicWorkflows,
nodeExecutionsById,
);
newMergedDag = dynamicWorkflow.dag;
}
}
}
setDagError(error);
setMergedDag(newMergedDag);
plainNodes.map(node => {
const initialNode = initialNodes.find(n => n.scopedId === node.scopedId);
if (initialNode) {
node.expanded = initialNode.expanded;
}
});
setInitialNodes(plainNodes);
}, [compiledWorkflowClosure, dynamicWorkflows]);
}, [
compiledWorkflowClosure,
dynamicWorkflows,
dynamicParents,
nodeExecutionsById,
]);

useEffect(() => {
if (!isEqual(filters, appliedFilters)) {
Expand Down Expand Up @@ -219,19 +250,22 @@ export const ExecutionTabContent: React.FC<ExecutionTabContentProps> = ({
<NodeExecutionsTable
initialNodes={initialNodes}
filteredNodes={initialFilteredNodes}
setShouldUpdate={setShouldUpdate}
/>
);
case tabs.graph.id:
return (
<WorkflowGraph
mergedDag={mergedDag}
error={error}
error={dagError}
dynamicWorkflows={dynamicWorkflows}
initialNodes={initialNodes}
onNodeSelectionChanged={onNodeSelectionChanged}
selectedPhase={selectedPhase}
onPhaseSelectionChanged={setSelectedPhase}
isDetailsTabClosed={isDetailsTabClosed}
shouldUpdate={shouldUpdate}
setShouldUpdate={setShouldUpdate}
/>
);
case tabs.timeline.id:
Expand All @@ -241,6 +275,7 @@ export const ExecutionTabContent: React.FC<ExecutionTabContentProps> = ({
<ExecutionTimeline
chartTimezone={chartTimezone}
initialNodes={initialNodes}
setShouldUpdate={setShouldUpdate}
/>
</div>
<ExecutionTimelineFooter onTimezoneChange={handleTimezoneChange} />
Expand Down
Loading

0 comments on commit 27d4344

Please sign in to comment.