import React, {useState, useEffect, useLayoutEffect} from 'react'; import { useInterval } from 'react-powerhooks'; import uuid from "uuid"; import TextField from '@material-ui/core/TextField'; import Button from '@material-ui/core/Button'; import Paper from '@material-ui/core/Paper'; import Grid from '@material-ui/core/Grid'; import ButtonBase from '@material-ui/core/ButtonBase'; import Tooltip from '@material-ui/core/Tooltip'; import Select from '@material-ui/core/Select'; import MenuItem from '@material-ui/core/MenuItem'; import Divider from '@material-ui/core/Divider'; import Dialog from '@material-ui/core/Dialog'; import DialogActions from '@material-ui/core/DialogActions'; import DialogTitle from '@material-ui/core/DialogTitle'; import DialogContent from '@material-ui/core/DialogContent'; import FormControl from '@material-ui/core/FormControl'; import IconButton from '@material-ui/core/IconButton'; import Menu from '@material-ui/core/Menu'; import Input from '@material-ui/core/Input'; import FormGroup from '@material-ui/core/FormGroup'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import Checkbox from '@material-ui/core/Checkbox'; import PlayArrowIcon from '@material-ui/icons/PlayArrow'; import AspectRatioIcon from '@material-ui/icons/AspectRatio'; import MoreVertIcon from '@material-ui/icons/MoreVert'; import AppsIcon from '@material-ui/icons/Apps'; import ScheduleIcon from '@material-ui/icons/Schedule'; import FavoriteBorderIcon from '@material-ui/icons/FavoriteBorder'; import PauseIcon from '@material-ui/icons/Pause'; import DeleteIcon from '@material-ui/icons/Delete'; import SaveIcon from '@material-ui/icons/Save'; import KeyboardArrowLeftIcon from '@material-ui/icons/KeyboardArrowLeft'; import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight'; import * as cytoscape from 'cytoscape'; import * as edgehandles from 'cytoscape-edgehandles'; import * as clipboard from 'cytoscape-clipboard'; import CytoscapeComponent from 'react-cytoscapejs'; import undoRedo from 'cytoscape-undo-redo'; import Draggable from 'react-draggable'; import environmentdata from './environmentdata'; import cytoscapestyle from './defaultCytoscapeStyle'; import cxtmenu from 'cytoscape-cxtmenu'; import { w3cwebsocket as W3CWebSocket } from "websocket"; import { useAlert } from "react-alert"; const hoverColor = "#f85a3e" const hoverOutColor = "#e8eaf6" const surfaceColor = "#27292D" const inputColor = "#383B40" // http://apps.cytoscape.org/apps/yfileslayoutalgorithms cytoscape.use(edgehandles); cytoscape.use(clipboard); cytoscape.use(undoRedo); cytoscape.use( cxtmenu ); // https://stackoverflow.com/questions/19014250/rerender-view-on-browser-resize-with-react function useWindowSize() { const [size, setSize] = useState([0, 0]); useLayoutEffect(() => { function updateSize() { setSize([window.innerWidth, window.innerHeight]); } window.addEventListener('resize', updateSize); updateSize(); return () => window.removeEventListener('resize', updateSize); }, []); return size; } const splitter = "|~|" //const referenceUrl = "https://shuffler.io/functions/webhooks/" //const referenceUrl = window.location.origin+"/api/v1/hooks/" console.log(window.location) const AngularWorkflow = (props) => { const { globalUrl, isLoggedIn, isLoaded } = props; const referenceUrl = globalUrl+"/api/v1/hooks/" const alert = useAlert() const [bodyWidth, bodyHeight] = useWindowSize(); const appBarSize = 74 const [cystyle, ] = useState(cytoscapestyle) const [cy, setCy] = React.useState() const [currentView, setCurrentView] = React.useState("apps") const [, setAppSearchValue] = React.useState("") const [triggerAuthentication, setTriggerAuthentication] = React.useState({}) const [triggerFolders, setTriggerFolders] = React.useState([]) const [workflow, setWorkflow] = React.useState({}); const [leftViewOpen, setLeftViewOpen] = React.useState(true); const [leftBarSize, setLeftBarSize] = React.useState(350) const [executionText, setExecutionText] = React.useState(""); const [executionRequestStarted, setExecutionRequestStarted] = React.useState(false); const [appAuthentication, setAppAuthentication] = React.useState({}); const [variablesModalOpen, setVariablesModalOpen] = React.useState(false); const [authenticationModalOpen, setAuthenticationModalOpen] = React.useState(false); const [conditionsModalOpen, setConditionsModalOpen] = React.useState(false); const [newVariableName, setNewVariableName] = React.useState(""); const [newVariableDescription, setNewVariableDescription] = React.useState(""); const [newVariableValue, setNewVariableValue] = React.useState(""); const [workflowDone, setWorkflowDone] = React.useState(false) const [localFirstrequest, setLocalFirstrequest] = React.useState(true) const [requiresAuthentication, setRequiresAuthentication] = React.useState(true) const [variableAnchorEl, setVariableAnchorEl] = React.useState(null) const [sourceValue, setSourceValue] = React.useState({}) const [destinationValue, setDestinationValue] = React.useState({}) const [conditionValue, setConditionValue] = React.useState({}) // Trigger stuff const [selectedTrigger, setSelectedTrigger] = React.useState({}); const [selectedTriggerIndex, setSelectedTriggerIndex] = React.useState({}); const [selectedEdge, setSelectedEdge] = React.useState({}); const [selectedEdgeIndex, setSelectedEdgeIndex] = React.useState({}); const [visited, setVisited] = React.useState([]); //const [workflow, setWorkflow] = React.useState(workflowdata); const [apps, setApps] = React.useState([]); const [filteredApps, setFilteredApps] = React.useState([]); const [firstrequest, setFirstrequest] = React.useState(true) //const [apps, setApps] = React.useState(appdata); //const [filteredApps, setFilteredApps] = React.useState(); const [environments, setEnvironments] = React.useState([]); const [established, setEstablished] = React.useState(false); const [graphSetup, setGraphSetup] = React.useState(false); const [selectedApp, setSelectedApp] = React.useState({}); const [selectedAction, setSelectedAction] = React.useState({}); const [selectedActionName, setSelectedActionName] = React.useState({}); const [selectedActionEnvironment, setSelectedActionEnvironment] = React.useState({}); const [executionRequest, setExecutionRequest] = React.useState({}) const [, setExecutingNodes] = React.useState([]) const [executionRunning, setExecutionRunning] = React.useState(false) const [lastSaved, setLastSaved] = React.useState(true) const [AppsHoverColor, setAppsHoverColor] = useState(hoverOutColor); const [VariablesHoverColor, setVariablesHoverColor] = useState(hoverOutColor); const [HookHoverColor, setHookHoverColor] = useState(hoverOutColor); const [update, setUpdate] = useState(""); const [elements, setElements] = useState([]) const { start, stop } = useInterval({ duration: 5000, startImmediate: false, callback: () => { fetchUpdates() } }); const fetchUpdates = () => { fetch(globalUrl+"/api/v1/streams/results", { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify(executionRequest), credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for stream results :O!") } return response.json() }) .then((responseJson) => { handleUpdateResults(responseJson) }) .catch(error => { alert.error(error.toString()) }); } const abortExecution = () => { setExecutionRunning(false) alert.success("Aborting execution") fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/executions/"+executionRequest.execution_id+"/abort", { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for WORKFLOW EXECUTION :O!") } return response.json() }) .catch(error => { alert.error(error.toString()) }); } const handleUpdateResults = (responseJson) => { //console.log(responseJson) // Loop nodes and find results // Update on every interval? idk if (responseJson.execution_id !== executionRequest.execution_id) { cy.elements().removeClass('success-highlight failure-highlight executing-highlight') return } if (responseJson.results !== null && responseJson.results !== []) { for (var key in responseJson.results) { var item = responseJson.results[key] var currentnode = cy.getElementById(item.action.id) if (currentnode.length === 0) { continue } currentnode = currentnode[0] const outgoingEdges = currentnode.outgoers('edge') const incomingEdges = currentnode.incomers('edge') //currentnode.removeClass('success-highlight failure-highlight executing-highlight') switch (item.status) { case "EXECUTING": currentnode.removeClass('not-executing-highlight') currentnode.removeClass('success-highlight') currentnode.removeClass('failure-highlight') currentnode.removeClass('awaiting-data-highlight') currentnode.addClass('executing-highlight') incomingEdges.addClass('success-highlight') break case "WAITING": currentnode.removeClass('not-executing-highlight') currentnode.removeClass('success-highlight') currentnode.removeClass('failure-highlight') currentnode.removeClass('awaiting-data-highlight') currentnode.addClass('executing-highlight') if (!visited.includes(item.action.label)) { if (executionRunning) { alert.show("WAITING FOR "+item.action.label+" with result "+item.result) visited.push(item.action.label) setVisited(visited) } } // FIXME - add outgoing nodes to executing //const outgoingNodes = outgoingEdges.find().data().target if (outgoingEdges.length > 0) { outgoingEdges.addClass('success-highlight') } break case "SUCCESS": currentnode.removeClass('not-executing-highlight') currentnode.removeClass('executing-highlight') currentnode.removeClass('failure-highlight') currentnode.removeClass('awaiting-data-highlight') currentnode.addClass('success-highlight') if (!visited.includes(item.action.label)) { if (executionRunning) { alert.show("Success for "+item.action.label+" with result "+item.result) visited.push(item.action.label) setVisited(visited) } } // FIXME - add outgoing nodes to executing //const outgoingNodes = outgoingEdges.find().data().target if (outgoingEdges.length > 0) { outgoingEdges.addClass('success-highlight') } break case "FAILURE": currentnode.removeClass('not-executing-highlight') currentnode.removeClass('executing-highlight') currentnode.removeClass('success-highlight') currentnode.removeClass('awaiting-data-highlight') currentnode.addClass('failure-highlight') if (!visited.includes(item.action.label)) { alert.error("Success for "+item.action.label+" with result "+item.result) visited.push(item.action.label) setVisited(visited) } break case "AWAITING_DATA": currentnode.removeClass('not-executing-highlight') currentnode.removeClass('executing-highlight') currentnode.removeClass('success-highlight') currentnode.removeClass('failure-highlight') currentnode.addClass('awaiting-data-highlight') break default: break } } } if (responseJson.status === "ABORTED" || responseJson.status === "STOPPED" || responseJson.status === "FAILURE") { stop() setExecutionRunning(false) var curelements = cy.elements() for (var i = 0; i < curelements.length; i++) { if (curelements[i].classes().includes("executing-highlight")) { curelements[i].removeClass("executing-highlight") curelements[i].addClass("failure-highlight") } else { curelements[i].removeClass('not-executing-highlight') curelements[i].removeClass('executing-highlight') curelements[i].removeClass('success-highlight') curelements[i].removeClass('awaiting-data-highlight') curelements[i].removeClass('failure-highlight') } } } else if (responseJson.status === "FINISHED") { setExecutionRunning(false) stop() } } const saveWorkflow = (curworkflow) => { var success = false // This might not be the right course of action, but seems logical, as items could be running already // Makes it possible to update with a version in current render stop() var useworkflow = workflow if (curworkflow !== undefined) { useworkflow = curworkflow } else { alert.info("Saving workflow") } var cyelements = cy.elements() var newActions = [] var newTriggers = [] var newBranches = [] for (var key in cyelements) { if (cyelements[key].data === undefined) { continue } var type = cyelements[key].data()["type"] if (type === undefined) { if (cyelements[key].data().source === undefined || cyelements[key].data().target === undefined) { continue } var parsedElement = { id: cyelements[key].data().id, source_id: cyelements[key].data().source, destination_id: cyelements[key].data().target, conditions: cyelements[key].data().conditions, } newBranches.push(parsedElement) } else { if (type === "ACTION") { // FIXME - check whether position is new to not fuck up params etc. var curworkflowAction = useworkflow.actions.find(a => a.id === cyelements[key].data()["id"]) if (curworkflowAction === undefined) { curworkflowAction = cyelements[key].data() } curworkflowAction.position = cyelements[key].position() // workaround to fix some edgecases if (curworkflowAction.parameters === "" || curworkflowAction.parameters === null) { curworkflowAction.parameters = [] } newActions.push(curworkflowAction) } else if (type === "TRIGGER") { //console.log("TRIGGER") var curworkflowTrigger = useworkflow.triggers.find(a => a.id === cyelements[key].data()["id"]) if (curworkflowTrigger === undefined) { curworkflowTrigger = cyelements[key].data() } curworkflowTrigger.position = cyelements[key].position() //console.log(curworkflowTrigger) newTriggers.push(curworkflowTrigger) } } } useworkflow.actions = newActions useworkflow.triggers = newTriggers useworkflow.branches = newBranches setLastSaved(true) fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify(useworkflow), credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for setting workflows :O!") } return response.json() }) .then((responseJson) => { if (!responseJson.success) { console.log(responseJson) alert.error("Failed to save: "+responseJson.reason) } else { success = true alert.success("Successfully saved workflow") } }) .catch(error => { alert.error(error.toString()) }); return success } const monitorUpdates = () => { const firstnode = cy.getElementById(workflow.start) if (firstnode.length === 0) { return false } cy.elements().removeClass('success-highlight failure-highlight executing-highlight') firstnode[0].addClass('executing-highlight') return true } const executeWorkflowWebsocket = () => { if (!lastSaved) { //alert.error("You might have forgotten to save before executing.") console.log("FIXME: Might have forgotten to save before executing.") } var returncheck = monitorUpdates() if (!returncheck) { alert.error("No startnode set.") return } setVisited([]) setExecutionRunning(true) setExecutionRequest({}) var curelements = cy.elements() for (var i = 0; i < curelements.length; i++) { curelements[i].addClass("not-executing-highlight") } if (executionText.length > 0) { alert.success("Starting execution with argument "+executionText) } else { alert.success("Starting execution") } const data = {"execution_argument": executionText} fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/execute_fs", { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", body: JSON.stringify(data), }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for WORKFLOW EXECUTION :O!") } return response.json() }) .then((responseJson) => { if (!responseJson.success) { alert.error("Failed to start: "+responseJson.reason) stop() return } setExecutionRequest({ "execution_id": responseJson.execution_id, "authorization": responseJson.authorization, }) setExecutingNodes([workflow.start]) }) .catch(error => { alert.error(error.toString()) }); } const executeWorkflow = () => { if (!lastSaved) { //alert.error("You might have forgotten to save before executing.") console.log("FIXME: Might have forgotten to save before executing.") } var returncheck = monitorUpdates() if (!returncheck) { alert.error("No startnode set.") return } setVisited([]) setExecutionRequest({}) setExecutionRequestStarted(true) stop() var curelements = cy.elements() for (var i = 0; i < curelements.length; i++) { curelements[i].addClass("not-executing-highlight") } if (executionText.length > 0) { alert.success("Starting execution with argument "+executionText) } else { alert.success("Starting execution") } const data = {"execution_argument": executionText, "start": workflow.start} fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/execute", { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", body: JSON.stringify(data), }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for WORKFLOW EXECUTION :O!") } return response.json() }) .then((responseJson) => { if (!responseJson.success) { alert.error("Failed to start: "+responseJson.reason) stop() return } else { setExecutionRunning(true) setExecutionRequestStarted(false) } setExecutionRequest({ "execution_id": responseJson.execution_id, "authorization": responseJson.authorization, }) setExecutingNodes([workflow.start]) start() }) .catch(error => { alert.error(error.toString()) }); } //const handleAppVersioning = (apps) => { // var newapps = [] // for (var key in apps) { // var item = apps[key] // const previtem = newapps.findIndex(data => data.name === item.name) // if (previtem === -1) { // item["versions"] = [item.app_version] // newapps.push(item) // continue // } // // THere might be duplicates for some reason.. // if (!newapps[previtem]["versions"].includes(item.app_version)) { // newapps[previtem]["versions"].push(item.app_version) // } // } // // // FIXME - handle this, as we can't have more than one of each :) // //setVersionedApps(newapps) //} const getApps = () => { fetch(globalUrl+"/api/v1/workflows/apps", { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for apps :O!") } return response.json() }) .then((responseJson) => { // FIXME - handle versions on left bar //handleAppVersioning(responseJson) setApps(responseJson) setFilteredApps(responseJson) }) .catch(error => { alert.error(error.toString()) }); } const getWorkflow = () => { fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key, { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for workflows :O!") window.location.pathname = "/workflows" } return response.json() }) .then((responseJson) => { setWorkflow(responseJson) setWorkflowDone(true) }) .catch(error => { alert.error(error.toString()) }); } const onUnselect = (event) => { //console.log("Unselect?") console.time("UNSELECT") // FIXME - check if they have value before overriding like this for no reason. // Would save a lot of time (400~ ms -> 30ms) //setSelectedActionName({}) setSelectedAction({}) setSelectedApp({}) setSelectedTrigger({}) setSelectedEdge({}) // setSelectedTriggerIndex(-1) //setSelectedActionEnvironment({}) //setSelectedEdge({}) //setTriggerAuthentication({}) //setSelectedTriggerIndex(-1) //setTriggerFolders([]) //setLocalFirstrequest(true) console.timeEnd("UNSELECT") } const onEdgeSelect = (event) => { setSelectedEdgeIndex(workflow.branches.findIndex(data => data.id === event.target.data()["id"])) setSelectedEdge(event.target.data()) } const onNodeSelect = (event) => { const data = event.target.data() //console.log(data) if (data.type === "ACTION") { // FIXME - unselect //console.log(cy.elements('[_id!="${data._id}"]`)) console.time('ACTIONSTART'); const curaction = workflow.actions.find(a => a.id === data.id) if (!curaction || curaction === undefined) { //console.log("Action not found error") return } const curapp = apps.find(a => a.name === curaction.app_name && a.app_version === curaction.app_version) if (!curapp || curapp === undefined) { //console.log("App "+curaction.app_name+":"+curaction.app_version+" not found error") return } var env = environments.find(a => a.name === curaction.environment) if (!env || env === undefined) { env = environments[0] } console.log(curapp) setRequiresAuthentication(curapp.authentication.required) setSelectedApp(curapp) setSelectedActionEnvironment(env) setSelectedActionName(curaction.name) setSelectedAction(curaction) console.timeEnd("ACTIONSTART") } else if (data.type === "TRIGGER") { //console.log("Should handle trigger "+data.triggertype) //console.log(data) setSelectedTriggerIndex(workflow.triggers.findIndex(a => a.id === data.id)) setSelectedTrigger(data) setSelectedActionEnvironment(data.env) setSelectedActionName(data.name) } else { console.log("Should handle type "+data.type) } } const onEdgeAdded = (event) => { setLastSaved(false) const edge = event.target.data() // Check if: // dest == source && source == dest // dest == dest && source == source // backend: check all children? to stop recursion var found = false for (var key in workflow.branches) { if (workflow.branches[key].destination_id === edge.source && workflow.branches[key].source_id === edge.target) { alert.error("A pointer in the opposite direction already exists") event.target.remove() found = true break } else if (workflow.branches[key].destination_id === edge.target && workflow.branches[key].source_id === edge.source) { // Checks if NOT trigger if (workflow.triggers !== undefined && workflow.triggers.find(data => data.id === workflow.branches[key].source_id).length === 0) { alert.error("That pointer already exists") event.target.remove() } found = true break } else if (edge.target === workflow.start) { var targetnode = workflow.triggers.findIndex(data => data.id === edge.source) if (targetnode === -1) { alert.error("Can't make arrow to starting node") event.target.remove() found = true break } } else { // Find the targetnode and check if its a trigger // FIXME - do this for both actions and other types? //targetnode = workflow.triggers.findIndex(data => data.id === edge.target) //if (targetnode !== -1) { // alert.error("Can't have triggers as target") // event.target.remove() // found = true // break //} } } var newbranch = { "source_id": edge.source, "destination_id": edge.target, "id_": edge.id, "id": edge.id, "hasErrors": false, } if (!found) { newbranch["hasErrors"] = false } workflow.branches.push(newbranch) setWorkflow(workflow) } const onNodeAdded = (event) => { //setLastSaved(false) const node = event.target if (node.isNode() && cy.nodes().size() === 1) { //setStartNode(node.data('id')) workflow.start = node.data('id') setWorkflow(workflow) } } const onEdgeRemoved = (event) => { const edge = event.target workflow.branches = workflow.branches.filter(a => a.id !== edge.data().id) setWorkflow(workflow) // trigger as source check const indexcheck = workflow.triggers.findIndex(data => edge.data()["source"] === data.id) if (indexcheck !== -1) { //alert.error("Can't remove edge from a trigger") console.log("Shouldnt remove edge from trigger") //const edgeToBeAdded = { // group: "edges", // data: newcybranch, //} } } const onNodeRemoved = (event) => { const node = event.target const data = node.data() //var currentnode = cy.getElementById(data.id) //if (currentnode.length === 0) { //} if (workflow.start === data.id && workflow.actions.length > 1) { // FIXME - should check branches connected to startnode, as picking random // might just be confusing cy.nodes().forEach(function( ele ) { if (ele.id() !== workflow.start && ele.data()["label"] !== undefined) { alert.success("Changed startnode to "+ele.data()["label"]) ele.data("isStartNode", true) workflow.start = ele.id() return } }); } workflow.actions = workflow.actions.filter(a => a.id !== data.id) workflow.triggers = workflow.triggers.filter(a => a.id !== data.id) setWorkflow(workflow) if (data.type === "TRIGGER") { saveWorkflow(workflow) } } var previouskey = 0 const handleKeyDown = (event) => { // SHIFT = 16 // CTRL = 17 //console.log(event.keyCode) switch( event.keyCode ) { case 27: console.log("ESCAPE") break; case 46: removeNode() console.log("DELETE") break; case 38: console.log("UP") break; case 37: console.log("LEFT") break; case 40: console.log("DOWN") break; case 39: console.log("RIGHT") break; case 90: if (previouskey === 17) { console.log("CTRL+Z") } break; case 67: if (previouskey === 17) { console.log("CTRL+C") } break; case 86: if (previouskey === 17) { console.log("CTRL+V") } break; case 88: if (previouskey === 17) { console.log("CTRL+V") } break; case 83: if (previouskey === 17) { event.preventDefault() saveWorkflow() } break; case 70: if (previouskey === 17) { event.preventDefault() cy.fit(null, 50) } break; case 65: // As a poweruser myself, I found myself hitting this a few // too many times to just edit text. Need a better bind // //if (previouskey === 17) { // event.preventDefault() // if (executionRunning || executionRequestStarted) { // abortExecution() // } else { // executeWorkflow() // } // cy.fit(null, 50) //} break; default: //console.log(event.keyCode) break; } previouskey = event.keyCode } const registerKeys = () => { document.addEventListener("keydown", handleKeyDown); } const getEnvironments = () => { fetch(globalUrl+"/api/v1/getenvironments", { method: 'GET', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for apps :O!") return } return response.json() }) .then((responseJson) => { setEnvironments(responseJson) }) .catch(error => { alert.error(error.toString()) }); } useEffect(() => { if (firstrequest) { setFirstrequest(false) getWorkflow() getApps() getEnvironments() return } // App length necessary cus of cy initialization if (elements.length === 0 && !graphSetup && Object.getOwnPropertyNames(workflow).length > 0) { setGraphSetup(true) setupGraph() } else if (!established && cy !== undefined && apps.length > 0 && Object.getOwnPropertyNames(workflow).length > 0){ setEstablished(true) cy.edgehandles({ handleNodes: (el) => el.isNode(), preview: false, toggleOffOnLeave: false, loopAllowed: function( node ){ return false; }, }) cy.fit(null, 200) cy.on('select', 'node', (e) => onNodeSelect(e)) cy.on('select', 'edge', (e) => onEdgeSelect(e)) cy.on('unselect', (e) => onUnselect(e)) cy.on('add', 'node', (e) => onNodeAdded(e)) cy.on('add', 'edge', (e) => onEdgeAdded(e)) cy.on('remove', 'node', (e) => onNodeRemoved(e)) cy.on('remove', 'edge', (e) => onEdgeRemoved(e)) cy.on('mouseover', 'edge', (e) => onEdgeHover(e)) cy.on('mouseout', 'edge', (e) => onEdgeHoverOut(e)) cy.on('mouseover', 'node', (e) => onNodeHover(e)) cy.on('mouseout', 'node', (e) => onNodeHoverOut(e)) //cy.on('mouseover', 'node', () => $(targetElement).addClass('mouseover')); //cy.on('cxttapstart', 'node', (e) => edgeHandler.start(e.target)) //cy.on('cxttapend', 'node', (e) => edgeHandler.stop()) //cy.on('cxtdragover', 'node', (e) => edgeHandler.preview(e.target)) //cy.on('cxtdragout', 'node', (e) => edgeHandler.unpreview(e.target)) // RIGHT HERE..? // This is wrong sometimes.. I'm mad document.title = "Workflow - "+workflow.name registerKeys() //setStartNode(workflow.start) } else if (established) { //console.log("established - should fix colors of things") //console.log(cy.elements()) } }) var previousnodecolor = "" //var previousedgecolor = "" const animationDuration = 150 const onNodeHoverOut = (event) => { event.target.animate({ style: { "border-width": "1px", } }, { duration: animationDuration, }) console.log(previousnodecolor) } const onNodeHover = (event) => { //event.target.style("border-width", "5px") event.target.animate({ style: { "border-width": "5px", "border-opacity": ".7", } }, { duration: animationDuration, }) previousnodecolor = event.target.style("border-color") } const onEdgeHoverOut = (event) => { //event.target.removeStyle() } // This is here to have a proper transition for lines const onEdgeHover = (event) => { //console.log(event.target.data()) //const sourcecolor = cy.getElementById(event.target.data("source")).style("border-color") //const targetcolor = cy.getElementById(event.target.data("target")).style("border-color") //event.target.animate({ // style: { // "line-fill": "linear-gradient", // 'target-arrow-color': targetcolor, // "line-gradient-stop-colors": [sourcecolor, targetcolor], // "line-gradient-stop-positions": [0, 1], // }, // duration: 0, //}) } const setupGraph = () => { const actions = workflow.actions.map(action => { const node = {} node.position = action.position node.data = action node.data._id = action["id"] node.data.type = "ACTION" node.isStartNode = action["id"] === workflow.start return node; }) const triggers = workflow.triggers.map(trigger => { const node = {} node.position = trigger.position node.data = trigger node.data._id = trigger["id"] node.data.type = "TRIGGER" return node; }) // FIXME - tmp branch update var insertedNodes = [].concat(actions, triggers) const edges = workflow.branches.map((branch, index) => { //workflow.branches[index].conditions = [{ const edge = { }; var conditions = workflow.branches[index].conditions if (conditions === undefined || conditions === null) { conditions = [] } var label = "" if (conditions.length === 1) { label = conditions.length+" condition" } else if (conditions.length > 1) { label = conditions.length+" conditions" } edge.data = { id: branch.id, _id: branch.id, source: branch.source_id, target: branch.destination_id, label: label, conditions: conditions, hasErrors: branch.has_errors }; return edge; }) setWorkflow(workflow) // Verifies if a branch is valid and skips others var newedges = [] for (var key in edges) { var item = edges[key] const sourcecheck = insertedNodes.find(data => data.data.id === item.data.source) const destcheck = insertedNodes.find(data => data.data.id === item.data.target) if (sourcecheck === undefined || destcheck === undefined) { continue } newedges.push(item) } insertedNodes = insertedNodes.concat(newedges) setElements(insertedNodes) } const removeNode = () => { setSelectedApp({}) setSelectedAction({}) setSelectedActionName("") const selectedNode = cy.$(':selected') if (selectedNode.data() === undefined) { return } if (selectedNode.data().type === "TRIGGER") { console.log("Should remove trigger!") console.log(selectedNode.data()) const triggerindex = workflow.triggers.findIndex(data => data.id === selectedNode.data().id) setSelectedTriggerIndex(triggerindex) if (selectedNode.data().trigger_type === "SCHEDULE") { setSelectedTrigger(selectedNode.data()) stopSchedule(selectedNode.data(), triggerindex) } else if (selectedNode.data().trigger_type === "WEBHOOK") { setSelectedTrigger(selectedNode.data()) deleteWebhook(selectedNode.data(), triggerindex) } else if (selectedNode.data().trigger_type === "EMAIL") { setSelectedTrigger(selectedNode.data()) stopMailSub(selectedTrigger, triggerindex) } } selectedNode.remove() setSelectedTrigger({}) setSelectedTriggerIndex({}) } const stopSchedule = (trigger, triggerindex) => { alert.info("Stopping schedule") fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/schedule/"+trigger.id, { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for stream results :O!") } return response.json() }) .then((responseJson) => { if (!responseJson.success) { alert.error("Failed to delete schedule: " + responseJson.reason) } else { alert.success("Successfully stopped schedule") workflow.triggers[triggerindex].status = "stopped" trigger.status = "stopped" setSelectedTrigger(trigger) setWorkflow(workflow) saveWorkflow(workflow) } }) .catch(error => { alert.error(error.toString()) }); } //const submitSchedule = (id, name, frequency, executionArg) => { const submitSchedule = (trigger, triggerindex) => { const cronSplit = workflow.triggers[triggerindex].parameters[0].value.split("*") if (cronSplit.length <= 5 || cronSplit.length > 6) { alert.error("Error: Bad cron, example run every 1 minute: */1 * * * *") return } if (trigger.name.length <= 0) { alert.error("Error: name can't be empty") return } alert.info("Attempting to create schedule with name " + trigger.name) const data = { "name": trigger.name, "frequency": workflow.triggers[triggerindex].parameters[0].value, "execution_argument": workflow.triggers[triggerindex].parameters[1].value, "id": trigger.id, } fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/schedule", { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify(data), credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for stream results :O!") } return response.json() }) .then((responseJson) => { if (!responseJson.success) { alert.error("Failed to set schedule: " + responseJson.reason) } else { alert.success("Successfully created schedule") workflow.triggers[triggerindex].status = "running" trigger.status = "running" setSelectedTrigger(trigger) setWorkflow(workflow) console.log("Should set the status to running and save") saveWorkflow(workflow) } }) .catch(error => { alert.error(error.toString()) }); } const setAppSearch = (event) => { setFilteredApps(apps.filter(app => app.name.includes(event.target.value))) } const appViewStyle = { marginLeft: "5px", marginRight: "5px", display: "flex", flexDirection: "column", } const scrollStyle = { marginTop: "10px", overflow: "scroll", height: "66vh", overflowX: "auto", overflowY: "auto", } const paperAppStyle = { minHeight: "100px", maxHeight: "100px", minWidth: "100%", maxWidth: "100%", marginTop: "5px", color: "white", backgroundColor: surfaceColor, cursor: "pointer", display: "flex", } // All this is stupid lmao const handleHookHover = () => { setHookHoverColor(hoverColor) setAppsHoverColor(hoverOutColor) setVariablesHoverColor(hoverOutColor) } const handleHookHoverOut = () => { setHookHoverColor(hoverOutColor) } const handleAppsHover = () => { setAppsHoverColor(hoverColor) setHookHoverColor(hoverOutColor) setVariablesHoverColor(hoverOutColor) } const handleAppsHoverOut = () => { setAppsHoverColor(hoverOutColor) } const handleVariablesHover = () => { setVariablesHoverColor(hoverColor) setHookHoverColor(hoverOutColor) setAppsHoverColor(hoverOutColor) } const handleVariablesHoverOut = () => { setVariablesHoverColor(hoverOutColor) } const VariablesView = () => { const [open, setOpen] = React.useState(false); const [anchorEl, setAnchorEl] = React.useState(null); if (workflow.workflow_variables === undefined || workflow.workflow_variables === null || workflow.workflow_variables.length === 0) { return (
Looks like you don't have any variables yet.
) } const paperVariableStyle = { minHeight: "50px", maxHeight: "50px", minWidth: "100%", maxWidth: "100%", marginTop: "5px", color: "white", backgroundColor: surfaceColor, cursor: "pointer", display: "flex", } const menuClick = (event) => { setOpen(!open) setAnchorEl(event.currentTarget); } const deleteVariable = (variableName) => { workflow.workflow_variables = workflow.workflow_variables.filter(data => data.name !== variableName) setWorkflow(workflow) } const variableScrollStyle = { marginTop: "10px", overflow: "scroll", height: "66vh", overflowX: "auto", overflowY: "auto", flex: "10", } return (
{workflow.workflow_variables.map(variable=> { return (
{ }}>
{ setNewVariableName(variable.name) setNewVariableDescription(variable.description) setNewVariableValue(variable.value) setVariablesModalOpen(true)}}> Name: {variable.name}
{ setOpen(false) setAnchorEl(null) }} > { setOpen(false) setNewVariableName(variable.name) setNewVariableDescription(variable.description) setNewVariableValue(variable.value) setVariablesModalOpen(true) }} key={"Edit"}>{"Edit"} { deleteVariable(variable.name) setOpen(false) }} key={"Delete"}>{"Delete"}
) })}
) } const HandleLeftView = () => { var thisview = if (currentView === "triggers") { thisview = } else if (currentView === "variables") { thisview = } return(
{ setLeftViewOpen(false) setLeftBarSize(50) }}>
{thisview}
{setCurrentView("apps")}}> Apps
{setCurrentView("triggers")}}> Triggers
{setCurrentView("variables")}}> Variables
) } const TriggersView = () => { const triggersViewStyle = { marginLeft: "10px", marginRight: "10px", display: "flex", flexDirection: "column", } // Predefined hurr var triggers = [{ "name": "Webhook", "type": "TRIGGER", "status": "uninitialized", "trigger_type": "WEBHOOK", "errors": null, "large_image": 'data:image/png;base64,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', "is_valid": true, "label": "Webhook", "environment": environments[0].Name, "description": "Simple HTTP webhook", "long_description": "Execute a workflow with an unauthicated POST request", }, { "name": "User Input", "type": "TRIGGER", "status": "running", "large_image": "data:image/png;base64,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", "description": "Wait for user input", "trigger_type": "USERINPUT", "errors": null, "is_valid": false, "label": "User input", "environment": environments[0].Name, "long_description": "Take user input to continue execution", }, { "name": "Schedule", "type": "TRIGGER", "status": "uninitialized", "description": "Schedule execution time", "trigger_type": "SCHEDULE", "errors": null, "large_image": "data:image/png;base64,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", "is_valid": false, "label": "Schedule", "environment": environments[0].Name, "long_description": "Create a schedule based on cron", }, { "name": "Email", "type": "TRIGGER", "status": "uninitialized", "description": "Add your email provider", "trigger_type": "EMAIL", "errors": null, "is_valid": true, "label": "Email", "environment": environments[0].Name, "large_image": 'data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/hAytodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Nzg4QTJBMjVEMDI1MTFFN0EwQUVDODc5QjYyQkFCMUQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Nzg4QTJBMjZEMDI1MTFFN0EwQUVDODc5QjYyQkFCMUQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3ODhBMkEyM0QwMjUxMUU3QTBBRUM4NzlCNjJCQUIxRCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3ODhBMkEyNEQwMjUxMUU3QTBBRUM4NzlCNjJCQUIxRCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/bAEMAAwICAgICAwICAgMDAwMEBgQEBAQECAYGBQYJCAoKCQgJCQoM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', "long_description": "Execute a workflow when you get an email", }] return (
{triggers.map(trigger => { var imageline = trigger.large_image.length === 0 ? : return( {handleTriggerDrag(e, trigger)}} onStop={(e) => {handleDragStop(e)}} dragging={false} position={{ x: 0, y: 0, }} > {}}>
{imageline}

{trigger.name}

{trigger.description}
) })}
) } var newNodeId = "" const handleTriggerDrag = (e, data) => { 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) { if (newNodeId.length > 0) { var currentnode = cy.getElementById(newNodeId) if (currentnode.length === 0) { return } currentnode[0].renderedPosition("x", e.pageX-cycontainer.offsetLeft) currentnode[0].renderedPosition("y", e.pageY-cycontainer.offsetTop) } else{ console.log(workflow) if (workflow.start === "" || workflow.start === undefined) { alert.error("Define a starting action first.") return } if (data.is_valid === false) { alert.error("Sorry, "+data.name+" is not ready yet") return } newNodeId = uuid.v4() console.log(data) const newposition = { "x": e.pageX-cycontainer.offsetLeft, "y": e.pageY-cycontainer.offsetTop, } console.log(data) const newAppData = { app_name: data.name, app_version: "1.0.0", environment: data.environment, description: data.description, long_description: data.long_description, errors: [], id_: newNodeId, _id_: newNodeId, id: newNodeId, label: data.label, type: data.type, is_valid: true, trigger_type: data.trigger_type, large_image: data.large_image, status: "uninitialized", name: data.name, isStartNode: false, position: newposition, } // Can all the data be in here? hmm const nodeToBeAdded = { group: "nodes", data: newAppData, renderedPosition: newposition, } cy.add(nodeToBeAdded) if (workflow.triggers === undefined) { workflow.triggers = [newAppData] } else { workflow.triggers.push(newAppData) } const newEdgeUuid = uuid.v4() const newbranch = { "source_id": newNodeId, "destination_id": workflow.start, "id_": newEdgeUuid, "id": newEdgeUuid, "hasErrors": false, } const newcybranch = { "source": newNodeId, "target": workflow.start, "_id": newEdgeUuid, "id": newEdgeUuid, "hasErrors": false, } const edgeToBeAdded = { group: "edges", data: newcybranch, } if (data.name !== "User Input") { workflow.branches.push(newbranch) cy.add(edgeToBeAdded) } setWorkflow(workflow) } } } 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) { if (newNodeId.length > 0) { var currentnode = cy.getElementById(newNodeId) if (currentnode.length === 0) { return } currentnode[0].renderedPosition("x", e.pageX-cycontainer.offsetLeft) currentnode[0].renderedPosition("y", e.pageY-cycontainer.offsetTop) } else{ 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 } newNodeId = uuid.v4() const actionType = "ACTION" const actionLabel = getNextActionName(app.name) var parameters = null if (app.actions[0].parameters !== null && app.actions[0].parameters.length > 0) { parameters = app.actions[0].parameters } var newAppPopup = false if (app.authentication !== undefined && app.authentication !== null && app.authentication.required === true) { console.log("Should make modal popup for new app") newAppPopup = true } 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[0].Name, errors: [], id_: newNodeId, _id_: newNodeId, id: newNodeId, is_valid: true, label: actionLabel, type: actionType, name: app.actions[0].name, parameters: parameters, isStartNode: false, large_image: app.large_image, authentication: [], } console.log(newAppData) // FIXME - find the cytoscape offset position // Can this be done with zoom calculations? const nodeToBeAdded = { group: "nodes", data: newAppData, renderedPosition: { x: e.layerX, y: e.layerY, } } cy.add(nodeToBeAdded) if (workflow.actions === undefined || workflow.actions.length === 0) { workflow.start = newAppData.id workflow.actions = [] newAppData.isStartNode = true //setStartNode(newAppData.id) } if (workflow.actions.length > 0 && elements.length === 0) { const actions = workflow.actions.map(action => { const node = {} node.position = action.position node.data = action node.data._id = action["id"] node.data.type = "ACTION" node.isStartNode = action["id"] === workflow.start return node; }) const tmpelements = [].concat(actions) setElements(tmpelements) } if (workflow.actions.length === 1 && workflow.actions[0].id === workflow.start) { const newEdgeUuid = uuid.v4() const newcybranch = { "source": workflow.start, "target": newNodeId, "_id": newEdgeUuid, "id": newEdgeUuid, "hasErrors": false, } const edgeToBeAdded = { group: "edges", data: newcybranch, } console.log("SHOULD STITCH WITH STARTNODE") cy.add(edgeToBeAdded) } workflow.actions.push(newAppData) setWorkflow(workflow) if (newAppPopup) { //alert.error("SHOULD MAKE USER AUTHENTICATE THE APP OR SET hasError") //alert.info("Remember: set the authentication for the user itself, not the app") } } } } const handleDragStop = (e) => { newNodeId = "" } const appScrollStyle = { overflow: "scroll", maxHeight: bodyHeight-appBarSize-150, minHeight: bodyHeight-appBarSize-150, overflowY: "auto", overflowX: "hidden", } const AppView = () => { return(
{filteredApps.map(app=> { // FIXME - add label to apps, as this might be slow with A LOT of apps var newAppname = app.name newAppname = newAppname.replace("_", " ") newAppname = newAppname.charAt(0).toUpperCase()+newAppname.substring(1) const image = "url("+app.large_image+")" return( {handleAppDrag(e, app)}} onStop={(e) => {handleDragStop(e)}} dragging={false} position={{ x: 0, y: 0, }} >

{newAppname}

Description Version: {app.app_version}
) })}
) } const getNextActionName = (appName) => { var highest = "" //label = name + _number for (var key in workflow.actions) { const item = workflow.actions[key] if (item.app_name === appName) { var number = item.label.split("_") if (isNaN(number[-1]) && parseInt(number[number.length-1]) > highest) { highest = number[number.length-1] } } } if (highest) { return appName+"_"+(parseInt(highest)+1) } else { return appName+"_"+1 } } const setNewSelectedAction = (e) => { const newaction = selectedApp.actions.find(a => a.name === e.target.value) selectedAction.name = newaction.name selectedAction.parameters = newaction.parameters // FIXME - this is broken sometimes lol //var env = environments.find(a => a.name === newaction.environment) //if ((!env || env === undefined) && selectedAction.environment === undefined ) { // env = environments[0] //} //setSelectedActionEnvironment(env) setSelectedAction(selectedAction) setSelectedActionName(e.target.value) } // APPSELECT at top // appname & version // description // ACTION select const selectedNameChange = (event) => { selectedAction.label = event.target.value setSelectedAction(selectedAction) } const selectedTriggerChange = (event) => { selectedTrigger.label = event.target.value setSelectedTrigger(selectedTrigger) } const getParents = (action) => { var allkeys = [action.id] var handled = [] var results = [] while(true) { for (var key in allkeys) { var currentnode = cy.getElementById(allkeys[key]) if (handled.includes(currentnode.data().id)) { continue } else { // Get the name / label here too? handled.push(currentnode.data().id) results.push(currentnode.data()) } if (currentnode.length === 0) { continue } const incomingEdges = currentnode.incomers('edge') if (incomingEdges.length === 0) { continue } for (var i = 0; i < incomingEdges.length; i++) { var tmp = incomingEdges[i] if (!allkeys.includes(tmp.data().source)) { allkeys.push(tmp.data().source) } } } if (results.length === allkeys.length) { break } } // Remove self results = results.filter(data => data.id !== action.id) results = results.filter(data => data.type !== "TRIGGER") results.push({"label": "Execution Argument", "type": "INTERNAL"}) return results } // BOLD name: type: required? // FORM // Dropdown -> static, action, local env, global env // VALUE (JSON) // {data.name}, {data.description}, {data.required}, {data.schema.type} const AppActionArguments = () => { const [selectedActionParameters, setSelectedActionParameters] = React.useState([]) const [selectedVariableParameter, setSelectedVariableParameter] = React.useState() useEffect(() => { if (selectedActionParameters !== null && selectedActionParameters.length === 0) { if (requiresAuthentication) { console.log("ADD AUTHENTICATION FIELDS") } 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) } }) const changeActionParameter = (event, count) => { selectedActionParameters[count].value = event.target.value selectedAction.parameters = selectedActionParameters setSelectedAction(selectedAction) } const changeActionParameterVariable = (fieldvalue, count) => { setSelectedVariableParameter(fieldvalue) // this isn't updated anywhere in the workflow setSelectedActionName({}) setSelectedAction({}) setSelectedTrigger({}) setSelectedApp({}) setSelectedEdge({}) selectedActionParameters[count].action_field = fieldvalue selectedAction.parameters = selectedActionParameters setSelectedActionName(selectedActionName) setSelectedApp(selectedApp) setSelectedAction(selectedAction) } // 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 setSelectedActionName({}) setSelectedAction({}) setSelectedTrigger({}) setSelectedApp({}) setSelectedEdge({}) // FIXME - check if startnode // Set value setSelectedActionName(selectedActionName) setSelectedApp(selectedApp) setSelectedAction(selectedAction) } 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 setSelectedActionName({}) setSelectedAction({}) setSelectedTrigger({}) setSelectedApp({}) setSelectedEdge({}) // FIXME - check if startnode // Set value setSelectedActionName(selectedActionName) setSelectedApp(selectedApp) setSelectedAction(selectedAction) } if (Object.getOwnPropertyNames(selectedAction).length > 0 && selectedActionParameters) { return (
Arguments {selectedActionParameters.map((data, count) => { if (data.variant === "") { data.variant = "STATIC_VALUE" } var staticcolor = "inherit" var actioncolor = "inherit" var varcolor = "inherit" var multiline = false if (data.multiline !== undefined && data.multiline !== null && data.multiline === true) { multiline = true } var placeholder = "Static value" if (data.example !== undefined && data.example !== null && data.example.length > 0) { placeholder = data.example } var datafield = { changeActionParameter(event, count) }} /> // Remap data based on variant 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) }} />
} else if (data.variant === "WORKFLOW_VARIABLE") { varcolor = "#f85a3e" if (workflow.workflow_variables === null || workflow.workflow_variables === undefined || workflow.workflow_variables.length === 0) { setCurrentView("variables") 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 = } } var itemColor = "#f85a3e" if (!data.required) { itemColor = "#ffeb3b" } return (
{data.name}:
{ changeActionParameterVariant("STATIC_VALUE", count) }}> static
 | 
{ changeActionParameterVariant("ACTION_RESULT", count) }}> action
 | 
{ changeActionParameterVariant("WORKFLOW_VARIABLE", count) }}> var
{datafield}
)})}
) } return null } //height: "100%", const appApiViewStyle = { display: "flex", flexDirection: "column", backgroundColor: "#1F2023", color: "white", paddingRight: 10, paddingLeft: 10, minHeight: "100%", } 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) 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) workflow.actions[newnodecnt].isStartNode = true } // 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()) } } } workflow.start = selectedAction.id setWorkflow(workflow) //setStartNode(selectedAction.id) } const appApiView = Object.getOwnPropertyNames(selectedAction).length > 0 && Object.getOwnPropertyNames(selectedApp).length > 0 ?

{selectedAction.app_name}

What are apps?
Name
Environment:
{/*requiresAuthentication ?
: null*/}
Actions
: null const headerSize = 74 const rightsidebarStyle = { position: "fixed", right: 0, top: headerSize+1, height: "100%", bottom: 0, minWidth: "350px", maxWidth: "350px", borderLeft: "1px solid rgb(91, 96, 100)", overflow: "scroll", overflowX: "auto", overflowY: "auto", } const setTriggerFolderWrapperMulti = event => { const { options } = event.target; const value = []; for (let i = 0, l = options.length; i < l; i += 1) { if (options[i].selected) { value.push(options[i].value); } } if (selectedTrigger.parameters === null) { selectedTrigger.parameters = [[]] workflow.triggers[selectedTriggerIndex].parameters = [[]] } // This is a dirty workaround for the static values in the go backend and datastore db const fixedValue = value.join(splitter) selectedTrigger.parameters[0] = {"value": fixedValue, "name": "outlookfolder"} workflow.triggers[selectedTriggerIndex].parameters[0] = {"value": fixedValue, "name": "outlookfolder"} // This resets state for some reason (: setSelectedActionName({}) setSelectedAction({}) setSelectedTrigger({}) setSelectedApp({}) setSelectedEdge({}) // Set value setSelectedTrigger(selectedTrigger) setWorkflow(workflow) }; //const setTriggerFolderWrapper = (event) => { // if (selectedTrigger.parameters === null) { // selectedTrigger.parameters = [] // workflow.triggers[selectedTriggerIndex].parameters = [] // } // const folder = triggerFolders.find(a => a.displayName === event.target.value) // console.log(event.target.value) // console.log(folder) // // if (folder !== undefined) { // workflow.triggers[selectedTriggerIndex].parameters[0] = {"value": folder.displayName, "name": "outlookfolder", "id": folder.id} // selectedTrigger.parameters[0] = {"value": folder.displayName, "name": "outlookfolder", "id": folder.id} // setWorkflow(workflow) // // This resets state for some reason (: // setSelectedActionName({}) // setSelectedActionEnvironment({}) // setSelectedAction({}) // setSelectedTrigger({}) // setSelectedApp({}) // setSelectedEdge({}) // // Set value // setSelectedTrigger(selectedTrigger) // } else { // alert.error("Some error occurred with folder "+event.target.value) // } //} const setTriggerCronWrapper = (value) => { if (selectedTrigger.parameters === null) { selectedTrigger.parameters = [] } workflow.triggers[selectedTriggerIndex].parameters[0] = {"value": value, "name": "cron"} setWorkflow(workflow) } const setTriggerOptionsWrapper = (value) => { if (selectedTrigger.parameters === null) { selectedTrigger.parameters = [] } const splitItems = workflow.triggers[selectedTriggerIndex].parameters[2].value.split(",") console.log(splitItems) if (splitItems.includes(value)) { for( var i = 0; i < splitItems.length; i++){ if (splitItems[i] === value) { splitItems.splice(i, 1); } } } else { splitItems.push(value) } for( var i = 0; i < splitItems.length; i++){ if (splitItems[i] === "") { splitItems.splice(i, 1); } } workflow.triggers[selectedTriggerIndex].parameters[2].value = splitItems.join(",") console.log(splitItems) setWorkflow(workflow) setLocalFirstrequest(!localFirstrequest) } const setTriggerTextInformationWrapper = (value) => { if (selectedTrigger.parameters === null) { selectedTrigger.parameters = [] } workflow.triggers[selectedTriggerIndex].parameters[0] = {"value": value, "name": "alertinfo"} setWorkflow(workflow) } const setTriggerBodyWrapper = (value) => { if (selectedTrigger.parameters === null) { selectedTrigger.parameters = [] workflow.triggers[selectedTriggerIndex].parameters[0] = {"value": value, "name": "cron"} } workflow.triggers[selectedTriggerIndex].parameters[1] = {"value": value, "name": "execution_argument"} setWorkflow(workflow) } const AppConditionHandler = (props) => { const { tmpdata, type } = props; if (tmpdata === undefined) { return tmpdata } const [data, ] = useState(tmpdata) if (data.variant === "") { data.variant = "STATIC_VALUE" } var staticcolor = "inherit" var actioncolor = "inherit" var varcolor = "inherit" var multiline = false if (data.multiline !== undefined && data.multiline !== null && data.multiline === true) { multiline = true } var placeholder = "Static value" if (data.example !== undefined && data.example !== null && data.example.length > 0) { placeholder = data.example } var datafield = { changeActionVariable(data.action_field, e.target.value) }} /> console.log(data) // Remap data based on variant if (data.variant === "STATIC_VALUE") { staticcolor = "#f85a3e" } else if (data.variant === "ACTION_RESULT") { // Gets the parents of the current node var parents = getParents(workflow.actions.find(a => a.id === selectedEdge["target"])) actioncolor = "#f85a3e" // set the datafield //var datafieldvalue = "Error: No parents. Action not eligible" //if (parents.length > 0) { // datafieldvalue = parents[0].label //} datafield =
{ changeActionVariable(data.action_field, e.target.value) }} />
} else if (data.variant === "WORKFLOW_VARIABLE") { varcolor = "#f85a3e" if (workflow.workflow_variables === null || workflow.workflow_variables === undefined || workflow.workflow_variables.length === 0) { setCurrentView("variables") 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 = } } const changeActionVariable = (variable, value) => { // set the name data.value = value data.action_field = variable //setConditionValue({}) if (type === "source") { setSourceValue(data) //setDestinationValue(destinationValue) } else if (type === "destination") { setDestinationValue(data) //setSourceValue(sourceValue) } } const changeActionParameterVariant = (variant) => { if (data.variant === variant) { return } data.variant = variant data.value = "" if (variant === "ACTION_RESULT") { console.log("SHOULD FIND PARENTS OF EDGE") // Uses the target's parents, as the target should be executing the checks (I think) var parents = getParents(workflow.actions.find(a => a.id === selectedEdge["target"])) if (parents.length > 0) { console.log(parents) data.action_field = parents[0].label } else { data.action_field = "" } } else if (variant === "WORKFLOW_VARIABLE") { if (workflow.workflow_variables !== null && workflow.workflow_variables !== undefined && workflow.workflow_variables.length > 0) { data.action_field = workflow.workflow_variables[0].name } } setSourceValue({}) setConditionValue({}) setDestinationValue({}) if (type === "source") { setSourceValue(data) setDestinationValue(destinationValue) } else if (type === "destination") { setDestinationValue(data) setSourceValue(sourceValue) } } return (
{data.name}
{ changeActionParameterVariant("STATIC_VALUE") }}> static
 | 
{ changeActionParameterVariant("ACTION_RESULT") }}> action
 | 
{ changeActionParameterVariant("WORKFLOW_VARIABLE") }}> var
{datafield}
) } const menuItemStyle = { color: "white", } const conditionsModal = { setConditionsModalOpen(false) setSourceValue({}) setConditionValue({}) setDestinationValue({}) }} >
Condition
{ setVariableAnchorEl(null) }} > { conditionValue.value = "equals" setConditionValue(conditionValue) setVariableAnchorEl(null) }} key={"equals"}>equals { conditionValue.value = "does not equal" setConditionValue(conditionValue) setVariableAnchorEl(null) }} key={"does not equal"}>does not equal { conditionValue.value = "startswith" setConditionValue(conditionValue) setVariableAnchorEl(null) }} key={"starts with"}>starts with { conditionValue.value = "endswith" setConditionValue(conditionValue) setVariableAnchorEl(null) }} key={"ends with"}>ends with { conditionValue.value = "endswith" setConditionValue(conditionValue) setVariableAnchorEl(null) }} key={"ends with"}>ends with { conditionValue.value = "contains" setConditionValue(conditionValue) setVariableAnchorEl(null) }} key={"contains"}>contains { conditionValue.value = "matches regex" setConditionValue(conditionValue) setVariableAnchorEl(null) }} key={"matches regex"}>matches regex
const EdgeSidebar = () => { const ConditionHandler = (condition, index) => { const [open, setOpen] = React.useState(false); const [anchorEl, setAnchorEl] = React.useState(null); const deleteCondition = (conditionIndex) => { //selectedEdge.conditions.splice(conditionIndex, 1) //setSelectedEdge(selectedEdge) } const paperVariableStyle = { minHeight: "50px", maxHeight: "50px", minWidth: "100%", maxWidth: "100%", marginTop: "5px", color: "white", backgroundColor: surfaceColor, cursor: "pointer", display: "flex", } const menuClick = (event) => { setOpen(!open) setAnchorEl(event.currentTarget); } return ( {}}>
{ setSourceValue(condition.source) setConditionValue(condition.condition) setDestinationValue(condition.destination) setConditionsModalOpen(true) }}>
{condition.source.value}
{}}> {condition.condition.value}
{condition.destination.value}
{ setOpen(false) setAnchorEl(null) }} > { setOpen(false) deleteCondition(index) }} key={"Delete"}>{"Delete"}
) } var injectedData =
if (selectedEdge.conditions !== undefined && selectedEdge.conditions !== null && selectedEdge.conditions.length > 0) { injectedData = selectedEdge.conditions.map((condition, index) => { return ConditionHandler(condition, index) }) } // FIXME - remove index const conditionId = uuid.v4() return(

Branch: Conditions - {selectedEdgeIndex}

What are conditions?
Conditions
{injectedData}
) } // 1. GET the trigger authentication data // 2. Parse the fields that are used (outlook & gmail) // 3. Parse the folders that are selected // 4. Start / stop const EmailSidebar = () => { if (Object.getOwnPropertyNames(selectedTrigger).length === 0) { return null } if (workflow.triggers[selectedTriggerIndex] === undefined) { return null } if (workflow.triggers[selectedTriggerIndex].parameters === undefined || workflow.triggers[selectedTriggerIndex].parameters === null || workflow.triggers[selectedTriggerIndex].parameters.length === 0) { workflow.triggers[selectedTriggerIndex].parameters = [{"value": "No folders selected yet", "name": "outlookfolder"}] selectedTrigger.parameters = [{"value": "No folders selected yet", "name": "outlookfolder"}] setWorkflow(workflow) setSelectedTrigger(selectedTrigger) } const setFolders = () => { fetch(globalUrl+"/functions/outlook/getFolders?trigger_id="+selectedTrigger.id, { method: "GET", headers: {"content-type": "application/json"}, credentials: "include", }) .then((response) => { if (response.status !== 200) { throw new Error("No folders :o!") } return response.json() }) .then((responseJson) => { setTriggerFolders(responseJson) if (workflow.triggers[selectedTriggerIndex].parameters.length === 0 && responseJson.length > 0) { workflow.triggers[selectedTriggerIndex].parameters = [{"value": responseJson[0].displayName, "name": "outlookfolder", "id": responseJson[0].id}] selectedTrigger.parameters = [{"value": responseJson[0].displayName, "name": "outlookfolder", "id": responseJson[0].id}] setWorkflow(workflow) setSelectedTrigger(selectedTrigger) } }) .catch(error => { console.log(error.toString()) }); } const getTriggerAuth = () => { fetch(globalUrl+"/api/v1/triggers/"+selectedTrigger.id, { method: "GET", headers: {"content-type": "application/json"}, credentials: "include", }) .then((response) => { if (response.status !== 200) { throw new Error("No trigger info :o!") } return response.json() }) .then((responseJson) => { setTriggerAuthentication(responseJson) }) .catch(error => { console.log(error.toString()) }); } // Getting the triggers and the folders if they exist // This is horrible hahah if (localFirstrequest) { getTriggerAuth() setFolders() setLocalFirstrequest(false) } const outlookButton = // FIXME - set everything in here to multifolder etc var triggerInfo = "SET UP BUT NO TYPE :)" if (Object.getOwnPropertyNames(triggerAuthentication).length > 0) { // Should get the folders if they don't already exist if (triggerAuthentication.type === "outlook") { triggerInfo =
Login:
{outlookButton}
Folders: (hold CTRL to select multiple)
} key={selectedTrigger} > {triggerFolders.map(folder => { var folderItem = if (folder.childFolderCount > 0) { // Here to handle subfolders sometime later folderItem = } return folderItem })}
} else if (triggerAuthentication.type === "gmail") { triggerInfo = "SPECIAL GMAIL" } } // Check const argumentView = Object.getOwnPropertyNames(triggerAuthentication).length > 0 ?
{triggerInfo}
:
Login to either:
{outlookButton}
return(

{selectedTrigger.app_name}: {selectedTrigger.status}

What are webhooks?
Name
Environment:
{argumentView}
) } const WebhookSidebar = () => { if (Object.getOwnPropertyNames(selectedTrigger).length > 0) { if (workflow.triggers[selectedTriggerIndex] === undefined) { return null } if (workflow.triggers[selectedTriggerIndex].parameters === undefined || workflow.triggers[selectedTriggerIndex].parameters === null || workflow.triggers[selectedTriggerIndex].parameters.length === 0) { workflow.triggers[selectedTriggerIndex].parameters = [] workflow.triggers[selectedTriggerIndex].parameters[0] = {"name": "url", "value": referenceUrl+"webhook_"+selectedTrigger.id} workflow.triggers[selectedTriggerIndex].parameters[1] = {"name": "tmp", "value": "webhook_"+selectedTrigger.id} setWorkflow(workflow) } //const cronValue = "*/15 * * * *" return(

{selectedTrigger.app_name}: {selectedTrigger.status}

What are webhooks?
Name
Environment:
Arguments
Webhook URI:
{ //alert.info("Saved URI to clipboard") console.log("Copy to clipboooooard") }} InputProps={{ style:{ color: "white", height: "50px", marginLeft: "5px", maxWidth: "95%", fontSize: "1em", }, }} fullWidth disabled defaultValue={workflow.triggers[selectedTriggerIndex].parameters[0].value} color="primary" placeholder="defaultValue" onBlur={(e) => { setTriggerCronWrapper(e.target.value) }} />
Execution argument:
{ setTriggerBodyWrapper(e.target.value) }} />
) } return null } //const getTriggerAuth = (trigger_id) => { // fetch(globalUrl+"/api/v1/triggers/"+trigger_id, { // method: "GET", // headers: {"content-type": "application/json"}, // credentials: "include", // }) // .then((response) => response.json()) // .then((responseJson) => { // if (responseJson.success) { // console.log("SUCCESS") // console.log(responseJson) // } else { // console.log("FAIL") // } // }) // .catch(error => { // console.log(error.toString()) // }); //} const stopMailSub = (trigger, triggerindex) => { // DELETE if (trigger.id === undefined) { return } alert.info("Stopping trigger") fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/outlook/"+trigger.id, { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", }) .then((response) => { if (response.status !== 200) { throw new Error("Status not 200 for stream results :O!") } return response.json() }) .then((responseJson) => { if (responseJson.success) { alert.success("Successfully stopped trigger") // Set the status workflow.triggers[triggerindex].status = "stopped" trigger.status = "stopped" setWorkflow(workflow) setSelectedTrigger(trigger) saveWorkflow(workflow) } else { alert.error("Failed stopping trigger: "+responseJson.reason) } }) .catch(error => { alert.error(error.toString()) }); } const startMailSub = (trigger, triggerindex) => { var folders = [] const splitItem = workflow.triggers[selectedTriggerIndex].parameters[0].value.split(splitter) for (var key in splitItem) { const item = splitItem[key] const curfolder = triggerFolders.find(a => a.displayName === item) if (curfolder === undefined) { alert.error("Something went wrong with outlook folder "+item) return } folders.push(curfolder.id) } alert.info("Creating outlook subscription with name " + trigger.name) const data = { "name": trigger.name, "folders": folders, "id": trigger.id, } fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/outlook", { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, body: JSON.stringify(data), credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for stream results :O!") } return response.json() }) .then((responseJson) => { if (!responseJson.success) { alert.error("Failed to start outlook: " + responseJson.reason) } else { alert.success("Successfully started outlook sub") workflow.triggers[triggerindex].status = "running" trigger.status = "running" setWorkflow(workflow) setSelectedTrigger(trigger) saveWorkflow(workflow) } }) .catch(error => { alert.error(error.toString()) }); } const newWebhook = (trigger) => { const hookname = trigger.label if (hookname.length === 0) { alert.error("Missing name") return } if (trigger.id.length !== 36) { alert.error("Missing id") return } alert.info("Starting webhook") const data = { "name": hookname, "type": "webhook", "id": trigger.id, "workflow": workflow.id, } fetch(globalUrl+"/api/v1/hooks/new", { method: "POST", headers: {"content-type": "application/json"}, body: JSON.stringify(data), credentials: "include", }) .then((response) => response.json()) .then((responseJson) => { if (responseJson.success) { // Set the status alert.success("Successfully started webhook") workflow.triggers[selectedTriggerIndex].status = "running" trigger.status = "running" setWorkflow(workflow) setSelectedTrigger(trigger) saveWorkflow(workflow) } else { alert.error("Failed starting webhook: "+responseJson.reason) } }) .catch(error => { console.log(error.toString()) }); } const deleteWebhook = (trigger, triggerindex) => { if (trigger.id === undefined) { return } alert.info("Stopping webhook") fetch(globalUrl+"/api/v1/hooks/"+trigger.id+"/delete", { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, credentials: "include", }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for stream results :O!") } return response.json() }) .then((responseJson) => { if (responseJson.success) { alert.success("Successfully stopped webhook") // Set the status workflow.triggers[triggerindex].status = "stopped" trigger.status = "stopped" setWorkflow(workflow) setSelectedTrigger(trigger) saveWorkflow(workflow) } else { alert.error("Failed stopping webhook: "+responseJson.reason) } }) .catch(error => { alert.error(error.toString()) }); } const UserinputSidebar = () => { if (Object.getOwnPropertyNames(selectedTrigger).length > 0 && workflow.triggers[selectedTriggerIndex] !== undefined) { console.log(workflow.triggers[selectedTriggerIndex]) console.log(selectedTrigger) if (workflow.triggers[selectedTriggerIndex].parameters === undefined || workflow.triggers[selectedTriggerIndex].parameters === null || workflow.triggers[selectedTriggerIndex].parameters.length === 0) { workflow.triggers[selectedTriggerIndex].parameters = [] workflow.triggers[selectedTriggerIndex].parameters[0] = {"name": "alertinfo", "value": "hello this is an alert"} // boolean, workflow.triggers[selectedTriggerIndex].parameters[1] = {"name": "options", "value": "boolean"} // email,sms,app ... workflow.triggers[selectedTriggerIndex].parameters[2] = {"name": "type", "value": "email"} setWorkflow(workflow) } return(

{selectedTrigger.app_name}: {selectedTrigger.status}

What are schedules?
Name
Environment:
Arguments
Information:
{ setTriggerTextInformationWrapper(e.target.value) }} />
Contact options:
{ setTriggerOptionsWrapper("email") }} color="primary" value="email" /> } label={
Email
} /> { setTriggerOptionsWrapper("sms") }} color="primary" value="sms" /> } label={
SMS
} />
) } return null } const ScheduleSidebar = () => { if (Object.getOwnPropertyNames(selectedTrigger).length > 0 && workflow.triggers[selectedTriggerIndex] !== undefined) { if (workflow.triggers[selectedTriggerIndex].parameters === undefined || workflow.triggers[selectedTriggerIndex].parameters === null || workflow.triggers[selectedTriggerIndex].parameters.length === 0) { workflow.triggers[selectedTriggerIndex].parameters = [] workflow.triggers[selectedTriggerIndex].parameters[0] = {"name": "cron", "value": "*/15 * * * *"} workflow.triggers[selectedTriggerIndex].parameters[1] = {"name": "execution_argument", "value": '{"example": {"json": "is cool"}}'} setWorkflow(workflow) } return(

{selectedTrigger.app_name}: {selectedTrigger.status}

What are schedules?
Name
Environment:
Arguments
Cron:
{ setTriggerCronWrapper(e.target.value) }} />
Execution argument:
{ setTriggerBodyWrapper(e.target.value) }} />
) } return null } const bottomBarStyle = { position: "fixed", right: 20, left: leftBarSize, bottom: 0, minWidth: "100%", marginLeft: 20, marginBottom: 20, } const topBarStyle= { position: "fixed", right: 0, left: leftBarSize, top: appBarSize, minWidth: "60%", marginLeft: 20, marginBottom: 20, } const TopCytoscapeBar = () => { return (

Editing workflow {workflow.name}

) } const BottomCytoscapeBar = () => { const boxSize = 100 const executionButton = executionRunning ? : return(
{executionButton}
{ setExecutionText(e.target.value) }} />
) } const RightSideBar = () => { setLastSaved(false) if (Object.getOwnPropertyNames(selectedAction).length > 0 && Object.getOwnPropertyNames(selectedApp).length > 0) { //console.time('ACTIONSTART'); return(
{appApiView}
) } else if (Object.getOwnPropertyNames(selectedTrigger).length > 0) { if (selectedTrigger.trigger_type === "SCHEDULE") { console.log("SCHEDULE") return(
) } else if (selectedTrigger.trigger_type === "WEBHOOK") { console.log("WEBHOOK") return(
) } else if (selectedTrigger.trigger_type === "EMAIL") { console.log("EMAIL") return(
) } else if (selectedTrigger.trigger_type === "USERINPUT") { console.log("USER INPUT SIDEBAR") return(
) } else if (selectedTrigger.trigger_type === undefined) { return null } else { console.log("Unable to handle invalid trigger type "+selectedTrigger.trigger_type) return null } } else if (Object.getOwnPropertyNames(selectedEdge).length > 0) { return(
) } return( null ) } // This can execute a workflow with firestore. Used for test, as datastore is old and stuff // Too much work to move everything over alone, so won't touch it for now // // const leftView = leftViewOpen ?
:
{ setLeftViewOpen(true) setLeftBarSize(350) }}>
const newView = isLoggedIn ?
{leftView} { setCy(incy) }} />
:
TMP FOR NOT LOGGED IN
const variablesModal = variablesModalOpen ? { setNewVariableName("") setNewVariableDescription("") setNewVariableValue("") setVariablesModalOpen(false) }} PaperProps={{ style: { backgroundColor: surfaceColor, color: "white", }, }} > Workflow Variable setNewVariableName(event.target.value)} color="primary" placeholder="Name" InputProps={{ style:{ color: "white" } }} margin="dense" fullWidth defaultValue={newVariableName} /> setNewVariableDescription(event.target.value)} color="primary" placeholder="Description" margin="dense" fullWidth InputProps={{ style:{ color: "white" } }} defaultValue={newVariableDescription} /> setNewVariableValue(event.target.value)} rows="6" multiline color="primary" placeholder="Value" margin="dense" InputProps={{ style:{ color: "white" } }} fullWidth defaultValue={newVariableValue} /> : null const AuthenticationData = () => { console.log("AUTH: ", selectedApp.authentication) const [tmpVar, setTmpVar] = React.useState("") if (selectedApp.authentication === undefined) { return null } if (selectedApp.authentication.parameters.length === undefined || selectedApp.authentication.parameters.length === 0) { return null } // Yes, it should be possible to have more than one, but.. :) // This data should be written to a KMS, then have the ID point back const currentAuth = selectedApp.authentication.parameters[0] if (currentAuth.scheme.toLowerCase() === "bearer") { return
Insert your API token for {selectedApp.name} { setTmpVar(event.target.value) }} onBlur={() => { selectedApp.authentication.parameters[0].value = tmpVar setSelectedApp(selectedApp) }} />
} return (
NOT IMPLEMENTED
Unknown auth: {currentAuth.scheme}
) } const EndpointData = () => { const [tmpVar, setTmpVar] = React.useState("") return (
The API endpoint to use (URL) - leave this if you're unsure { setTmpVar(event.target.value) }} onBlur={() => { selectedApp.link = tmpVar console.log("LINK: ", selectedApp.link) setSelectedApp(selectedApp) }} />
) } const authenticationModal = authenticationModalOpen ? { setAuthenticationModalOpen(false) setAppAuthentication({}) }} PaperProps={{ style: { backgroundColor: surfaceColor, color: "white", minWidth: "800px", }, }} >
Authentication for {selectedApp.name}
What is this?
: null const loadedCheck = isLoaded && isLoggedIn && workflowDone ?
{newView} {variablesModal} {conditionsModal} {authenticationModal}
:
return (
{loadedCheck}
) } export default AngularWorkflow;