#71: Started adding sub-result control

This commit is contained in:
frikky
2020-06-20 11:08:06 +02:00
parent 4a6595e56a
commit 8f40fa01e9
6 changed files with 114 additions and 48 deletions
+2 -2
View File
@@ -328,7 +328,7 @@ class AppBase:
print("BEFORE VARIABLES!")
if len(baseresult) == 0:
try:
print("WF Variables: %s" % execution_data["workflow"]["workflow_variables"])
#print("WF Variables: %s" % execution_data["workflow"]["workflow_variables"])
for variable in execution_data["workflow"]["workflow_variables"]:
variablename = variable["name"].replace(" ", "_", -1).lower()
@@ -345,7 +345,7 @@ class AppBase:
print("BEFORE EXECUTION VAR")
if len(baseresult) == 0:
try:
print("Execution Variables: %s" % execution_data["execution_variables"])
#print("Execution Variables: %s" % execution_data["execution_variables"])
for variable in execution_data["execution_variables"]:
variablename = variable["name"].replace(" ", "_", -1).lower()
if variablename.lower() == actionname_lower:
+104 -40
View File
@@ -250,21 +250,24 @@ type Schedule struct {
}
type Workflow struct {
Actions []Action `json:"actions" datastore:"actions,noindex"`
Branches []Branch `json:"branches" datastore:"branches,noindex"`
Triggers []Trigger `json:"triggers" datastore:"triggers,noindex"`
Schedules []Schedule `json:"schedules" datastore:"schedules,noindex"`
Errors []string `json:"errors,omitempty" datastore:"errors"`
Tags []string `json:"tags,omitempty" datastore:"tags"`
ID string `json:"id" datastore:"id"`
IsValid bool `json:"is_valid" datastore:"is_valid"`
Name string `json:"name" datastore:"name"`
Description string `json:"description" datastore:"description"`
Start string `json:"start" datastore:"start"`
Owner string `json:"owner" datastore:"owner"`
Sharing string `json:"sharing" datastore:"sharing"`
Org []Org `json:"org,omitempty" datastore:"org"`
ExecutingOrg Org `json:"execution_org,omitempty" datastore:"execution_org"`
Actions []Action `json:"actions" datastore:"actions,noindex"`
Branches []Branch `json:"branches" datastore:"branches,noindex"`
Triggers []Trigger `json:"triggers" datastore:"triggers,noindex"`
Schedules []Schedule `json:"schedules" datastore:"schedules,noindex"`
Configuration struct {
ExitOnError bool `json:"exit_on_error" datastore:"exit_on_error"`
} `json:"configuration,omitempty" datastore:"configuration"`
Errors []string `json:"errors,omitempty" datastore:"errors"`
Tags []string `json:"tags,omitempty" datastore:"tags"`
ID string `json:"id" datastore:"id"`
IsValid bool `json:"is_valid" datastore:"is_valid"`
Name string `json:"name" datastore:"name"`
Description string `json:"description" datastore:"description"`
Start string `json:"start" datastore:"start"`
Owner string `json:"owner" datastore:"owner"`
Sharing string `json:"sharing" datastore:"sharing"`
Org []Org `json:"org,omitempty" datastore:"org"`
ExecutingOrg Org `json:"execution_org,omitempty" datastore:"execution_org"`
WorkflowVariables []struct {
Description string `json:"description" datastore:"description"`
ID string `json:"id" datastore:"id"`
@@ -276,7 +279,7 @@ type Workflow struct {
ID string `json:"id" datastore:"id"`
Name string `json:"name" datastore:"name"`
Value string `json:"value" datastore:"value"`
} `json:"execution_variables,omitempty" datastore:"execution_variables,omitempty"`
} `json:"execution_variables,omitempty" datastore:"execution_variables"`
}
type ActionResult struct {
@@ -675,6 +678,24 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) {
}
// Finds the child nodes of a node in execution and returns them
// Used if e.g. a node in a branch is exited, and all children have to be stopped
func findChildNodes(workflowExecution WorkflowExecution, nodeId string) []string {
log.Printf("\nNODE TO FIX: %s\n\n", nodeId)
allChildren := []string{nodeId}
// 1. Find children of this specific node
// 2. Find the children of those nodes etc.
for _, branch := range workflowExecution.Workflow.Branches {
if branch.SourceID == nodeId {
log.Printf("Children: %s", branch.DestinationID)
allChildren = append(allChildren, branch.DestinationID)
}
}
return allChildren
}
func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
cors := handleCors(resp, request)
if cors {
@@ -732,20 +753,65 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
// FIXME - remove comment
if workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" {
log.Printf("Workflowexecution is already aborted. No further action can be taken")
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is aborted because of %s with result %s and status %s"}`, workflowExecution.LastNode, workflowExecution.Result, workflowExecution.Status)))
return
if workflowExecution.Workflow.Configuration.ExitOnError {
log.Printf("Workflowexecution already has status %s. No further action can be taken", workflowExecution.Status)
resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Workflowexecution is aborted because of %s with result %s and status %s"}`, workflowExecution.LastNode, workflowExecution.Result, workflowExecution.Status)))
return
} else {
log.Printf("Continuing even though it's aborted.")
}
}
if actionResult.Status == "ABORTED" || actionResult.Status == "FAILURE" {
log.Printf("Actionresult is %s. Should set workflowExecution and exit all running functions", actionResult.Status)
workflowExecution.Status = actionResult.Status
workflowExecution.LastNode = actionResult.Action.ID
newResults := []ActionResult{}
childNodes := []string{}
if workflowExecution.Workflow.Configuration.ExitOnError {
workflowExecution.Status = actionResult.Status
workflowExecution.LastNode = actionResult.Action.ID
// Find underlying nodes and add them
} else {
// Finds childnodes to set them to SKIPPED
childNodes = findChildNodes(*workflowExecution, actionResult.Action.ID)
for _, nodeId := range childNodes {
if nodeId == actionResult.Action.ID {
continue
}
// 1. Find the action itself
// 2. Create an actionresult
curAction := Action{ID: ""}
for _, action := range workflowExecution.Workflow.Actions {
if action.ID == nodeId {
curAction = action
break
}
}
if len(curAction.ID) == 0 {
log.Printf("Couldn't find subnode %s", nodeId)
continue
}
newResult := ActionResult{
Action: curAction,
ExecutionId: actionResult.ExecutionId,
Authorization: actionResult.Authorization,
Result: "Skipped because of previous node",
StartedAt: 0,
CompletedAt: 0,
Status: "SKIPPED",
}
newResults = append(newResults, newResult)
increaseStatisticsField(ctx, "workflow_execution_actions_skipped", workflowExecution.Workflow.ID, 1)
}
}
// Cleans up aborted, and always gives a result
lastResult := ""
newResults := []ActionResult{}
// type ActionResult struct {
for _, result := range workflowExecution.Results {
if result.Status == "EXECUTING" {
@@ -776,21 +842,6 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
}
}
// This means it should continue I think :)
if actionResult.Status == "SKIPPED" {
// How the fuck do I do this tho
// Parse _all_ children of the skipped and add them to "finished"
//
log.Printf("Find out how to handle skipped items, as there might be more branches to continue anyway")
// FIXME - simulate that every subnode is skipped
// Check worker, as it contains this code
// Children of children of children...
// Recurse, woo
//for _, item := range children {
//}
}
// FIXME rebuild to be like this or something
// workflowExecution/ExecutionId/Nodes/NodeId
// Find the appropriate action
@@ -840,19 +891,31 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
}
}
log.Printf("LENGTH: %d - %d", len(workflowExecution.Results), len(workflowExecution.Workflow.Actions))
//log.Printf("Checking results %d vs %d", len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)+extraInputs)
if len(workflowExecution.Results) == len(workflowExecution.Workflow.Actions)+extraInputs {
finished := true
lastResult := ""
// Doesn't have to be SUCCESS and FINISHED everywhere anymore.
skippedNodes := false
for _, result := range workflowExecution.Results {
if result.Status != "SUCCESS" && result.Status != "FINISHED" {
if result.Status == "EXECUTING" {
finished = false
break
}
if result.Status == "SKIPPED" {
skippedNodes = true
}
lastResult = result.Result
}
// FIXME: Handle skip nodes - change status?
_ = skippedNodes
if finished {
log.Printf("Execution of %s finished.", workflowExecution.ExecutionId)
//log.Println("Might be finished based on length of results and everything being SUCCESS or FINISHED - VERIFY THIS. Setting status to finished.")
@@ -1070,6 +1133,7 @@ func setNewWorkflow(resp http.ResponseWriter, request *http.Request) {
workflow.Actions = newActions
workflow.IsValid = true
workflow.Configuration.ExitOnError = true
workflowjson, err := json.Marshal(workflow)
if err != nil {
@@ -1431,7 +1495,7 @@ func saveWorkflow(resp http.ResponseWriter, request *http.Request) {
"0ca8887e-b4af-4e3e-887c-87e9d3bc3d3e",
}
log.Printf("%s Action execution var: %s", action.Label, action.ExecutionVariable.Name)
//log.Printf("%s Action execution var: %s", action.Label, action.ExecutionVariable.Name)
builtin := false
for _, id := range reservedApps {
+2 -2
View File
@@ -1,7 +1,7 @@
version: '3'
services:
frontend:
#build: ./frontend
build: ./frontend
image: frikky/shuffle:frontend
container_name: shuffle-frontend
hostname: shuffle-frontend
@@ -14,7 +14,7 @@ services:
depends_on:
- backend
backend:
#build: ./backend
build: ./backend
image: frikky/shuffle:backend
container_name: shuffle-backend
hostname: ${BACKEND_HOSTNAME}
+1 -1
View File
@@ -1455,7 +1455,7 @@ const AngularWorkflow = (props) => {
return (
<div style={appViewStyle}>
<div style={variableScrollStyle}>
What are <a href="https://shuffler.io/docs/workflows#variables" target="_blank" style={{textDecoration: "none", color: "#f85a3e"}}>WORKFLOW variables?</a>
What are <a href="https://shuffler.io/docs/workflows#workflow_variables" target="_blank" style={{textDecoration: "none", color: "#f85a3e"}}>WORKFLOW variables?</a>
{workflow.workflow_variables === null ?
null : workflow.workflow_variables.map(variable=> {
return (
+3 -1
View File
@@ -39,8 +39,10 @@ import { positions, Provider } from "react-alert";
// Production - backend proxy forwarding in nginx
var globalUrl = window.location.origin
// CORS used for testing purposes. Should only happen with specific port and http
if (window.location.protocol == "http:" && window.location.port === "3000") {
globalUrl = "http://192.168.3.6:5001"
globalUrl = "http://localhost:5001"
}
const surfaceColor = "#27292D"
+2 -2
View File
@@ -189,10 +189,10 @@ const Docs = (props) => {
}
function Heading(props) {
const element = React.createElement(`h${props.level}`, {style: {marginTop: 25}}, props.children)
const element = React.createElement(`h${props.level}`, {style: {marginTop: 40}}, props.children)
return (
<span>
{props.level !== 1 ? <Divider style={{width: "90%", marginTop: 25, backgroundColor: inputColor}} /> : null}
{props.level !== 1 ? <Divider style={{width: "90%", marginTop: 40, backgroundColor: inputColor}} /> : null}
{element}
</span>
)