diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 9131d5d8..3f7a788f 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -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: diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 2499c225..b9dd860a 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -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 { diff --git a/docker-compose.yml b/docker-compose.yml index 288063bb..ba3a495b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -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} diff --git a/frontend/src/AngularWorkflow.js b/frontend/src/AngularWorkflow.js index 51200e49..fa3d349e 100644 --- a/frontend/src/AngularWorkflow.js +++ b/frontend/src/AngularWorkflow.js @@ -1455,7 +1455,7 @@ const AngularWorkflow = (props) => { return (
- What are WORKFLOW variables? + What are WORKFLOW variables? {workflow.workflow_variables === null ? null : workflow.workflow_variables.map(variable=> { return ( diff --git a/frontend/src/App.js b/frontend/src/App.js index 891371d0..fd8d20ab 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -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" diff --git a/frontend/src/Docs.js b/frontend/src/Docs.js index 69d0eb3f..06917bda 100644 --- a/frontend/src/Docs.js +++ b/frontend/src/Docs.js @@ -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 ( - {props.level !== 1 ? : null} + {props.level !== 1 ? : null} {element} )