From cb591c9c05ab518e653960e062e597eab4c3cc7c Mon Sep 17 00:00:00 2001 From: olga-union <101579322+olga-union@users.noreply.github.com> Date: Wed, 12 Oct 2022 11:43:48 -0500 Subject: [PATCH] NodeExecutionRow refactor (#610) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: move src to a new location Signed-off-by: Nastya Rusina * ci: allow branch to be built again Signed-off-by: Nastya Rusina * chore: replace link to github repository to a proper one Signed-off-by: Nastya Rusina * ci: basic test and test-coverage setup Signed-off-by: Nastya Rusina * chore: add first plugin package Signed-off-by: Nastya Rusina * chore: allow to start test from main directory Signed-off-by: Nastya Rusina * ci: update lowest mkdirp version (#423) Signed-off-by: Nastya Rusina * chore: add build scripts for console app (#429) Signed-off-by: Carina Ursu Co-authored-by: Carina Ursu * fix: hide the legend (#435) Signed-off-by: eugenejahn * ci: move storybook related packages to top level (#434) * ci: move storybook related packages to top level * ci: ensure that lint can be run in PR checks * ci: allow to run tests linux setup Signed-off-by: Nastya Rusina * fix: fix issue 386 (#437) Signed-off-by: eugenejahn * ci: ensure unified tsconfig and remove mocks, test, stories from build (#436) * ci: ensure unified tsconfig and remove mocks,test,stories from build * ci: move certificates into certificate folder inside script Signed-off-by: Nastya Rusina * Make whole row clickable to open TaskExecutionDetails panel (#444) * fix: issue 398 * fix: prevent parent onclick event trigger Signed-off-by: eugenejahn * ci: allow to start jest config for all project at once (#447) * minor: unable to view all the workflow versions (#446) * fix: unable to view all the workflow versions Signed-off-by: Carina Ursu * chore: cleanup Signed-off-by: Carina Ursu Co-authored-by: Carina Ursu * chore(tsc): exclude test/stories/mock files only from build (#451) * chore(tsc): exclude test/stories/mock files only from build Signed-off-by: Nastya Rusina * ci: ensure that webpack too doesn't include test/mock/specs files (#452) Signed-off-by: Nastya Rusina * minor: add support for StructuredDataSet Input/Output type (#445) * chore: support for StructuredDataSet Input/Output type Signed-off-by: Carina Ursu * chore: add tests Signed-off-by: Carina Ursu * chore: add package Signed-off-by: Carina Ursu * chore: resolving local package issue Signed-off-by: Carina Ursu * chore: yarn lock Signed-off-by: Carina Ursu * chore: stories Signed-off-by: Carina Ursu * chore: fix associative arrays in copy functionality Signed-off-by: Carina Ursu * chore: fix scalar stories Signed-off-by: Carina Ursu * chore: oops Signed-off-by: Carina Ursu * chore: cleanup Signed-off-by: Carina Ursu * chore: revert old viewer to original Signed-off-by: Carina Ursu * chore: fix Signed-off-by: Carina Ursu * chore: remove non-null assertion Signed-off-by: Carina Ursu * chore: fix types Signed-off-by: Carina Ursu * chore: cleanup Signed-off-by: Carina Ursu * chore: fix tests Signed-off-by: Carina Ursu * chore: fix test types Signed-off-by: Carina Ursu * chore: cleanup Signed-off-by: Carina Ursu * chore: cleanup Signed-off-by: Carina Ursu * chore: cleanup Signed-off-by: Carina Ursu * chore: cleanup Signed-off-by: Carina Ursu Co-authored-by: Carina Ursu * chore: Provide basic repo structure and ensure that stories, tests, vscode setup properly (#458) * chore: separate AppInfo * chore: vscode properly understands @flyteconsole * chore: add basics/locale entry * test: add storybooks and tests * test: fix istanbul test-coverage issue by binding babel versions Signed-off-by: Nastya Rusina * ci: minimize babel presence (#457) * ci: minimize babel presence Signed-off-by: Nastya Rusina * fix: update node executions to display map tasks (#455) * fix: update node executions to display map tasks * fix: update map task logs styles * test: add/update unit tests * fix: fix flickering and unnecessary re-renders Signed-off-by: Olga Nad * ci: resolve docker build issues (#462) * ci: resolve docker build issues * chore: remove unneeded changes Signed-off-by: Nastya Rusina * Chore: Rebase devmain on top of master (#464) * fix: ensure that admin version is fully shown (#465) * chore: fix for console not respecting admin url (#468) * chore: fix Signed-off-by: Carina Ursu * chore: remove console Signed-off-by: Carina Ursu * chore: add cherio o=to package Signed-off-by: Carina Ursu * chore: add cherio o=to package Signed-off-by: Carina Ursu * chore: fix start prod Signed-off-by: Carina Ursu * chore: fix start prod Signed-off-by: Carina Ursu * chore: cleanup Signed-off-by: Carina Ursu * chore: cleanup Signed-off-by: Carina Ursu Co-authored-by: Carina Ursu * chore: rebase on top of master Signed-off-by: Nastya <55718143+anrusina@users.noreply.github.com> * fix: trigger release #patch (#477) Signed-off-by: Nastya <55718143+anrusina@users.noreply.github.com> * feat: add task version info (#485) made the entities component become more generic that can support Workflow, Task, and others added the inputs and outputs to task details page added the versions table in task details page added the version details page added task details link in node side panel Signed-off-by: eugenejahn Co-authored-by: Eugene Jahn * chore: update Readme with python note + package version update (#486) Signed-off-by: Nastya Rusina Co-authored-by: Nastya Rusina * hotfix ci pipeline (#487) * hotfix ci pipeline Signed-off-by: Yuvraj * more changes Signed-off-by: Yuvraj Co-authored-by: Yuvraj * [Mapping][TaskInfo] V.2 - Update Task details to allow check information for child task execution (#467) * fix: add eventVersion check for map tasks (#484) * fix: resolve minimist package to v1.2.6 (#492) Signed-off-by: Nastya <55718143+anrusina@users.noreply.github.com> * ci: add flyte-api plugin package (#490) Signed-off-by: Nastya <55718143+anrusina@users.noreply.github.com> * fix: update/add graph related stories (#493) * fix: separate api auth to plugin folder (#495) * fix: separate api auth to plugin folder * chore: for local admin URL is undefined * chore: update README.md * test: fix tests + add new ones * v0.0.2 - release for flyte-api plugin Signed-off-by: Nastya <55718143+anrusina@users.noreply.github.com> * feat: rerun task action in execution page (#488) * feat: task rerun done Signed-off-by: Eugene Jahn * feat: fix initialParameters Signed-off-by: Eugene Jahn * fix: remove a file Signed-off-by: Eugene Jahn * feat: rerun task done Signed-off-by: Eugene Jahn * feat: rerun fix literal type Signed-off-by: Eugene Jahn * feat: small typo fixed Signed-off-by: Eugene Jahn * feat: small typo fixed Signed-off-by: Eugene Jahn * feat: fix test coverage Signed-off-by: Eugene Jahn * chore: update release version (#501) Signed-off-by: Nastya <55718143+anrusina@users.noreply.github.com> * chore(deps-dev): bump protobufjs from 6.8.9 to 6.11.3 (#502) Bumps [protobufjs](https://github.com/protobufjs/protobuf.js) from 6.8.9 to 6.11.3. - [Release notes](https://github.com/protobufjs/protobuf.js/releases) - [Changelog](https://github.com/protobufjs/protobuf.js/blob/v6.11.3/CHANGELOG.md) - [Commits](https://github.com/protobufjs/protobuf.js/commits/v6.11.3) --- updated-dependencies: - dependency-name: protobufjs dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * chore: add package generator for basics/composites/plugins (#503) * chore: add package generator for basics/composites/plugins Signed-off-by: Nastya <55718143+anrusina@users.noreply.github.com> * Update CONTRIBUTING.md with package generate info * fix(bug-508): executions can not be filtered by start time (#509) Signed-off-by: Nastya <55718143+anrusina@users.noreply.github.com> * chore(deps-dev): bump semantic-release from 17.2.3 to 19.0.3 (#510) Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 17.2.3 to 19.0.3. - [Release notes](https://github.com/semantic-release/semantic-release/releases) - [Commits](https://github.com/semantic-release/semantic-release/compare/v17.2.3...v19.0.3) --- updated-dependencies: - dependency-name: semantic-release dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: support flyte decks (#504) * feat: support flyte deck #none Signed-off-by: James * fix: rebase with master #none Signed-off-by: James * fix: fix iframe height and api response type; #none Signed-off-by: James * fix: modal style; #none; Signed-off-by: James * fix: use env variable for server ssl config #none; Signed-off-by: James * fix: use h2 instead of h3 for future migration #none Signed-off-by: James * feat: navbar navigation dropdown (#511) * feat: navbar navigstion dropdown * chore: storybook update * chore: fix test + add base readme Signed-off-by: Nastya <55718143+anrusina@users.noreply.github.com> * chore: support internal/external navigsation better (#513) Signed-off-by: Nastya <55718143+anrusina@users.noreply.github.com> * Update Contributing.md (#515) Add line how to add nodejs plugin to asdf, to simplify setup * chore: update navigationDropdown usage (#517) Signed-off-by: Work Co-authored-by: Work * chore(deps): bump jsdom from 16.4.0 to 16.7.0 (#516) Bumps [jsdom](https://github.com/jsdom/jsdom) from 16.4.0 to 16.7.0. - [Release notes](https://github.com/jsdom/jsdom/releases) - [Changelog](https://github.com/jsdom/jsdom/blob/master/Changelog.md) - [Commits](https://github.com/jsdom/jsdom/compare/16.4.0...16.7.0) --- updated-dependencies: - dependency-name: jsdom dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat: launch plans list & detail page #none; (#507) * feat: launch plans list #none; * fix: launch plan detail page the top navigation header * fix: added expected inputs and fixed inputs; #none * fix: remove radio buttons for view all launch plan versions; #none Signed-off-by: James * chore: release 1.1.3 (#520) Signed-off-by: Nastya Rusina * fix: cache icon fro map task (#519) * fix: cache icon fro map task * fix: icon and text center Signed-off-by: eugenejahn * fix: Relaunch form does not persist security context values when changed (#527) fix: use execution security context in relaunch; #none Signed-off-by: James * fix: release process (#529) * fix release process Signed-off-by: Yuvraj * more changes Signed-off-by: Yuvraj * more changes Signed-off-by: Yuvraj * fix workflow Signed-off-by: Yuvraj Co-authored-by: Yuvraj * fix: fix semantic-release config (#532) Signed-off-by: Yuvraj * test: fix time sensitive test (#533) chore: fix test Signed-off-by: Nastya Rusina * Rename upgrade idl workflow (#534) Signed-off-by: Yuvraj Co-authored-by: Yuvraj * fix: support mapped tasks (#494) * fix: support mapped tasks #none Signed-off-by: James * fix: fix comments #none Signed-off-by: James * fix: fix unit test #none Signed-off-by: James * fix: add string constants #none Signed-off-by: James * fix: added test for mapInputHelper #none Signed-off-by: James * fix: fix test for utils.test.ts #none Signed-off-by: James * chore: trigger snyk re-run * fix: multiple keys for mapped types; #none Signed-off-by: James * chore: storybook item (#530) Signed-off-by: Nastya Rusina * fix: fix validation for duplicate and fix focus issue Signed-off-by: James * chore: provide previous run values on relaunch Signed-off-by: Nastya Rusina Co-authored-by: Nastya <55718143+anrusina@users.noreply.github.com> Co-authored-by: Nastya Rusina * fix(491): remove favicon package + use favicon.svg by default (#537) * fix(491): remove favicon package + use favicon.svg by default Signed-off-by: Nastya Rusina * chore: resolve yarn.lock merge conflict Signed-off-by: Nastya Rusina * Fixed undefined task input types access in NodeExecutionActions (#538) * Fixed undefined task input types access in NodeExecutionActions Issue occurred due to race condition while loading data, indentical fix applies as for #506 Signed-off-by: Nick Müller * fix: cannot read properties of undefined (#506) fix: nullref Signed-off-by: Carina Ursu Co-authored-by: Carina Ursu (cherry picked from commit d38b98b2d68700415224ad6c621d79e928fded01) Signed-off-by: Nick Müller Co-authored-by: Carina Ursu * feat: support union type for launch plan (#540) * feat: support union type for launch plan Signed-off-by: eugenejahn * fix: format Signed-off-by: eugenejahn * fix: update type label Signed-off-by: eugenejahn * fix: update the format Signed-off-by: eugenejahn * fix: graph has realtime updates as execution progresses (#543) Signed-off-by: Olga Nad * fix: make sure groups used in graph aren't undefined (#545) Signed-off-by: Olga Nad * Hotfix docker push GHWF (#547) * Rename upgrade idl workflow Signed-off-by: Yuvraj * fix docker push wf Signed-off-by: Yuvraj Co-authored-by: Yuvraj * fix: Graph Center on initial render (#541) * fix: upgrade react-flow-renderer version Signed-off-by: James * fix: use setTimeout for queue on the next render Signed-off-by: James * fix: use one state variable and fitView after nodesChange Signed-off-by: James * introduce needFitView * fix: graph edge overlaps nodes (#542) * fix: upgrade react-flow-renderer version Signed-off-by: James * fix: use setTimeout for queue on the next render Signed-off-by: James * fix: use one state variable and fitView after nodesChange Signed-off-by: James * fix: graph edge overlaps nodes issue Signed-off-by: James * introduce needFitView * edge overlap * fix: flyteconsole tag in ci pipeline (#550) * fix tag issue in ci Signed-off-by: Yuvraj * remove welcome bot from boilerplate config Signed-off-by: Yuvraj Co-authored-by: Yuvraj * enable docker push for console (#552) * chore(deps): bump terser from 4.8.0 to 4.8.1 (#548) Bumps [terser](https://github.com/terser/terser) from 4.8.0 to 4.8.1. - [Release notes](https://github.com/terser/terser/releases) - [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md) - [Commits](https://github.com/terser/terser/commits) --- updated-dependencies: - dependency-name: terser dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Update Flyteidl version (#558) Signed-off-by: Flyte-Bot Signed-off-by: Flyte-Bot Co-authored-by: flyte-bot * fix: fix searchbar X button (#564) chore: fix searchbar X button Signed-off-by: Carina Ursu Signed-off-by: Carina Ursu * fix: update timeline view to show dynamic wf internals on first render (#562) * fix: update timeline view to show dynamic wf internals on first render Signed-off-by: Olga Nad * fix: update tests and clean up code Signed-off-by: Olga Nad * fix: test Signed-off-by: Olga Nad Signed-off-by: Olga Nad * fix: webmanifest missing crossorigin attribute (#566) Signed-off-by: Carina Ursu Signed-off-by: Carina Ursu * fix: console showing subworkflows as unknown (#570) * fix: console showing subworkflows as unknown Signed-off-by: Olga Nad * fix: replace comparison with lodash isEqual Signed-off-by: Olga Nad * fix: remove checkIfObjectsAreSame Signed-off-by: Olga Nad Signed-off-by: Olga Nad * fix: Dict value loses 1 trailing character on UI Launch. (#561) fix: dict lose trailing issue Signed-off-by: James Signed-off-by: James * fix: launchform validation (#557) * fix: disable launch button and show validation messaages Signed-off-by: James * fix: code clean Signed-off-by: James Signed-off-by: James * fix: integrate timeline and graph tabs wrappers under one component (#572) * fix: integrate timeline and graph tabs wrappers under one component Signed-off-by: Olga Nad * fix: details tab in graph view and clean-up Signed-off-by: Olga Nad * fix: old unit tests Signed-off-by: Olga Nad * fix: temp filename change Signed-off-by: Olga Nad * fix: final filename change Signed-off-by: Olga Nad * fix: tsconfig Signed-off-by: Olga Nad Signed-off-by: Olga Nad * chore(deps-dev): bump moment from 2.29.3 to 2.29.4 (#549) Bumps [moment](https://github.com/moment/moment) from 2.29.3 to 2.29.4. - [Release notes](https://github.com/moment/moment/releases) - [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md) - [Commits](https://github.com/moment/moment/compare/2.29.3...2.29.4) --- updated-dependencies: - dependency-name: moment dependency-type: direct:development ... Signed-off-by: dependabot[bot] Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * added none type in union type (#577) * added none type in union type Signed-off-by: eugenejahn * udpated the test case Signed-off-by: eugenejahn * fix: fixed eslint Signed-off-by: eugenejahn Signed-off-by: eugenejahn * fix: correctly coerce `defaultValue` when rendering LaunchPlan details (#578) fix: correctly coerce defaultValue when rendering LaunchPlan details Signed-off-by: Rahul Mehta Signed-off-by: Rahul Mehta * #minor: inputHelpers InputProps (#579) fix: inputHelpers InputProps Signed-off-by: Carina Ursu Signed-off-by: Carina Ursu * fix: fix test of launchform (#581) * fix: fix test of launchform Signed-off-by: James * fix: inputHelpers tests regression Signed-off-by: Olga Nad * fix: update tests description Signed-off-by: Olga Nad * fix: spelling typo Signed-off-by: Olga Nad Signed-off-by: James Signed-off-by: Olga Nad Co-authored-by: Olga Nad * Pruning some unused packages (#583) Signed-off-by: Jason Porter Signed-off-by: Jason Porter * feat: minor change (#584) Minor change Signed-off-by: Jason Porter Signed-off-by: Jason Porter * fix: floor seconds to int in the edge case moment returns it as float (#582) Signed-off-by: Olga Nad Signed-off-by: Olga Nad * Update Flyteidl version (#575) Signed-off-by: Flyte-Bot Signed-off-by: Flyte-Bot Co-authored-by: flyte-bot * fix: add BASE_URL to dev startup, open deeply nested urls (#589) * fix: add BASE_URL to dev startup, open deeply nested urls Signed-off-by: Carina Ursu * fix: remove debug code Signed-off-by: Carina Ursu * chore: empty BASE_URL handling Signed-off-by: Carina Ursu Signed-off-by: Carina Ursu * fix: add default disabled state for only mine filter (#585) * fix: add default disabled state for only mine filter Signed-off-by: Olga Nad * fix: tests Signed-off-by: Olga Nad * fix: use api context to default filter state Signed-off-by: Olga Nad * fix: revert test updates Signed-off-by: Olga Nad Signed-off-by: Olga Nad * Update Flyteidl version (#590) Signed-off-by: Flyte-Bot Signed-off-by: Flyte-Bot Co-authored-by: flyte-bot * Launch plan ref v2 (#601) * progress * Fixed * Removed debug code Signed-off-by: Jason Porter Signed-off-by: Jason Porter * fix: enable deeplinks in development (#602) chore: enable deeplinks in development Signed-off-by: Carina Ursu Signed-off-by: Carina Ursu * chore: release 1.3.5 (#605) Signed-off-by: Jason Porter Signed-off-by: Jason Porter * fix: refactor contexts around node executions table and rows Signed-off-by: Olga Nad * fix: add missing file Signed-off-by: Olga Nad * fix: add temp row component Signed-off-by: Olga Nad * fix: refactor final touch-ups Signed-off-by: Olga Nad * fix: add/update unit tests Signed-off-by: Olga Nad * fix: replace getBy with queryBy Signed-off-by: Olga Nad Signed-off-by: Nastya Rusina Signed-off-by: eugenejahn Signed-off-by: Olga Nad Signed-off-by: Nastya <55718143+anrusina@users.noreply.github.com> Signed-off-by: Eugene Jahn Signed-off-by: James Signed-off-by: Yuvraj Signed-off-by: Flyte-Bot Signed-off-by: Carina Ursu Signed-off-by: dependabot[bot] Signed-off-by: Rahul Mehta Signed-off-by: Jason Porter Co-authored-by: Nastya Rusina Co-authored-by: Nastya <55718143+anrusina@users.noreply.github.com> Co-authored-by: Carina Ursu Co-authored-by: Carina Ursu Co-authored-by: Eugene Jahn Co-authored-by: apTalya <99441958+apTalya@users.noreply.github.com> Co-authored-by: Yuvraj Co-authored-by: Yuvraj Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: james-union <105876962+james-union@users.noreply.github.com> Co-authored-by: Work Co-authored-by: Nick Müller Co-authored-by: Flyte Bot Co-authored-by: flyte-bot Co-authored-by: Rahul Mehta <98349643+rahul-theorem@users.noreply.github.com> Co-authored-by: Jason Porter <84735036+jsonporter@users.noreply.github.com> --- .../ExecutionDetails/DetailsPanelContext.ts | 11 ++ .../ExecutionDetails/ExecutionNodeViews.tsx | 12 +- .../ExecutionDetails/ExecutionTab.tsx | 14 +- .../ExecutionDetails/ExecutionTabContent.tsx | 41 ++--- .../Timeline/ExecutionTimeline.tsx | 3 +- .../Timeline/NodeExecutionName.tsx | 73 ++++++-- .../ExecutionDetails/Timeline/TaskNames.tsx | 19 +- .../ExecutionDetails/Timeline/context.ts | 11 -- .../Executions/ExecutionDetails/strings.tsx | 1 + .../test/NodeExecutionName.test.tsx | 66 +++++++ .../Tables/NodeExecutionActions.tsx | 13 +- .../Tables/NodeExecutionChildren.tsx | 107 ----------- .../Executions/Tables/NodeExecutionRow.tsx | 145 ++++++--------- .../Executions/Tables/NodeExecutionsTable.tsx | 171 +++++++++--------- .../Executions/Tables/RowExpander.tsx | 4 +- .../components/Executions/Tables/constants.ts | 6 - .../components/Executions/Tables/contexts.ts | 11 -- .../Tables/nodeExecutionColumns.tsx | 73 ++------ .../components/Executions/Tables/strings.tsx | 4 +- .../components/Executions/Tables/styles.ts | 34 ---- .../Tables/test/NodeExecutionRow.test.tsx | 80 ++++++++ .../Tables/test/NodeExecutionsTable.test.tsx | 25 +-- .../src/components/Executions/Tables/types.ts | 3 +- .../Executions/nodeExecutionQueries.ts | 26 --- .../src/components/Tables/DataList.tsx | 27 ++- .../WorkflowGraph/test/utils.test.ts | 33 ++++ .../src/components/WorkflowGraph/utils.ts | 4 +- .../ReactFlow/ReactFlowGraphComponent.tsx | 4 +- 28 files changed, 461 insertions(+), 560 deletions(-) create mode 100644 packages/zapp/console/src/components/Executions/ExecutionDetails/DetailsPanelContext.ts delete mode 100644 packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/context.ts create mode 100644 packages/zapp/console/src/components/Executions/ExecutionDetails/test/NodeExecutionName.test.tsx delete mode 100644 packages/zapp/console/src/components/Executions/Tables/NodeExecutionChildren.tsx delete mode 100644 packages/zapp/console/src/components/Executions/Tables/contexts.ts create mode 100644 packages/zapp/console/src/components/Executions/Tables/test/NodeExecutionRow.test.tsx diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/DetailsPanelContext.ts b/packages/zapp/console/src/components/Executions/ExecutionDetails/DetailsPanelContext.ts new file mode 100644 index 000000000..c0c27650d --- /dev/null +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/DetailsPanelContext.ts @@ -0,0 +1,11 @@ +import { NodeExecutionIdentifier } from 'models/Execution/types'; +import { createContext } from 'react'; + +export interface DetailsPanelContextData { + selectedExecution?: NodeExecutionIdentifier | null; + setSelectedExecution: (selectedExecutionId: NodeExecutionIdentifier | null) => void; +} + +export const DetailsPanelContext = createContext( + {} as DetailsPanelContextData, +); diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx index 60fdac125..f7f51f694 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionNodeViews.tsx @@ -57,11 +57,11 @@ export const ExecutionNodeViews: React.FC = ({ executio const filterState = useNodeExecutionFiltersState(); const tabState = useTabState(tabs, defaultTab); const queryClient = useQueryClient(); - const requestConfig = useContext(NodeExecutionsRequestConfigContext); + const requestConfig = useContext(NodeExecutionsRequestConfigContext); // Can't find initialization of the provider const [nodeExecutionsLoading, setNodeExecutionsLoading] = useState(true); const { - closure: { abortMetadata, workflowId }, + closure: { workflowId }, } = execution; const [nodeExecutions, setNodeExecutions] = useState([]); @@ -161,11 +161,7 @@ export const ExecutionNodeViews: React.FC = ({ executio loadingComponent={LoadingComponent} > {() => ( - + )} ); @@ -188,8 +184,8 @@ export const ExecutionNodeViews: React.FC = ({ executio )} {() => renderTab(tabState.value)} diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionTab.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionTab.tsx index afa02226b..5f6eb55e7 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionTab.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionTab.tsx @@ -1,7 +1,6 @@ import { WaitForQuery } from 'components/common/WaitForQuery'; import { DataError } from 'components/Errors/DataError'; import { makeWorkflowQuery } from 'components/Workflow/workflowQueries'; -import { Admin } from 'flyteidl'; import { Workflow } from 'models/Workflow/types'; import * as React from 'react'; import { useQuery, useQueryClient } from 'react-query'; @@ -12,16 +11,11 @@ import { ExecutionTabContent } from './ExecutionTabContent'; export interface ExecutionTabProps { tabType: string; - abortMetadata?: Admin.IAbortMetadata; filteredNodeExecutions: NodeExecution[]; } /** Contains the available ways to visualize the nodes of a WorkflowExecution */ -export const ExecutionTab: React.FC = ({ - tabType, - abortMetadata, - filteredNodeExecutions, -}) => { +export const ExecutionTab: React.FC = ({ tabType, filteredNodeExecutions }) => { const queryClient = useQueryClient(); const { workflowId } = useNodeExecutionContext(); const workflowQuery = useQuery(makeWorkflowQuery(queryClient, workflowId)); @@ -30,11 +24,7 @@ export const ExecutionTab: React.FC = ({ {() => ( - + )} diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionTabContent.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionTabContent.tsx index 2fd5a71a3..b5029e0b2 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionTabContent.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/ExecutionTabContent.tsx @@ -5,7 +5,6 @@ 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 { Admin } from 'flyteidl'; import * as React from 'react'; import { transformerWorkflowToDag } from 'components/WorkflowGraph/transformerWorkflowToDag'; import { checkForDynamicExecutions } from 'components/common/utils'; @@ -17,14 +16,13 @@ import { NodeExecutionsByIdContext } from '../contexts'; import { NodeExecutionsTable } from '../Tables/NodeExecutionsTable'; import { tabs } from './constants'; import { NodeExecutionDetailsPanelContent } from './NodeExecutionDetailsPanelContent'; -import { NodeExecutionsTimelineContext } from './Timeline/context'; import { ExecutionTimeline } from './Timeline/ExecutionTimeline'; import { ExecutionTimelineFooter } from './Timeline/ExecutionTimelineFooter'; import { convertToPlainNodes, TimeZone } from './Timeline/helpers'; +import { DetailsPanelContext } from './DetailsPanelContext'; export interface ExecutionTabContentProps { tabType: string; - abortMetadata?: Admin.IAbortMetadata; filteredNodeExecutions: NodeExecution[]; } @@ -43,7 +41,6 @@ const useStyles = makeStyles(() => ({ export const ExecutionTabContent: React.FC = ({ tabType, - abortMetadata, filteredNodeExecutions, }) => { const styles = useStyles(); @@ -115,7 +112,7 @@ export const ExecutionTabContent: React.FC = ({ const handleTimezoneChange = (tz) => setChartTimezone(tz); - const timelineContext = useMemo( + const detailsPanelContext = useMemo( () => ({ selectedExecution, setSelectedExecution }), [selectedExecution, setSelectedExecution], ); @@ -139,19 +136,14 @@ export const ExecutionTabContent: React.FC = ({ setSelectedExecution(newSelectedExecution); }; - const onExecutionSelectionChanged = (execution: NodeExecutionIdentifier | null) => - setSelectedExecution(execution); - const renderContent = () => { switch (tabType) { - case tabs.timeline.id: + case tabs.nodes.id: return ( -
-
- -
- -
+ ); case tabs.graph.id: return ( @@ -166,15 +158,14 @@ export const ExecutionTabContent: React.FC = ({ isDetailsTabClosed={isDetailsTabClosed} /> ); - case tabs.nodes.id: + case tabs.timeline.id: return ( - +
+
+ +
+ +
); default: return null; @@ -183,9 +174,9 @@ export const ExecutionTabContent: React.FC = ({ return ( <> - + {renderContent()} - + {/* Side panel, shows information for specific node */} {!isDetailsTabClosed && selectedExecution && ( diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx index 50d70accf..e71eb0e02 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/ExecutionTimeline.tsx @@ -12,6 +12,7 @@ import { useScaleContext } from './scaleContext'; import { TaskNames } from './TaskNames'; import { getChartDurationData } from './TimelineChart/chartData'; import { TimelineChart } from './TimelineChart'; +import t from '../strings'; interface StyleProps { chartWidth: number; @@ -165,7 +166,7 @@ export const ExecutionTimeline: React.FC = ({ chartTimezone, initialNod return ( <>
- Task Name + {t('taskNameColumnHeader')} ({ selectedExecutionName: { fontWeight: 'bold', }, + displayName: { + marginTop: 4, + textOverflow: 'ellipsis', + width: '100%', + overflow: 'hidden', + }, })); -export const NodeExecutionName: React.FC = ({ name, execution }) => { +export const NodeExecutionName: React.FC = ({ + name, + templateName, + execution, +}) => { const commonStyles = useCommonStyles(); const styles = useStyles(); - const { selectedExecution, setSelectedExecution } = useContext(NodeExecutionsTimelineContext); + const { getNodeExecutionDetails } = useNodeExecutionContext(); + const { selectedExecution, setSelectedExecution } = useContext(DetailsPanelContext); + 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); - return isSelected ? ( - - {name} - - ) : ( - + + const defaultName = displayName ?? name; + const truncatedName = defaultName?.split('.').pop() || defaultName; + + return ( + <> + {isSelected || execution.closure.phase === NodeExecutionPhase.UNDEFINED ? ( + + {truncatedName} + + ) : ( + + )} + {templateName && ( + + {templateName} + + )} + ); }; diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx index 57040bde8..a2d8c11ca 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/TaskNames.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { IconButton, makeStyles, Theme, Tooltip, Typography } from '@material-ui/core'; +import { IconButton, makeStyles, Theme, Tooltip } from '@material-ui/core'; import { RowExpander } from 'components/Executions/Tables/RowExpander'; import { getNodeTemplateName } from 'components/WorkflowGraph/utils'; @@ -38,12 +38,6 @@ const useStyles = makeStyles((theme: Theme) => ({ height: '100%', overflow: 'hidden', }, - displayName: { - marginTop: 4, - textOverflow: 'ellipsis', - width: '100%', - overflow: 'hidden', - }, leaf: { width: 30, }, @@ -63,7 +57,6 @@ export const TaskNames = React.forwardRef( return (
{nodes.map((node) => { - const templateName = getNodeTemplateName(node); const nodeLevel = node?.level ?? 0; return (
(
- - {templateName} -
{onAction && ( diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/context.ts b/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/context.ts deleted file mode 100644 index f60b97e0f..000000000 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/Timeline/context.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { NodeExecutionIdentifier } from 'models/Execution/types'; -import * as React from 'react'; - -export interface NodeExecutionsTimelineContextData { - selectedExecution?: NodeExecutionIdentifier | null; - setSelectedExecution: (selectedExecutionId: NodeExecutionIdentifier | null) => void; -} - -export const NodeExecutionsTimelineContext = React.createContext( - {} as NodeExecutionsTimelineContextData, -); diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/strings.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/strings.tsx index 08f4b98c6..be34cde38 100644 --- a/packages/zapp/console/src/components/Executions/ExecutionDetails/strings.tsx +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/strings.tsx @@ -4,6 +4,7 @@ const str = { rerun: 'RERUN', flyteDeck: 'Flyte Deck', resume: 'Resume', + taskNameColumnHeader: 'Task Name', }; export { patternKey } from '@flyteconsole/locale'; diff --git a/packages/zapp/console/src/components/Executions/ExecutionDetails/test/NodeExecutionName.test.tsx b/packages/zapp/console/src/components/Executions/ExecutionDetails/test/NodeExecutionName.test.tsx new file mode 100644 index 000000000..4dc161d0a --- /dev/null +++ b/packages/zapp/console/src/components/Executions/ExecutionDetails/test/NodeExecutionName.test.tsx @@ -0,0 +1,66 @@ +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/zapp/console/src/components/Executions/Tables/NodeExecutionActions.tsx b/packages/zapp/console/src/components/Executions/Tables/NodeExecutionActions.tsx index 32f7fcb0c..27511a688 100644 --- a/packages/zapp/console/src/components/Executions/Tables/NodeExecutionActions.tsx +++ b/packages/zapp/console/src/components/Executions/Tables/NodeExecutionActions.tsx @@ -10,24 +10,21 @@ 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 { useContext, useEffect, useState } from 'react'; import { NodeExecutionPhase } from 'models/Execution/enums'; -import { NodeExecutionsTableState } from './types'; import { useNodeExecutionContext } from '../contextProvider/NodeExecutionDetails'; import { NodeExecutionDetails } from '../types'; import t from './strings'; import { getNodeFrontendPhase, isNodeGateNode } from '../utils'; +import { DetailsPanelContext } from '../ExecutionDetails/DetailsPanelContext'; interface NodeExecutionActionsProps { execution: NodeExecution; - state: NodeExecutionsTableState; } -export const NodeExecutionActions = ({ - execution, - state, -}: NodeExecutionActionsProps): JSX.Element => { +export const NodeExecutionActions = ({ execution }: NodeExecutionActionsProps): JSX.Element => { const { compiledWorkflowClosure, getNodeExecutionDetails } = useNodeExecutionContext(); + const { setSelectedExecution } = useContext(DetailsPanelContext); const [showLaunchForm, setShowLaunchForm] = useState(false); const [nodeExecutionDetails, setNodeExecutionDetails] = useState< @@ -77,7 +74,7 @@ export const NodeExecutionActions = ({ // 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 - state.setSelectedExecution(execution?.id ?? null); + setSelectedExecution(execution?.id ?? null); }; const rerunIconOnClick = (e: React.MouseEvent) => { diff --git a/packages/zapp/console/src/components/Executions/Tables/NodeExecutionChildren.tsx b/packages/zapp/console/src/components/Executions/Tables/NodeExecutionChildren.tsx deleted file mode 100644 index 53e2a1b6e..000000000 --- a/packages/zapp/console/src/components/Executions/Tables/NodeExecutionChildren.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { Button, Typography } from '@material-ui/core'; -import { Theme, makeStyles } from '@material-ui/core/styles'; -import classnames from 'classnames'; -import { getCacheKey } from 'components/Cache/utils'; -import { useTheme } from 'components/Theme/useTheme'; -import { Admin } from 'flyteidl'; -import * as React from 'react'; -import { NodeExecutionGroup } from '../types'; -import { NodeExecutionRow } from './NodeExecutionRow'; -import { useExecutionTableStyles } from './styles'; -import { calculateNodeExecutionRowLeftSpacing } from './utils'; - -export interface NodeExecutionChildrenProps { - abortMetadata?: Admin.IAbortMetadata; - childGroups: NodeExecutionGroup[]; - level: number; -} - -const PAGE_SIZE = 50; - -const useStyles = makeStyles((theme: Theme) => ({ - loadMoreContainer: { - display: 'flex', - justifyContent: 'center', - marginTop: theme.spacing(1), - marginBottom: theme.spacing(1), - }, -})); - -/** Renders a nested list of row items for children of a NodeExecution */ -export const NodeExecutionChildren: React.FC = ({ - abortMetadata, - childGroups, - level, -}) => { - const styles = useStyles(); - const showNames = childGroups.length > 1; - const tableStyles = useExecutionTableStyles(); - const theme = useTheme(); - const childGroupLabelStyle = { - // The label is aligned with the parent above, so remove one level of spacing - marginLeft: `${calculateNodeExecutionRowLeftSpacing(level - 1, theme.spacing)}px`, - }; - const [loadedNodes, setLoadedNodes] = React.useState( - new Array(childGroups.length).fill(PAGE_SIZE), - ); - - const loadMoreRows = React.useCallback( - (which: number) => () => { - const newLoadedNodes = [...loadedNodes]; - newLoadedNodes[which] += PAGE_SIZE; - setLoadedNodes(newLoadedNodes); - }, - [loadedNodes], - ); - - const loadMoreButton = (which: number) => ( -
- -
- ); - - return ( - <> - {childGroups.map(({ name, nodeExecutions }, groupIndex) => { - const rows = nodeExecutions - .slice(0, loadedNodes[groupIndex]) - .map((nodeExecution, index) => ( - - )); - const key = `group-${name}`; - return showNames ? ( -
-
0 }, - tableStyles.borderBottom, - tableStyles.childGroupLabel, - )} - title={name} - style={childGroupLabelStyle} - > - - {name} - -
-
{rows}
- {loadMoreButton(groupIndex)} -
- ) : ( -
- {rows} - {nodeExecutions.length > loadedNodes[groupIndex] && loadMoreButton(groupIndex)} -
- ); - })} - - ); -}; diff --git a/packages/zapp/console/src/components/Executions/Tables/NodeExecutionRow.tsx b/packages/zapp/console/src/components/Executions/Tables/NodeExecutionRow.tsx index cdc81bd4f..14d18d0f2 100644 --- a/packages/zapp/console/src/components/Executions/Tables/NodeExecutionRow.tsx +++ b/packages/zapp/console/src/components/Executions/Tables/NodeExecutionRow.tsx @@ -1,118 +1,83 @@ -import { CircularProgress, IconButton } from '@material-ui/core'; -import { Admin } from 'flyteidl'; -import ErrorOutline from '@material-ui/icons/ErrorOutline'; import classnames from 'classnames'; -import { useTheme } from 'components/Theme/useTheme'; -import { isEqual } from 'lodash'; import { NodeExecution } from 'models/Execution/types'; -import * as React from 'react'; -import { useContext, useState } from 'react'; +import { dNode } from 'models/Graph/types'; import { NodeExecutionPhase } from 'models/Execution/enums'; -import { NodeExecutionsRequestConfigContext } from '../contexts'; -import { useChildNodeExecutionGroupsQuery } from '../nodeExecutionQueries'; -import { titleStrings } from './constants'; -import { NodeExecutionsTableContext } from './contexts'; -import { ExpandableExecutionError } from './ExpandableExecutionError'; -import { NodeExecutionChildren } from './NodeExecutionChildren'; -import { RowExpander } from './RowExpander'; +import * as React from 'react'; +import { useContext } from 'react'; +import { isExpanded } from 'components/WorkflowGraph/utils'; +import { isEqual } from 'lodash'; +import { useTheme } from 'components/Theme/useTheme'; +import { makeStyles } from '@material-ui/core'; import { selectedClassName, useExecutionTableStyles } from './styles'; +import { NodeExecutionColumnDefinition } from './types'; +import { DetailsPanelContext } from '../ExecutionDetails/DetailsPanelContext'; +import { RowExpander } from './RowExpander'; import { calculateNodeExecutionRowLeftSpacing } from './utils'; +const useStyles = makeStyles(() => ({ + namesContainerExpander: { + display: 'flex', + marginTop: 'auto', + marginBottom: 'auto', + }, + leaf: { + width: 30, + }, +})); + interface NodeExecutionRowProps { - abortMetadata?: Admin.IAbortMetadata; - index: number; - execution: NodeExecution; + columns: NodeExecutionColumnDefinition[]; + nodeExecution: NodeExecution; level?: number; style?: React.CSSProperties; + node: dNode; + onToggle: (id: string, scopeId: string, level: number) => void; } -const ChildFetchErrorIcon: React.FC<{ - query: ReturnType; -}> = ({ query }) => { - return query.isFetching ? ( - - ) : ( - ) => { - // prevent the parent row body onClick event trigger - e.stopPropagation(); - query.refetch(); - }} - > - - - ); -}; - /** Renders a NodeExecution as a row inside a `NodeExecutionsTable` */ export const NodeExecutionRow: React.FC = ({ - abortMetadata, - execution: nodeExecution, - index, - level = 0, + columns, + nodeExecution, + node, style, + onToggle, }) => { + const styles = useStyles(); const theme = useTheme(); - const { columns, state } = useContext(NodeExecutionsTableContext); - const requestConfig = useContext(NodeExecutionsRequestConfigContext); + const tableStyles = useExecutionTableStyles(); + const { selectedExecution, setSelectedExecution } = useContext(DetailsPanelContext); - const [expanded, setExpanded] = useState(false); - const toggleExpanded = () => { - setExpanded(!expanded); - }; + const nodeLevel = node?.level ?? 0; + const expanded = isExpanded(node); // 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 = level === 0 ? 'paddingLeft' : 'marginLeft'; + const spacingProp = nodeLevel === 0 ? 'paddingLeft' : 'marginLeft'; const rowContentStyle = { - [spacingProp]: `${calculateNodeExecutionRowLeftSpacing(level, theme.spacing)}px`, + [spacingProp]: `${calculateNodeExecutionRowLeftSpacing(nodeLevel, theme.spacing)}px`, }; - const childGroupsQuery = useChildNodeExecutionGroupsQuery(nodeExecution, requestConfig); - const { data: childGroups = [] } = childGroupsQuery; - - const isExpandable = childGroups.length > 0; - const tableStyles = useExecutionTableStyles(); - - const selected = state.selectedExecution - ? isEqual(state.selectedExecution, nodeExecution) - : false; - const { error } = nodeExecution.closure; + const selected = selectedExecution ? isEqual(selectedExecution, nodeExecution) : false; - const expanderContent = childGroupsQuery.error ? ( - - ) : isExpandable ? ( - - ) : null; - - const errorContent = error ? ( - - ) : null; - - const extraContent = expanded ? ( -
- + const expanderContent = ( +
+ {node.nodes?.length ? ( + onToggle(node.id, node.scopedId, nodeLevel)} + /> + ) : ( +
+ )}
- ) : null; + ); // 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 = () => nodeExecution.closure.phase !== NodeExecutionPhase.UNDEFINED && - state.setSelectedExecution(nodeExecution?.id ?? null); + setSelectedExecution(nodeExecution?.id ?? null); return (
= ({ style={style} onClick={onClickRow} > -
0 && expanded), - [tableStyles.borderTop]: level > 0 && index > 0, - })} - style={rowContentStyle} - > +
{expanderContent} @@ -137,15 +96,13 @@ export const NodeExecutionRow: React.FC = ({ {columns.map(({ className, key: columnKey, cellRenderer }) => (
{cellRenderer({ - state, + node, execution: nodeExecution, })}
))}
- {errorContent}
- {extraContent}
); }; diff --git a/packages/zapp/console/src/components/Executions/Tables/NodeExecutionsTable.tsx b/packages/zapp/console/src/components/Executions/Tables/NodeExecutionsTable.tsx index 14cf32998..0979b0b33 100644 --- a/packages/zapp/console/src/components/Executions/Tables/NodeExecutionsTable.tsx +++ b/packages/zapp/console/src/components/Executions/Tables/NodeExecutionsTable.tsx @@ -1,129 +1,136 @@ import classnames from 'classnames'; -import { Admin } from 'flyteidl'; import { getCacheKey } from 'components/Cache/utils'; import { useCommonStyles } from 'components/common/styles'; import * as scrollbarSize from 'dom-helpers/util/scrollbarSize'; -import { NodeExecution, NodeExecutionIdentifier } from 'models/Execution/types'; +import { NodeExecution } from 'models/Execution/types'; import { dNode } from 'models/Graph/types'; import { NodeExecutionPhase } from 'models/Execution/enums'; import { dateToTimestamp } from 'common/utils'; import * as React from 'react'; import { useMemo, useEffect, useState, useContext } from 'react'; -import { NodeExecutionsTableContext } from './contexts'; +import { isEndNode, isExpanded, isStartNode } from 'components/WorkflowGraph/utils'; import { ExecutionsTableHeader } from './ExecutionsTableHeader'; import { generateColumns } from './nodeExecutionColumns'; -import { NodeExecutionRow } from './NodeExecutionRow'; import { NoExecutionsContent } from './NoExecutionsContent'; import { useColumnStyles, useExecutionTableStyles } from './styles'; import { NodeExecutionsByIdContext } from '../contexts'; -import { useNodeExecutionFiltersState } from '../filters/useExecutionFiltersState'; +import { convertToPlainNodes } from '../ExecutionDetails/Timeline/helpers'; +// import { useNodeExecutionFiltersState } from '../filters/useExecutionFiltersState'; import { useNodeExecutionContext } from '../contextProvider/NodeExecutionDetails'; +import { NodeExecutionRow } from './NodeExecutionRow'; export interface NodeExecutionsTableProps { - setSelectedExecution: (execution: NodeExecutionIdentifier | null) => void; - selectedExecution: NodeExecutionIdentifier | null; - abortMetadata?: Admin.IAbortMetadata; initialNodes: dNode[]; filteredNodeExecutions: NodeExecution[]; } const scrollbarPadding = scrollbarSize(); +/** + * TODO + * Refactor to avoid code duplication here and in ExecutionTimeline, ie toggleNode, the insides of the effect + */ + /** 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 = ({ - setSelectedExecution, - selectedExecution, - abortMetadata, - initialNodes, - filteredNodeExecutions, -}) => { - const [nodeExecutions, setNodeExecutions] = useState([]); +export const NodeExecutionsTable: React.FC = ({ initialNodes }) => { const commonStyles = useCommonStyles(); const tableStyles = useExecutionTableStyles(); const nodeExecutionsById = useContext(NodeExecutionsByIdContext); - const filterState = useNodeExecutionFiltersState(); + const [originalNodes, setOriginalNodes] = useState(initialNodes); + const [showNodes, setShowNodes] = useState([]); + // const filterState = useNodeExecutionFiltersState(); const { compiledWorkflowClosure } = useNodeExecutionContext(); - useEffect(() => { - if (nodeExecutionsById) { - const executions: NodeExecution[] = [...filteredNodeExecutions]; - if (!filterState.appliedFilters?.length) { - initialNodes.forEach((node) => { - if (!nodeExecutionsById[node.scopedId]) { - executions.push({ - 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, - }); - } - }); - } - setNodeExecutions(executions); - } - }, [nodeExecutionsById, initialNodes, filteredNodeExecutions]); - - const executionsWithKeys = useMemo( - () => - nodeExecutions.map((nodeExecution) => ({ - nodeExecution, - cacheKey: getCacheKey(nodeExecution.id), - })), - [nodeExecutions], - ); - const columnStyles = useColumnStyles(); // Memoizing columns so they won't be re-generated unless the styles change const columns = useMemo( () => generateColumns(columnStyles, compiledWorkflowClosure?.primary.template.nodes ?? []), [columnStyles], ); - const tableContext = useMemo( - () => ({ columns, state: { selectedExecution, setSelectedExecution } }), - [columns, selectedExecution, setSelectedExecution], - ); - const rowProps = { - selectedExecution, - setSelectedExecution, + useEffect(() => { + setOriginalNodes(initialNodes); + const plainNodes = convertToPlainNodes(originalNodes); + const updatedShownNodesMap = plainNodes.map((node) => { + const execution = nodeExecutionsById[node.scopedId]; + return { + ...node, + startedAt: execution?.closure.startedAt, + execution, + }; + }); + setShowNodes(updatedShownNodesMap); + }, [initialNodes, originalNodes, nodeExecutionsById]); + + const toggleNode = (id: string, scopeId: string, level: number) => { + const searchNode = (nodes: dNode[], nodeLevel: number) => { + if (!nodes || nodes.length === 0) { + return; + } + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i]; + if (isStartNode(node) || isEndNode(node)) { + continue; + } + if (node.id === id && node.scopedId === scopeId && nodeLevel === level) { + nodes[i].expanded = !nodes[i].expanded; + return; + } + if (node.nodes.length > 0 && isExpanded(node)) { + searchNode(node.nodes, nodeLevel + 1); + } + } + }; + searchNode(originalNodes, 0); + setOriginalNodes([...originalNodes]); }; + return (
- -
- {executionsWithKeys.length > 0 ? ( - executionsWithKeys.map(({ nodeExecution, cacheKey }, index) => { - return ( - - ); - }) - ) : ( - - )} -
-
+
+ {showNodes.length > 0 ? ( + showNodes.map((node) => { + let nodeExecution: NodeExecution; + if (nodeExecutionsById[node.scopedId]) { + nodeExecution = nodeExecutionsById[node.scopedId]; + } else { + nodeExecution = { + 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, + }; + } + return ( + + ); + }) + ) : ( + + )} +
); }; diff --git a/packages/zapp/console/src/components/Executions/Tables/RowExpander.tsx b/packages/zapp/console/src/components/Executions/Tables/RowExpander.tsx index e85a68349..cca089ddb 100644 --- a/packages/zapp/console/src/components/Executions/Tables/RowExpander.tsx +++ b/packages/zapp/console/src/components/Executions/Tables/RowExpander.tsx @@ -2,7 +2,7 @@ import { IconButton } from '@material-ui/core'; import ChevronRight from '@material-ui/icons/ChevronRight'; import ExpandMore from '@material-ui/icons/ExpandMore'; import * as React from 'react'; -import { titleStrings } from './constants'; +import t from './strings'; /** A simple expand/collapse arrow to be rendered next to row items. */ export const RowExpander: React.FC<{ @@ -13,7 +13,7 @@ export const RowExpander: React.FC<{ disableRipple={true} disableTouchRipple={true} size="small" - title={titleStrings.expandRow} + title={t('expanderTitle', expanded)} onClick={(e: React.MouseEvent) => { // prevent the parent row body onClick event trigger e.stopPropagation(); diff --git a/packages/zapp/console/src/components/Executions/Tables/constants.ts b/packages/zapp/console/src/components/Executions/Tables/constants.ts index e8e3c6159..1a182a334 100644 --- a/packages/zapp/console/src/components/Executions/Tables/constants.ts +++ b/packages/zapp/console/src/components/Executions/Tables/constants.ts @@ -17,12 +17,6 @@ export const nodeExecutionsTableColumnWidths = { startedAt: 200, }; -export const titleStrings = { - childGroupFetchFailed: 'Failed to fetch children. Click to retry.', - expandRow: 'Expand row', - groupName: 'Group name', -}; - export const workflowVersionsTableColumnWidths = { radio: 40, name: 380, diff --git a/packages/zapp/console/src/components/Executions/Tables/contexts.ts b/packages/zapp/console/src/components/Executions/Tables/contexts.ts deleted file mode 100644 index 1f8a8ca90..000000000 --- a/packages/zapp/console/src/components/Executions/Tables/contexts.ts +++ /dev/null @@ -1,11 +0,0 @@ -import * as React from 'react'; -import { NodeExecutionColumnDefinition, NodeExecutionsTableState } from './types'; - -export interface NodeExecutionsTableContextData { - columns: NodeExecutionColumnDefinition[]; - state: NodeExecutionsTableState; -} - -export const NodeExecutionsTableContext = React.createContext( - {} as NodeExecutionsTableContextData, -); diff --git a/packages/zapp/console/src/components/Executions/Tables/nodeExecutionColumns.tsx b/packages/zapp/console/src/components/Executions/Tables/nodeExecutionColumns.tsx index b85ce7a2b..67521079b 100644 --- a/packages/zapp/console/src/components/Executions/Tables/nodeExecutionColumns.tsx +++ b/packages/zapp/console/src/components/Executions/Tables/nodeExecutionColumns.tsx @@ -2,78 +2,29 @@ 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 { isEqual } from 'lodash'; 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 { useNodeExecutionContext } from '../contextProvider/NodeExecutionDetails'; import { ExecutionStatusBadge } from '../ExecutionStatusBadge'; import { NodeExecutionCacheStatus } from '../NodeExecutionCacheStatus'; import { getNodeExecutionTimingMS, getNodeFrontendPhase, isNodeGateNode } from '../utils'; import { NodeExecutionActions } from './NodeExecutionActions'; -import { SelectNodeExecutionLink } from './SelectNodeExecutionLink'; import { useColumnStyles } from './styles'; import { NodeExecutionCellRendererData, NodeExecutionColumnDefinition } from './types'; import t from '../strings'; - -const ExecutionName: React.FC = ({ execution, state }) => { - const detailsContext = useNodeExecutionContext(); - const [displayName, setDisplayName] = useState(); - - useEffect(() => { - let isCurrent = true; - detailsContext.getNodeExecutionDetails(execution).then((res) => { - if (isCurrent) { - setDisplayName(res.displayName); - } - }); - return () => { - isCurrent = false; - }; - }); - - const commonStyles = useCommonStyles(); - const styles = useColumnStyles(); - const { selectedExecution, setSelectedExecution } = state; - - const isSelected = state.selectedExecution != null && isEqual(execution.id, selectedExecution); - - const name = displayName ?? execution.id.nodeId; - const truncatedName = name?.split('.').pop() || name; - - const readableName = - isSelected || execution.closure.phase === NodeExecutionPhase.UNDEFINED ? ( - - {truncatedName} - - ) : ( - - ); - - return ( - <> - {readableName} - - {displayName} - - - ); -}; +import { NodeExecutionName } from '../ExecutionDetails/Timeline/NodeExecutionName'; const DisplayId: React.FC = ({ execution }) => { const commonStyles = useCommonStyles(); - const detailsContext = useNodeExecutionContext(); + const { getNodeExecutionDetails } = useNodeExecutionContext(); const [displayId, setDisplayId] = useState(); useEffect(() => { let isCurrent = true; - detailsContext.getNodeExecutionDetails(execution).then((res) => { + getNodeExecutionDetails(execution).then((res) => { if (isCurrent) { setDisplayId(res.displayId); } @@ -92,12 +43,12 @@ const DisplayId: React.FC = ({ execution }) => { }; const DisplayType: React.FC = ({ execution }) => { - const detailsContext = useNodeExecutionContext(); + const { getNodeExecutionDetails } = useNodeExecutionContext(); const [type, setType] = useState(); useEffect(() => { let isCurrent = true; - detailsContext.getNodeExecutionDetails(execution).then((res) => { + getNodeExecutionDetails(execution).then((res) => { if (isCurrent) { setType(res.displayType); } @@ -116,7 +67,13 @@ export function generateColumns( ): NodeExecutionColumnDefinition[] { return [ { - cellRenderer: (props) => , + cellRenderer: ({ node }) => ( + + ), className: styles.columnName, key: 'name', label: t('nameLabel'), @@ -198,9 +155,9 @@ export function generateColumns( ), }, { - cellRenderer: ({ execution, state }) => + cellRenderer: ({ execution }) => execution.closure.phase === NodeExecutionPhase.UNDEFINED ? null : ( - + ), className: styles.columnLogs, key: 'actions', diff --git a/packages/zapp/console/src/components/Executions/Tables/strings.tsx b/packages/zapp/console/src/components/Executions/Tables/strings.tsx index c540a5e85..923b2c693 100644 --- a/packages/zapp/console/src/components/Executions/Tables/strings.tsx +++ b/packages/zapp/console/src/components/Executions/Tables/strings.tsx @@ -6,11 +6,13 @@ const str = { nameLabel: 'task name', nodeIdLabel: 'node id', phaseLabel: 'status', - queuedTimeLabel: 'Queued Time', + 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 '@flyteconsole/locale'; diff --git a/packages/zapp/console/src/components/Executions/Tables/styles.ts b/packages/zapp/console/src/components/Executions/Tables/styles.ts index d1469db13..5991ade9e 100644 --- a/packages/zapp/console/src/components/Executions/Tables/styles.ts +++ b/packages/zapp/console/src/components/Executions/Tables/styles.ts @@ -3,8 +3,6 @@ import { headerGridHeight } from 'components/Tables/constants'; import { headerFontFamily, listhoverColor, - nestedListColor, - smallFontSize, tableHeaderColor, tablePlaceholderColor, } from 'components/Theme/constants'; @@ -20,23 +18,8 @@ export const useExecutionTableStyles = makeStyles((theme: Theme) => ({ borderBottom: { borderBottom: `1px solid ${theme.palette.divider}`, }, - borderTop: { - borderTop: `1px solid ${theme.palette.divider}`, - }, - childrenContainer: { - backgroundColor: nestedListColor, - minHeight: theme.spacing(7), - }, - childGroupLabel: { - borderWidth: '2px', - padding: `${theme.spacing(2)}px 0`, - }, errorContainer: { padding: `0 ${theme.spacing(8)}px ${theme.spacing(2)}px`, - '$childrenContainer &': { - paddingTop: theme.spacing(2), - paddingLeft: theme.spacing(2), - }, }, expander: { alignItems: 'center', @@ -56,11 +39,6 @@ export const useExecutionTableStyles = makeStyles((theme: Theme) => ({ headerColumnVersion: { width: theme.spacing(4), }, - headerColumnName: { - fontSize: smallFontSize, - fontWeight: 'bold', - textTransform: 'uppercase', - }, headerRow: { alignItems: 'center', borderBottom: `4px solid ${theme.palette.divider}`, @@ -71,17 +49,6 @@ export const useExecutionTableStyles = makeStyles((theme: Theme) => ({ flexDirection: 'row', height: theme.spacing(headerGridHeight), }, - logLink: { - '&:not(:first-child)': { - borderLeft: `1px solid ${theme.palette.divider}`, - marginLeft: theme.spacing(1), - paddingLeft: theme.spacing(1), - }, - }, - logLinksContainer: { - display: 'flex', - flexDirection: 'row', - }, noRowsContent: { color: tablePlaceholderColor, margin: `${theme.spacing(5)}px auto`, @@ -101,7 +68,6 @@ export const useExecutionTableStyles = makeStyles((theme: Theme) => ({ clickableRow: { cursor: 'pointer', }, - rowContent: {}, rowColumns: { alignItems: 'center', display: 'flex', diff --git a/packages/zapp/console/src/components/Executions/Tables/test/NodeExecutionRow.test.tsx b/packages/zapp/console/src/components/Executions/Tables/test/NodeExecutionRow.test.tsx new file mode 100644 index 000000000..6401fe79b --- /dev/null +++ b/packages/zapp/console/src/components/Executions/Tables/test/NodeExecutionRow.test.tsx @@ -0,0 +1,80 @@ +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 { dTypes } from 'models/Graph/types'; +import { NodeExecutionRow } from '../NodeExecutionRow'; + +jest.mock('components/Workflow/workflowQueries'); +const { fetchWorkflow } = require('components/Workflow/workflowQueries'); + +jest.mock('components/Executions/Tables/RowExpander', () => ({ + RowExpander: jest.fn(() =>
), +})); + +const columns = []; +const node = { + 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; + queryClient = createTestQueryClient(); + insertFixture(mockServer, fixture); + fetchWorkflow.mockImplementation(() => Promise.resolve(fixture.workflows.top)); + }); + + const renderComponent = (props) => + render( + + + + + , + ); + + it('should not render expander if node is a leaf', async () => { + const { queryByRole, queryByTestId } = renderComponent({ + columns, + node, + nodeExecution: execution, + 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 () => { + const mockNode = { ...node, nodes: [node, node] }; + const { queryByRole, queryByTestId } = renderComponent({ + columns, + node: mockNode, + nodeExecution: execution, + onToggle, + }); + await waitFor(() => queryByRole('listitem')); + + expect(queryByRole('listitem')).toBeInTheDocument(); + expect(queryByTestId('expander')).toBeInTheDocument(); + }); +}); diff --git a/packages/zapp/console/src/components/Executions/Tables/test/NodeExecutionsTable.test.tsx b/packages/zapp/console/src/components/Executions/Tables/test/NodeExecutionsTable.test.tsx index 23abded88..4b7f06a32 100644 --- a/packages/zapp/console/src/components/Executions/Tables/test/NodeExecutionsTable.test.tsx +++ b/packages/zapp/console/src/components/Executions/Tables/test/NodeExecutionsTable.test.tsx @@ -23,11 +23,10 @@ jest.mock('components/Workflow/workflowQueries'); const { fetchWorkflow } = require('components/Workflow/workflowQueries'); jest.mock('components/Executions/Tables/NodeExecutionRow', () => ({ - NodeExecutionRow: jest.fn(({ children, execution }) => ( + NodeExecutionRow: jest.fn(({ nodeExecution }) => (
-
{execution?.id?.nodeId}
-
{execution?.closure?.phase}
- {children} +
{nodeExecution?.id?.nodeId}
+
{nodeExecution?.closure?.phase}
)), })); @@ -93,8 +92,6 @@ describe('NodeExecutionsTableExecutions > Tables > NodeExecutionsTable', () => { let requestConfig: RequestConfig; let fixture: ReturnType; const initialNodes = mockNodes(2); - const selectedExecution = null; - const setSelectedExecution = jest.fn(); beforeEach(() => { requestConfig = {}; @@ -104,13 +101,7 @@ describe('NodeExecutionsTableExecutions > Tables > NodeExecutionsTable', () => { fetchWorkflow.mockImplementation(() => Promise.resolve(fixture.workflows.top)); }); - const renderTable = ({ - nodeExecutionsById, - initialNodes, - filteredNodeExecutions, - selectedExecution, - setSelectedExecution, - }) => + const renderTable = ({ nodeExecutionsById, initialNodes, filteredNodeExecutions }) => render( @@ -118,8 +109,6 @@ describe('NodeExecutionsTableExecutions > Tables > NodeExecutionsTable', () => { @@ -131,8 +120,6 @@ describe('NodeExecutionsTableExecutions > Tables > NodeExecutionsTable', () => { it('renders empty content when there are no nodes', async () => { const { queryByText, queryByTestId } = renderTable({ initialNodes: [], - selectedExecution, - setSelectedExecution, nodeExecutionsById: {}, filteredNodeExecutions: [], }); @@ -150,8 +137,6 @@ describe('NodeExecutionsTableExecutions > Tables > NodeExecutionsTable', () => { const { queryAllByTestId } = renderTable({ initialNodes, - selectedExecution, - setSelectedExecution, nodeExecutionsById, filteredNodeExecutions, }); @@ -176,8 +161,6 @@ describe('NodeExecutionsTableExecutions > Tables > NodeExecutionsTable', () => { const { queryAllByTestId } = renderTable({ initialNodes, - selectedExecution, - setSelectedExecution, nodeExecutionsById, filteredNodeExecutions, }); diff --git a/packages/zapp/console/src/components/Executions/Tables/types.ts b/packages/zapp/console/src/components/Executions/Tables/types.ts index 49bf992a0..0f7ac40fe 100644 --- a/packages/zapp/console/src/components/Executions/Tables/types.ts +++ b/packages/zapp/console/src/components/Executions/Tables/types.ts @@ -1,5 +1,6 @@ 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 { @@ -20,7 +21,7 @@ export interface ColumnDefinition { export interface NodeExecutionCellRendererData { execution: NodeExecution; - state: NodeExecutionsTableState; + node: dNode; } export type NodeExecutionColumnDefinition = ColumnDefinition; diff --git a/packages/zapp/console/src/components/Executions/nodeExecutionQueries.ts b/packages/zapp/console/src/components/Executions/nodeExecutionQueries.ts index 5fd66a760..4d696900d 100644 --- a/packages/zapp/console/src/components/Executions/nodeExecutionQueries.ts +++ b/packages/zapp/console/src/components/Executions/nodeExecutionQueries.ts @@ -281,32 +281,6 @@ function fetchChildNodeExecutionGroups( return fetchGroupsForTaskExecutionNode(queryClient, nodeExecution, config); } -/** Fetches and groups `NodeExecution`s which are direct children of the given - * `NodeExecution`. - */ -export function useChildNodeExecutionGroupsQuery( - nodeExecution: NodeExecution, - config: RequestConfig, -): QueryObserverResult { - const queryClient = useQueryClient(); - // Use cached data if the parent node execution is terminal and all children - // in all groups are terminal - const shouldEnableFn = (groups: NodeExecutionGroup[]) => { - if (!nodeExecutionIsTerminal(nodeExecution)) { - return true; - } - return groups.some((group) => group.nodeExecutions.some((ne) => !nodeExecutionIsTerminal(ne))); - }; - - return useConditionalQuery( - { - queryKey: [QueryType.NodeExecutionChildList, nodeExecution.id, config], - queryFn: () => fetchChildNodeExecutionGroups(queryClient, nodeExecution, config), - }, - shouldEnableFn, - ); -} - /** * Query returns all children (not only direct childs) for a list of `nodeExecutions` */ diff --git a/packages/zapp/console/src/components/Tables/DataList.tsx b/packages/zapp/console/src/components/Tables/DataList.tsx index 2f481a4f3..5f94cc69c 100644 --- a/packages/zapp/console/src/components/Tables/DataList.tsx +++ b/packages/zapp/console/src/components/Tables/DataList.tsx @@ -2,8 +2,9 @@ import { ListProps, Typography } from '@material-ui/core'; import { makeStyles, Theme, useTheme } from '@material-ui/core/styles'; import classnames from 'classnames'; import { useCommonStyles } from 'components/common/styles'; -import { tablePlaceholderColor } from 'components/Theme/constants'; +import { useExecutionTableStyles } from 'components/Executions/Tables/styles'; import * as React from 'react'; +import { forwardRef, useImperativeHandle, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { AutoSizer, CellMeasurer, @@ -27,11 +28,6 @@ const useStyles = makeStyles((theme: Theme) => ({ // set minHeight to avoid AutoResizer setting height to 0 minHeight: theme.spacing(minListContainerHeight), }, - noRowsContent: { - color: tablePlaceholderColor, - margin: `${theme.spacing(5)}px auto`, - textAlign: 'center', - }, })); export interface DataListRowClickEventParams { @@ -83,17 +79,18 @@ const DataListImplComponent: React.RefForwardingComponent(); - const lengthRef = React.useRef(0); - const listRef = React.useRef(null); + const lengthRef = useRef(0); + const listRef = useRef(null); /** We want the cache to persist across renders, which useState will do. * But we also don't want to be needlessly creating new caches that are * thrown away immediately. So we're using a creation function which useState * will call to create the initial value. */ - const [cellCache, setCellCache] = React.useState(createCellMeasurerCache); + const [cellCache, setCellCache] = useState(createCellMeasurerCache); - const recomputeRow = React.useMemo( + const recomputeRow = useMemo( () => (rowIndex: number) => { cellCache.clear(rowIndex, 0); if (listRef.current !== null) { @@ -102,7 +99,7 @@ const DataListImplComponent: React.RefForwardingComponent ({ recomputeRowHeights: recomputeRow, @@ -110,7 +107,7 @@ const DataListImplComponent: React.RefForwardingComponent { + useLayoutEffect(() => { if (lengthRef.current >= 0 && items.length > lengthRef.current) { recomputeRow(lengthRef.current); } @@ -133,7 +130,7 @@ const DataListImplComponent: React.RefForwardingComponent ( -
+
{typeof NoRowsContent === 'string' ? ( {NoRowsContent} ) : ( @@ -194,7 +191,7 @@ const DataListImplComponent: React.RefForwardingComponent ); }; -const DataListImpl = React.forwardRef(DataListImplComponent); +const DataListImpl = forwardRef(DataListImplComponent); /** The default version of DataList doesn't require a width/height and will expand to * fill its parent container (this can have odd behavior when using flex or the parent @@ -223,6 +220,6 @@ const DataListComponent: React.RefForwardingComponent ); }; -const DataList = React.forwardRef(DataListComponent); +const DataList = forwardRef(DataListComponent); export { DataList, DataListImpl }; diff --git a/packages/zapp/console/src/components/WorkflowGraph/test/utils.test.ts b/packages/zapp/console/src/components/WorkflowGraph/test/utils.test.ts index 04f6a657f..49cfca70c 100644 --- a/packages/zapp/console/src/components/WorkflowGraph/test/utils.test.ts +++ b/packages/zapp/console/src/components/WorkflowGraph/test/utils.test.ts @@ -12,6 +12,7 @@ import { getNodeTypeFromCompiledNode, isStartNode, isEndNode, + getNodeTemplateName, } from '../utils'; describe('getDisplayName', () => { @@ -84,3 +85,35 @@ describe('getSubWorkflowFromId', () => { expect(isStartNode(mockCompiledTaskNode)).toBe(false); }); }); + +describe('getNodeTemplateName', () => { + const name = 'Test'; + const node = { + id: 'n1', + scopedId: 'n1', + type: dTypes.start, + name: 'node1', + nodes: [], + edges: [], + }; + + it('should return undefined when node does not have value field', () => { + expect(getNodeTemplateName(node)).toBeUndefined(); + }); + it('should return undefined when node value is neither workflowNode nor taskNode', () => { + const otherNode = { ...node, value: { gateNode: {} } }; + expect(getNodeTemplateName(otherNode)).toBeUndefined(); + }); + it('should return referenceId name for taskNode', () => { + const otherNode = { ...node, value: { taskNode: { referenceId: { name } } } }; + expect(getNodeTemplateName(otherNode)).toEqual(name); + }); + it('should return launchplan name for launch plan', () => { + const otherNode = { ...node, value: { workflowNode: { launchplanRef: { name } } } }; + expect(getNodeTemplateName(otherNode)).toEqual(name); + }); + it('should return subworkflow name for standard workflowNode', () => { + const otherNode = { ...node, value: { workflowNode: { subWorkflowRef: { name } } } }; + expect(getNodeTemplateName(otherNode)).toEqual(name); + }); +}); diff --git a/packages/zapp/console/src/components/WorkflowGraph/utils.ts b/packages/zapp/console/src/components/WorkflowGraph/utils.ts index d5743021f..96c1cfa4b 100644 --- a/packages/zapp/console/src/components/WorkflowGraph/utils.ts +++ b/packages/zapp/console/src/components/WorkflowGraph/utils.ts @@ -133,7 +133,7 @@ export const getNodeNameFromDag = (dagData: dNode, nodeId: string) => { return getNodeTemplateName(node); }; -export const getNodeTemplateName = (node: dNode) => { +export const getNodeTemplateName = (node: dNode): string | undefined => { const value = node?.value; if (value?.workflowNode) { const { launchplanRef, subWorkflowRef } = node.value.workflowNode; @@ -145,7 +145,7 @@ export const getNodeTemplateName = (node: dNode) => { return value.taskNode.referenceId.name; } - return ''; + return undefined; }; export const transformWorkflowToKeyedDag = (workflow: Workflow) => { diff --git a/packages/zapp/console/src/components/flytegraph/ReactFlow/ReactFlowGraphComponent.tsx b/packages/zapp/console/src/components/flytegraph/ReactFlow/ReactFlowGraphComponent.tsx index c0c6ff222..806f5f9ef 100644 --- a/packages/zapp/console/src/components/flytegraph/ReactFlow/ReactFlowGraphComponent.tsx +++ b/packages/zapp/console/src/components/flytegraph/ReactFlow/ReactFlowGraphComponent.tsx @@ -151,7 +151,7 @@ const ReactFlowGraphComponent = ({ const backgroundStyle = getRFBackground().nested; useEffect(() => { - const pausedNodes: dNode[] = initialNodes.filter((node) => { + const updatedPausedNodes: dNode[] = initialNodes.filter((node) => { const nodeExecution = nodeExecutionsById[node.id]; if (nodeExecution) { const phase = nodeExecution?.closure.phase; @@ -163,7 +163,7 @@ const ReactFlowGraphComponent = ({ } return false; }); - const nodesWithExecutions = pausedNodes.map((node) => { + const nodesWithExecutions = updatedPausedNodes.map((node) => { const execution = nodeExecutionsById[node.scopedId]; return { ...node,