From 8112bd16c507bf40728a0a92d096d36a2a04b679 Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 5 Apr 2021 23:27:45 +0200 Subject: [PATCH] Moved Action Parsing out of AngularWorkflow. Should be reusable in App explorer UI --- backend/go-app/main.go | 5 - frontend/src/components/ConfigureWorkflow.jsx | 64 +- frontend/src/components/ParsedAction.jsx | 1426 +++++++++++++++ frontend/src/theme.js | 57 + frontend/src/views/AngularWorkflow.jsx | 1580 ++--------------- 5 files changed, 1615 insertions(+), 1517 deletions(-) create mode 100644 frontend/src/components/ParsedAction.jsx create mode 100644 frontend/src/theme.js diff --git a/backend/go-app/main.go b/backend/go-app/main.go index e775ebf5..f86a557c 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -6085,11 +6085,6 @@ func initHandlers() { panic(fmt.Sprintf("DBclient error during init: %s", err)) } - //dbclient, err := shuffle.GetDatastoreClient(ctx, gceProject) - //if err != nil { - // panic(fmt.Sprintf("Error setting datastore connector: %s", err)) - //} - _ = shuffle.RunInit(*dbclient, storage.Client{}, gceProject, "onprem", true) log.Printf("Finished Shuffle database init") diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index b311dc95..95457675 100644 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -95,7 +95,7 @@ const Workflow = (props) => { } if (action.errors !== undefined && action.errors !== null && action.errors.length > 0) { - console.log("Node has errors!: ", action.errors) + //console.log("Node has errors!: ", action.errors) } if (newaction.must_authenticate) { @@ -103,19 +103,23 @@ const Workflow = (props) => { for (var key in appAuthentication) { const auth = appAuthentication[key] if (auth.app.name === app.name && auth.active) { - console.log("Found auth: ", auth) + //console.log("Found auth: ", auth) authenticationOptions.push(auth) newaction.authenticationId = auth.id break } } - console.log("APPAUTH: ", app.authentication, action) if (newaction.authenticationId === null || newaction.authenticationId === undefined || newaction.authenticationId.length === "") { - console.log("FAILED to authentication node!") - newactions.push(newaction) + //console.log("FAILED to authenticate node!") + + if (newactions.find(tmpaction => tmpaction.app_id === newaction.app_id && tmpaction.app_name === newaction.app_name) !== undefined) { + console.log("Action already found.") + } else { + newactions.push(newaction) + } } else { - console.log("Skipping node as it's already authenticated.") + //console.log("Skipping node as it's already authenticated.") newaction.authentication = authenticationOptions workflow.actions[key] = newaction } @@ -157,16 +161,12 @@ const Workflow = (props) => { setConfigureWorkflowModalOpen(false) } - console.log("VARIABLES: ", requiredVariables) - console.log("ACTIONS: ", newactions) setRequiredTriggers(requiredTriggers) setRequiredVariables(requiredVariables) setRequiredActions(newactions) } - console.log("AUTH: ", appAuthentication) if (appAuthentication.length !== previousAuth.length) { - console.log("APP AUTH CHANGED!") var newactions = [] for (var actionkey in requiredActions) { var newaction = requiredActions[actionkey] @@ -175,7 +175,6 @@ const Workflow = (props) => { for (var key in appAuthentication) { const auth = appAuthentication[key] if (auth.app.name === app.name && auth.active) { - console.log("FOUND AUTH FOR: ", auth.app.name) newaction.auth_done = true break } @@ -193,10 +192,7 @@ const Workflow = (props) => { const TriggerSection = (props) => { const {trigger} = props - console.log(trigger) - return ( -
@@ -275,7 +271,6 @@ const Workflow = (props) => { /> */} -
) } @@ -343,16 +338,13 @@ const Workflow = (props) => { {action.must_authenticate ? action.auth_done ? -
- -
+ : selectedAction.app_name === action.app_name ? @@ -377,8 +369,6 @@ const Workflow = (props) => { ) } - console.log(requiredActions) - return (
{workflow.name} @@ -401,11 +391,13 @@ const Workflow = (props) => { {requiredVariables.length > 0 ? Variables - {requiredVariables.map((data, index) => { - return ( - - ) - })} + + {requiredVariables.map((data, index) => { + return ( + + ) + })} + : null} @@ -413,21 +405,25 @@ const Workflow = (props) => { {requiredTriggers.length > 0 ? Triggers - {requiredTriggers.map((data, index) => { - return ( - - ) - })} + + {requiredTriggers.map((data, index) => { + return ( + + ) + })} + : null }
+ {/* + */} +
+
+ } else { + // FIXME - this is a shitty solution that needs re-renders all the time + datafield = + + } + } + + // Shows nested list of nodes > their JSON lists + const ActionlistWrapper = (props) => { + + const handleMenuClose = () => { + setShowAutocomplete(false) + + if (!selectedActionParameters[count].value[selectedActionParameters[count].value.length-1] === "$") { + setShowDropdown(false) + } + + setUpdate(Math.random()) + setMenuPosition(null) + } + + const handleItemClick = (values) => { + if (values === undefined || values === null || values.length === 0) { + return + } + + var toComplete = selectedActionParameters[count].value.trim().endsWith("$") ? values[0].autocomplete : "$"+values[0].autocomplete + for (var key in values) { + if (key == 0 || values[key].autocomplete.length === 0) { + continue + } + + toComplete += values[key].autocomplete + } + + // Handles the fields under OpenAPI body to be parsed. + if (data.name.startsWith("${") && data.name.endsWith("}")) { + console.log("INSIDE VALUE REPLACE: ", data.name, toComplete) + // PARAM FIX - Gonna use the ID field, even though it's a hack + const paramcheck = selectedAction.parameters.find(param => param.name === "body") + if (paramcheck !== undefined) { + if (paramcheck["value_replace"] === undefined || paramcheck["value_replace"] === null) { + paramcheck["value_replace"] = [{ + "key": data.name, + "value": toComplete, + }] + + } else { + const subparamindex = paramcheck["value_replace"].findIndex(param => param.key === data.name) + if (subparamindex === -1) { + paramcheck["value_replace"].push({ + "key": data.name, + "value": toComplete, + }) + } else { + paramcheck["value_replace"][subparamindex]["value"] += toComplete + } + } + + selectedActionParameters[count]["value_replace"] = paramcheck + selectedAction.parameters[count]["value_replace"] = paramcheck + setSelectedAction(selectedAction) + setUpdate(Math.random()) + + setShowDropdown(false) + setMenuPosition(null) + return + } + } + + selectedActionParameters[count].value += toComplete + selectedAction.parameters[count].value = selectedActionParameters[count].value + setSelectedAction(selectedAction) + setUpdate(Math.random()) + + setShowDropdown(false) + setMenuPosition(null) + } + + const iconStyle = { + marginRight: 15, + } + + return ( + { + handleMenuClose() + }} + open={!!menuPosition} + style={{ + border: `2px solid #f85a3e`, + color: "white", + marginTop: 2, + }} + > + {actionlist.map(innerdata => { + const icon = innerdata.type === "action" ? : innerdata.type === "workflow_variable" || innerdata.type === "execution_variable" ? : + + const handleExecArgumentHover = (inside) => { + var exec_text_field = document.getElementById("execution_argument_input_field") + if (exec_text_field !== null) { + if (inside) { + exec_text_field.style.border = "2px solid #f85a3e" + } else { + exec_text_field.style.border = "" + } + } + + // Also doing arguments + if (workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers.length > 0) { + for (var key in workflow.triggers) { + const item = workflow.triggers[key] + + var node = cy.getElementById(item.id) + if (node.length > 0) { + if (inside) { + node.addClass('shuffle-hover-highlight') + } else { + node.removeClass('shuffle-hover-highlight') + } + } + + } + } + } + + const handleActionHover = (inside, actionId) => { + var node = cy.getElementById(actionId) + if (node.length > 0) { + if (inside) { + node.addClass('shuffle-hover-highlight') + } else { + node.removeClass('shuffle-hover-highlight') + } + } + } + + const handleMouseover = () => { + if (innerdata.type === "Execution Argument") { + handleExecArgumentHover(true) + } else if (innerdata.type === "action") { + handleActionHover(true, innerdata.id) + } + } + + const handleMouseOut = () => { + if (innerdata.type === "Execution Argument") { + handleExecArgumentHover(false) + } else if (innerdata.type === "action") { + handleActionHover(false, innerdata.id) + } + } + + var parsedPaths = [] + if (typeof(innerdata.example) === "object") { + parsedPaths = GetParsedPaths(innerdata.example, "") + } + + return ( + parsedPaths.length > 0 ? + + {icon} {innerdata.name} + + } + parentMenuOpen={!!menuPosition} + style={{backgroundColor: theme.palette.inputColor, color: "white", minWidth: 250,}} + onClick={() => { + handleItemClick([innerdata]) + }} + > + {parsedPaths.map((pathdata, index) => { + // FIXME: Should be recursive in here + const icon = pathdata.type === "value" ? : pathdata.type === "list" ? : + return ( + {}} + onClick={() => { + handleItemClick([innerdata, pathdata]) + }} + > + +
+ {icon} {pathdata.name} +
+
+
+ ) + + })} +
+ : + handleMouseover()} onMouseOut={() => {handleMouseOut()}} + onClick={() => { + handleItemClick([innerdata]) + }} + > + +
+ {icon} {innerdata.name} +
+
+
+ + ) + })} +
+ ) + } + + var itemColor = "#f85a3e" + if (!data.required) { + itemColor = "#ffeb3b" + } + + var tmpitem = data.name.valueOf() + if (data.name.startsWith("${") && data.name.endsWith("}")) { + tmpitem = tmpitem.slice(2, data.name.length-1) + } + + tmpitem = tmpitem.charAt(0).toUpperCase()+tmpitem.substring(1) + tmpitem = tmpitem.replaceAll("_", " ") + const description = data.description === undefined ? "" : data.description + + return ( +
+
+ + + {data.configuration === true ? + + { + setAuthenticationModalOpen(true) + }}/> + + : +
+ } +
+ + {tmpitem} + +
+ + {/*selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0 ? null : +
+ +
{ + e.preventDefault() + changeActionParameterVariant("STATIC_VALUE", count) + }}> + +
+
+  |  + +
{ + e.preventDefault() + changeActionParameterVariant("ACTION_RESULT", count) + }}> + +
+
+  |  + +
{ + e.preventDefault() + changeActionParameterVariant("WORKFLOW_VARIABLE", count) + }}> + +
+
+
+ */} + {selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0 && selectedActionParameters[count].required === true && selectedActionParameters[count].unique_toggled !== undefined ? null : +
+ +
{}}> + { + //console.log("CHECKED!: ", selectedActionParameters[count]) + selectedActionParameters[count].unique_toggled = !selectedActionParameters[count].unique_toggled + selectedAction.parameters[count].unique_toggled = selectedActionParameters[count].unique_toggled + setSelectedActionParameters(selectedActionParameters) + setSelectedAction(selectedAction) + setUpdate(Math.random()) + }} + name="requires_unique" + /> +
+
+
+ } +
+ {datafield} + {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && jsonList.length > 0 ? + + Autocomplete + + + : null} + {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && jsonList.length === 0 ? + + : null} + + +
+ )})} +
+ ) + } + return null + } + return ( +
+
+
+

{selectedAction.app_name.replaceAll("_", " ")}

+
+ { + console.log("FIND EXAMPLE RESULTS FOR ", selectedAction) + if (workflowExecutions.length > 0) { + // Look for the ID + const found = false + for (var key in workflowExecutions) { + if (workflowExecutions[key].results === undefined || workflowExecutions[key].results === null) { + continue + } + + var foundResult = workflowExecutions[key].results.find(result => result.action.id === selectedAction.id) + if (foundResult === undefined || foundResult === null) { + continue + } + + setSelectedResult(foundResult) + setCodeModalOpen(true) + break + } + } + }}> + + + + + + What are actions? + {selectedAction.errors !== null && selectedAction.errors.length > 0 ? +
+ Errors: {selectedAction.errors.join("\n")} +
+ : null + } +
+
+
+
+ {selectedAction.id === workflow.start ? null : + + + + } + {selectedApp.versions !== null && selectedApp.versions !== undefined && selectedApp.versions.length > 0 ? + + : null } +
+
+ + + Name + + + {selectedApp.name !== undefined && selectedAction.authentication !== undefined && selectedAction.authentication.length === 0 && requiresAuthentication ? +
+ + + + + +
+ : null} + {selectedAction.authentication !== undefined && selectedAction.authentication.length > 0 ? +
+ Authentication +
+ + + {/* + + + curaction.authentication = authenticationOptions + if (curaction.selectedAuthentication === null || curaction.selectedAuthentication === undefined || curaction.selectedAuthentication.length === "") + */} + + { + setAuthenticationModalOpen(true) + }}> + + + +
+
+ : null} + {showEnvironment ? +
+ + Environment + + +
+ : null} + {workflow.execution_variables !== undefined && workflow.execution_variables !== null && workflow.execution_variables.length > 0 ? +
+ Set execution variable (optional) + +
+ : null} + +
+
+ Actions +
+ + {selectedAction.description !== undefined && selectedAction.description.length > 0 ? +
+ {selectedAction.description} +
: null} +
+ +
+
+
+ ) + } + + +export default ParsedAction; diff --git a/frontend/src/theme.js b/frontend/src/theme.js new file mode 100644 index 00000000..13b47d64 --- /dev/null +++ b/frontend/src/theme.js @@ -0,0 +1,57 @@ +import React from 'react'; +import { createMuiTheme } from '@material-ui/core/styles'; + +const theme = createMuiTheme({ + palette: { + primary: { + main: "#f85a3e" + }, + secondary: { + main: '#e8eaf6', + }, + text: { + secondary: "rgba(255,255,255,0.7)", + }, + surfaceColor: "#27292d", + inputColor: "#383B40", + borderRadius: 5, + textFieldStyle: { + backgroundColor: "#383B40", + borderRadius: 5, + }, + innerTextfieldStyle: { + color: "white", + minHeight: 50, + marginLeft: "5px", + maxWidth: "95%", + fontSize: "1em", + borderRadius: 5, + } + }, + typography: { + "fontFamily": `"Roboto", "Helvetica", "Arial", sans-serif`, + useNextVariants: true, + h1: { + fontSize: 40, + }, + h4: { + fontSize: 30, + fontWeight: 500, + }, + h6: { + fontSize: 22, + }, + body1: { + fontSize: 18, + }, + }, + overrides: { + MuiMenu: { + list: { + backgroundColor: "#383B40", + }, + }, + }, +}); + +export default theme; diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index b32fb4b2..ed8e2295 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -27,6 +27,7 @@ import { useAlert } from "react-alert"; import { validateJson } from "./Workflows.jsx"; import { GetParsedPaths } from "./Apps.jsx"; import ConfigureWorkflow from '../components/ConfigureWorkflow.jsx'; +import ParsedAction from '../components/ParsedAction.jsx'; const surfaceColor = "#27292D" const inputColor = "#383B40" @@ -87,7 +88,6 @@ const AngularWorkflow = (props) => { const { globalUrl, isLoggedIn, isLoaded, userdata } = props; const referenceUrl = globalUrl+"/api/v1/hooks/" const alert = useAlert() - const borderRadius = 5 const theme = useTheme(); const green = "#86c142" const yellow = "#FECC00" @@ -128,7 +128,7 @@ const AngularWorkflow = (props) => { const [rightSideBarOpen, setRightSideBarOpen] = React.useState(false) const [showSkippedActions, setShowSkippedActions] = React.useState(false) const [lastExecution, setLastExecution] = React.useState("") - const [configureWorkflowModalOpen, setConfigureWorkflowModalOpen] = React.useState(true) + const [configureWorkflowModalOpen, setConfigureWorkflowModalOpen] = React.useState(false) const [curpath, setCurpath] = useState(typeof window === 'undefined' || window.location === undefined ? "" : window.location.pathname) // 0 = normal, 1 = just done, 2 = normal @@ -1151,8 +1151,11 @@ const AngularWorkflow = (props) => { //console.log(responseJson) // Add error checks - if (!responseJson.public && (!responseJson.previously_saved || (!responseJson.is_valid || (responseJson.errors !== undefined || responseJson.errors !== null || responseJson.errors !== responseJson.errors.length > 0)))) { - setConfigureWorkflowModalOpen(true) + console.log(responseJson) + if (!responseJson.public) { + if ((!responseJson.previously_saved || (!responseJson.is_valid || (responseJson.errors !== undefined || responseJson.errors !== null || responseJson.errors !== responseJson.errors.length > 0)))) { + setConfigureWorkflowModalOpen(true) + } } }) .catch(error => { @@ -2127,7 +2130,7 @@ const AngularWorkflow = (props) => { const paperAppStyle = { - borderRadius: borderRadius, + borderRadius: theme.palette.borderRadius, minHeight: 100, maxHeight: 100, minWidth: "100%", @@ -2140,7 +2143,7 @@ const AngularWorkflow = (props) => { } const paperVariableStyle = { - borderRadius: borderRadius, + borderRadius: theme.palette.borderRadius, minHeight: 50, maxHeight: 50, minWidth: "100%", @@ -2629,6 +2632,7 @@ const AngularWorkflow = (props) => { const handleAppDrag = (e, app) => { const cycontainer = cy.container() + // Chrome lol //if (e.srcElement !== undefined && e.srcElement.localName === "canvas") { if (e.pageX > cycontainer.offsetLeft && e.pageX < cycontainer.offsetLeft+cycontainer.offsetWidth && e.pageY > cycontainer.offsetTop && e.pageY < cycontainer.offsetTop+cycontainer.offsetHeight) { @@ -2641,6 +2645,10 @@ const AngularWorkflow = (props) => { currentnode[0].renderedPosition("x", e.pageX-cycontainer.offsetLeft) currentnode[0].renderedPosition("y", e.pageY-cycontainer.offsetTop) } else{ + if (workflow.public) { + return + } + if (app.actions === undefined || app.actions === null || app.actions.length === 0) { alert.error("App "+app.name+" currently has no actions to perform. Please go to https://shuffler.io/apps to edit it.") return @@ -2669,13 +2677,14 @@ const AngularWorkflow = (props) => { setUpdate(Math.random()) */ + console.log("ENVS: ", environments) const newAppData = { app_name: app.name, app_version: app.app_version, app_id: app.id, sharing: app.sharing, private_id: app.private_id, - environment: environments === null ? "cloud" : environments[defaultEnvironmentIndex].Name, + environment: environments === null || environments === [] ? "cloud" : environments[defaultEnvironmentIndex].Name, errors: [], id_: newNodeId, _id_: newNodeId, @@ -2865,7 +2874,7 @@ const AngularWorkflow = (props) => { {setHover(true)}} onMouseOut={() => {setHover(false)}}> - {newAppname} + {newAppname} @@ -2900,7 +2909,7 @@ const AngularWorkflow = (props) => {
{ // Dropdown -> static, action, local env, global env // VALUE (JSON) // {data.name}, {data.description}, {data.required}, {data.schema.type} - const AppActionArguments = (props) => { - const [selectedActionParameters, setSelectedActionParameters] = React.useState([]) - const [selectedVariableParameter, setSelectedVariableParameter] = React.useState("") - const [actionlist, setActionlist] = React.useState([]) - const [jsonList, setJsonList] = React.useState([]) - const [showDropdown, setShowDropdown] = React.useState(false) - const [showDropdownNumber, setShowDropdownNumber] = React.useState(0) - const [showAutocomplete, setShowAutocomplete] = React.useState(false) - const [menuPosition, setMenuPosition] = useState(null) - - useEffect(() => { - if (selectedActionParameters !== null && selectedActionParameters.length === 0) { - if (selectedAction.parameters !== null && selectedAction.parameters.length > 0) { - setSelectedActionParameters(selectedAction.parameters) - } - } - - if ((selectedVariableParameter === null || selectedVariableParameter === undefined) && (workflow.workflow_variables !== null && workflow.workflow_variables.length > 0)) { - // FIXME - this is the bad thing - setSelectedVariableParameter(workflow.workflow_variables[0].name) - } - - if (actionlist.length === 0) { - // FIXME: Have previous execution values in here - actionlist.push({"type": "Execution Argument", "name": "Execution Argument", "value": "$exec", "highlight": "exec", "autocomplete": "exec", "example": "hello"}) - if (workflow.workflow_variables !== null && workflow.workflow_variables !== undefined && workflow.workflow_variables.length > 0) { - for (var key in workflow.workflow_variables) { - const item = workflow.workflow_variables[key] - actionlist.push({"type": "workflow_variable", "name": item.name, "value": item.value, "id": item.id, "autocomplete": `${item.name.split(" ").join("_")}`, "example": item.value}) - } - } - - // FIXME: Add values from previous executions if they exist - if (workflow.execution_variables !== null && workflow.execution_variables !== undefined && workflow.execution_variables.length > 0) { - for (var key in workflow.execution_variables) { - const item = workflow.execution_variables[key] - actionlist.push({"type": "execution_variable", "name": item.name, "value": item.value, "id": item.id, "autocomplete": `${item.name.split(" ").join("_")}`, "example": ""}) - } - } - - // Loops parent nodes' old results to fix autocomplete - var parents = getParents(selectedAction) - if (parents.length > 1) { - for (var key in parents) { - const item = parents[key] - if (item.label === "Execution Argument") { - continue - } - - var exampledata = item.example === undefined ? "" : item.example - // Find previous execution and their variables - //exampledata === "" && - if (workflowExecutions.length > 0) { - // Look for the ID - const found = false - for (var key in workflowExecutions) { - if (workflowExecutions[key].results === undefined || workflowExecutions[key].results === null) { - continue - } - - var foundResult = workflowExecutions[key].results.find(result => result.action.id === item.id) - if (foundResult === undefined) { - continue - } - - foundResult.result = foundResult.result.trim() - foundResult.result = foundResult.result.split(" None").join(" \"None\"") - foundResult.result = foundResult.result.split(" False").join(" false") - foundResult.result = foundResult.result.split(" True").join(" true") - - var jsonvalid = true - try { - const tmp = String(JSON.parse(foundResult.result)) - if (!foundResult.result.includes("{") && !foundResult.result.includes("[")) { - jsonvalid = false - } - } catch (e) { - try { - foundResult.result = foundResult.result.split("\'").join("\"") - const tmp = String(JSON.parse(foundResult.result)) - if (!foundResult.result.includes("{") && !foundResult.result.includes("[")) { - jsonvalid = false - } - } catch (e) { - jsonvalid = false - } - } - - // Finds the FIRST json only - if (jsonvalid) { - exampledata = JSON.parse(foundResult.result) - break - } - //else { - // console.log("Invalid JSON: ", foundResult.result) - //} - } - } - - // 1. Take - const actionvalue = {"type": "action", "id": item.id, "name": item.label, "autocomplete": `${item.label.split(" ").join("_")}`, "example": exampledata} - actionlist.push(actionvalue) - } - } - - setActionlist(actionlist) - } - }) - - const changeActionParameter = (event, count, data) => { - if (data.name.startsWith("${") && data.name.endsWith("}")) { - // PARAM FIX - Gonna use the ID field, even though it's a hack - const paramcheck = selectedAction.parameters.find(param => param.name === "body") - if (paramcheck !== undefined) { - if (paramcheck["value_replace"] === undefined || paramcheck["value_replace"] === null) { - paramcheck["value_replace"] = [{ - "key": data.name, - "value": event.target.value, - }] - - } else { - const subparamindex = paramcheck["value_replace"].findIndex(param => param.key === data.name) - if (subparamindex === -1) { - paramcheck["value_replace"].push({ - "key": data.name, - "value": event.target.value, - }) - } else { - paramcheck["value_replace"][subparamindex]["value"] = event.target.value - } - } - //console.log("PARAM: ", paramcheck) - - selectedActionParameters[count]["value_replace"] = paramcheck - selectedAction.parameters[count]["value_replace"] = paramcheck - setSelectedAction(selectedAction) - //setUpdate(Math.random()) - return - } - } - - if (event.target.value[event.target.value.length-1] === "$") { - if (!showDropdown) { - setShowAutocomplete(false) - setShowDropdown(true) - setShowDropdownNumber(count) - } - } else { - if (showDropdown) { - setShowDropdown(false) - } - } - - // bad detection mechanism probably - if (event.target.value[event.target.value.length-1] === "." && actionlist.length > 0) { - console.log("GET THE LAST ARGUMENT FOR NODE!") - // THIS IS AN EXAMPLE OF SHOWING IT - /* - const inputdata = {"data": "1.2.3.4", "dataType": "4.5.6.6"} - setJsonList(GetParsedPaths(inputdata, "")) - if (!showDropdown) { - setShowAutocomplete(false) - setShowDropdown(true) - setShowDropdownNumber(count) - } - console.log(jsonList) - */ - - // Search for the item backwards - // 1. Reverse search backwards from . -> $ - // 2. Search the actionlist for the item - // 3. Find the data for the specific item - - var curstring = "" - var record = false - for (var key in selectedActionParameters[count].value) { - const item = selectedActionParameters[count].value[key] - if (record) { - curstring += item - } - - if (item === "$") { - record = true - curstring = "" - } - } - - //console.log("CURSTRING: ", curstring) - if (curstring.length > 0 && actionlist !== null) { - // Search back in the action list - curstring = curstring.split(" ").join("_").toLowerCase() - var actionItem = actionlist.find(data => data.autocomplete.split(" ").join("_").toLowerCase() === curstring) - if (actionItem !== undefined) { - console.log("Found item: ", actionItem) - - //actionItem.example = actionItem.example.trim() - //actionItem.example = actionItem.example.split(" None").join(" \"None\"") - //actionItem.example = actionItem.example.split("\'").join("\"") - - var jsonvalid = true - try { - const tmp = String(JSON.parse(actionItem.example)) - if (!actionItem.example.includes("{") && !actionItem.example.includes("[")) { - jsonvalid = false - } - } catch (e) { - jsonvalid = false - } - - if (jsonvalid) { - setJsonList(GetParsedPaths(JSON.parse(actionItem.example), "")) - - if (!showDropdown) { - setShowAutocomplete(false) - setShowDropdown(true) - setShowDropdownNumber(count) - } - } - } - } - } else { - if (jsonList.length > 0) { - setJsonList([]) - } - } - - selectedActionParameters[count].value = event.target.value - selectedAction.parameters[count].value = event.target.value - setSelectedAction(selectedAction) - //setUpdate(Math.random()) - //setUpdate(event.target.value) - } - - const changeActionParameterVariable = (fieldvalue, count) => { - //console.log("CALLED THIS ONE WITH VALUE!", fieldvalue) - //if (selectedVariableParameter === fieldvalue) { - // return - //} - - setSelectedVariableParameter(fieldvalue) - - selectedActionParameters[count].action_field = fieldvalue - selectedAction.parameters = selectedActionParameters - - setSelectedApp(selectedApp) - setSelectedAction(selectedAction) - setUpdate(fieldvalue) - } - - // Sets ACTION_RESULT things - const changeActionParameterActionResult = (fieldvalue, count) => { - //cy.nodes().forEach(function( ele ) { - // if (ele.data()["label"] === fieldvalue) { - // selectedActionParameters[count].action_field = ele.id() - // return - // } - //}); - - selectedActionParameters[count].action_field = fieldvalue - selectedAction.parameters = selectedActionParameters - - // FIXME - check if startnode - - // Set value - setSelectedApp(selectedApp) - - setSelectedAction(selectedAction) - setUpdate(Math.random()) - } - - const changeActionParameterVariant = (data, count) => { - selectedActionParameters[count].variant = data - selectedActionParameters[count].value = "" - - if (data === "ACTION_RESULT") { - var parents = getParents(selectedAction) - if (parents.length > 0) { - selectedActionParameters[count].action_field = parents[0].label - } else { - selectedActionParameters[count].action_field = "" - } - } else if (data === "WORKFLOW_VARIABLE") { - if (workflow.workflow_variables !== null && workflow.workflow_variables !== undefined && workflow.workflow_variables.length > 0) { - selectedActionParameters[count].action_field = workflow.workflow_variables[0].name - } - } - - selectedAction.parameters = selectedActionParameters - - // This is a stupid workaround to make it refresh rofl - setSelectedAction({}) - setSelectedTrigger({}) - setSelectedApp({}) - setSelectedEdge({}) - // FIXME - check if startnode - - // Set value - setSelectedApp(selectedApp) - setSelectedAction(selectedAction) - setUpdate(Math.random()) - } - - // FIXME: Issue #40 - selectedActionParameters not reset - if (Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters.length > 0) { - return ( -
- Parameters - {selectedActionParameters.map((data, count) => { - if (data.variant === "") { - data.variant = "STATIC_VALUE" - } - - - // selectedAction.selectedAuthentication = e.target.value - // selectedAction.authentication_id = e.target.value.id - if (!selectedAction.auth_not_required && selectedAction.selectedAuthentication !== undefined && selectedAction.selectedAuthentication.fields !== undefined && selectedAction.selectedAuthentication.fields[data.name] !== undefined) { - // This sets the placeholder in the frontend. (Replaced in backend) - selectedActionParameters[count].value = selectedAction.selectedAuthentication.fields[data.name] - selectedAction.parameters[count].value = selectedAction.selectedAuthentication.fields[data.name] - setSelectedAction(selectedAction) - //setUpdate(Math.random()) - - return null - } - - var staticcolor = "inherit" - var actioncolor = "inherit" - var varcolor = "inherit" - var multiline - if (data.multiline !== undefined && data.multiline !== null && data.multiline === true) { - multiline = true - } - - if (data.value !== undefined && data.value !== null && data.value.startsWith("{") && data.value.endsWith("}")) { - multiline = true - } - - var placeholder = "Static value" - if (data.example !== undefined && data.example !== null && data.example.length > 0) { - placeholder = data.example - } - - if (data.name.startsWith("${") && data.name.endsWith("}")) { - const paramcheck = selectedAction.parameters.find(param => param.name === "body") - if (paramcheck !== undefined && paramcheck !== null) { - if (paramcheck["value_replace"] !== undefined && paramcheck["value_replace"] !== null) { - //console.log("IN THE VALUE REPLACE: ", paramcheck["value_replace"]) - const subparamindex = paramcheck["value_replace"].findIndex(param => param.key === data.name) - if (subparamindex !== -1) { - data.value = paramcheck["value_replace"][subparamindex]["value"] - } - } - } - } - - var disabled = false - var rows = "5" - var openApiHelperText = "This is an OpenAPI specific field" - if (selectedApp.generated && selectedApp.activated && data.name === "body") { - const regex = /\${(\w+)}/g - const found = placeholder.match(regex) - if (found === null) { - //setExtraBodyFields([]) - } else { - rows = "1" - disabled = true - openApiHelperText = "OpenAPI spec: fill the following fields." - //console.log("SHOULD ADD TO selectedActionParameters!: ", found, selectedActionParameters) - var changed = false - for (var specKey in found) { - const tmpitem = found[specKey] - var skip = false - for (var innerkey in selectedActionParameters) { - if (selectedActionParameters[innerkey].name === tmpitem) { - skip = true - break - } - } - - if (skip) { - //console.log("SKIPPING ", tmpitem) - continue - } - - changed = true - selectedActionParameters.push({ - action_field: "", - configuration: false, - description: "Generated by OpenAPI body example", - example: "", - id: "", - multiline: false, - name: tmpitem, - options: null, - required: false, - schema: {type: "string"}, - skip_multicheck: false, - tags: null, - value: "", - variant: "STATIC_VALUE", - }) - } - - if (changed) { - setSelectedActionParameters(selectedActionParameters) - } - - return - } - } - - //console.log("Data: ", data) - var datafield = - - - { - setMenuPosition({ - top: event.pageY+10, - left: event.pageX+10, - }) - setShowDropdownNumber(count) - setShowDropdown(true) - setShowAutocomplete(true) - }}/> - - - ) - }} - fullWidth - multiline={multiline} - rows={rows} - color="primary" - defaultValue={data.value} - type={placeholder.includes("***") ? "password" : "text"} - placeholder={placeholder} - onChange={(event) => { - changeActionParameter(event, count, data) - }} - helperText={selectedApp.generated && selectedApp.activated && data.name === "body" ? - - {openApiHelperText} - - : - data.name.startsWith("${") && data.name.endsWith("}") ? - - OpenAPI helperfield - - : - null - } - onBlur={(event) => { - // Super basic check - //if (event.target.value.startsWith("{")) { - // console.log("VALIDATING JSON") - // try { - // JSON.parse(event.target.value) - // } catch (e) { - // alert.error("Failed to parse json: ", e) - // } - //} - }} - /> - - if (selectedActionParameters[count].schema !== undefined && selectedActionParameters[count].schema !== null && selectedActionParameters[count].schema.type === "file") { - datafield = - - - { - setMenuPosition({ - top: event.pageY+10, - left: event.pageX+10, - }) - setShowDropdownNumber(count) - setShowDropdown(true) - setShowAutocomplete(true) - }}/> - - - ) - }} - fullWidth - multiline={multiline} - rows="5" - color="primary" - defaultValue={data.value} - type={"text"} - placeholder={"The file ID to get"} - onChange={(event) => { - changeActionParameter(event, count, data) - }} - onBlur={(event) => { - }} - /> - //const fileId = "6daabec1-892b-469c-b603-c902e47223a9" - //datafield = `SHOW FILES FROM OTHER NODES? Filename: ${selectedActionParameters[count].value}` - /* - if (selectedActionParameters[count].value != fileId) { - changeActionParameter(fileId, count, data) - setUpdate(Math.random()) - - } - */ - } else if (selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0) { - if (selectedActionParameters[count].value === "" && selectedActionParameters[count].required) { - // Rofl, dirty workaround :) - const e = { - target: { - value: selectedActionParameters[count].options[0], - } - } - - changeActionParameter(e, count, data) - } - - datafield = - - - } else if (data.variant === "STATIC_VALUE") { - staticcolor = "#f85a3e" - } else if (data.variant === "ACTION_RESULT") { - // Gets the parents of the current node - var parents = getParents(selectedAction) - actioncolor = "#f85a3e" - // set the datafield - //var datafieldvalue = "Error: No parents. Action not eligible" - //if (parents.length > 0) { - // datafieldvalue = parents[0].label - //} - const fixedActionText = selectedActionParameters[count].value - - datafield = -
- - Example: $.body will get "data" from {'{"body": "data"}'}
} - placeholder="Action variable ($.)" - onChange={(event) => { - changeActionParameter(event, count, data) - }} - /> -
- - } else if (data.variant === "WORKFLOW_VARIABLE") { - varcolor = "#f85a3e" - if ((workflow.workflow_variables === null || workflow.workflow_variables === undefined || workflow.workflow_variables.length === 0) && (workflow.execution_variables === null || workflow.execution_variables === undefined || workflow.execution_variables.length === 0)) { - setCurrentView(2) - datafield = -
-
- Looks like you don't have any variables yet. -
-
- -
-
- } else { - // FIXME - this is a shitty solution that needs re-renders all the time - datafield = - - } - } - - // Shows nested list of nodes > their JSON lists - const ActionlistWrapper = (props) => { - - const handleMenuClose = () => { - setShowAutocomplete(false) - - if (!selectedActionParameters[count].value[selectedActionParameters[count].value.length-1] === "$") { - setShowDropdown(false) - } - - setUpdate(Math.random()) - setMenuPosition(null) - } - - const handleItemClick = (values) => { - if (values === undefined || values === null || values.length === 0) { - return - } - - var toComplete = selectedActionParameters[count].value.trim().endsWith("$") ? values[0].autocomplete : "$"+values[0].autocomplete - for (var key in values) { - if (key == 0 || values[key].autocomplete.length === 0) { - continue - } - - toComplete += values[key].autocomplete - } - - // Handles the fields under OpenAPI body to be parsed. - if (data.name.startsWith("${") && data.name.endsWith("}")) { - console.log("INSIDE VALUE REPLACE: ", data.name, toComplete) - // PARAM FIX - Gonna use the ID field, even though it's a hack - const paramcheck = selectedAction.parameters.find(param => param.name === "body") - if (paramcheck !== undefined) { - if (paramcheck["value_replace"] === undefined || paramcheck["value_replace"] === null) { - paramcheck["value_replace"] = [{ - "key": data.name, - "value": toComplete, - }] - - } else { - const subparamindex = paramcheck["value_replace"].findIndex(param => param.key === data.name) - if (subparamindex === -1) { - paramcheck["value_replace"].push({ - "key": data.name, - "value": toComplete, - }) - } else { - paramcheck["value_replace"][subparamindex]["value"] += toComplete - } - } - - selectedActionParameters[count]["value_replace"] = paramcheck - selectedAction.parameters[count]["value_replace"] = paramcheck - setSelectedAction(selectedAction) - setUpdate(Math.random()) - - setShowDropdown(false) - setMenuPosition(null) - return - } - } - - selectedActionParameters[count].value += toComplete - selectedAction.parameters[count].value = selectedActionParameters[count].value - setSelectedAction(selectedAction) - setUpdate(Math.random()) - - setShowDropdown(false) - setMenuPosition(null) - } - - const iconStyle = { - marginRight: 15, - } - - return ( - { - handleMenuClose() - }} - open={!!menuPosition} - style={{ - border: `2px solid #f85a3e`, - color: "white", - marginTop: 2, - }} - > - {actionlist.map(innerdata => { - const icon = innerdata.type === "action" ? : innerdata.type === "workflow_variable" || innerdata.type === "execution_variable" ? : - - const handleExecArgumentHover = (inside) => { - var exec_text_field = document.getElementById("execution_argument_input_field") - if (exec_text_field !== null) { - if (inside) { - exec_text_field.style.border = "2px solid #f85a3e" - } else { - exec_text_field.style.border = "" - } - } - - // Also doing arguments - if (workflow.triggers !== undefined && workflow.triggers !== null && workflow.triggers.length > 0) { - for (var key in workflow.triggers) { - const item = workflow.triggers[key] - - var node = cy.getElementById(item.id) - if (node.length > 0) { - if (inside) { - node.addClass('shuffle-hover-highlight') - } else { - node.removeClass('shuffle-hover-highlight') - } - } - - } - } - } - - const handleActionHover = (inside, actionId) => { - var node = cy.getElementById(actionId) - if (node.length > 0) { - if (inside) { - node.addClass('shuffle-hover-highlight') - } else { - node.removeClass('shuffle-hover-highlight') - } - } - } - - const handleMouseover = () => { - if (innerdata.type === "Execution Argument") { - handleExecArgumentHover(true) - } else if (innerdata.type === "action") { - handleActionHover(true, innerdata.id) - } - } - - const handleMouseOut = () => { - if (innerdata.type === "Execution Argument") { - handleExecArgumentHover(false) - } else if (innerdata.type === "action") { - handleActionHover(false, innerdata.id) - } - } - - var parsedPaths = [] - if (typeof(innerdata.example) === "object") { - parsedPaths = GetParsedPaths(innerdata.example, "") - } - - return ( - parsedPaths.length > 0 ? - - {icon} {innerdata.name} -
- } - parentMenuOpen={!!menuPosition} - style={{backgroundColor: inputColor, color: "white", minWidth: 250,}} - onClick={() => { - handleItemClick([innerdata]) - }} - > - {parsedPaths.map((pathdata, index) => { - // FIXME: Should be recursive in here - const icon = pathdata.type === "value" ? : pathdata.type === "list" ? : - return ( - {}} - onClick={() => { - handleItemClick([innerdata, pathdata]) - }} - > - -
- {icon} {pathdata.name} -
-
-
- ) - - })} - - : - handleMouseover()} onMouseOut={() => {handleMouseOut()}} - onClick={() => { - handleItemClick([innerdata]) - }} - > - -
- {icon} {innerdata.name} -
-
-
- - ) - })} - - ) - } - - var itemColor = "#f85a3e" - if (!data.required) { - itemColor = "#ffeb3b" - } - - var tmpitem = data.name.valueOf() - if (data.name.startsWith("${") && data.name.endsWith("}")) { - tmpitem = tmpitem.slice(2, data.name.length-1) - } - - tmpitem = tmpitem.charAt(0).toUpperCase()+tmpitem.substring(1) - tmpitem = tmpitem.replaceAll("_", " ") - const description = data.description === undefined ? "" : data.description - - return ( -
-
- - - {data.configuration === true ? - - { - setAuthenticationModalOpen(true) - }}/> - - : -
- } -
- - {tmpitem} - -
- - {/*selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0 ? null : -
- -
{ - e.preventDefault() - changeActionParameterVariant("STATIC_VALUE", count) - }}> - -
-
-  |  - -
{ - e.preventDefault() - changeActionParameterVariant("ACTION_RESULT", count) - }}> - -
-
-  |  - -
{ - e.preventDefault() - changeActionParameterVariant("WORKFLOW_VARIABLE", count) - }}> - -
-
-
- */} - {selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0 && selectedActionParameters[count].required === true && selectedActionParameters[count].unique_toggled !== undefined ? null : -
- -
{}}> - { - //console.log("CHECKED!: ", selectedActionParameters[count]) - selectedActionParameters[count].unique_toggled = !selectedActionParameters[count].unique_toggled - selectedAction.parameters[count].unique_toggled = selectedActionParameters[count].unique_toggled - setSelectedActionParameters(selectedActionParameters) - setSelectedAction(selectedAction) - setUpdate(Math.random()) - }} - name="requires_unique" - /> -
-
-
- } -
- {datafield} - {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && jsonList.length > 0 ? - - Autocomplete - - - : null} - {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && jsonList.length === 0 ? - - : null} - - -
- )})} -
- ) - } - return null - } + //height: "100%", const appApiViewStyle = { @@ -4148,49 +3124,7 @@ const AngularWorkflow = (props) => { overflow: "auto", } - const defineStartnode = () => { - var oldstartnode = cy.getElementById(workflow.start) - if (oldstartnode.length > 0) { - oldstartnode[0].data("isStartNode", false) - var oldnodecnt = workflow.actions.findIndex(a => a.id === workflow.start) - if (workflow.actions[oldnodecnt] !== undefined) { - workflow.actions[oldnodecnt].isStartNode = false - } - } - - var newstartnode = cy.getElementById(selectedAction.id) - if (newstartnode.length > 0) { - newstartnode[0].data("isStartNode", true) - var newnodecnt = workflow.actions.findIndex(a => a.id === selectedAction.id) - console.log("NEW NODE CNT: ", newnodecnt) - if (workflow.actions[newnodecnt] !== undefined) { - workflow.actions[newnodecnt].isStartNode = true - console.log(workflow.actions[newnodecnt]) - } - } - - // Find branches with triggers as source nodes - // Move these targets to be the new node - // Set arrows pointing to new startnode with errors - //for (var key in workflow.branches) { - // var item = workflow.branches[key] - // if (item.destination_id === oldstartnode[0].data()["id"]) { - // var curbranch = cy.getElementById(item.id) - // if (curbranch.length > 0) { - // //console.log(curbranch[0].data()) - // //curbranch[0].data("target", selectedAction.id) - // //curbranch[0].data("hasErrors", true) - // //workflow.branches[key].destination_id = selectedAction.id - // //console.log(curbranch[0].data()) - // } - // } - //} - - setUpdate("start_node"+selectedAction.id) - workflow.start = selectedAction.id - setWorkflow(workflow) - //setStartNode(selectedAction.id) - } + function sortByKey(array, key) { if (array === undefined) { @@ -4226,353 +3160,43 @@ const AngularWorkflow = (props) => { zIndex: 1000, } - const textFieldStyle = { - backgroundColor: inputColor, - borderRadius: borderRadius, - } - - const getApp = (appId, setApp) => { - fetch(globalUrl+"/api/v1/apps/"+appId+"/config?openapi=false", { - headers: { - 'Accept': 'application/json', - }, - credentials: "include", - }) - .then((response) => { - if (response.status === 200) { - //alert.success("Successfully GOT app "+appId) - } else { - alert.error("Failed getting app") - } - - return response.json() - }) - .then((responseJson) => { - if (setApp && responseJson.actions !== undefined && responseJson.actions !== null) { - if (selectedApp.versions !== undefined && selectedApp.versions !== null) { - responseJson.versions = selectedApp.versions - } - - if (selectedApp.loop_versions !== undefined && selectedApp.loop_versions !== null) { - responseJson.loop_versions = selectedApp.loop_versions - } - - var foundAction = responseJson.actions.find(action => action.name === selectedAction.name) - if (foundAction !== null && foundAction !== undefined) { - for (var paramkey in foundAction.parameters) { - const param = foundAction.parameters[paramkey] - - const foundParam = selectedAction.parameters.find(item => item.name === param.name) - if (foundParam === undefined) { - //console.log("COULDNT find Param: ", param) - } else { - foundAction.parameters[paramkey] = foundParam - } - } - } else { - alert.error("Couldn't find action "+selectedAction.name) - } - - // Updating params for the new action - selectedAction.parameters = foundAction.parameters - selectedAction.app_id = appId - selectedAction.app_version = responseJson.app_version - - setSelectedAction(selectedAction) - setSelectedApp(responseJson) - } - }) - .catch(error => { - alert.error(error.toString()) - }); - } - - const innerTextfieldStyle = { - color: "white", - minHeight: 50, - marginLeft: "5px", - maxWidth: "95%", - fontSize: "1em", - borderRadius: borderRadius, - } - const appApiView = Object.getOwnPropertyNames(selectedAction).length > 0 ? -
-
-
-

{selectedAction.app_name.replaceAll("_", " ")}

-
- { - console.log("FIND EXAMPLE RESULTS FOR ", selectedAction) - if (workflowExecutions.length > 0) { - // Look for the ID - const found = false - for (var key in workflowExecutions) { - if (workflowExecutions[key].results === undefined || workflowExecutions[key].results === null) { - continue - } - - var foundResult = workflowExecutions[key].results.find(result => result.action.id === selectedAction.id) - if (foundResult === undefined || foundResult === null) { - continue - } - - setSelectedResult(foundResult) - setCodeModalOpen(true) - break - } - } - }}> - - - - - - What are actions? - {selectedAction.errors !== null && selectedAction.errors.length > 0 ? -
- Errors: {selectedAction.errors.join("\n")} -
- : null - } -
-
-
-
- {selectedAction.id === workflow.start ? null : - - - - } - {selectedApp.versions !== null && selectedApp.versions !== undefined && selectedApp.versions.length > 0 ? - - : null } -
-
- - - Name - - - {selectedApp.name !== undefined && selectedAction.authentication !== undefined && selectedAction.authentication.length === 0 && requiresAuthentication ? -
- - - - - -
- : null} - {selectedAction.authentication !== undefined && selectedAction.authentication.length > 0 ? -
- Authentication -
- - - {/* - - - curaction.authentication = authenticationOptions - if (curaction.selectedAuthentication === null || curaction.selectedAuthentication === undefined || curaction.selectedAuthentication.length === "") - */} - - { - setAuthenticationModalOpen(true) - }}> - - - -
-
- : null} - {showEnvironment ? -
- - Environment - - -
- : null} - {workflow.execution_variables !== undefined && workflow.execution_variables !== null && workflow.execution_variables.length > 0 ? -
- Set execution variable (optional) - -
- : null} - -
-
- Actions -
- - {selectedAction.description !== undefined && selectedAction.description.length > 0 ? -
- {selectedAction.description} -
: null} -
- - -
-
- -
-
-
- : null + + + : + null const setTriggerFolderWrapperMulti = event => { const { options } = event.target @@ -4763,7 +3387,7 @@ const AngularWorkflow = (props) => { var datafield = { Name
{
Environment: { Name
{ { { Name { { var copyText = document.getElementById("webhook_uri_field"); @@ -6224,7 +4848,7 @@ const AngularWorkflow = (props) => { Name {
Environment: {
{ {workflow.triggers[selectedTriggerIndex].parameters[2] !== undefined && workflow.triggers[selectedTriggerIndex].parameters[2].value.includes("email") ? { } {workflow.triggers[selectedTriggerIndex].parameters[2] !== undefined && workflow.triggers[selectedTriggerIndex].parameters[2].value.includes("sms") ? { Name { { { { - - - - - - - - - - - - - - - {workflow.public ? @@ -6830,6 +5428,32 @@ const AngularWorkflow = (props) => { : null} + + + + + + + + + + + + + + + {/* */} {workflow.configuration !== null && workflow.configuration !== undefined && workflow.configuration.exit_on_error !== undefined ? : null} @@ -7942,7 +6566,7 @@ const AngularWorkflow = (props) => {
Name - what is this used for? { {data.name} {
The API endpoint to use (URL) - predefined in the app