diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 44e8dfaba..c557d4e87 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,10 +3,10 @@ name: CI
on:
push:
paths-ignore:
- - 'conductor-clients/**'
+ - "conductor-clients/**"
pull_request:
paths-ignore:
- - 'conductor-clients/**'
+ - "conductor-clients/**"
jobs:
build:
@@ -21,8 +21,8 @@ jobs:
- name: Set up Zulu JDK 17
uses: actions/setup-java@v3
with:
- distribution: 'zulu'
- java-version: '17'
+ distribution: "zulu"
+ java-version: "17"
- name: Cache SonarCloud packages
uses: actions/cache@v3
with:
@@ -53,20 +53,20 @@ jobs:
uses: mikepenz/action-junit-report@v3
if: always()
with:
- report_paths: '**/build/test-results/test/TEST-*.xml'
+ report_paths: "**/build/test-results/test/TEST-*.xml"
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-artifacts
- path: '**/build/reports'
+ path: "**/build/reports"
- name: Store Buildscan URL
uses: actions/upload-artifact@v3
with:
name: build-scan
- path: 'buildscan.log'
+ path: "buildscan.log"
build-ui:
runs-on: ubuntu-latest
- container: cypress/browsers:node14.17.6-chrome100-ff98
+ container: cypress/browsers:node-22.11.0-chrome-130.0.6723.116-1-ff-132.0.1-edge-130.0.2849.68-1
defaults:
run:
working-directory: ui
@@ -81,15 +81,15 @@ jobs:
- name: Run E2E Tests
uses: cypress-io/github-action@v4
- with:
+ with:
working-directory: ui
install: false
start: yarn run serve-build
- wait-on: 'http://localhost:5000'
-
+ wait-on: "http://localhost:5000"
+
- name: Run Component Tests
uses: cypress-io/github-action@v4
- with:
+ with:
working-directory: ui
install: false
component: true
@@ -100,11 +100,10 @@ jobs:
with:
name: cypress-screenshots
path: ui/cypress/screenshots
-
+
- name: Archive test videos
uses: actions/upload-artifact@v3
if: always()
with:
name: cypress-videos
path: ui/cypress/videos
-
diff --git a/ui/package.json b/ui/package.json
index 623a5c6dc..6b384d93e 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -20,12 +20,13 @@
"moment": "^2.29.2",
"monaco-editor": "^0.44.0",
"node-forge": "^1.3.0",
+ "orkes-workflow-visualizer": "^1.0.0",
"parse-svg-path": "^0.1.2",
"prop-types": "^15.7.2",
- "react": "^16.8.0",
+ "react": "^18.3.1",
"react-cron-generator": "^1.3.5",
"react-data-table-component": "^6.11.8",
- "react-dom": "^16.8.0",
+ "react-dom": "^18.3.1",
"react-helmet": "^6.1.0",
"react-is": "^17.0.2",
"react-query": "^3.19.4",
diff --git a/ui/src/components/diagram/WorkflowDAG.js b/ui/src/components/diagram/WorkflowDAG.js
index cabf35fa0..684fec032 100644
--- a/ui/src/components/diagram/WorkflowDAG.js
+++ b/ui/src/components/diagram/WorkflowDAG.js
@@ -580,7 +580,7 @@ export default class WorkflowDAG {
return this.taskResultsById.get(taskPointer.id);
} else {
const node = this.graph.node(taskPointer.ref);
- return _.last(node.taskResults);
+ return _.last(node?.taskResults);
}
}
}
diff --git a/ui/src/pages/definition/WorkflowDefinition.jsx b/ui/src/pages/definition/WorkflowDefinition.jsx
index 77963f518..7f18244ca 100644
--- a/ui/src/pages/definition/WorkflowDefinition.jsx
+++ b/ui/src/pages/definition/WorkflowDefinition.jsx
@@ -12,7 +12,6 @@ import {
useWorkflowNamesAndVersions,
} from "../../data/workflow";
import WorkflowDAG from "../../components/diagram/WorkflowDAG";
-import WorkflowGraph from "../../components/diagram/WorkflowGraph";
import ResetConfirmationDialog from "./ResetConfirmationDialog";
import {
configureMonaco,
@@ -23,6 +22,7 @@ import SaveWorkflowDialog from "./SaveWorkflowDialog";
import update from "immutability-helper";
import { usePushHistory } from "../../components/NavLink";
import { timestampRenderer } from "../../utils/helpers";
+import { WorkflowVisualizer } from "orkes-workflow-visualizer";
import {
KeyboardArrowLeftRounded,
@@ -67,8 +67,8 @@ const useStyles = makeStyles({
gap: 8,
},
editorLineDecorator: {
- backgroundColor: "rgb(45, 45, 45, 0.1)"
- }
+ backgroundColor: "rgb(45, 45, 45, 0.1)",
+ },
});
const actions = {
@@ -240,21 +240,26 @@ export default function Workflow() {
};
const handleWorkflowNodeClick = (node) => {
- let editor = editorRef.current.getModel()
- let searchResult = editor.findMatches(`"taskReferenceName": "${node.ref}"`)
- if (searchResult.length){
- editorRef.current.revealLineInCenter(searchResult[0]?.range?.startLineNumber, 0);
- setDecorations(editorRef.current.deltaDecorations(decorations, [
- {
- range: searchResult[0]?.range,
- options: {
- isWholeLine: true,
- inlineClassName: classes.editorLineDecorator
- }
- }
- ]))
+ let editor = editorRef.current.getModel();
+ let searchResult = editor.findMatches(`"taskReferenceName": "${node.ref}"`);
+ if (searchResult.length) {
+ editorRef.current.revealLineInCenter(
+ searchResult[0]?.range?.startLineNumber,
+ 0
+ );
+ setDecorations(
+ editorRef.current.deltaDecorations(decorations, [
+ {
+ range: searchResult[0]?.range,
+ options: {
+ isWholeLine: true,
+ inlineClassName: classes.editorLineDecorator,
+ },
+ },
+ ])
+ );
}
- }
+ };
return (
<>
@@ -369,8 +374,17 @@ export default function Workflow() {
className={classes.resizer}
onMouseDown={(e) => handleMouseDown(e)}
/>
-
- {dag &&
}
+
+ {dag && dag?.workflowDef && (
+ handleWorkflowNodeClick({ ref: data?.id })}
+ />
+ )}
>
diff --git a/ui/src/pages/execution/Execution.jsx b/ui/src/pages/execution/Execution.jsx
index 656713697..b05970bb5 100644
--- a/ui/src/pages/execution/Execution.jsx
+++ b/ui/src/pages/execution/Execution.jsx
@@ -126,6 +126,7 @@ export default function Execution() {
const [isFullWidth, setIsFullWidth] = useState(false);
const [isResizing, setIsResizing] = useState(false);
const [drawerWidth, setDrawerWidth] = useState(INIT_DRAWER_WIDTH);
+ const [selectedNode, setSelectedNode] = useState();
const [tabIndex, setTabIndex] = useQueryState("tabIndex", 0);
const [selectedTaskRison, setSelectedTaskRison] = useQueryState("task", "");
@@ -222,7 +223,12 @@ export default function Execution() {
)}
- Definition
+
+ Definition
+
Refresh
@@ -260,6 +266,7 @@ export default function Execution() {
execution={execution}
setSelectedTask={setSelectedTask}
selectedTask={selectedTask}
+ setSelectedNode={setSelectedNode}
/>
)}
{tabIndex === 1 && }
@@ -302,6 +309,8 @@ export default function Execution() {
className={classes.rightPanel}
selectedTask={selectedTask}
dag={dag}
+ execution={execution}
+ selectedNode={selectedNode}
onTaskChange={setSelectedTask}
/>
diff --git a/ui/src/pages/execution/RightPanel.jsx b/ui/src/pages/execution/RightPanel.jsx
index 7e2e0572b..b3dc7b79e 100644
--- a/ui/src/pages/execution/RightPanel.jsx
+++ b/ui/src/pages/execution/RightPanel.jsx
@@ -8,6 +8,10 @@ import TaskLogs from "./TaskLogs";
import { makeStyles } from "@material-ui/styles";
import _ from "lodash";
import TaskPollData from "./TaskPollData";
+import {
+ pendingTaskSelection,
+ taskWithLatestIteration,
+} from "../../utils/helpers";
const useStyles = makeStyles({
banner: {
@@ -24,7 +28,13 @@ const useStyles = makeStyles({
},
});
-export default function RightPanel({ selectedTask, dag, onTaskChange }) {
+export default function RightPanel({
+ selectedTask,
+ dag,
+ execution,
+ onTaskChange,
+ selectedNode,
+}) {
const [tabIndex, setTabIndex] = useState("summary");
const classes = useStyles();
@@ -33,10 +43,11 @@ export default function RightPanel({ selectedTask, dag, onTaskChange }) {
setTabIndex("summary"); // Reset to Status Tab on ref change
}, [selectedTask]);
- const taskResult = useMemo(
- () => dag && dag.resolveTaskResult(selectedTask),
- [dag, selectedTask]
- );
+ const taskResult =
+ selectedNode?.data?.task?.executionData?.status === "PENDING"
+ ? pendingTaskSelection(selectedNode?.data?.task)
+ : taskWithLatestIteration(execution?.tasks, selectedTask?.ref);
+
const dfOptions = useMemo(
() => dag && dag.getSiblings(selectedTask),
[dag, selectedTask]
diff --git a/ui/src/pages/execution/TaskDetails.jsx b/ui/src/pages/execution/TaskDetails.jsx
index 6948f645f..8cbfa192f 100644
--- a/ui/src/pages/execution/TaskDetails.jsx
+++ b/ui/src/pages/execution/TaskDetails.jsx
@@ -1,9 +1,13 @@
-import React, { useState } from "react";
+import { useState } from "react";
import { Tabs, Tab, Paper } from "../../components";
import Timeline from "./Timeline";
import TaskList from "./TaskList";
-import WorkflowGraph from "../../components/diagram/WorkflowGraph";
import { makeStyles } from "@material-ui/styles";
+import { WorkflowVisualizer } from "orkes-workflow-visualizer";
+import {
+ pendingTaskSelection,
+ taskWithLatestIteration,
+} from "../../utils/helpers";
const useStyles = makeStyles({
taskWrapper: {
@@ -18,6 +22,7 @@ export default function TaskDetails({
dag,
selectedTask,
setSelectedTask,
+ setSelectedNode,
}) {
const [tabIndex, setTabIndex] = useState(0);
const classes = useStyles();
@@ -32,11 +37,23 @@ export default function TaskDetails({
{tabIndex === 0 && (
- {
+ const selectedTaskRefName =
+ data?.data?.task?.executionData?.status === "PENDING"
+ ? pendingTaskSelection(data?.data?.task)?.workflowTask
+ ?.taskReferenceName
+ : taskWithLatestIteration(execution?.tasks, data?.id)
+ ?.referenceTaskName;
+ setSelectedNode(data);
+ setSelectedTask({ ref: selectedTaskRefName });
+ }}
/>
)}
{tabIndex === 1 && (
diff --git a/ui/src/utils/helpers.js b/ui/src/utils/helpers.js
index 11bf45e94..407732f63 100644
--- a/ui/src/utils/helpers.js
+++ b/ui/src/utils/helpers.js
@@ -1,6 +1,7 @@
import { format, formatDuration, intervalToDuration } from "date-fns";
import _ from "lodash";
-import packageJson from '../../package.json';
+import packageJson from "../../package.json";
+import _nth from "lodash/nth";
export function timestampRenderer(date) {
if (_.isNil(date)) return null;
@@ -91,9 +92,45 @@ export function isEmptyIterable(iterable) {
}
export function getBasename() {
- let basename = '/';
- try{
+ let basename = "/";
+ try {
basename = new URL(packageJson.homepage).pathname;
- } catch(e) {}
- return _.isEmpty(basename) ? '/' : basename;
+ } catch (e) {}
+ return _.isEmpty(basename) ? "/" : basename;
}
+
+export const taskWithLatestIteration = (tasksList, taskReferenceName) => {
+ const filteredTasks = tasksList?.filter(
+ (task) =>
+ task?.workflowTask?.taskReferenceName === taskReferenceName ||
+ task?.referenceTaskName === taskReferenceName
+ );
+
+ if (filteredTasks && filteredTasks.length === 1) {
+ // task without any retry/iteration
+ return _nth(filteredTasks, 0);
+ } else if (filteredTasks && filteredTasks.length > 1) {
+ const result = filteredTasks.reduce(
+ (acc, task, idx) => {
+ if (task?.seq && acc?.seqNumber < Number(task.seq)) {
+ return { seqNumber: Number(task.seq), idx };
+ }
+ return acc;
+ },
+ { seqNumber: 0, idx: -1 }
+ );
+
+ if (result?.idx > -1) {
+ return _nth(filteredTasks, result.idx);
+ }
+ }
+ return undefined;
+};
+
+export const pendingTaskSelection = (task) => {
+ const result = {
+ ...task?.executionData,
+ workflowTask: task,
+ };
+ return result;
+};