/// <inheritdoc /> public Task <object> Execute(WorkflowInstance workflow, WorkableLogger tasklogger, IDictionary <string, object> arguments, bool profiling, CancellationToken token) { StateVariableProvider variables = new StateVariableProvider(ProcessImports(tasklogger, workflow.StartNode.Parameters?.Imports)); variables.Add(arguments); return(Execute(new WorkflowInstanceState(new WorkflowIdentifier(workflow.Id, workflow.Revision, workflow.Name), tasklogger, variables, GetWorkflowInstance, this, workflow.Language, profiling), token, workflow.StartNode)); }
async Task <WorkableTask> Execute(IScript scriptinstance, WorkableTask scripttask, IDictionary <string, object> variables, TimeSpan?wait) { WorkableLogger scriptlogger = new WorkableLogger(logger, scripttask); scripttask.Task = Execute(scriptinstance, scriptlogger, variables, scripttask.Token.Token).ContinueWith(t => { if (t.IsCanceled) { scriptlogger.Warning("Script execution was aborted"); scripttask.Status = TaskStatus.Canceled; } else if (t.IsFaulted) { scriptlogger.Error("Script failed to execute", t.Exception?.InnerException ?? t.Exception); scripttask.Status = TaskStatus.Failure; } else { scripttask.Result = t.Result; scriptlogger.Info($"Script executed successfully with result '{scripttask.Result}'"); scripttask.Status = TaskStatus.Success; } scripttask.Finished = DateTime.Now; scriptinstances.FinishTask(scripttask.Id).GetAwaiter().GetResult(); }); if (wait.HasValue && !scripttask.Task.IsCompleted) { await Task.WhenAny(scripttask.Task, Task.Delay(wait.Value)); } return(scripttask); }
/// <inheritdoc /> public async Task <WorkableTask> Execute(WorkflowInstance workflow, IDictionary <string, object> arguments, bool profiling, TimeSpan?wait = null) { WorkableTask task = taskservice.CreateTask(WorkableType.Workflow, workflow.Id, workflow.Revision, workflow.Name, arguments); WorkableLogger tasklogger = new WorkableLogger(logger, task); try { task.Task = Task.Run(() => { StateVariableProvider variables = new StateVariableProvider(ProcessImports(tasklogger, workflow.StartNode.Parameters?.Imports)); variables.Add(arguments); WorkflowInstanceState workflowstate = new WorkflowInstanceState(new WorkflowIdentifier(workflow.Id, workflow.Revision, workflow.Name), tasklogger, variables, GetWorkflowInstance, this, workflow.Language, profiling); return(Execute(workflowstate, task.Token.Token, workflow.StartNode)); }).ContinueWith(t => HandleTaskResult(t, task, tasklogger)); } catch (Exception e) { tasklogger.Error("Failed to execute workflow", e); task.Finished = DateTime.Now; task.Status = TaskStatus.Failure; await taskservice.FinishTask(task.Id); } if (wait.HasValue && !task.Task.IsCompleted) { await Task.WhenAny(task.Task, Task.Delay(wait.Value)); } return(task); }
void HandleTaskResult(Task <object> t, WorkableTask task, WorkableLogger tasklogger) { tasklogger.LogPerformance(); if (t.IsCanceled) { tasklogger.Warning("Workflow execution was aborted"); task.Status = TaskStatus.Canceled; } else if (t.IsFaulted) { tasklogger.Error("Workflow failed to execute", t.Exception?.InnerException ?? t.Exception); task.Status = TaskStatus.Failure; } else { if (t.Result is SuspendState state) { tasklogger.Info($"Workflow was suspended at '{state}'"); task.SuspensionState = state; task.Status = TaskStatus.Suspended; // don't finish task when it gets suspended // else it would get serialized and suspension state is lost // TODO: this could get refactored to allow for state serialization return; } task.Result = t.Result; tasklogger.Info($"Workflow executed successfully with result '{task.Result}'"); task.Status = TaskStatus.Success; } task.Finished = DateTime.Now; taskservice.FinishTask(task.Id).GetAwaiter().GetResult(); }
Dictionary <string, object> ProcessImports(WorkableLogger tasklogger, ImportDeclaration[] imports) { Dictionary <string, object> variables = new Dictionary <string, object> { ["log"] = tasklogger }; if (imports != null) { foreach (ImportDeclaration import in imports) { variables[import.Variable] = importprovider.Import(new object[] { import.Type, import.Name }); } } return(variables); }
async Task <InstanceTransition> EvaluateTransitions(IInstanceNode current, WorkableLogger tasklogger, IVariableProvider variableprovider, List <InstanceTransition> transitions, CancellationToken token) { if (transitions == null) { return(null); } InstanceTransition transition = null; foreach (InstanceTransition conditionaltransition in transitions.Where(c => c.Condition != null)) { if (await conditionaltransition.Condition.ExecuteAsync <bool>(variableprovider, token)) { transition = conditionaltransition; break; } } if (transition == null) { InstanceTransition[] defaulttransitions = transitions.Where(t => t.Condition == null).ToArray(); if (defaulttransitions.Length > 1) { throw new InvalidOperationException($"More than one default transition defined for '{current.NodeName}'."); } transition = defaulttransitions.FirstOrDefault(); } if (transition?.Log != null) { try { tasklogger.Info(await transition.Log.ExecuteAsync <string>(variableprovider, token)); } catch (Exception e) { tasklogger.Error($"Error executing transition log of '{current.NodeName}'->'{transition.Target?.NodeName}'", e); throw; } } return(transition); }
/// <inheritdoc /> public async Task <WorkableTask> Continue(Guid taskid, IDictionary <string, object> variables = null, TimeSpan?wait = null) { WorkableTask task = await taskservice.GetTask(taskid); if (task.Status != TaskStatus.Suspended) { throw new ArgumentException($"Task '{taskid}' is not suspended."); } if (task.SuspensionState == null) { throw new InvalidOperationException($"Task '{taskid}' has no suspension state linked to it."); } WorkableLogger tasklogger = new WorkableLogger(logger, task); tasklogger.Info("Resuming execution of workflow", string.Join("\n", variables?.Select(p => $"{p.Key}={p.Value}") ?? new string[0])); try { task.Status = TaskStatus.Running; task.Task = Task.Run(async() => { WorkflowInstanceState workflowstate = new WorkflowInstanceState(task.SuspensionState.Workflow, tasklogger, task.SuspensionState.Variables, GetWorkflowInstance, this, task.SuspensionState.Language, task.SuspensionState.Profiling); return(await ContinueState(task.SuspensionState, workflowstate, tasklogger, variables, task.Token.Token)); }).ContinueWith(t => HandleTaskResult(t, task, tasklogger)); } catch (Exception e) { tasklogger.Error("Failed to execute workflow", e); task.Finished = DateTime.Now; task.Status = TaskStatus.Failure; await taskservice.FinishTask(task.Id); } if (wait.HasValue && !task.Task.IsCompleted) { await Task.WhenAny(task.Task, Task.Delay(wait.Value)); } return(task); }
/// <inheritdoc /> public Task <object> Execute(IScript script, WorkableLogger scriptlogger, IDictionary <string, object> variables, CancellationToken token) { IVariableProvider scopevariables = new StateVariableProvider(variables, new Variable("log", scriptlogger)); return(script.ExecuteAsync(scopevariables, token)); }
async Task <object> ContinueState(SuspendState state, WorkflowInstanceState workflowstate, WorkableLogger tasklogger, IDictionary <string, object> variables, CancellationToken token) { object lastresult = null; if (state.Subflow != null) { WorkflowIdentifier workflow = workflowstate.Workflow; workflowstate.Workflow = state.Subflow.Workflow; lastresult = await ContinueState(state.Subflow, workflowstate, tasklogger, variables, token); workflowstate.Workflow = workflow; } else { if (variables != null) { foreach (KeyValuePair <string, object> entry in variables) { state.Variables[entry.Key] = entry.Value.DetermineValue(state.Variables); } } } InstanceTransition transition = await EvaluateTransitions(state.Node, tasklogger, state.Variables, state.Node.Transitions, token); if (transition == null) { tasklogger.Warning("Suspend node has no transition defined for current state. Workflow ends by default."); return(lastresult); } return(await Execute(workflowstate, token, transition.Target, lastresult)); }