import React, { useState, } from "react"; import { makeStyles, createStyles } from "@mui/styles"; import { toast } from "react-toastify" import { Tooltip, Chip, Typography, IconButton, Avatar, AvatarGroup, } from "@mui/material" import { ErrorOutline as ErrorOutlineIcon, } from "@mui/icons-material" import { green, yellow, red, grey, } from "../views/AngularWorkflow.jsx" import WorkflowTemplatePopup2 from "../components/WorkflowTemplatePopup2.jsx" import { validateJson, GetIconInfo } from "../views/Workflows.jsx"; import theme from "../theme.jsx"; const itemHeight = 24 export const getParentNodes = (workflow, action, count) => { if (count === undefined) { count = 0 } // 50 levels of parent nodes if (count > 50) { return [] } if (action === undefined || action === null) { return [] } if (workflow.actions === undefined || workflow.actions === null) { workflow.actions = [] } if (workflow.triggers === undefined || workflow.triggers === null) { workflow.triggers = [] } if (workflow.branches === undefined || workflow.branches === null) { workflow.branches = [] } var allkeys = [action.id]; var handled = []; var results = []; // maxiter = max amount of parent nodes to loop // also handles breaks if there are issues var iterations = 0; var maxiter = 10; while (true) { for (let parentkey in allkeys) { if (allkeys[parentkey] === undefined) { continue } var currentnode = workflow.actions.find((element) => element.id === allkeys[parentkey]) if (currentnode === undefined) { currentnode = workflow.triggers.find((element) => element.id === allkeys[parentkey]) if (currentnode === undefined) { //console.log("Could not find parent node for: ", allkeys[parentkey]) continue } } if (handled.includes(currentnode.id)) { continue } else { handled.push(currentnode.id); results.push(currentnode); } // Get the name / label here too? if (currentnode.length === 0) { continue; } // FIXME: recursion var incomingEdges = [] for (var branchkey in workflow.branches) { const branch = workflow.branches[branchkey] if (branch.destination_id !== currentnode.id) { continue } // FIXME: Go up in the levels // This somehow creates infinite recursion for now, so // we are skipping it. // This function is also not used for cytoscape recursion, // so it doesn't matter much (yet) /* const parents = getParentNodes(workflow, { id: branch.source_id, }, count+1) if (parents.length > 0) { incomingEdges = incomingEdges.concat(parents) } */ incomingEdges.push(branch) } for (let i = 0; i < incomingEdges.length; i++) { var tmp = incomingEdges[i]; if (tmp.decorator === true) { continue } if (!allkeys.includes(tmp.source_id)) { allkeys.push(tmp.source_id) } } } if (results.length === allkeys.length || iterations === maxiter) { break } iterations += 1 } // Remove on the end as we don't want to remove everything results = results.filter((data) => data.id !== action.id) results = results.filter((data) => data.type === "ACTION" || data.app_name === "Shuffle Workflow" || data.app_name === "User Input" || data.app_name === "shuffle-subflow") results.push({ label: "Execution Argument", type: "INTERNAL" }) return results } const WorkflowValidationTimeline = (props) => { const { globalUrl, userdata, workflow, originalWorkflow, apps, getParents, execution, showHoverColor, } = props const [hovering, setHovering] = useState(false) const [decidedColor, setDecidedColor] = useState(grey) const [isClicked, setIsClicked] = useState(false) const showMiddle = false if (workflow === undefined || workflow === null) { return null } if (workflow.validation === undefined || workflow.validation === null) { return null } if (workflow.actions === undefined || workflow.actions === null) { workflow.actions = [] } if (workflow.triggers === undefined || workflow.triggers === null) { workflow.triggers = [] } if (workflow.branches === undefined || workflow.branches === null) { workflow.branches = [] } var results = [] if (execution !== undefined) { results = execution.results } // 1. Find startnode // 2. Map childnodes from it var startnodeId = workflow.start if (execution !== undefined && execution !== null) { startnodeId = execution.start } // Find parent of startnodeId and if it's a webhook var relevantactions = [] for (var key in workflow.branches) { const branch = workflow.branches[key] if (branch.destination_id !== startnodeId) { continue } for (var triggerkey in workflow.triggers) { const trigger = workflow.triggers[triggerkey] if (trigger.trigger_type !== "WEBHOOK") { continue } if (trigger.id === branch.source_id) { trigger.order = -1 relevantactions.push(trigger) break } } } for (var key in workflow.actions) { const action = workflow.actions[key] if (action.id === startnodeId) { action.order = 0 relevantactions.push(action) continue } var parents = [] if (getParents !== undefined) { parents = getParents(action) } else { parents = getParentNodes(workflow, action) } if (action.app_name === "Integration Framework" || action.app_name === "Integration") { for (var paramkey in action.parameters) { const param = action.parameters[paramkey] if (param.name === "app_name") { action.app_name = param.value.charAt(0).toUpperCase() + param.value.slice(1) } } } if (parents !== undefined && parents !== null && parents.length > 0) { const parentfound = parents.find((element) => element.id === startnodeId) if (parentfound !== undefined) { // FIXME: add order here based on how many steps away from the startnode // This just has the parent count action.order = parents.length relevantactions.push(action) } } } if (getParents === undefined) { var newactions = [] for (var key in workflow.triggers) { const trigger = workflow.triggers[key] if (trigger.trigger_type !== "SUBFLOW" && trigger.trigger_type !== "USERINPUT") { continue } for (var branchkey in workflow.branches) { const branch = workflow.branches[branchkey] // Checking for OUTBOUND branches from it. // This will mean it's NOT the last node and is easy to visualize if (branch.source_id !== trigger.id) { continue } trigger.order = 2 } // Just in case (: if (workflow.actions.find((element) => element.id === trigger.id) === undefined) { newactions.push(trigger) //workflow.actions.push(trigger) } } relevantactions.push(...newactions) } if (relevantactions.length <= 1) { return null } // Sort according to how many parents a node has. MAY be wrong~ relevantactions.sort((a, b) => { if (a.order === undefined) { return 1 } if (b.order === undefined) { return -1 } return a.order - b.order }) // FIXME: Add other relevant items as well from subflows (?) var nodecolor = grey var branchcolor = grey var skipped = false var previousTools = false var scheduleNotStarted = false if (workflow.validation !== undefined && workflow.validation !== null && workflow.validation.validation_ran === false) { //console.log("Validation didn't run or get set for workflow. Why?") return null } if (workflow.validation !== undefined && workflow.validation !== null && workflow.validation.errors !== undefined && workflow.validation.errors !== null && workflow.validation.errors.length > 0) { var newErrors = [] for (var key in workflow.validation.errors) { const error = workflow.validation.errors[key] if (error.type === "SCHEDULE") { scheduleNotStarted = true continue } newErrors.push(error) } workflow.validation.errors = newErrors } // Use this variable to control visualization //const showMiddle = false // border: workflow.validation.valid ? `2px solid ${green}` : "1px solid rgba(255,255,255,0.4)", var middleError = "" var startBranchColor = "" var middleBranchColor = "" const showHoverForClick = showHoverColor === true ? true : false return (