public virtual IStepBody ConstructBody(IServiceProvider serviceProvider) { IStepBody body = (serviceProvider.GetService(BodyType) as IStepBody); if (body == null) { var stepCtor = BodyType.GetConstructor(new Type[] { }); if (stepCtor != null) { body = (stepCtor.Invoke(null) as IStepBody); } } return(body); }
public async Task <ExecutionResult> HandleAsync( IStepExecutionContext context, IStepBody body, WorkflowStepDelegate next) { var workflowId = context.Workflow.Id; var stepId = context.Step.Id; // Uses log scope to add a few attributes to the scope using (_log.BeginScope("{@WorkflowId}", workflowId)) using (_log.BeginScope("{@StepId}", stepId)) { // Calling next ensures step gets executed return(await next()); } }
private WorkflowStep BuildFakeStep(IStepBody stepBody, List <DataMapping> inputs, List <DataMapping> outputs) { var result = A.Fake <WorkflowStep>(); A.CallTo(() => result.Id).Returns(0); A.CallTo(() => result.BodyType).Returns(stepBody.GetType()); A.CallTo(() => result.ResumeChildrenAfterCompensation).Returns(true); A.CallTo(() => result.RevertChildrenAfterCompensation).Returns(false); A.CallTo(() => result.ConstructBody(ServiceProvider)).Returns(stepBody); A.CallTo(() => result.Inputs).Returns(inputs); A.CallTo(() => result.Outputs).Returns(outputs); A.CallTo(() => result.Outcomes).Returns(new List <StepOutcome>()); A.CallTo(() => result.InitForExecution(A <WorkflowExecutorResult> .Ignored, A <WorkflowDefinition> .Ignored, A <WorkflowInstance> .Ignored, A <ExecutionPointer> .Ignored)).Returns(ExecutionPipelineDirective.Next); A.CallTo(() => result.BeforeExecute(A <WorkflowExecutorResult> .Ignored, A <IStepExecutionContext> .Ignored, A <ExecutionPointer> .Ignored, A <IStepBody> .Ignored)).Returns(ExecutionPipelineDirective.Next); return(result); }
private void ProcessOutputs(WorkflowInstance workflow, WorkflowStep step, IStepBody body) { foreach (var output in step.Outputs) { var member = (output.Target.Body as MemberExpression); if (member != null) // to resolve a field or property { var list = new List <String>() { "system.int64", "system.int32", "system.string", "system.boolean" }; var resolvedValue = output.Source.Compile().DynamicInvoke(body); Type resolvedValueType = resolvedValue.GetType(); var type = resolvedValueType.FullName.ToLower(); if (!list.Contains(type)) { var data = workflow.Data; var value = resolvedValue.GetType().GetProperty(member.Member.Name).GetValue(resolvedValue, null); data.GetType().GetProperty(member.Member.Name).SetValue(data, value); } else if (list.Contains(type)) { var data = workflow.Data; data.GetType().GetProperty(member.Member.Name).SetValue(data, resolvedValue); } } if (member == null) // to resolve an object { var resolvedValue = output.Source.Compile().DynamicInvoke(body); var data = workflow.Data; Type dataType = data.GetType(); foreach (PropertyInfo propertyInfo in dataType.GetProperties()) { var value = resolvedValue.GetType().GetProperty(propertyInfo.Name).GetValue(resolvedValue, null); data.GetType().GetProperty(propertyInfo.Name).SetValue(data, value); } } } }
private void ProcessInputs(WorkflowInstance workflow, WorkflowStep step, IStepBody body, IStepExecutionContext context) { //TODO: Move to own class foreach (var input in step.Inputs) { var member = (input.Target.Body as MemberExpression); if (member == null) { UnaryExpression ubody = (UnaryExpression)input.Target.Body; member = ubody.Operand as MemberExpression; } object resolvedValue = null; switch (input.Source.Parameters.Count) { case 1: resolvedValue = input.Source.Compile().DynamicInvoke(workflow.Data); break; case 2: resolvedValue = input.Source.Compile().DynamicInvoke(workflow.Data, context); break; default: throw new ArgumentException(); } var property = step.BodyType.GetProperty(member.Member.Name); var propertyType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; if (CanChangeType(resolvedValue, propertyType)) { var safeValue = (resolvedValue == null) ? null : Convert.ChangeType(resolvedValue, propertyType); property.SetValue(body, safeValue); } else { property.SetValue(body, resolvedValue); } } }
/// <summary> /// Runs the passed <see cref="IStepBody"/> in the given <see cref="IStepExecutionContext"/> while applying /// any <see cref="IWorkflowStepMiddleware"/> registered in the system. Middleware will be run in the /// order in which they were registered with DI with middleware declared earlier starting earlier and /// completing later. /// </summary> /// <param name="context">The <see cref="IStepExecutionContext"/> in which to execute the step.</param> /// <param name="body">The <see cref="IStepBody"/> body.</param> /// <returns>A <see cref="Task{ExecutionResult}"/> to wait for the result of running the step</returns> public async Task <ExecutionResult> ExecuteStep( IStepExecutionContext context, IStepBody body ) { // Build the middleware chain by reducing over all the middleware in reverse starting with step body // and building step delegates that call out to the next delegate in the chain Task <ExecutionResult> Step() => body.RunAsync(context); var middlewareChain = _stepMiddleware .Reverse() .Aggregate( (WorkflowStepDelegate)Step, (previous, middleware) => () => middleware.HandleAsync(context, body, previous) ); // Run the middleware chain return(await middlewareChain()); }
private void ProcessOutputs(WorkflowInstance workflow, WorkflowStep step, IStepBody body, IStepExecutionContext context) { foreach (var output in step.Outputs) { var resolvedValue = output.Source.Compile().DynamicInvoke(body); var data = workflow.Data; var setter = ExpressionHelpers.CreateSetter(output.Target); var convertedValue = Convert.ChangeType(resolvedValue, setter.Parameters.Last().Type); if (setter.Parameters.Count == 2) { setter.Compile().DynamicInvoke(data, convertedValue); } else { setter.Compile().DynamicInvoke(data, context, convertedValue); } } }
public void AssignOutput(object data, IStepBody body, IStepExecutionContext context) { Assign(data, body, context); }
public override ExecutionPipelineDirective BeforeExecute(WorkflowExecutorResult executorResult, IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body) { if (executionPointer.EventPublished) { if ((body is UserStep) && (executionPointer.EventData is UserAction)) { (body as UserStep).UserAction = (executionPointer.EventData as UserAction); executionPointer.ExtensionAttributes["ActionUser"] = (executionPointer.EventData as UserAction).User; } } return(ExecutionPipelineDirective.Next); }
public async Task <WorkflowExecutorResult> Execute(WorkflowInstance workflow, WorkflowOptions options) { var wfResult = new WorkflowExecutorResult(); var exePointers = new List <ExecutionPointer>(workflow.ExecutionPointers.Where(x => x.Active && (!x.SleepUntil.HasValue || x.SleepUntil < _datetimeProvider.Now.ToUniversalTime()))); var def = _registry.GetDefinition(workflow.WorkflowDefinitionId, workflow.Version); if (def == null) { _logger.LogError("Workflow {0} version {1} is not registered", workflow.WorkflowDefinitionId, workflow.Version); return(wfResult); } foreach (var pointer in exePointers) { var step = def.Steps.First(x => x.Id == pointer.StepId); if (step != null) { try { switch (step.InitForExecution(wfResult, def, workflow, pointer)) { case ExecutionPipelineDirective.Defer: continue; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); continue; } if (!pointer.StartTime.HasValue) { pointer.StartTime = _datetimeProvider.Now.ToUniversalTime(); } _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); IStepBody body = step.ConstructBody(_serviceProvider); if (body == null) { _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = String.Format("Unable to construct step body {0}", step.BodyType.ToString()) }); continue; } IStepExecutionContext context = new StepExecutionContext() { Workflow = workflow, Step = step, PersistenceData = pointer.PersistenceData, ExecutionPointer = pointer, Item = pointer.ContextItem }; ProcessInputs(workflow, step, body, context); switch (step.BeforeExecute(wfResult, context, pointer, body)) { case ExecutionPipelineDirective.Defer: continue; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); continue; } var result = await body.RunAsync(context); if (result.Proceed) { ProcessOutputs(workflow, step, body); } ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); step.AfterExecute(wfResult, context, result, pointer); } catch (Exception ex) { pointer.RetryCount++; _logger.LogError("Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = ex.Message }); switch (step.ErrorBehavior ?? def.DefaultErrorBehavior) { case WorkflowErrorHandling.Retry: pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(step.RetryInterval ?? def.DefaultErrorRetryInterval ?? options.ErrorRetryInterval); break; case WorkflowErrorHandling.Suspend: workflow.Status = WorkflowStatus.Suspended; break; case WorkflowErrorHandling.Terminate: workflow.Status = WorkflowStatus.Terminated; break; } Host.ReportStepError(workflow, step, ex); } } else { _logger.LogError("Unable to find step {0} in workflow definition", pointer.StepId); pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = String.Format("Unable to find step {0} in workflow definition", pointer.StepId) }); } } ProcessAfterExecutionIteration(workflow, def, wfResult); DetermineNextExecutionTime(workflow); return(wfResult); }
private void Assign(object data, IStepBody step, IStepExecutionContext context) { _action.Invoke((TStepBody)step, (TData)data, context); }
private WorkflowStep BuildFakeStep(IStepBody stepBody) { return(BuildFakeStep(stepBody, new List <DataMapping>(), new List <DataMapping>())); }
public override ExecutionPipelineDirective BeforeExecute(WorkflowExecutorResult executorResult, IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body) { if (executionPointer.EventPublished) { if (body is ISubscriptionBody) { (body as ISubscriptionBody).EventData = executionPointer.EventData; } } return(ExecutionPipelineDirective.Next); }
public override ExecutionPipelineDirective BeforeExecute(IWorkflowHost host, IPersistenceProvider persistenceStore, IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body) { if (executionPointer.EventPublished) { if ((body is UserStepBody) && (executionPointer.EventData is UserAction)) { (body as UserStepBody).UserAction = (executionPointer.EventData as UserAction); executionPointer.ExtensionAttributes["ActionUser"] = (executionPointer.EventData as UserAction).User; } } return(ExecutionPipelineDirective.Next); }
private WorkflowStep BuildFakeStep(IStepBody stepBody) { return(BuildFakeStep(stepBody, new List <IStepParameter>(), new List <IStepParameter>())); }
public virtual ExecutionPipelineDirective BeforeExecute(IWorkflowHost host, IPersistenceProvider persistenceStore, IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body) { return(ExecutionPipelineDirective.Next); }
public override ExecutionPipelineDirective BeforeExecute(IWorkflowHost host, IPersistenceProvider persistenceStore, IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body) { if (executionPointer.EventPublished) { if (body is ISubscriptionBody) { (body as ISubscriptionBody).EventData = executionPointer.EventData; } } return(ExecutionPipelineDirective.Next); }
public void AssignOutput(object data, IStepBody body, IStepExecutionContext context) { Assign(body, _source, data, _target, context); }
public async Task <WorkflowExecutorResult> Execute(WorkflowInstance workflow) { var wfResult = new WorkflowExecutorResult(); var exePointers = new List <ExecutionPointer>(workflow.ExecutionPointers.Where(x => x.Active && (!x.SleepUntil.HasValue || x.SleepUntil < _datetimeProvider.Now.ToUniversalTime()))); var def = _registry.GetDefinition(workflow.WorkflowDefinitionId, workflow.Version); if (def == null) { _logger.LogError("Workflow {0} version {1} is not registered", workflow.WorkflowDefinitionId, workflow.Version); return(wfResult); } foreach (var pointer in exePointers) { if (pointer.Status == PointerStatus.Cancelled) { continue; } var step = def.Steps.FindById(pointer.StepId); if (step == null) { _logger.LogError("Unable to find step {0} in workflow definition", pointer.StepId); pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = String.Format("Unable to find step {0} in workflow definition", pointer.StepId) }); continue; } try { switch (step.InitForExecution(wfResult, def, workflow, pointer)) { case ExecutionPipelineDirective.Defer: continue; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); continue; } if (pointer.Status != PointerStatus.Running) { pointer.Status = PointerStatus.Running; _publisher.PublishNotification(new StepStarted() { EventTimeUtc = _datetimeProvider.Now, Reference = workflow.Reference, ExecutionPointerId = pointer.Id, StepId = step.Id, WorkflowInstanceId = workflow.Id, WorkflowDefinitionId = workflow.WorkflowDefinitionId, Version = workflow.Version }); } if (!pointer.StartTime.HasValue) { pointer.StartTime = _datetimeProvider.Now.ToUniversalTime(); } using (var scope = _scopeProvider.CreateScope()) { _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); IStepBody body = step.ConstructBody(scope.ServiceProvider); if (body == null) { _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = String.Format("Unable to construct step body {0}", step.BodyType.ToString()) }); continue; } IStepExecutionContext context = new StepExecutionContext() { Workflow = workflow, Step = step, PersistenceData = pointer.PersistenceData, ExecutionPointer = pointer, Item = pointer.ContextItem }; foreach (var input in step.Inputs) { input.AssignInput(workflow.Data, body, context); } switch (step.BeforeExecute(wfResult, context, pointer, body)) { case ExecutionPipelineDirective.Defer: continue; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); continue; } var result = await body.RunAsync(context); if (result.Proceed) { foreach (var output in step.Outputs) { output.AssignOutput(workflow.Data, body, context); } } _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); step.AfterExecute(wfResult, context, result, pointer); } } catch (Exception ex) { _logger.LogError("Workflow {0} raised error on step {1} Message: {2}", workflow.Id, pointer.StepId, ex.Message); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = ex.Message }); _executionResultProcessor.HandleStepException(workflow, def, pointer, step, ex); Host.ReportStepError(workflow, step, ex); } _cancellationProcessor.ProcessCancellations(workflow, def, wfResult); } ProcessAfterExecutionIteration(workflow, def, wfResult); DetermineNextExecutionTime(workflow); return(wfResult); }
public async Task <ExecutionResult> HandleAsync(IStepExecutionContext context, IStepBody body, WorkflowStepDelegate next) { _eventManager.OnStepStatusChanged( new WorkflowEventManager.StepStatusEventArgs(context, WorkflowEventManager.StepStatus.STARTED)); var result = await next(); _eventManager.OnStepStatusChanged( new WorkflowEventManager.StepStatusEventArgs(context, WorkflowEventManager.StepStatus.ENDED)); return(result); }
public async Task <ExecutionResult> HandleAsync(IStepExecutionContext context, IStepBody body, WorkflowStepDelegate next) { StartTime = DateTime.UtcNow; await Task.Delay(Delay); var result = await next(); await Task.Delay(Delay); EndTime = DateTime.UtcNow; return(result); }
private static Expression <Func <Task <ExecutionResult> > > RunMethodFor(IStepBody body) => () => body.RunAsync(A <IStepExecutionContext> ._);
public virtual ExecutionPipelineDirective BeforeExecute(WorkflowExecutorResult executorResult, IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body) { return(ExecutionPipelineDirective.Next); }
private async Task ExecuteStep(WorkflowInstance workflow, WorkflowStep step, ExecutionPointer pointer, WorkflowExecutorResult wfResult, WorkflowDefinition def) { using (var scope = _scopeProvider.CreateScope()) { _logger.LogDebug("Starting step {0} on workflow {1}", step.Name, workflow.Id); IStepBody body = step.ConstructBody(scope.ServiceProvider); if (body == null) { _logger.LogError("Unable to construct step body {0}", step.BodyType.ToString()); pointer.SleepUntil = _datetimeProvider.Now.ToUniversalTime().Add(_options.ErrorRetryInterval); wfResult.Errors.Add(new ExecutionError() { WorkflowId = workflow.Id, ExecutionPointerId = pointer.Id, ErrorTime = _datetimeProvider.Now.ToUniversalTime(), Message = $"Unable to construct step body {step.BodyType.ToString()}" }); return; } IStepExecutionContext context = new StepExecutionContext() { Workflow = workflow, Step = step, PersistenceData = pointer.PersistenceData, ExecutionPointer = pointer, Item = pointer.ContextItem }; foreach (var input in step.Inputs) { input.AssignInput(workflow.Data, body, context); } switch (step.BeforeExecute(wfResult, context, pointer, body)) { case ExecutionPipelineDirective.Defer: return; case ExecutionPipelineDirective.EndWorkflow: workflow.Status = WorkflowStatus.Complete; workflow.CompleteTime = _datetimeProvider.Now.ToUniversalTime(); return; } var result = await body.RunAsync(context); if (result.Proceed) { foreach (var output in step.Outputs) { output.AssignOutput(workflow.Data, body, context); } } _executionResultProcessor.ProcessExecutionResult(workflow, def, pointer, step, result, wfResult); step.AfterExecute(wfResult, context, result, pointer); } }