diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index fa5a2e35..4e64fc86 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -18,7 +18,7 @@ require ( github.com/docker/docker v1.13.1 github.com/docker/go-connections v0.4.0 github.com/docker/go-units v0.4.0 // indirect - github.com/frikky/shuffle-shared v0.0.20 + github.com/frikky/shuffle-shared v0.0.23 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 github.com/go-git/go-git/v5 v5.0.0 diff --git a/backend/go-app/go.sum b/backend/go-app/go.sum index a20b5e4c..cc0824a0 100644 --- a/backend/go-app/go.sum +++ b/backend/go-app/go.sum @@ -90,6 +90,12 @@ github.com/frikky/shuffle-shared v0.0.12 h1:+0EIfThmK47Po+LogPYZR4XjbS4Ds19WNMFu github.com/frikky/shuffle-shared v0.0.12/go.mod h1:SEY432/xs4oBkOUnGwxiYKCZWZLRaheGInhAoW7N8ww= github.com/frikky/shuffle-shared v0.0.15 h1:508ceeEHfPBMCC8/K4Zve3kwRQqiXNJSw6+BDoq9X4E= github.com/frikky/shuffle-shared v0.0.15/go.mod h1:SEY432/xs4oBkOUnGwxiYKCZWZLRaheGInhAoW7N8ww= +github.com/frikky/shuffle-shared v0.0.20 h1:y6JlPnQDq//elICWvVfUfJyU9gH3fSpQmPy+agqZ5sA= +github.com/frikky/shuffle-shared v0.0.20/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ= +github.com/frikky/shuffle-shared v0.0.21 h1:xj/XPsXTa2rx41mm4nUc7+2K9RGkq2/mpjSPtIfpjE4= +github.com/frikky/shuffle-shared v0.0.21/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ= +github.com/frikky/shuffle-shared v0.0.22 h1:TFMcJCNmOOSneMMWbg5dNzp2z6m0aZLROuL+bzVToRE= +github.com/frikky/shuffle-shared v0.0.22/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ= github.com/getkin/kin-openapi v0.8.0 h1:a6TQjTqwkyscC4/hShJX7WhCVE+4bi9lzw61XHQW5hE= github.com/getkin/kin-openapi v0.8.0/go.mod h1:zZQMFkVgRHCdhgb6ihCTIo9dyDZFvX0k/xAKqw1FhPw= github.com/getkin/kin-openapi v0.52.0 h1:6WqsF5d6PfJ8AscdD+9Rtb2RP2iBWyC7V6GcjssWg7M= diff --git a/docker-compose.yml b/docker-compose.yml index 45656376..1e208eb2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,8 +1,8 @@ version: '3' services: frontend: - build: ./frontend - image: ghcr.io/frikky/shuffle-frontend:0.8.70 + #build: ./frontend + image: ghcr.io/frikky/shuffle-frontend:0.8.64 container_name: shuffle-frontend hostname: shuffle-frontend ports: @@ -16,8 +16,8 @@ services: depends_on: - backend backend: - build: ./backend - image: ghcr.io/frikky/shuffle-backend:0.8.70 + #build: ./backend + image: ghcr.io/frikky/shuffle-backend:0.8.64 container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} # Here for debugging: @@ -47,7 +47,7 @@ services: - database orborus: #build: ./functions/onprem/orborus - image: ghcr.io/frikky/shuffle-orborus:0.8.70 + image: ghcr.io/frikky/shuffle-orborus:0.8.64 container_name: shuffle-orborus hostname: shuffle-orborus networks: diff --git a/frontend/src/defaultCytoscapeStyle.js b/frontend/src/defaultCytoscapeStyle.js index f2538a94..2732e381 100644 --- a/frontend/src/defaultCytoscapeStyle.js +++ b/frontend/src/defaultCytoscapeStyle.js @@ -55,7 +55,6 @@ const data = [{ css: { 'width': '30px', 'height': '30px', - 'font-size': '0px', }, }, { diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 8082415d..dcc792fd 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -10,8 +10,7 @@ import { useBeforeunload } from 'react-beforeunload'; import NestedMenuItem from "material-ui-nested-menu-item"; import {TextField, Drawer, Button, Paper, Grid, Tabs, InputAdornment, Tab, ButtonBase, Tooltip, Select, MenuItem, Divider, Dialog, Modal, DialogActions, DialogTitle, InputLabel, DialogContent, FormControl, IconButton, Menu, Input, FormGroup, FormControlLabel, Typography, Checkbox, Breadcrumbs, CircularProgress, Switch, Fade} from '@material-ui/core'; - -import {ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; +import {GetApp as GetAppIcon, Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; import * as cytoscape from 'cytoscape'; import * as edgehandles from 'cytoscape-edgehandles'; @@ -90,6 +89,8 @@ const AngularWorkflow = (props) => { const alert = useAlert() const borderRadius = 5 const theme = useTheme(); + const green = "#86c142" + const yellow = "#FECC00" const [bodyWidth, bodyHeight] = useWindowSize(); @@ -97,7 +98,6 @@ const AngularWorkflow = (props) => { const [cystyle, ] = useState(cytoscapestyle) const [cy, setCy] = React.useState() - const [appSearch, setAppSearch] = React.useState("") const [currentView, setCurrentView] = React.useState(0) const [triggerAuthentication, setTriggerAuthentication] = React.useState({}) const [triggerFolders, setTriggerFolders] = React.useState([]) @@ -284,14 +284,14 @@ const AngularWorkflow = (props) => { }) .then((response) => { if (response.status !== 200) { - console.log("Status not 200 for WORKFLOW EXECUTION :O!") + console.log("Status not 200 for APIKEY gen :O!") } return response.json() }) .then((responseJson) => { setUserSettings(responseJson) - }) + }) .catch(error => { console.log(error) }); @@ -308,7 +308,7 @@ const AngularWorkflow = (props) => { }) .then((response) => { if (response.status !== 200) { - console.log("Status not 200 for WORKFLOW EXECUTION :O!") + console.log("Status not 200 for get settings :O!") } return response.json() @@ -477,7 +477,7 @@ const AngularWorkflow = (props) => { }) .then((response) => { if (response.status !== 200) { - console.log("Status not 200 for WORKFLOW EXECUTION :O!") + console.log("Status not 200 for ABORT EXECUTION :O!") } else { alert.success("Execution aborted") } @@ -798,6 +798,21 @@ const AngularWorkflow = (props) => { workflow.errors = responseJson.errors if (responseJson.errors.length === 0) { workflow.isValid = true + workflow.is_valid = true + + //console.log("ELEMENTS: ", cy.elements()) + //const setupGraph = () => { + const cyelements = cy.elements() + for (var i = 0; i < cyelements.length; i++) { + cyelements[i].removeStyle() + cyelements[i].data().is_valid = true + cyelements[i].data().errors = [] + } + + for (var key in workflow.actions) { + workflow.actions[key].is_valid = true + workflow.actions[key].errors = [] + } } for (var key in workflow.errors) { @@ -1049,7 +1064,7 @@ const AngularWorkflow = (props) => { setAuthLoaded(true) } else { setAuthLoaded(true) - alert.error("Failed getting authentications") + //alert.error("Failed getting authentications") } }) .catch(error => { @@ -1119,6 +1134,7 @@ const AngularWorkflow = (props) => { if (responseJson.isValid === undefined) { responseJson.isValid = true } + if (responseJson.errors === undefined) { responseJson.errors = [] } @@ -1219,10 +1235,21 @@ const AngularWorkflow = (props) => { } // Comparing locations between nodes and setting views - const onNodeDrag = (event, newAppAuth) => { + const onNodeDrag = (event) => { //console.log("DRAGGING: ", event.target) //console.log("LEN2: ", event.target.edges.length) + const nodedata = event.target.data() + if (nodedata.app_name == "Shuffle Tools" || nodedata.app_name == "Testing") { + //console.log("NODE: ", + //selector: `node[app_name="Shuffle Tools"]`, + console.log(event.target) + + // 1. Find location of node + // 2. Check if it's within view of another node (inside) + // 3. If it is, then hide text + } + /* event.target.animate({ @@ -1258,7 +1285,7 @@ const AngularWorkflow = (props) => { //const branch = workflow.branches.filter(branch => branch.source_id === data.id || branch.destination_id === data.id) console.log("NODE: ", data) - console.log("APPAUTH: ", newAppAuth) + //console.log("APPAUTH: ", newAppAuth) //console.log("BRANCHES: ", branch) if (data.type === "ACTION") { @@ -1682,15 +1709,21 @@ const AngularWorkflow = (props) => { return } + // App length necessary cus of cy initialization + //console.log("PRE ELEMENTS: !", workflow.actions, graphSetup, apps, authLoaded, cy) if (elements.length === 0 && workflow.actions !== undefined && !graphSetup && Object.getOwnPropertyNames(workflow).length > 0) { setGraphSetup(true) setupGraph() + + //console.log("IN ELEMENT CHECK!") } else if (!established && cy !== undefined && apps !== null && apps !== undefined && apps.length > 0 && Object.getOwnPropertyNames(workflow).length > 0 && authLoaded){ //This part has to load LAST, as it's kind of not async. //This means we need everything else to happen first. // //console.log("IN THIS PART AGAIN") + + //console.log("IN ESTABLISHED!") setEstablished(true) cy.edgehandles({ @@ -1719,7 +1752,7 @@ const AngularWorkflow = (props) => { cy.on('mouseout', 'node', (e) => onNodeHoverOut(e)) // Handles dragging - //cy.on('drag', 'node', (e) => onNodeDrag(e)) + cy.on('drag', 'node', (e) => onNodeDrag(e)) //cy.on('mouseover', 'node', () => $(targetElement).addClass('mouseover')); @@ -1801,6 +1834,7 @@ const AngularWorkflow = (props) => { node.data.type = "ACTION" node.isStartNode = action["id"] === workflow.start + var example = "" if (action.example !== undefined && action.example !== null && action.example.length > 0) { example = action.example @@ -1808,6 +1842,8 @@ const AngularWorkflow = (props) => { node.data.example = example + //node.data.is_valid = false + return node; }) @@ -2094,7 +2130,7 @@ const AngularWorkflow = (props) => {
{ }}> -
+
{ setNewVariableName(variable.name) @@ -2161,11 +2197,12 @@ const AngularWorkflow = (props) => {
{ }}> -
+
{ - setNewVariableName(variable.name) - setExecutionVariablesModalOpen(true)}}> + setNewVariableName(variable.name) + setExecutionVariablesModalOpen(true) + }}> Name: {variable.name}
@@ -2228,7 +2265,7 @@ const AngularWorkflow = (props) => { const HandleLeftView = () => { // Defaults to apps. - var thisview = + var thisview = if (currentView === 1) { thisview = } else if (currentView === 2) { @@ -2379,7 +2416,7 @@ const AngularWorkflow = (props) => { : - const color = trigger.is_valid ? "green" : "orange" + const color = trigger.is_valid ? green : yellow return( { const appScrollStyle = { overflow: "scroll", - maxHeight: bodyHeight-appBarSize-55, - minHeight: bodyHeight-appBarSize-55, + maxHeight: bodyHeight-appBarSize-55-50, + minHeight: bodyHeight-appBarSize-55-50, + marginTop: 1, overflowY: "auto", overflowX: "hidden", } - const runAppSearch = (event) => { - setAppSearch(event.target.value) - setFilteredApps(apps.filter(app => app.name.toLowerCase().includes(event.target.value.trim().toLowerCase()))) - } + const AppView = (props) => { + const { allApps, prioritizedApps, filteredApps } = props; + const [visibleApps, setVisibleApps] = React.useState(prioritizedApps.concat(filteredApps.filter(innerapp => !internalIds.includes(innerapp.id)))) - const ParsedAppPaper = (props) => { - const app = props.app - const [hover, setHover] = React.useState(false) + const ParsedAppPaper = (props) => { + const app = props.app + const [hover, setHover] = React.useState(false) - // FIXME - add label to apps, as this might be slow with A LOT of apps - const maxlen = 24 - var newAppname = app.name.replace("_", " ", -1) - newAppname = newAppname.charAt(0).toUpperCase()+newAppname.substring(1) - if (newAppname.length > maxlen) { - newAppname = newAppname.slice(0, maxlen)+".." - } + const maxlen = 24 + var newAppname = app.name.replace("_", " ", -1) + newAppname = newAppname.charAt(0).toUpperCase()+newAppname.substring(1) + if (newAppname.length > maxlen) { + newAppname = newAppname.slice(0, maxlen)+".." + } - //const image = "url("+app.large_image+")" - const image = app.large_image - const newAppStyle = JSON.parse(JSON.stringify(paperAppStyle)) - const pixelSize = !hover ? "2px" : "4px" - newAppStyle.borderLeft = app.is_valid ? `${pixelSize} solid green` : `${pixelSize} solid orange` + //const image = "url("+app.large_image+")" + const image = app.large_image + const newAppStyle = JSON.parse(JSON.stringify(paperAppStyle)) + const pixelSize = !hover ? "2px" : "4px" + newAppStyle.borderLeft = app.is_valid ? `${pixelSize} solid ${green}` : `${pixelSize} solid ${yellow}` - return ( - {handleAppDrag(e, app)}} - onStop={(e) => {handleDragStop(e, app)}} - key={app.id} - dragging={false} - position={{ - x: 0, - y: 0, - }} - > - {setHover(true)}} onMouseOut={() => {setHover(false)}}> - - - {newAppname} - - - -

{newAppname}

+ return ( + {handleAppDrag(e, app)}} + onStop={(e) => {handleDragStop(e, app)}} + key={app.id} + dragging={false} + position={{ + x: 0, + y: 0, + }} + > + {setHover(true)}} onMouseOut={() => {setHover(false)}}> + + + {newAppname} - - Version: {app.app_version} - - - {app.description} + + +

{newAppname}

+
+ + Version: {app.app_version} + + + {app.description} +
-
- ) - } + ) + } + + const runSearch = (event) => { + if (event.target.value.length > 0) { + setVisibleApps(allApps.filter(app => app.name.toLowerCase().includes(event.target.value.trim().toLowerCase()))) + } else { + setVisibleApps(prioritizedApps.concat(filteredApps.filter(innerapp => !internalIds.includes(innerapp.id)))) + } + } - const AppView = () => { return(
+ + + + + + ) + }} + fullWidth + color="primary" + placeholder={"Search Apps"} + id="appsearch" + onBlur={(event) => { + runSearch(event) + }} + />
- {/* - { - runAppSearch(event) - }} - /> - */} - {prioritizedApps.map((app, index) => { - return( - - ) - })} - {filteredApps.filter(innerapp => !internalIds.includes(innerapp.id)).map((app, index) => { - if (app.invalid) { - return null - } + {visibleApps.map((app, index) => { + if (app.invalid) { + return null + } - return( - - ) - })} + return( + + ) + })}
@@ -2979,10 +3022,10 @@ const AngularWorkflow = (props) => { const AppActionArguments = (props) => { const [selectedActionParameters, setSelectedActionParameters] = React.useState([]) const [selectedVariableParameter, setSelectedVariableParameter] = React.useState("") - const [showDropdown, setShowDropdown] = React.useState(false) - const [showDropdownNumber, setShowDropdownNumber] = React.useState(0) 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) @@ -3865,6 +3908,7 @@ const AngularWorkflow = (props) => { } tmpitem = tmpitem.charAt(0).toUpperCase()+tmpitem.substring(1) + tmpitem = tmpitem.replaceAll("_", " ") const description = data.description === undefined ? "" : data.description return ( @@ -4121,7 +4165,7 @@ const AngularWorkflow = (props) => {
-

{selectedAction.app_name}

+

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

{ console.log("FIND EXAMPLE RESULTS FOR ", selectedAction) @@ -4969,7 +5013,7 @@ const AngularWorkflow = (props) => { return ( {}}> -
+
{ setSourceValue(condition.source) @@ -5371,6 +5415,11 @@ const AngularWorkflow = (props) => { } const SubflowSidebar = () => { + const [menuPosition, setMenuPosition] = useState(null) + const [showDropdown, setShowDropdown] = React.useState(false) + const [showDropdownNumber, setShowDropdownNumber] = React.useState(0) + const [showAutocomplete, setShowAutocomplete] = React.useState(false) + if (Object.getOwnPropertyNames(selectedTrigger).length > 0) { if (workflow.triggers[selectedTriggerIndex] === undefined) { return null @@ -5485,48 +5534,50 @@ const AngularWorkflow = (props) => { Explore selected workflow } -
-
-
- Select the Startnode -
-
{subworkflow === undefined || subworkflow === null || subworkflow.id === undefined || subworkflow.actions === null || subworkflow.actions === undefined || subworkflow.actions.length === 0 ? null : - { - setSubworkflowStartnode(e.target.value) - - try { - workflow.triggers[selectedTriggerIndex].parameters[3].value = e.target.value.id - } catch { - workflow.triggers[selectedTriggerIndex].parameters[3] = { - "name": "startnode", - "value": e.target.value.id, } - } + }} + fullWidth + onChange={(e) => { + setSubworkflowStartnode(e.target.value) - setWorkflow(workflow) - //setUpdate(Math.random()) - }} - style={{backgroundColor: inputColor, color: "white", height: "50px"}} - > - {subworkflow.actions.map((action, index) => { - //console.log(action) - return ( - parent.id === action.id)} key={index} style={{backgroundColor: inputColor, color: "white"}} value={action}> - {action.label} - - ) - })} - + try { + workflow.triggers[selectedTriggerIndex].parameters[3].value = e.target.value.id + } catch { + workflow.triggers[selectedTriggerIndex].parameters[3] = { + "name": "startnode", + "value": e.target.value.id, + } + } + + setWorkflow(workflow) + //setUpdate(Math.random()) + }} + style={{backgroundColor: inputColor, color: "white", height: "50px"}} + > + {subworkflow.actions.map((action, index) => { + //console.log(action) + return ( + parent.id === action.id)} key={index} style={{backgroundColor: inputColor, color: "white"}} value={action}> + {action.label} + + ) + })} + + }
@@ -5543,6 +5594,21 @@ const AngularWorkflow = (props) => { maxWidth: "95%", fontSize: "1em", }, + endAdornment: ( + + + { + setMenuPosition({ + top: event.pageY+10, + left: event.pageX+10, + }) + //setShowDropdownNumber(count) + setShowDropdown(true) + setShowAutocomplete(true) + }}/> + + + ) }} rows="6" multiline @@ -6321,7 +6387,7 @@ const AngularWorkflow = (props) => { return null } - const cytoscapeViewWidths = 750 + const cytoscapeViewWidths = 800 const bottomBarStyle = { position: "fixed", right: 20, @@ -6551,7 +6617,7 @@ const AngularWorkflow = (props) => { @@ -6562,7 +6628,7 @@ const AngularWorkflow = (props) => { - + + {workflow.public ? + + + + + + : null} {/* */} {workflow.configuration !== null && workflow.configuration !== undefined && workflow.configuration.exit_on_error !== undefined ? : null}
@@ -6830,7 +6918,7 @@ const AngularWorkflow = (props) => { {workflowExecutions.length > 0 ?
{workflowExecutions.map((data, index) => { - const statusColor = data.status === "FINISHED" ? "green" : data.status === "ABORTED" || data.status === "FAILED" ? "red" : "orange" + const statusColor = data.status === "FINISHED" ? green : data.status === "ABORTED" || data.status === "FAILED" ? "red" : yellow const timeElapsed = data.completed_at-data.started_at const resultsLength = data.results !== undefined && data.results !== null ? data.results.length : 0 @@ -7006,7 +7094,7 @@ const AngularWorkflow = (props) => { const curapp = apps.find(a => a.name === data.action.app_name && a.app_version === data.action.app_version) const imgsize = 50 - const statusColor = data.status === "FINISHED" || data.status === "SUCCESS" ? "green" : data.status === "ABORTED" || data.status === "FAILURE" ? "red" : "orange" + const statusColor = data.status === "FINISHED" || data.status === "SUCCESS" ? green : data.status === "ABORTED" || data.status === "FAILURE" ? "red" : yellow var imgSrc = curapp === undefined ? "" : curapp.large_image if (imgSrc.length === 0 && workflow.actions !== undefined && workflow.actions !== null) { @@ -7116,7 +7204,7 @@ const AngularWorkflow = (props) => { // This sucks :) const curapp = !codeModalOpen ? {} : selectedResult.action.app_name === "shuffle-subflow" ? triggers[1] : selectedResult.action.app_name === "User Input" ? triggers[2] : apps.find(a => a.name === selectedResult.action.app_name && a.app_version === selectedResult.action.app_version) const imgsize = 50 - const statusColor = !codeModalOpen ? "red" : selectedResult.status === "FINISHED" || selectedResult.status === "SUCCESS" ? "green" : selectedResult.status === "ABORTED" || selectedResult.status === "FAILURE" ? "red" : "orange" + const statusColor = !codeModalOpen ? "red" : selectedResult.status === "FINISHED" || selectedResult.status === "SUCCESS" ? green : selectedResult.status === "ABORTED" || selectedResult.status === "FAILURE" ? "red" : yellow const validate = !codeModalOpen ? "" : validateJson(selectedResult.result.trim()) if (validate.valid && typeof(validate.result) === "string") { validate.result = JSON.parse(validate.result) @@ -7287,7 +7375,7 @@ const AngularWorkflow = (props) => { - const newView = isLoggedIn ? + const newView = //isLoggedIn ?
{leftView} @@ -7313,10 +7401,13 @@ const AngularWorkflow = (props) => {
+ + /* :
TMP FOR NOT LOGGED IN
+ */ const executionVariableModal = executionVariablesModalOpen ? { : null - const loadedCheck = isLoaded && isLoggedIn && workflowDone ? + //const loadedCheck = isLoaded && isLoggedIn && workflowDone ? + const loadedCheck = isLoaded && workflowDone ?
{newView} {variablesModal} diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 38f40744..bbf85301 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -1340,7 +1340,9 @@ const AppCreator = (props) => { placeholder={'Query name'} helperText={Click required switch} onBlur={(e) => { - urlPathQueries[index].name = e.target.value + console.log("IN BLUR: ", e.target.value) + urlPathQueries[index].name = e.target.value.replaceAll("=", "") + setUrlPathQueries(urlPathQueries) }} InputProps={{ diff --git a/frontend/src/views/MyView.jsx b/frontend/src/views/MyView.jsx index b456c6ed..70927e69 100644 --- a/frontend/src/views/MyView.jsx +++ b/frontend/src/views/MyView.jsx @@ -68,17 +68,21 @@ const flexContainerStyle = { } const flexBoxStyle = { - width: 333, height: 125, borderRadius: 4, boxSizing: "border-box", letterSpacing: "0.4px", color: "#D6791E", + margin: 10, + flex: 1, } -const activeWorkflowStyle = {backgroundColor: "#FFF5EE"} -const availableWorkflowStyle = {backgroundColor: "#F0F3FF"} -const notificationStyle = {backgroundColor: "#E5F9FF"} +//const activeWorkflowStyle = {backgroundColor: "#FFF5EE"} +//const notificationStyle = {backgroundColor: "#E5F9FF"} +//const activeWorkflowStyle = {backgroundColor: "#3d3f43"} +const availableWorkflowStyle = {backgroundColor: "#3d3f43"} +const notificationStyle = {backgroundColor: "#3d3f43"} +const activeWorkflowStyle = {backgroundColor: "#3d3f43"} const flexContentStyle = { display: "flex", @@ -302,8 +306,6 @@ const MyView = (props) => { alignContent: "space-between", } - - const paperAppStyle = { minHeight: 130, width: "100%", @@ -573,13 +575,16 @@ const MyView = (props) => { boxColor = "#86c142" } + if (!data.previously_saved) { + boxColor = "#f85a3e" + } + const menuClick = (event) => { setOpen(!open) setAnchorEl(event.currentTarget); } var parsedName = data.name - console.log("LEN: ", parsedName.length) if (parsedName !== undefined && parsedName !== null && parsedName.length > 25) { parsedName = parsedName.slice(0,25)+".." } @@ -589,7 +594,7 @@ const MyView = (props) => { const [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data) return ( - +
@@ -608,7 +613,7 @@ const MyView = (props) => { - + {triggers} @@ -616,7 +621,7 @@ const MyView = (props) => { - + @@ -1420,10 +1425,6 @@ const MyView = (props) => {

Workflows

-

THIS IS SOME WORKFLOW INFORMATION WHY ISN’T IT GROWING THE CORRECT WAY.

-
-
- {workflowButtons}
@@ -1457,6 +1458,15 @@ const MyView = (props) => {
+
+
+ This is your workflow view. Learn more about Workflows +
+
+ {workflowButtons} +
+
+
{view === "grid" && ( {workflows.map((data, index) => { @@ -1471,6 +1481,7 @@ const MyView = (props) => { )} +
) diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 9e477c29..0b30170e 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1,8 +1,12 @@ import React, { useEffect} from 'react'; import { useInterval } from 'react-powerhooks'; +import { makeStyles } from '@material-ui/core/styles'; import {Grid, Paper, Tooltip, Divider, Button, TextField, FormControl, IconButton, Menu, MenuItem, FormControlLabel, Chip, Switch, Typography, Zoom, CircularProgress, Dialog, DialogTitle, DialogActions, DialogContent} from '@material-ui/core'; -import {Cached as CachedIcon, GetApp as GetAppIcon, Apps as AppsIcon, Edit as EditIcon, MoreVert as MoreVertIcon, PlayArrow as PlayArrowIcon, Add as AddIcon, Publish as PublishIcon, CloudDownload as CloudDownloadIcon} from '@material-ui/icons'; +import {FileCopy as FileCopyIcon, Delete as DeleteIcon, BubbleChart as BubbleChartIcon, Restore as RestoreIcon, Cached as CachedIcon, GetApp as GetAppIcon, Apps as AppsIcon, Edit as EditIcon, MoreVert as MoreVertIcon, PlayArrow as PlayArrowIcon, Add as AddIcon, Publish as PublishIcon, CloudUpload as CloudUploadIcon, CloudDownload as CloudDownloadIcon} from '@material-ui/icons'; +//import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; + +import {DataGrid, GridToolbarContainer, GridDensitySelector, GridToolbar} from '@material-ui/data-grid'; //import JSONPretty from 'react-json-pretty'; //import JSONPrettyMon from 'react-json-pretty/dist/monikai' @@ -13,10 +17,86 @@ import {Link} from 'react-router-dom'; import { useAlert } from "react-alert"; import ChipInput from 'material-ui-chip-input' import uuid from "uuid" +import CytoscapeWrapper from '../components/RenderCytoscape' + +//import mobileImage from '../assets/img/mobile.svg'; +//import bagImage from '../assets/img/bag.svg'; +//import bookImage from '../assets/img/book.svg'; const inputColor = "#383B40" const surfaceColor = "#27292D" +const flexContainerStyle = { + display: "flex", + flexDirection: "row", + justifyContent: "left", + alignContent: "space-between", +} + +const flexBoxStyle = { + height: 125, + borderRadius: 4, + boxSizing: "border-box", + letterSpacing: "0.4px", + color: "#D6791E", + margin: 10, + flex: 1, +} + +const useStyles = makeStyles((theme) => ({ + root: { + border: 0, + '& .MuiDataGrid-columnsContainer': { + backgroundColor: theme.palette.type === 'light' ? '#fafafa' : '#1d1d1d', + }, + '& .MuiDataGrid-iconSeparator': { + display: 'none', + }, + '& .MuiDataGrid-colCell, .MuiDataGrid-cell': { + borderRight: `1px solid ${ + theme.palette.type === 'light' ? 'white' : '#303030' + }`, + }, + '& .MuiDataGrid-columnsContainer, .MuiDataGrid-cell': { + borderBottom: `1px solid ${ + theme.palette.type === 'light' ? '#f0f0f0' : '#303030' + }`, + }, + '& .MuiDataGrid-cell': { + color: + theme.palette.type === 'light' + ? 'white' + : 'rgba(255,255,255,0.65)', + }, + '& .MuiPaginationItem-root, .MuiTablePagination-actions, .MuiTablePagination-caption': { + borderRadius: 0, + color: "white", + }, + }, +})); + +//const activeWorkflowStyle = {backgroundColor: "#FFF5EE"} +//const notificationStyle = {backgroundColor: "#E5F9FF"} +//const activeWorkflowStyle = {backgroundColor: "#3d3f43"} +const availableWorkflowStyle = {backgroundColor: "#3d3f43"} +const notificationStyle = {backgroundColor: "#3d3f43"} +const activeWorkflowStyle = {backgroundColor: "#3d3f43"} + +const fontSize_16 = {fontSize: "16px",} +const counterStyle = {fontSize: "36px",fontWeight:"bold"} +const blockRightStyle = {textAlign: "right",padding: "20px 20px 0px 0px",width:"100%"} + +const flexContentStyle = { + display: "flex", + flexDirection: "row" +} + +const iconStyle = { + width: "75px", + height: "75px", + padding: "20px" +} + export const validateJson = (showResult) => { //showResult = showResult.split(" None").join(" \"None\"") showResult = showResult.split(" False").join(" false") @@ -54,6 +134,7 @@ const Workflows = (props) => { document.title = "Shuffle - Workflows" const alert = useAlert() + const classes = useStyles(); var upload = "" const [file, setFile] = React.useState(""); @@ -84,16 +165,9 @@ const Workflows = (props) => { const [executionLoading, setExecutionLoading] = React.useState(false) const [importLoading, setImportLoading] = React.useState(false) const [isDropzone, setIsDropzone] = React.useState(false); + const [view, setView] = React.useState("grid") const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" - const { start, stop } = useInterval({ - duration: 5000, - startImmediate: false, - callback: () => { - //getWorkflowExecution(selectedWorkflow.id) - } - }) - const deleteModal = deleteModalOpen ? { .then((response) => { if (response.status !== 200) { console.log("Status not 200 for workflows :O!: ", response.status) + + if (isCloud) { + window.location.pathname = "/login" + } + alert.info("Failed getting workflows.") setWorkflowDone(true) @@ -244,7 +323,7 @@ const Workflows = (props) => { minWidth: 1024, maxWidth: 1024, margin: "auto", - maxHeight: "90vh", + /*maxHeight: "90vh",*/ } const emptyWorkflowStyle = { @@ -272,78 +351,38 @@ const Workflows = (props) => { overflowY: "auto", } + const paperAppContainer = { + display: "flex", + flexWrap: 'wrap', + alignContent: "space-between", + } + const paperAppStyle = { - minHeight: "100px", - minWidth: "100%", - maxWidth: "100%", - marginTop: "5px", + minHeight: 130, + width: "100%", color: "white", backgroundColor: surfaceColor, + padding: "12px 12px 0px 15px", borderRadius: 5, - padding: 10, - cursor: "pointer", display: "flex", + boxSizing: "border-box", + position: "relative", } - const getWorkflowExecution = (id) => { - setExecutionLoading(true) - fetch(globalUrl+"/api/v1/workflows/"+id+"/executions", { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', - }, - credentials: "include", - }) - .then((response) => { - setExecutionLoading(false) - if (response.status !== 200) { - console.log("Status not 200 for WORKFLOW EXECUTION :O!") - } - - return response.json() - }) - .then((responseJson) => { - if (responseJson.success === false) { - alert.error("Failed getting executions") - } else { - if (responseJson.length > 0) { - setSelectedExecution(responseJson[0]) - setWorkflowExecutions(responseJson) - } else { - //alert.info("Couldn't find executions for the workflow") - setSelectedExecution({}) - setWorkflowExecutions([]) - } - } - }) - .catch(error => { - setExecutionLoading(false) - alert.error(error.toString()) - }); + const gridContainer = { + height: "auto", + color: "white", + margin: "10px", + backgroundColor: surfaceColor, } - const abortExecution = (workflowid, executionid) => { - alert.success("Aborting execution") - fetch(globalUrl+"/api/v1/workflows/"+workflowid+"/executions/"+executionid+"/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!") - } - //getWorkflowExecution(workflowid) - - return response.json() - }) - .catch(error => { - alert.error(error.toString()) - }); + const workflowActionStyle = { + flex: "1", + display: "flex", + width: 150, + height: 44, + justifyContent: "space-between", + overflow: "hidden" } const executeWorkflow = (id) => { @@ -372,13 +411,6 @@ const Workflows = (props) => { .catch(error => { alert.error(error.toString()) }); - - if (id === selectedWorkflow.id) { - sleep(2000).then(() => { - stop() - start() - }) - } } function sleep (time) { @@ -393,7 +425,7 @@ const Workflows = (props) => { const sanitizeWorkflow = (data) => { data["owner"] = "" - console.log(data) + console.log("Sanitize start: ", data) if (data.triggers !== null && data.triggers !== undefined) { for (var key in data.triggers) { const trigger = data.triggers[key] @@ -457,7 +489,8 @@ const Workflows = (props) => { } } - data.actions[key].environment = isCloud ? "cloud" : "Shuffle" + //data.actions[key].environment = isCloud ? "cloud" : "Shuffle" + data.actions[key].environment = "" data.actions[key].id = newId } } @@ -482,16 +515,18 @@ const Workflows = (props) => { // These are backwards.. True = saved before. Very confuse. data["previously_saved"] = false data["first_save"] = false - console.log(data) + console.log("Sanitize end: ", data) return data } const exportWorkflow = (data) => { - console.log("export") let exportFileDefaultName = data.name+'.json'; data = sanitizeWorkflow(data) + //console.log("EXPORT: ", data) + //return + let dataStr = JSON.stringify(data) let dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr); let linkElement = document.createElement('a'); @@ -600,7 +635,6 @@ const Workflows = (props) => { }); } - // dropdown with copy etc I guess const WorkflowPaper = (props) => { const { data } = props; const [open, setOpen] = React.useState(false); @@ -611,12 +645,11 @@ const Workflows = (props) => { boxWidth = "4px" } - var boxColor = "#f85a3e" + var boxColor = "#FECC00" if (data.is_valid) { - boxColor = "green" + boxColor = "#86c142" } - //console.log(data) if (!data.previously_saved) { boxColor = "#f85a3e" } @@ -626,63 +659,110 @@ const Workflows = (props) => { setAnchorEl(event.currentTarget); } - const actions = data.actions !== null ? data.actions.length : 0 - var schedules = 0 - var webhooks = 0 - var webhookImg = "" - var scheduleImg = "" - if (data.triggers !== undefined && data.triggers !== null && data.triggers.length > 0) { - for (var key in data.triggers) { - if (data.triggers[key].app_name === "Webhook") { - webhooks += 1 - webhookImg = data.triggers[key].large_image - } else if (data.triggers[key].app_name === "Schedule") { - schedules += 1 - scheduleImg = data.triggers[key].large_image - } - } + var parsedName = data.name + if (parsedName !== undefined && parsedName !== null && parsedName.length > 25) { + parsedName = parsedName.slice(0,25)+".." } - const imgSize = 25 - //console.log("TOP INFO: ", data) - return ( - { - }}> -
- - - -
{ - if (selectedWorkflow.id !== data.id) { - //setSelectedWorkflow(data) - //getWorkflowExecution(data.id) - } - }}> - - {data.name} - -
-
- - - - { - setOpen(false) - setAnchorEl(null) - }} - > + const actions = data.actions !== null ? data.actions.length : 0 + const [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data) + + return ( + + +
+ + + + {parsedName} + + + + + + + + {actions} + + + + + + + + {triggers} + + + + + + + + + + {subflows} + + + + {/* + + + + + + + : null} + {schedules > 0 ? + + + + : null} + */} + + + {data.tags !== undefined ? + data.tags.map((tag, index) => { + if (index >= 3) { + return null + } + + return ( + + ) + }) + : null} + + + {data.actions !== undefined && data.actions !== null ? + + + + + + { + setOpen(false) + setAnchorEl(null) + }} + > { setModalOpen(true) setEditingWorkflow(data) @@ -691,146 +771,57 @@ const Workflows = (props) => { if (data.tags !== undefined && data.tags !== null) { setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags))) } - }} key={"change"}>{"Change details"} + }} key={"change"}> + + {"Change details"} + { - console.log("Should publish", data) - publishWorkflow(data) - }} key={"publish"}>{"Publish Workflow"} + publishWorkflow(data) + }} key={"publish"}> + + {"Publish Workflow"} + { copyWorkflow(data) setOpen(false) - }} key={"duplicate"}>{"Duplicate Workflow"} + }} key={"duplicate"}> + + {"Duplicate Workflow"} + { exportWorkflow(data) setOpen(false) - }} key={"export"}>{"Export"} - { + }} key={"export"}> + + {"Export Workflow"} + + { setDeleteModalOpen(true) setSelectedWorkflowId(data.id) setOpen(false) - }} key={"delete"}>{"Delete"} + }} key={"delete"}> + + {"Delete Workflow"} + - -
+
-
{ - if (selectedWorkflow.id !== data.id) { - //setSelectedWorkflow(data) - //getWorkflowExecution(data.id) - } - }}> - - - - - - - - - - + + + + - {data.tags !== undefined ? - data.tags.map((tag, index) => { - if (index >= 3) { - return null - } - - return ( - - ) - }) - : null} - -
- - - {data.actions !== undefined && data.actions !== null ? - - - - - - {webhooks > 0 ? - - {data.title} - - : null} - {schedules > 0 ? - - {data.title} - - : null} - - : null} - - ) - } - - const executionPaper = (data) => { - var boxWidth = "2px" - if (selectedExecution.execution_id === data.execution_id) { - boxWidth = "4px" - } - - var boxColor = "orange" - if (data.status === "ABORTED" || data.status === "UNFINISHED" || data.status === "FAILURE"){ - boxColor = "red" - } else if (data.status === "FINISHED") { - boxColor = "green" - } - - var t = new Date(data.started_at*1000) - if (data.workflow.actions === null || data.workflow.actions === undefined ) { - return null - } - - if (data.workflow.actions === null || data.workflow.actions === undefined) { - return null - } - - var actions = data.workflow.actions.length - if (data.results !== null) { - var results = data.results.length - } - - return ( - { - setSelectedExecution(data) - }}> -
- - - -
-

Status: {data.status}

- Actions: {results}/{actions} -
-
- -
+
-
- - Started: {t.toISOString()} - -
-
- + : null} + + ) } + + const dividerColor = "rgb(225, 228, 232)" const resultPaperAppStyle = { @@ -1025,32 +1016,6 @@ const Workflows = (props) => { ) } - const ExecutionsView = () => { - if (workflowExecutions.length > 0) { - const sortedWorkflows = workflowExecutions.sort((a, b) => a.started_at - b.started_at).reverse() - - return ( -
- {sortedWorkflows.map(data => { - return ( - executionPaper(data) - ) - })} -
- ) - } - return ( - executionLoading ? -
- -
- : -

- Executions have been moved to the Workflow itself.
Click here to see them -

- ) - } - // Can create and set workflows const setNewWorkflow = (name, description, tags, editingWorkflow, redirect) => { @@ -1173,6 +1138,111 @@ const Workflows = (props) => { setLoadWorkflowsModalOpen(false) } + const getWorkflowMeta = (data) => { + let triggers = 0 + let schedules = 0 + let webhooks = 0 + let subflows = 0 + if (data.triggers !== undefined && data.triggers !== null && data.triggers.length > 0) { + triggers = data.triggers.length + for (let key in data.triggers) { + + if (data.triggers[key].app_name === "Webhook") { + webhooks += 1 + //webhookImg = data.triggers[key].large_image + } else if (data.triggers[key].app_name === "Schedule") { + schedules += 1 + //scheduleImg = data.triggers[key].large_image + } else if (data.triggers[key].app_name === "Subflow") { + subflows += 1 + } + } + } + + return [triggers, schedules, webhooks, subflows] + } + + const WorkflowGridView = () => { + let workflowData = ""; + if (workflows.length > 0) { + const columns = [ + { field: 'title', headerName: 'Title', width: 330, }, + { field: 'actions', headerName: 'Actions', width: 200, sortable: false, + disableClickEventBubbling: true, + renderCell: (params) => { + const data = params.row.record; + let [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data); + + return + + + + + + + + executeWorkflow(data.id)} /> + + + + + {webhooks > 0 ? + + + + : null} + {schedules > 0 ? + + + + : null} + + } + }, + { field: 'tags', headerName: 'Tags', width: 390, sortable: false, + disableClickEventBubbling: true, + renderCell: (params) => { + const data = params.row.record; + return + {data.tags !== undefined ? + data.tags.map((tag, index) => { + if (index >= 3) { + return null + } + + return ( + + ) + }) + : null} + + } + }, + ]; + let rows = []; + rows = workflows.map((data, index) => { + let obj = {"id":index+1, "title":data.name, "record":data,}; + return obj; + }); + workflowData = + } + return ( +
+ {workflowData} +
+ ); + } + const modalView = modalOpen ? {
-
-

Workflows ({workflows.length})

+
+

Workflows

-
+
+ + {/* +
+
+
+
+
+
{workflows.length}
+
ACTIVE WORKFLOWS
+
+
+
+
+
+
+
+
{workflows.length}
+
AVAILABE WORKFLOWS
+
+
+
+
+
+
+
+
{workflows.length}
+
NOTIFICATIONS
+
+
+
+
+ */} + +
+
+ This is your workflow view. Learn more about Workflows +
+
{workflowButtons}
- - -
- {workflows.map((data, index) => { - return ( +
+ {view === "grid" && ( + + {workflows.map((data, index) => { + return ( - ) - })} -
+ ) + })} + + )} + + {view === "list" && ( + + )} + +
-
-
-
-

Executions: {selectedWorkflow.name}

-
- {/* -
- -
- */} -
- -
- -
-
- {/* -
-
-
-

Execution Timeline

-
-
- Collapse results
} - control={ {setCollapseJson(!collapseJson)}} />} - /> -
-
- -
- -
-
- */}
) }