Added execution view drawer
This commit is contained in:
@@ -638,10 +638,6 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//for _, action := range workflowExecution.Workflow.Actions {
|
|
||||||
// log.Printf("Name: %s, Env: %s", action.Name, action.Environment)
|
|
||||||
//}
|
|
||||||
|
|
||||||
newjson, err := json.Marshal(workflowExecution)
|
newjson, err := json.Marshal(workflowExecution)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.WriteHeader(401)
|
resp.WriteHeader(401)
|
||||||
@@ -3711,7 +3707,7 @@ func getWorkflowExecutions(resp http.ResponseWriter, request *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Query for the specifci workflowId
|
// Query for the specifci workflowId
|
||||||
q := datastore.NewQuery("workflowexecution").Filter("workflow_id =", fileId).Limit(50)
|
q := datastore.NewQuery("workflowexecution").Filter("workflow_id =", fileId).Order("-started_at").Limit(50)
|
||||||
var workflowExecutions []WorkflowExecution
|
var workflowExecutions []WorkflowExecution
|
||||||
_, err = dbclient.GetAll(ctx, q, &workflowExecutions)
|
_, err = dbclient.GetAll(ctx, q, &workflowExecutions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+165
-42
@@ -29,6 +29,7 @@ import Breadcrumbs from '@material-ui/core/Breadcrumbs';
|
|||||||
import CircularProgress from '@material-ui/core/CircularProgress';
|
import CircularProgress from '@material-ui/core/CircularProgress';
|
||||||
import ReactJson from 'react-json-view'
|
import ReactJson from 'react-json-view'
|
||||||
|
|
||||||
|
import DirectionsRunIcon from '@material-ui/icons/DirectionsRun';
|
||||||
import PolymerIcon from '@material-ui/icons/Polymer';
|
import PolymerIcon from '@material-ui/icons/Polymer';
|
||||||
import CreateIcon from '@material-ui/icons/Create';
|
import CreateIcon from '@material-ui/icons/Create';
|
||||||
import PlayArrowIcon from '@material-ui/icons/PlayArrow';
|
import PlayArrowIcon from '@material-ui/icons/PlayArrow';
|
||||||
@@ -155,7 +156,8 @@ const AngularWorkflow = (props) => {
|
|||||||
|
|
||||||
const [, setExecutingNodes] = React.useState([])
|
const [, setExecutingNodes] = React.useState([])
|
||||||
const [executionRunning, setExecutionRunning] = React.useState(false)
|
const [executionRunning, setExecutionRunning] = React.useState(false)
|
||||||
const [executionModalOpen, setExecutionModalOpen] = React.useState(true)
|
const [executionModalOpen, setExecutionModalOpen] = React.useState(false)
|
||||||
|
const [executionModalView, setExecutionModalView] = React.useState(0)
|
||||||
const [executionData, setExecutionData] = React.useState({})
|
const [executionData, setExecutionData] = React.useState({})
|
||||||
|
|
||||||
const [lastSaved, setLastSaved] = React.useState(true)
|
const [lastSaved, setLastSaved] = React.useState(true)
|
||||||
@@ -280,8 +282,11 @@ const AngularWorkflow = (props) => {
|
|||||||
// Update on every interval? idk
|
// Update on every interval? idk
|
||||||
|
|
||||||
if (JSON.stringify(responseJson) !== JSON.stringify(executionData)) {
|
if (JSON.stringify(responseJson) !== JSON.stringify(executionData)) {
|
||||||
console.log("Executiondata: ", responseJson)
|
// FIXME: If another is selected, don't edit..
|
||||||
setExecutionData(responseJson)
|
// Doesn't work because this is some async garbage
|
||||||
|
if (executionData.execution_id === undefined || responseJson.execution_id === executionData.execution_id) {
|
||||||
|
setExecutionData(responseJson)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (responseJson.execution_id !== executionRequest.execution_id) {
|
if (responseJson.execution_id !== executionRequest.execution_id) {
|
||||||
cy.elements().removeClass('success-highlight failure-highlight executing-highlight')
|
cy.elements().removeClass('success-highlight failure-highlight executing-highlight')
|
||||||
@@ -340,7 +345,8 @@ const AngularWorkflow = (props) => {
|
|||||||
|
|
||||||
if (!visited.includes(item.action.label)) {
|
if (!visited.includes(item.action.label)) {
|
||||||
if (executionRunning) {
|
if (executionRunning) {
|
||||||
alert.show("Success for "+item.action.label+" with result "+item.result)
|
alert.show("Success in node "+item.action.label)
|
||||||
|
//+" with result "+item.result)
|
||||||
visited.push(item.action.label)
|
visited.push(item.action.label)
|
||||||
setVisited(visited)
|
setVisited(visited)
|
||||||
}
|
}
|
||||||
@@ -656,6 +662,7 @@ const AngularWorkflow = (props) => {
|
|||||||
setExecutingNodes([workflow.start])
|
setExecutingNodes([workflow.start])
|
||||||
setExecutionData({})
|
setExecutionData({})
|
||||||
setExecutionModalOpen(true)
|
setExecutionModalOpen(true)
|
||||||
|
setExecutionModalView(1)
|
||||||
start()
|
start()
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
@@ -1457,8 +1464,20 @@ const AngularWorkflow = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleVariablesHoverOut = () => {
|
const handleVariablesHoverOut = () => {
|
||||||
setVariablesHoverColor(hoverOutColor)
|
setVariablesHoverColor(hoverOutColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const paperVariableStyle = {
|
||||||
|
minHeight: "50px",
|
||||||
|
maxHeight: "50px",
|
||||||
|
minWidth: "100%",
|
||||||
|
maxWidth: "100%",
|
||||||
|
marginTop: "5px",
|
||||||
|
color: "white",
|
||||||
|
backgroundColor: surfaceColor,
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
}
|
||||||
|
|
||||||
const VariablesView = () => {
|
const VariablesView = () => {
|
||||||
const [open, setOpen] = React.useState(false);
|
const [open, setOpen] = React.useState(false);
|
||||||
@@ -1480,18 +1499,6 @@ const AngularWorkflow = (props) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const paperVariableStyle = {
|
|
||||||
minHeight: "50px",
|
|
||||||
maxHeight: "50px",
|
|
||||||
minWidth: "100%",
|
|
||||||
maxWidth: "100%",
|
|
||||||
marginTop: "5px",
|
|
||||||
color: "white",
|
|
||||||
backgroundColor: surfaceColor,
|
|
||||||
cursor: "pointer",
|
|
||||||
display: "flex",
|
|
||||||
}
|
|
||||||
|
|
||||||
const menuClick = (event) => {
|
const menuClick = (event) => {
|
||||||
setOpen(!open)
|
setOpen(!open)
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
@@ -4184,7 +4191,7 @@ const AngularWorkflow = (props) => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const cytoscapeViewWidths = 600
|
const cytoscapeViewWidths = 650
|
||||||
const bottomBarStyle = {
|
const bottomBarStyle = {
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
right: 20,
|
right: 20,
|
||||||
@@ -4271,8 +4278,11 @@ const AngularWorkflow = (props) => {
|
|||||||
style={{backgroundColor: inputColor, }}
|
style={{backgroundColor: inputColor, }}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
style:{
|
style:{
|
||||||
color: "white",
|
|
||||||
height: 50,
|
height: 50,
|
||||||
|
color: "white",
|
||||||
|
marginLeft: 5,
|
||||||
|
maxWidth: "95%",
|
||||||
|
fontSize: "1em",
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
color="secondary"
|
color="secondary"
|
||||||
@@ -4300,6 +4310,13 @@ const AngularWorkflow = (props) => {
|
|||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Tooltip color="secondary" title="Show executions" placement="top-start">
|
||||||
|
<Button color="primary" style={{height: 50, marginLeft: 10, }} variant="outlined" onClick={() => {
|
||||||
|
setExecutionModalOpen(true)
|
||||||
|
}}>
|
||||||
|
<DirectionsRunIcon />
|
||||||
|
</Button>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -4389,21 +4406,118 @@ const AngularWorkflow = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
const ExecutionModal = () => {
|
const executionPaperStyle = {
|
||||||
return (
|
minWidth: "95%",
|
||||||
<Drawer anchor={"right"} open={executionModalOpen} onClose={() => setExecutionModalOpen(false)}>
|
maxWidth: "95%",
|
||||||
<div style={{padding: 20, maxWidth: 250, minWidth: 250,}}>
|
marginTop: "5px",
|
||||||
|
color: "white",
|
||||||
|
marginBottom: 10,
|
||||||
|
padding: 5,
|
||||||
|
backgroundColor: surfaceColor,
|
||||||
|
cursor: "pointer",
|
||||||
|
display: "flex",
|
||||||
|
minHeight: 40,
|
||||||
|
maxHeight: 40,
|
||||||
|
}
|
||||||
|
|
||||||
|
const executionModal =
|
||||||
|
<Drawer anchor={"right"} open={executionModalOpen} onClose={() => setExecutionModalOpen(false)} PaperProps={{style: {minWidth: 375, maxWidth: 375, backgroundColor: "#1F2023", color: "white", fontSize: 18}}}>
|
||||||
|
{executionModalView === 0 ?
|
||||||
|
<div style={{padding: 25, }}>
|
||||||
|
<Breadcrumbs aria-label="breadcrumb" separator="›" style={{color: "white", fontSize: 16}}>
|
||||||
|
<h2 style={{color: "rgba(255,255,255,0.5)"}}>
|
||||||
|
<DirectionsRunIcon style={{marginRight: 10}} />
|
||||||
|
All Executions
|
||||||
|
</h2>
|
||||||
|
</Breadcrumbs>
|
||||||
|
<Button
|
||||||
|
style={{borderRadius: "0px"}}
|
||||||
|
onClick={() => {
|
||||||
|
getWorkflowExecution(props.match.params.key)
|
||||||
|
}} color="primary">
|
||||||
|
Refresh executions
|
||||||
|
</Button>
|
||||||
|
<Divider style={{backgroundColor: "white", marginTop: 10, marginBottom: 10,}}/>
|
||||||
|
{workflowExecutions.length > 0 ?
|
||||||
|
<div>
|
||||||
|
{workflowExecutions.map(data => {
|
||||||
|
const statusColor = data.status === "FINISHED" ? "green" : data.status === "ABORTED" ? "red" : "orange"
|
||||||
|
const timeElapsed = data.completed_at-data.started_at
|
||||||
|
const resultsLength = data.results !== undefined && data.results !== null ? data.results.length : 0
|
||||||
|
|
||||||
|
const timestamp = new Date(data.started_at*1000).toISOString().split('.')[0].split("T").join(" ")
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Paper elevation={5} square style={executionPaperStyle} onMouseOver={() => {}} onMouseOut={() => {}} onClick={() => {
|
||||||
|
setExecutionModalView(1)
|
||||||
|
setExecutionData(data)
|
||||||
|
}}>
|
||||||
|
<div style={{display: "flex", flex: 1}}>
|
||||||
|
<div style={{marginLeft: 5, width: 2, backgroundColor: statusColor, marginRight: 15}} />
|
||||||
|
<div style={{ marginTop: "auto", marginBottom: "auto", marginRight: 15, }}>
|
||||||
|
{timestamp}
|
||||||
|
</div>
|
||||||
|
<Tooltip color="primary" title={resultsLength+" actions ran"} placement="top">
|
||||||
|
<div style={{marginRight: 10, marginTop: "auto", marginBottom: "auto",}}>
|
||||||
|
{resultsLength}/{data.workflow.actions.length}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
<Tooltip title={"Inspect execution"} placement="top">
|
||||||
|
<KeyboardArrowRightIcon style={{marginTop: "auto", marginBottom: "auto"}}/>
|
||||||
|
</Tooltip>
|
||||||
|
</Paper>
|
||||||
|
)
|
||||||
|
return
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div>
|
||||||
|
There are no executions yet
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
:
|
||||||
|
<div style={{padding: 25, }}>
|
||||||
|
<Breadcrumbs aria-label="breadcrumb" separator="›" style={{color: "white", fontSize: 16}}>
|
||||||
|
<h2 style={{color: "rgba(255,255,255,0.5)", cursor: "pointer"}} onClick={() => {setExecutionModalView(0)}}>
|
||||||
|
<DirectionsRunIcon style={{marginRight: 10}} />
|
||||||
|
Other Executions
|
||||||
|
</h2>
|
||||||
|
</Breadcrumbs>
|
||||||
|
<Divider style={{backgroundColor: "white", marginTop: 10, marginBottom: 10,}}/>
|
||||||
<h2>Executing Workflow</h2>
|
<h2>Executing Workflow</h2>
|
||||||
<Divider />
|
{executionData.execution_argument !== undefined && executionData.execution_argument.length > 0 ?
|
||||||
<h3>Execution Argument: </h3>{executionText}
|
<div>
|
||||||
|
<h3>Execution Argument: </h3>{executionText}
|
||||||
|
</div>
|
||||||
|
: null }
|
||||||
{executionData.status !== undefined && executionData.status.length > 0 ?
|
{executionData.status !== undefined && executionData.status.length > 0 ?
|
||||||
<div>
|
<div>
|
||||||
<h3>Status: </h3>{executionData.status}
|
<b>Status: </b>{executionData.status}
|
||||||
</div>
|
</div>
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
<h3>Results: </h3>
|
{executionData.started_at !== undefined ?
|
||||||
{executionData.results === undefined || executionData.results === null || executionData.results.length === 0 ?
|
<div>
|
||||||
|
<b>Started: </b>{new Date(executionData.started_at*1000).toISOString()}
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
{executionData.execution_id !== undefined && executionData.execution_id.length > 0 ?
|
||||||
|
<div>
|
||||||
|
<b>ID: </b>{executionData.execution_id}
|
||||||
|
</div>
|
||||||
|
: null
|
||||||
|
}
|
||||||
|
<Divider style={{backgroundColor: "white", marginTop: 30, marginBottom: 30,}}/>
|
||||||
|
<div style={{display: "flex", marginTop: 10, marginBottom: 30,}}>
|
||||||
|
<b>Actions</b>
|
||||||
|
<div>
|
||||||
|
{executionData.status !== undefined && executionData.status !== "ABORTED" && executionData.status !== "FINISHED" ? <CircularProgress style={{marginLeft: 20}}/> : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{executionData.results === undefined || executionData.results === null || executionData.results.length === 0 && executionData.status === "EXECUTING" ?
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
:
|
:
|
||||||
executionData.results.map(data => {
|
executionData.results.map(data => {
|
||||||
@@ -4417,12 +4531,19 @@ const AngularWorkflow = (props) => {
|
|||||||
jsonvalid = false
|
jsonvalid = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const curapp = apps.find(a => a.name === data.action.app_name && a.app_version === data.action.app_version)
|
||||||
|
const imgsize = 50
|
||||||
|
const actionimg = curapp === null ?
|
||||||
|
null :
|
||||||
|
<img alt={data.action.app_name} src={curapp.large_image} style={{marginRight: 20, width: imgsize, height: imgsize}} />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{margin: 10, border: "1px solid rgba(0,0,0,0.5)"}}>
|
<div style={{marginBottom: 40,}}>
|
||||||
<div style={{width: 17, height: 17, borderRadius: 17 / 2, backgroundColor: "#f85a3e", marginRight: 10}}/>
|
<div style={{display: "flex", marginBottom: 15,}}>
|
||||||
<b>Name:</b> {data.action.label}<div/>
|
{actionimg}
|
||||||
<b>Status:</b> {data.status}<div/>
|
<span style={{fontSize: 24, marginTop: "auto", marginBottom: "auto"}}><b>{data.action.label}</b></span>
|
||||||
<b>Result:</b>
|
</div>
|
||||||
|
<div style={{marginBottom: 5}}><b>Status </b> {data.status}</div>
|
||||||
{jsonvalid ? <ReactJson
|
{jsonvalid ? <ReactJson
|
||||||
src={JSON.parse(showResult)}
|
src={JSON.parse(showResult)}
|
||||||
theme="solarized"
|
theme="solarized"
|
||||||
@@ -4431,16 +4552,18 @@ const AngularWorkflow = (props) => {
|
|||||||
name={"Results for "+data.action.label}
|
name={"Results for "+data.action.label}
|
||||||
/>
|
/>
|
||||||
:
|
:
|
||||||
data.result
|
<div>
|
||||||
|
<b>Result</b>
|
||||||
|
{data.result}
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</Drawer>
|
</Drawer>
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const newView = isLoggedIn ?
|
const newView = isLoggedIn ?
|
||||||
<div style={{color: "white"}}>
|
<div style={{color: "white"}}>
|
||||||
@@ -4459,12 +4582,12 @@ const AngularWorkflow = (props) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<ExecutionModal />
|
e
|
||||||
|
{executionModal}
|
||||||
<RightSideBar />
|
<RightSideBar />
|
||||||
<BottomCytoscapeBar />
|
<BottomCytoscapeBar />
|
||||||
<TopCytoscapeBar />
|
<TopCytoscapeBar />
|
||||||
<NoActionsBar />
|
<NoActionsBar />
|
||||||
{debugView}
|
|
||||||
</div>
|
</div>
|
||||||
:
|
:
|
||||||
<div style={{color: "white"}}>
|
<div style={{color: "white"}}>
|
||||||
@@ -4533,7 +4656,7 @@ const AngularWorkflow = (props) => {
|
|||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
style={{borderRadius: "0px"}}
|
style={{borderRadius: "0px"}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setNewVariableName("")
|
setNewVariableName("")
|
||||||
@@ -4543,7 +4666,7 @@ const AngularWorkflow = (props) => {
|
|||||||
}} color="primary">
|
}} color="primary">
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button style={{borderRadius: "0px"}} disabled={newVariableName.length === 0 || newVariableValue.length === 0} onClick={() => {
|
<Button style={{borderRadius: "0px"}} disabled={newVariableName.length === 0 || newVariableValue.length === 0} onClick={() => {
|
||||||
if (workflow.workflow_variables === undefined || workflow.workflow_variables === null) {
|
if (workflow.workflow_variables === undefined || workflow.workflow_variables === null) {
|
||||||
workflow.workflow_variables = []
|
workflow.workflow_variables = []
|
||||||
}
|
}
|
||||||
@@ -4692,7 +4815,7 @@ const AngularWorkflow = (props) => {
|
|||||||
<AuthenticationData />
|
<AuthenticationData />
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
style={{borderRadius: "0px"}}
|
style={{borderRadius: "0px"}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setAuthenticationModalOpen(false)
|
setAuthenticationModalOpen(false)
|
||||||
|
|||||||
+2
-2
@@ -38,10 +38,10 @@ import AlertTemplate from "react-alert-template-basic";
|
|||||||
import { positions, Provider } from "react-alert";
|
import { positions, Provider } from "react-alert";
|
||||||
|
|
||||||
// Testing - localhost
|
// Testing - localhost
|
||||||
const globalUrl = "http://192.168.3.6:5001"
|
//const globalUrl = "http://192.168.3.6:5001"
|
||||||
|
|
||||||
// Production - backend proxy forwarding in nginx
|
// Production - backend proxy forwarding in nginx
|
||||||
//const globalUrl = window.location.origin
|
const globalUrl = window.location.origin
|
||||||
|
|
||||||
const surfaceColor = "#27292D"
|
const surfaceColor = "#27292D"
|
||||||
const inputColor = "#383B40"
|
const inputColor = "#383B40"
|
||||||
|
|||||||
Reference in New Issue
Block a user