Пример #1
0
        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);
        }
Пример #2
0
        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());
                }
        }
Пример #3
0
        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);
        }
Пример #4
0
        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);
                    }
                }
            }
        }
Пример #5
0
        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);
                }
            }
        }
Пример #6
0
        /// <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());
        }
Пример #7
0
        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);
                }
            }
        }
Пример #8
0
 public void AssignOutput(object data, IStepBody body, IStepExecutionContext context)
 {
     Assign(data, body, context);
 }
Пример #9
0
 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);
 }
Пример #10
0
        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);
        }
Пример #11
0
 private void Assign(object data, IStepBody step, IStepExecutionContext context)
 {
     _action.Invoke((TStepBody)step, (TData)data, context);
 }
Пример #12
0
 private WorkflowStep BuildFakeStep(IStepBody stepBody)
 {
     return(BuildFakeStep(stepBody, new List <DataMapping>(), new List <DataMapping>()));
 }
Пример #13
0
 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);
 }
Пример #14
0
 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);
 }
Пример #15
0
 private WorkflowStep BuildFakeStep(IStepBody stepBody)
 {
     return(BuildFakeStep(stepBody, new List <IStepParameter>(), new List <IStepParameter>()));
 }
Пример #16
0
 public virtual ExecutionPipelineDirective BeforeExecute(IWorkflowHost host, IPersistenceProvider persistenceStore, IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body)
 {
     return(ExecutionPipelineDirective.Next);
 }
Пример #17
0
 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);
 }
Пример #19
0
        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);
        }
Пример #21
0
            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);
            }
Пример #22
0
 private static Expression <Func <Task <ExecutionResult> > > RunMethodFor(IStepBody body) =>
 () => body.RunAsync(A <IStepExecutionContext> ._);
Пример #23
0
 public virtual ExecutionPipelineDirective BeforeExecute(WorkflowExecutorResult executorResult, IStepExecutionContext context, ExecutionPointer executionPointer, IStepBody body)
 {
     return(ExecutionPipelineDirective.Next);
 }
Пример #24
0
        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);
            }
        }