diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 8bc17209a..eb6cae0ef 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -34,7 +34,7 @@ jobs: node-version: 18 cache: yarn - name: Install deps - run: yarn install --frozen-lockfile + run: yarn install --immutable - name: Run tests and generate coverage run: make test_unit_codecov - uses: codecov/codecov-action@v1 diff --git a/packages/console/package.json b/packages/console/package.json index 04b5fe285..5a654aa30 100644 --- a/packages/console/package.json +++ b/packages/console/package.json @@ -1,6 +1,6 @@ { "name": "@flyteorg/console", - "version": "0.0.24", + "version": "0.0.25", "description": "Flyteconsole main app module", "main": "./dist/index.js", "module": "./lib/index.js", diff --git a/packages/console/src/components/Errors/index.ts b/packages/console/src/components/Errors/index.ts new file mode 100644 index 000000000..b8ee89f61 --- /dev/null +++ b/packages/console/src/components/Errors/index.ts @@ -0,0 +1 @@ +export * from './DataError'; diff --git a/packages/console/src/components/Executions/ExecutionDetails/ExecutionDetails.tsx b/packages/console/src/components/Executions/ExecutionDetails/ExecutionDetails.tsx index 678306280..4ab513152 100644 --- a/packages/console/src/components/Executions/ExecutionDetails/ExecutionDetails.tsx +++ b/packages/console/src/components/Executions/ExecutionDetails/ExecutionDetails.tsx @@ -1,10 +1,13 @@ import * as React from 'react'; -import { useContext } from 'react'; +import { useContext, useEffect } from 'react'; import { Collapse, IconButton } from '@material-ui/core'; import { makeStyles, Theme } from '@material-ui/core/styles'; import ExpandMore from '@material-ui/icons/ExpandMore'; import classnames from 'classnames'; -import { LargeLoadingSpinner } from 'components/common/LoadingSpinner'; +import { + LargeLoadingComponent, + LargeLoadingSpinner, +} from 'components/common/LoadingSpinner'; import { WaitForQuery } from 'components/common/WaitForQuery'; import { withRouteParams } from 'components/common/withRouteParams'; import { DataError } from 'components/Errors/DataError'; @@ -15,10 +18,13 @@ import { Workflow } from 'models/Workflow/types'; import { makeWorkflowQuery } from 'components/Workflow/workflowQueries'; import { ExecutionContext } from '../contexts'; import { useWorkflowExecutionQuery } from '../useWorkflowExecution'; -import { ExecutionDetailsAppBarContent } from './ExecutionDetailsAppBarContent'; import { ExecutionMetadata } from './ExecutionMetadata'; import { ExecutionNodeViews } from './ExecutionNodeViews'; -import { NodeExecutionDetailsContextProvider } from '../contextProvider/NodeExecutionDetails'; +import { + NodeExecutionDetailsContextProvider, + WorkflowNodeExecutionsProvider, +} from '../contextProvider/NodeExecutionDetails'; +import { useExecutionNodeViewsStatePoll } from './useExecutionNodeViewsState'; const useStyles = makeStyles((theme: Theme) => ({ expandCollapseButton: { @@ -43,11 +49,35 @@ const useStyles = makeStyles((theme: Theme) => ({ }, })); -const RenderExecutionContainer: React.FC<{}> = () => { +const ExecutionContainer: React.FC<{}> = () => { const styles = useStyles(); const [metadataExpanded, setMetadataExpanded] = React.useState(true); const toggleMetadata = () => setMetadataExpanded(!metadataExpanded); + return ( + <> +
+ + + +
+ + + +
+
+ + + ); +}; + +const RenderExecutionContainer: React.FC> = ({ + children, +}) => { const { execution } = useContext(ExecutionContext); const { @@ -57,6 +87,9 @@ const RenderExecutionContainer: React.FC<{}> = () => { const workflowQuery = useQuery( makeWorkflowQuery(useQueryClient(), workflowId), ); + + // query to get all data to build Graph and Timeline + const { nodeExecutionsQuery } = useExecutionNodeViewsStatePoll(execution); return ( <> {/* Fetches the current workflow to build the execution tree inside NodeExecutionDetailsContextProvider */} @@ -65,23 +98,17 @@ const RenderExecutionContainer: React.FC<{}> = () => { <> {/* Provides a node execution tree for the current workflow */} - -
- - - -
- - - -
-
- - + + {data => ( + + {children} + + )} +
)} @@ -90,17 +117,10 @@ const RenderExecutionContainer: React.FC<{}> = () => { ); }; -export interface ExecutionDetailsRouteParams { - domainId: string; - executionId: string; - projectId: string; -} /** The view component for the Execution Details page */ -export const ExecutionDetailsWrapper: React.FC = ({ - executionId, - domainId, - projectId, -}) => { +export const ExecutionDetailsWrapper: React.FC< + React.PropsWithChildren +> = ({ children, executionId, domainId, projectId }) => { const id = { project: projectId, domain: domainId, @@ -122,13 +142,48 @@ export const ExecutionDetailsWrapper: React.FC = ({ 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(ExecutionDetailsWrapper); +> = withRouteParams( + withExecutionDetails(ExecutionContainer), +); diff --git a/packages/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx b/packages/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx index 91c379b59..85f6b3233 100644 --- a/packages/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx +++ b/packages/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx @@ -1,32 +1,14 @@ -import React, { useContext } from 'react'; -import { WaitForQuery } from 'components/common'; -import { DataError } from 'components/Errors/DataError'; -import { LargeLoadingComponent } from 'components/common/LoadingSpinner'; -import { ExecutionContext } from '../contexts'; -import { useExecutionNodeViewsStatePoll } from './useExecutionNodeViewsState'; +import React from 'react'; import { ExecutionTabView } from './ExecutionTabView'; -import { WorkflowNodeExecutionsProvider } from '../contextProvider/NodeExecutionDetails'; +import { ExecutionDetailsAppBarContent } from './ExecutionDetailsAppBarContent'; /** Contains the available ways to visualize the nodes of a WorkflowExecution */ export const ExecutionNodeViews: React.FC<{}> = () => { - const { execution } = useContext(ExecutionContext); - - // query to get all data to build Graph and Timeline - const { nodeExecutionsQuery } = useExecutionNodeViewsStatePoll(execution); - return ( <> - - {data => ( - - - - )} - + + + ); }; diff --git a/packages/console/src/components/Executions/ExecutionDetails/index.ts b/packages/console/src/components/Executions/ExecutionDetails/index.ts new file mode 100644 index 000000000..5474f205d --- /dev/null +++ b/packages/console/src/components/Executions/ExecutionDetails/index.ts @@ -0,0 +1,3 @@ +export * from './ExecutionDetails'; +export * from './ExecutionDetailsActions'; +export * from './useExecutionNodeViewsState'; diff --git a/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionNodeViews.test.tsx b/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionNodeViews.test.tsx index fbd47a964..9502e0702 100644 --- a/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionNodeViews.test.tsx +++ b/packages/console/src/components/Executions/ExecutionDetails/test/ExecutionNodeViews.test.tsx @@ -5,14 +5,17 @@ import { nodeExecutionStatusFilters } from 'components/Executions/filters/status import { oneFailedTaskWorkflow } from 'mocks/data/fixtures/oneFailedTaskWorkflow'; import { insertFixture } from 'mocks/data/insertFixture'; import { mockServer } from 'mocks/server'; -import { Execution } from 'models/Execution/types'; +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 } from 'components/Executions/contextProvider/NodeExecutionDetails'; +import { + NodeExecutionDetailsContext, + WorkflowNodeExecutionsProvider, +} from 'components/Executions/contextProvider/NodeExecutionDetails'; import { transformerWorkflowToDag } from 'components/WorkflowGraph/transformerWorkflowToDag'; import { ExecutionNodeViews } from '../ExecutionNodeViews'; import { tabs } from '../constants'; @@ -54,27 +57,29 @@ jest.mock('components/WorkflowGraph/transformerWorkflowToDag', () => ({ transformerWorkflowToDag: jest.fn(), })); -// ExecutionNodeViews uses query params for NE list, so we must match them -// for the list to be returned properly -const baseQueryParams = { - filters: '', - 'sort_by.direction': 'ASCENDING', - 'sort_by.key': 'created_at', -}; - 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; - const nodeExecutionsArray = Object.values(nodeExecutions).map( - ({ data }) => data, - ); + 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', @@ -113,16 +118,7 @@ describe('ExecutionNodeViews', () => { nodes: [], edges: [], }, - ...nodeExecutionsArray.map(n => ({ - id: n.id.nodeId, - scopedId: n.scopedId, - execution: n, - // type: dTypes.gateNode, - name: n.id.nodeId, - type: 3, - nodes: [], - edges: [], - })), + ...nodes, ], }, staticExecutionIdsMap: {}, @@ -161,7 +157,11 @@ describe('ExecutionNodeViews', () => { workflowId: mockWorkflowId, }} > - + + + , @@ -199,6 +199,7 @@ describe('ExecutionNodeViews', () => { // 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; diff --git a/packages/console/src/components/Executions/Tables/NodeExecutionsTable.tsx b/packages/console/src/components/Executions/Tables/NodeExecutionsTable.tsx index 7dcea752c..ec32c6e2a 100644 --- a/packages/console/src/components/Executions/Tables/NodeExecutionsTable.tsx +++ b/packages/console/src/components/Executions/Tables/NodeExecutionsTable.tsx @@ -2,7 +2,7 @@ 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, NodeExecutionsById } from 'models/Execution/types'; +import { NodeExecution } from 'models/Execution/types'; import { merge, isEqual, cloneDeep } from 'lodash'; import { extractCompiledNodes } from 'components/hooks/utils'; import { @@ -26,7 +26,11 @@ import { searchNode } from '../utils'; import { nodeExecutionPhaseConstants } from '../constants'; import { NodeExecutionDynamicProvider } from '../contextProvider/NodeExecutionDetails/NodeExecutionDynamicProvider'; import { ExecutionFilters } from '../ExecutionFilters'; -import { ExecutionContext, FilteredNodeExecutions } from '../contexts'; +import { + ExecutionContext, + FilteredNodeExecutions, + NodeExecutionsById, +} from '../contexts'; import { useExecutionNodeViewsStatePoll } from '../ExecutionDetails/useExecutionNodeViewsState'; import { stringifyIsEqual } from '../contextProvider/NodeExecutionDetails/utils'; diff --git a/packages/console/src/components/Executions/TaskExecutionsList/index.ts b/packages/console/src/components/Executions/TaskExecutionsList/index.ts new file mode 100644 index 000000000..59a46cbd9 --- /dev/null +++ b/packages/console/src/components/Executions/TaskExecutionsList/index.ts @@ -0,0 +1,2 @@ +export * from './TaskExecutionDetails'; +export * from './utils'; diff --git a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/NodeExecutionDynamicProvider.tsx b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/NodeExecutionDynamicProvider.tsx index a17861e7f..7456454e3 100644 --- a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/NodeExecutionDynamicProvider.tsx +++ b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/NodeExecutionDynamicProvider.tsx @@ -14,10 +14,9 @@ import { nodeExecutionIsTerminal, } from 'components/Executions/utils'; import { keyBy, values } from 'lodash'; -import { NodeExecution } from 'models'; -import { dNode } from 'models/Graph/types'; 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; @@ -30,7 +29,6 @@ export interface INodeExecutionDynamicContext { React.HTMLAttributes, HTMLDivElement >; - // setSkipChildList: (childList: NodeExecution[]) => void; } export const NodeExecutionDynamicContext = @@ -45,7 +43,7 @@ export const NodeExecutionDynamicContext = }); const checkEnableChildQuery = ( - childExecutions: NodeExecution[], + childExecutions: WorkflowNodeExecution[], nodeExecution: WorkflowNodeExecution, inView: boolean, ) => { @@ -105,9 +103,6 @@ export const NodeExecutionDynamicProvider = ({ const { setCurrentNodeExecutionsById, nodeExecutionsById } = useNodeExecutionsById(); - // get the node execution - const nodeExecution = node?.execution; // useMemo(() => node.execution, [node.execution]); - const childExecutions = useMemo(() => { const children = values(nodeExecutionsById).filter(execution => { return execution.fromUniqueParentId === node?.scopedId; @@ -118,11 +113,11 @@ export const NodeExecutionDynamicProvider = ({ const { nodeExecutionRowQuery } = useNodeExecutionRow( queryClient, - nodeExecution!, + node?.execution!, () => { const shouldRun = checkEnableChildQuery( childExecutions, - nodeExecution!, + node?.execution!, !!overloadedInView, ); diff --git a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/WorkflowNodeExecutionsProvider.tsx b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/WorkflowNodeExecutionsProvider.tsx index 859f5e65a..9afb5362c 100644 --- a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/WorkflowNodeExecutionsProvider.tsx +++ b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/WorkflowNodeExecutionsProvider.tsx @@ -5,9 +5,10 @@ import React, { useEffect, useState, } from 'react'; -import { NodeExecution, NodeExecutionsById } from 'models/Execution/types'; +import { NodeExecution } from 'models/Execution/types'; import { IWorkflowNodeExecutionsContext, + NodeExecutionsById, WorkflowNodeExecutionsContext, } from 'components/Executions/contexts'; import { isEqual, keyBy, merge, mergeWith } from 'lodash'; diff --git a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/index.tsx b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/index.ts similarity index 58% rename from packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/index.tsx rename to packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/index.ts index fc899112f..1211650c2 100644 --- a/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/index.tsx +++ b/packages/console/src/components/Executions/contextProvider/NodeExecutionDetails/index.ts @@ -1,2 +1,4 @@ export * from './NodeExecutionDetailsContextProvider'; export * from './WorkflowNodeExecutionsProvider'; +export * from './NodeExecutionDynamicProvider'; +export * from './utils'; diff --git a/packages/console/src/components/Executions/contextProvider/index.ts b/packages/console/src/components/Executions/contextProvider/index.ts new file mode 100644 index 000000000..05a2a8bf9 --- /dev/null +++ b/packages/console/src/components/Executions/contextProvider/index.ts @@ -0,0 +1 @@ +export * from './NodeExecutionDetails'; diff --git a/packages/console/src/components/Executions/contexts.ts b/packages/console/src/components/Executions/contexts.ts index 25d9a639f..3a1afce63 100644 --- a/packages/console/src/components/Executions/contexts.ts +++ b/packages/console/src/components/Executions/contexts.ts @@ -1,4 +1,11 @@ -import { Execution, LogsByPhase, NodeExecution } from 'models/Execution/types'; +import { Task } from 'models'; +import { + Execution, + ExecutionData, + LogsByPhase, + NodeExecution, + TaskExecution, +} from 'models/Execution/types'; import { dNode } from 'models/Graph/types'; import { createContext } from 'react'; @@ -6,9 +13,15 @@ export interface ExecutionContextData { execution: Execution; } +export type WorkflowTaskExecution = TaskExecution & { + task?: Task; + taskData?: ExecutionData; +}; export interface WorkflowNodeExecution extends NodeExecution { tasksFetched?: boolean; logsByPhase?: LogsByPhase; + taskExecutions?: WorkflowTaskExecution[]; + nodeExecutionData?: ExecutionData; } export const ExecutionContext = createContext( diff --git a/packages/console/src/components/Executions/index.ts b/packages/console/src/components/Executions/index.ts index bd30e9d40..9058bf7a3 100644 --- a/packages/console/src/components/Executions/index.ts +++ b/packages/console/src/components/Executions/index.ts @@ -1,13 +1,12 @@ export * from './types'; -export { type ExecutionDetailsRouteParams } from './ExecutionDetails/ExecutionDetails'; -export { ExecutionStatusBadge } from './ExecutionStatusBadge'; -export { formatRetryAttempt } from './TaskExecutionsList/utils'; -export { TaskExecutionDetails } from './TaskExecutionsList/TaskExecutionDetails'; -export { makeTaskExecutionListQuery } from './taskExecutionQueries'; -export { useWorkflowExecutionQuery } from './useWorkflowExecution'; -export { NodeExecutionDetailsContextProvider } from './contextProvider/NodeExecutionDetails'; -export { getNodeFrontendPhase } from './utils'; -export { useWorkflowExecutionData } from './useWorkflowExecution'; -export { useNodeExecutionContext } from './contextProvider/NodeExecutionDetails'; -export { ExecutionDetailsActions } from './ExecutionDetails/ExecutionDetailsActions'; -export { makeNodeExecutionQuery } from './nodeExecutionQueries'; +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 index 1fd2ce457..f8aac3622 100644 --- a/packages/console/src/components/Executions/nodeExecutionQueries.ts +++ b/packages/console/src/components/Executions/nodeExecutionQueries.ts @@ -7,6 +7,8 @@ import { } from 'models/AdminEntity/types'; import { getNodeExecution, + getNodeExecutionData, + getTaskExecutionData, listNodeExecutions, listTaskExecutionChildren, listTaskExecutions, @@ -24,7 +26,8 @@ import { import { ignoredNodeIds } from 'models/Node/constants'; import { isMapTaskV1 } from 'models/Task/utils'; import { QueryClient } from 'react-query'; -import { WorkflowNodeExecution } from './contexts'; +import { getTask } from 'models'; +import { WorkflowNodeExecution, WorkflowTaskExecution } from './contexts'; import { fetchTaskExecutionList } from './taskExecutionQueries'; import { formatRetryAttempt, getGroupedLogs } from './TaskExecutionsList/utils'; import { NodeExecutionGroup } from './types'; @@ -53,10 +56,67 @@ export function makeNodeExecutionQuery( }; } -export const getTaskExecutions = async ( +/** A query for fetching a single `NodeExecution` by id. */ +export function makeNodeExecutionAndTasksQuery( + id: NodeExecutionIdentifier, + queryClient: QueryClient, +): QueryInput { + return { + queryKey: [QueryType.NodeExecutionAndTasks, id], + queryFn: async () => { + // step 1: Fetch the Node execution + const nodeExecutionPure = await getNodeExecution(id); + + // step 2: Fetch the task executions and attach them to the node execution + const workflowNodeExecution = (await getTaskExecutions( + nodeExecutionPure, + queryClient, + )) 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; + const compiledTaskClosure = await (taskId + ? getTask(taskId!) + : 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; + }), + ), + ); + + return { + ...workflowNodeExecution, + taskExecutions: tasksExecutionData, + nodeExecutionData, + } as WorkflowNodeExecution; + }, + }; +} + +const getTaskExecutions = async ( nodeExecution: WorkflowNodeExecution, - queryClient, -) => { + queryClient: QueryClient, +): Promise => { const isTerminal = nodeExecutionIsTerminal(nodeExecution); const tasksFetched = !!nodeExecution.tasksFetched; const isDynamic = isDynamicNode(nodeExecution); @@ -88,14 +148,12 @@ export const getTaskExecutions = async ( const appendTasksFetched = !isDynamic || (isDynamic && isTerminal); - const { closure: _, metadata: __, ...nodeExecutionLight } = nodeExecution; return { - // to avoid overwiring data from queries that handle status updates - ...nodeExecutionLight, + ...nodeExecution, taskExecutions, ...(useNewMapTaskView && logsByPhase.size > 0 && { logsByPhase }), ...((appendTasksFetched && { tasksFetched: true }) || {}), - }; + } as any as WorkflowNodeExecution; }); }; @@ -103,12 +161,12 @@ export const getTaskExecutions = async ( export function makeNodeExecutionQueryEnhanced( nodeExecution: WorkflowNodeExecution, queryClient: QueryClient, -): QueryInput { +): QueryInput { const { id } = nodeExecution || {}; return { enabled: !!nodeExecution, - queryKey: [QueryType.NodeExecutionAndChildList, id], + queryKey: [QueryType.NodeExecutionEnhanced, id], queryFn: async () => { // complexity: // +1 for parent node tasks @@ -151,7 +209,13 @@ export function makeNodeExecutionQueryEnhanced( getTaskExecutions(nodeExecution, queryClient), parentNodeExecutions(), ]).then(([parent, children]) => { - return [parent, ...children].filter(n => !!n); + // 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[]; diff --git a/packages/console/src/components/Executions/utils.ts b/packages/console/src/components/Executions/utils.ts index 9bcd9cca6..7282377b0 100644 --- a/packages/console/src/components/Executions/utils.ts +++ b/packages/console/src/components/Executions/utils.ts @@ -29,6 +29,7 @@ import { workflowExecutionPhaseConstants, } from './constants'; import { + NodeExecutionsById, SetCurrentNodeExecutionsById, WorkflowNodeExecution, } from './contexts'; @@ -250,7 +251,7 @@ export function searchNode( export async function fetchChildrenExecutions( queryClient: QueryClient, scopedId: string, - nodeExecutionsById: Dictionary, + nodeExecutionsById: NodeExecutionsById, setCurrentNodeExecutionsById: SetCurrentNodeExecutionsById, skipCache = false, ) { diff --git a/packages/console/src/components/Launch/LaunchForm/LaunchFormActions.tsx b/packages/console/src/components/Launch/LaunchForm/LaunchFormActions.tsx index d79b177c9..bab689207 100644 --- a/packages/console/src/components/Launch/LaunchForm/LaunchFormActions.tsx +++ b/packages/console/src/components/Launch/LaunchForm/LaunchFormActions.tsx @@ -1,7 +1,7 @@ -import { Button, DialogActions, FormHelperText } from '@material-ui/core'; -import { ButtonCircularProgress } from 'components/common/ButtonCircularProgress'; import * as React from 'react'; import { useEffect } from 'react'; +import { Button, DialogActions, FormHelperText } from '@material-ui/core'; +import { ButtonCircularProgress } from 'components/common/ButtonCircularProgress'; import { history } from 'routes/history'; import { Routes } from 'routes/routes'; import { useDetailsPanel } from 'components/Executions/ExecutionDetails/DetailsPanelContext'; diff --git a/packages/console/src/components/Launch/LaunchForm/index.ts b/packages/console/src/components/Launch/LaunchForm/index.ts new file mode 100644 index 000000000..04bca77e0 --- /dev/null +++ b/packages/console/src/components/Launch/LaunchForm/index.ts @@ -0,0 +1 @@ +export * from './utils'; diff --git a/packages/console/src/components/Launch/LaunchForm/test/LaunchWorkflowForm.test.tsx b/packages/console/src/components/Launch/LaunchForm/test/LaunchWorkflowForm.test.tsx index 815f1c6de..ef4d2ff98 100644 --- a/packages/console/src/components/Launch/LaunchForm/test/LaunchWorkflowForm.test.tsx +++ b/packages/console/src/components/Launch/LaunchForm/test/LaunchWorkflowForm.test.tsx @@ -31,7 +31,13 @@ import { getWorkflow, listWorkflows } from 'models/Workflow/api'; import { Workflow } from 'models/Workflow/types'; import { createMockWorkflowClosure } from 'models/__mocks__/workflowData'; import * as React from 'react'; -import { delayedPromise, pendingPromise } from 'test/utils'; +import { + createTestQueryClient, + delayedPromise, + pendingPromise, +} from 'test/utils'; +import { WorkflowNodeExecutionsProvider } from 'components/Executions'; +import { QueryClient, QueryClientProvider } from 'react-query'; import t from '../strings'; import { LaunchForm } from '../LaunchForm'; import { LaunchFormProps, WorkflowInitialLaunchParameters } from '../types'; @@ -51,6 +57,14 @@ import { import { createMockObjects } from './utils'; import { workflowNoInputsString } from '../constants'; +jest.mock( + 'components/Executions/ExecutionDetails/Timeline/ExecutionTimelineContainer', + () => ({ + ExecutionTimelineContainer: jest.fn(() => ( +

+ )), + }), +); describe('LaunchForm: Workflow', () => { let onClose: jest.Mock; let mockLaunchPlans: LaunchPlan[]; @@ -59,6 +73,7 @@ describe('LaunchForm: Workflow', () => { let mockWorkflowVersions: Workflow[]; let workflowId: NamedEntityIdentifier; let variables: Record; + let queryClient: QueryClient; let mockListLaunchPlans: jest.Mock>; let mockListWorkflows: jest.Mock>; @@ -68,6 +83,7 @@ describe('LaunchForm: Workflow', () => { >; beforeEach(() => { + queryClient = createTestQueryClient(); onClose = jest.fn(); jest.useFakeTimers(); }); @@ -154,18 +170,26 @@ describe('LaunchForm: Workflow', () => { const renderForm = (props?: Partial) => { return render( - - - - - , + + + + + + + + + , ); }; diff --git a/packages/console/src/components/Launch/index.ts b/packages/console/src/components/Launch/index.ts new file mode 100644 index 000000000..4169a1fc9 --- /dev/null +++ b/packages/console/src/components/Launch/index.ts @@ -0,0 +1 @@ +export * from './LaunchForm'; diff --git a/packages/console/src/components/Task/index.ts b/packages/console/src/components/Task/index.ts new file mode 100644 index 000000000..81168bf65 --- /dev/null +++ b/packages/console/src/components/Task/index.ts @@ -0,0 +1 @@ +export * from './taskQueries'; diff --git a/packages/console/src/components/Workflow/index.ts b/packages/console/src/components/Workflow/index.ts new file mode 100644 index 000000000..de0436a3a --- /dev/null +++ b/packages/console/src/components/Workflow/index.ts @@ -0,0 +1 @@ +export * from './workflowQueries'; diff --git a/packages/console/src/components/WorkflowGraph/transformerWorkflowToDag.tsx b/packages/console/src/components/WorkflowGraph/transformerWorkflowToDag.tsx index d5a202ae7..18ee9bc0d 100644 --- a/packages/console/src/components/WorkflowGraph/transformerWorkflowToDag.tsx +++ b/packages/console/src/components/WorkflowGraph/transformerWorkflowToDag.tsx @@ -13,7 +13,7 @@ import { } from 'models/Workflow/types'; import { isParentNode } from 'components/Executions/utils'; import { isStartOrEndNode } from 'models/Node/utils'; -import { NodeExecutionsById } from 'models'; +import { NodeExecutionsById } from 'components/Executions/contexts'; import { getDisplayName, getSubWorkflowFromId, diff --git a/packages/console/src/components/common/index.ts b/packages/console/src/components/common/index.ts index b4f6a88f1..d1bc31151 100644 --- a/packages/console/src/components/common/index.ts +++ b/packages/console/src/components/common/index.ts @@ -1,5 +1,6 @@ export { useCommonStyles } from './styles'; export { withRouteParams } from './withRouteParams'; +export * from './LoadingSpinner'; export { WaitForData } from './WaitForData'; export { WaitForQuery } from './WaitForQuery'; export { DetailsGroup } from './DetailsGroup'; diff --git a/packages/console/src/components/data/index.ts b/packages/console/src/components/data/index.ts new file mode 100644 index 000000000..a36473679 --- /dev/null +++ b/packages/console/src/components/data/index.ts @@ -0,0 +1 @@ +export * from './apiContext'; diff --git a/packages/console/src/components/data/types.ts b/packages/console/src/components/data/types.ts index 1cb3d3aec..d751dca1a 100644 --- a/packages/console/src/components/data/types.ts +++ b/packages/console/src/components/data/types.ts @@ -12,6 +12,8 @@ export enum QueryType { NodeExecutionChildList = 'nodeExecutionChildList', NodeExecutionTreeList = 'nodeExecutionTreeList', TaskExecution = 'taskExecution', + NodeExecutionEnhanced = 'NodeExecutionEnhanced', + NodeExecutionAndTasks = 'NodeExecutionAndTasks', TaskExecutionList = 'taskExecutionList', TaskExecutionChildList = 'taskExecutionChildList', TaskTemplate = 'taskTemplate', diff --git a/packages/console/src/components/flytegraph/ReactFlow/BreadCrumb.tsx b/packages/console/src/components/flytegraph/ReactFlow/BreadCrumb.tsx index 3b6a64e31..ab4cd7971 100644 --- a/packages/console/src/components/flytegraph/ReactFlow/BreadCrumb.tsx +++ b/packages/console/src/components/flytegraph/ReactFlow/BreadCrumb.tsx @@ -92,7 +92,7 @@ export const BorderContainer = ({ borders = contextNode ? ( { const { selectedPhase, - isDetailsTabClosed, onNodeSelectionChanged, setSelectedPhase: onPhaseSelectionChanged, } = useDetailsPanel(); @@ -54,15 +53,7 @@ export const ReactFlowGraphComponent = () => { currentNestedView, } as ConvertDagProps); setrfGraphJson(newrfGraphJson); - }, [ - initialDNodes, - mergedDag, - isDetailsTabClosed, - onNodeSelectionChanged, - onPhaseSelectionChanged, - selectedPhase, - currentNestedView, - ]); + }, [mergedDag, selectedPhase, currentNestedView]); const backgroundStyle = getRFBackground().nested; diff --git a/packages/console/src/components/hooks/index.ts b/packages/console/src/components/hooks/index.ts index 3f9cb2773..53c4e548b 100644 --- a/packages/console/src/components/hooks/index.ts +++ b/packages/console/src/components/hooks/index.ts @@ -1,2 +1,3 @@ export { useNodeExecution } from './useNodeExecution'; export { useConditionalQuery } from './useConditionalQuery'; +export * from './utils'; diff --git a/packages/console/src/components/index.ts b/packages/console/src/components/index.ts index 9491639c3..5c02188d0 100644 --- a/packages/console/src/components/index.ts +++ b/packages/console/src/components/index.ts @@ -1,7 +1,13 @@ -export * from './App/App'; +export * from './data'; +export * from './hooks'; +export * from './common'; +export * from './Errors'; +export * from './Launch'; + export * from './Navigation'; export * from './Executions'; +export * from './Task'; +export * from './Workflow'; -export * from './hooks'; -export * from './common'; +export * from './App/App'; diff --git a/packages/console/src/mocks/data/generators.ts b/packages/console/src/mocks/data/generators.ts index 98606fe2b..15a594f28 100644 --- a/packages/console/src/mocks/data/generators.ts +++ b/packages/console/src/mocks/data/generators.ts @@ -165,9 +165,11 @@ export function generateNodeExecution( nodeId: string, overrides?: DeepPartial, ): NodeExecution { + const id = nodeExecutionId(parentExecution.id, nodeId); const base: NodeExecution = { - id: nodeExecutionId(parentExecution.id, nodeId), + id: id, metadata: { specNodeId: nodeId }, + scopedId: id.nodeId, closure: { createdAt: timeStampOffset(parentExecution.closure.createdAt, 0), startedAt: timeStampOffset(parentExecution.closure.createdAt, 0), diff --git a/packages/console/src/models/Execution/types.ts b/packages/console/src/models/Execution/types.ts index 5b9a80d8d..28b650a01 100644 --- a/packages/console/src/models/Execution/types.ts +++ b/packages/console/src/models/Execution/types.ts @@ -102,10 +102,6 @@ export interface NodeExecution extends Admin.INodeExecution { fromUniqueParentId?: string; } -export interface NodeExecutionsById { - [k: string]: NodeExecution; -} - export interface NodeExecutionClosure extends Admin.INodeExecutionClosure { createdAt: Protobuf.ITimestamp; duration?: Protobuf.Duration; diff --git a/packages/console/src/models/Task/index.ts b/packages/console/src/models/Task/index.ts index c94f80f84..0b9aab677 100644 --- a/packages/console/src/models/Task/index.ts +++ b/packages/console/src/models/Task/index.ts @@ -1 +1,3 @@ export * from './constants'; +export * from './types'; +export * from './api'; diff --git a/packages/console/src/models/Workflow/index.ts b/packages/console/src/models/Workflow/index.ts new file mode 100644 index 000000000..fcb073fef --- /dev/null +++ b/packages/console/src/models/Workflow/index.ts @@ -0,0 +1 @@ +export * from './types'; diff --git a/packages/console/src/models/index.ts b/packages/console/src/models/index.ts index 7677e8ead..a342742c9 100644 --- a/packages/console/src/models/index.ts +++ b/packages/console/src/models/index.ts @@ -1,3 +1,4 @@ +export * from './Workflow'; export * from './AdminEntity'; export * from './Execution/enums'; export * from './Execution/types'; diff --git a/website/package.json b/website/package.json index 8e8e80780..ca95bd44f 100644 --- a/website/package.json +++ b/website/package.json @@ -37,7 +37,7 @@ }, "dependencies": { "@flyteorg/common": "^0.0.4", - "@flyteorg/console": "^0.0.24", + "@flyteorg/console": "^0.0.25", "long": "^4.0.0", "protobufjs": "~6.11.3", "react-ga4": "^1.4.1", diff --git a/yarn.lock b/yarn.lock index 0636ddf34..955311d2f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1973,20 +1973,45 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^1.4.1": - version: 1.4.1 - resolution: "@eslint/eslintrc@npm:1.4.1" +"@eslint-community/eslint-utils@npm:^4.2.0": + version: 4.4.0 + resolution: "@eslint-community/eslint-utils@npm:4.4.0" + dependencies: + eslint-visitor-keys: ^3.3.0 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: cdfe3ae42b4f572cbfb46d20edafe6f36fc5fb52bf2d90875c58aefe226892b9677fef60820e2832caf864a326fe4fc225714c46e8389ccca04d5f9288aabd22 + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.4.0": + version: 4.5.0 + resolution: "@eslint-community/regexpp@npm:4.5.0" + checksum: 99c01335947dbd7f2129e954413067e217ccaa4e219fe0917b7d2bd96135789384b8fedbfb8eb09584d5130b27a7b876a7150ab7376f51b3a0c377d5ce026a10 + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^2.0.2": + version: 2.0.2 + resolution: "@eslint/eslintrc@npm:2.0.2" dependencies: ajv: ^6.12.4 debug: ^4.3.2 - espree: ^9.4.0 + espree: ^9.5.1 globals: ^13.19.0 ignore: ^5.2.0 import-fresh: ^3.2.1 js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: cd3e5a8683db604739938b1c1c8b77927dc04fce3e28e0c88e7f2cd4900b89466baf83dfbad76b2b9e4d2746abdd00dd3f9da544d3e311633d8693f327d04cd7 + checksum: cfcf5e12c7b2c4476482e7f12434e76eae16fcd163ee627309adb10b761e5caa4a4e52ed7be464423320ff3d11eca5b50de5bf8be3e25834222470835dd5c801 + languageName: node + linkType: hard + +"@eslint/js@npm:8.39.0": + version: 8.39.0 + resolution: "@eslint/js@npm:8.39.0" + checksum: 63fe36e2bfb5ff5705d1c1a8ccecd8eb2f81d9af239713489e767b0e398759c0177fcc75ad62581d02942f2776903a8496d5fae48dc2d883dff1b96fcb19e9e2 languageName: node linkType: hard @@ -1995,7 +2020,7 @@ __metadata: resolution: "@flyteconsole/client-app@workspace:website" dependencies: "@flyteorg/common": ^0.0.4 - "@flyteorg/console": ^0.0.24 + "@flyteorg/console": ^0.0.25 "@types/long": ^3.0.32 long: ^4.0.0 protobufjs: ~6.11.3 @@ -2034,7 +2059,7 @@ __metadata: languageName: unknown linkType: soft -"@flyteorg/console@^0.0.24, @flyteorg/console@workspace:packages/console": +"@flyteorg/console@^0.0.25, @flyteorg/console@workspace:packages/console": version: 0.0.0-use.local resolution: "@flyteorg/console@workspace:packages/console" dependencies: @@ -10819,13 +10844,13 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^7.1.1": - version: 7.1.1 - resolution: "eslint-scope@npm:7.1.1" +"eslint-scope@npm:^7.2.0": + version: 7.2.0 + resolution: "eslint-scope@npm:7.2.0" dependencies: esrecurse: ^4.3.0 estraverse: ^5.2.0 - checksum: 9f6e974ab2db641ca8ab13508c405b7b859e72afe9f254e8131ff154d2f40c99ad4545ce326fd9fde3212ff29707102562a4834f1c48617b35d98c71a97fbf3e + checksum: 64591a2d8b244ade9c690b59ef238a11d5c721a98bcee9e9f445454f442d03d3e04eda88e95a4daec558220a99fa384309d9faae3d459bd40e7a81b4063980ae languageName: node linkType: hard @@ -10854,11 +10879,21 @@ __metadata: languageName: node linkType: hard -"eslint@npm:^8.31.0": - version: 8.31.0 - resolution: "eslint@npm:8.31.0" +"eslint-visitor-keys@npm:^3.4.0": + version: 3.4.0 + resolution: "eslint-visitor-keys@npm:3.4.0" + checksum: 33159169462d3989321a1ec1e9aaaf6a24cc403d5d347e9886d1b5bfe18ffa1be73bdc6203143a28a606b142b1af49787f33cff0d6d0813eb5f2e8d2e1a6043c + languageName: node + linkType: hard + +"eslint@npm:^8.33.0": + version: 8.39.0 + resolution: "eslint@npm:8.39.0" dependencies: - "@eslint/eslintrc": ^1.4.1 + "@eslint-community/eslint-utils": ^4.2.0 + "@eslint-community/regexpp": ^4.4.0 + "@eslint/eslintrc": ^2.0.2 + "@eslint/js": 8.39.0 "@humanwhocodes/config-array": ^0.11.8 "@humanwhocodes/module-importer": ^1.0.1 "@nodelib/fs.walk": ^1.2.8 @@ -10868,11 +10903,10 @@ __metadata: debug: ^4.3.2 doctrine: ^3.0.0 escape-string-regexp: ^4.0.0 - eslint-scope: ^7.1.1 - eslint-utils: ^3.0.0 - eslint-visitor-keys: ^3.3.0 - espree: ^9.4.0 - esquery: ^1.4.0 + eslint-scope: ^7.2.0 + eslint-visitor-keys: ^3.4.0 + espree: ^9.5.1 + esquery: ^1.4.2 esutils: ^2.0.2 fast-deep-equal: ^3.1.3 file-entry-cache: ^6.0.1 @@ -10893,24 +10927,23 @@ __metadata: minimatch: ^3.1.2 natural-compare: ^1.4.0 optionator: ^0.9.1 - regexpp: ^3.2.0 strip-ansi: ^6.0.1 strip-json-comments: ^3.1.0 text-table: ^0.2.0 bin: eslint: bin/eslint.js - checksum: 5e5688bb864edc6b12d165849994812eefa67fb3fc44bb26f53659b63edcd8bcc68389d27cc6cc9e5b79ee22f24b6f311fa3ed047bddcafdec7d84c1b5561e4f + checksum: d7a074ff326e7ea482500dc0427a7d4b0260460f0f812d19b46b1cca681806b67309f23da9d17cd3de8eb74dd3c14cb549c4d58b05b140564d14cc1a391122a0 languageName: node linkType: hard -"espree@npm:^9.4.0": - version: 9.4.1 - resolution: "espree@npm:9.4.1" +"espree@npm:^9.5.1": + version: 9.5.1 + resolution: "espree@npm:9.5.1" dependencies: acorn: ^8.8.0 acorn-jsx: ^5.3.2 - eslint-visitor-keys: ^3.3.0 - checksum: 4d266b0cf81c7dfe69e542c7df0f246e78d29f5b04dda36e514eb4c7af117ee6cfbd3280e560571ed82ff6c9c3f0003c05b82583fc7a94006db7497c4fe4270e + eslint-visitor-keys: ^3.4.0 + checksum: cdf6e43540433d917c4f2ee087c6e987b2063baa85a1d9cdaf51533d78275ebd5910c42154e7baf8e3e89804b386da0a2f7fad2264d8f04420e7506bf87b3b88 languageName: node linkType: hard @@ -10924,12 +10957,12 @@ __metadata: languageName: node linkType: hard -"esquery@npm:^1.4.0": - version: 1.4.0 - resolution: "esquery@npm:1.4.0" +"esquery@npm:^1.4.2": + version: 1.5.0 + resolution: "esquery@npm:1.5.0" dependencies: estraverse: ^5.1.0 - checksum: a0807e17abd7fbe5fbd4fab673038d6d8a50675cdae6b04fbaa520c34581be0c5fa24582990e8acd8854f671dd291c78bb2efb9e0ed5b62f33bac4f9cf820210 + checksum: aefb0d2596c230118656cd4ec7532d447333a410a48834d80ea648b1e7b5c9bc9ed8b5e33a89cb04e487b60d622f44cf5713bf4abed7c97343edefdc84a35900 languageName: node linkType: hard @@ -11639,7 +11672,7 @@ __metadata: cookie-parser: ^1.4.3 copy-webpack-plugin: ^11.0.0 dotenv: ^5.0.1 - eslint: ^8.31.0 + eslint: ^8.33.0 eslint-config-airbnb: ^19.0.4 eslint-config-prettier: ^8.6.0 eslint-plugin-import: ^2.27.4