private async Task <WorkflowProcessingResult> ProcessMessageInternal(IDelayedTransitionWorkflowMessage delayedTransitionWorkflowMessage, CancellationToken cancellationToken) { Guard.ArgumentNotNull(delayedTransitionWorkflowMessage, nameof(delayedTransitionWorkflowMessage)); Guard.ArgumentNotNull(delayedTransitionWorkflowMessage.WorkflowInstanceId, nameof(delayedTransitionWorkflowMessage.WorkflowInstanceId)); Debug.Assert(delayedTransitionWorkflowMessage.WorkflowInstanceId != null, "workflowMessage.WorkflowInstanceId != null"); var workflowInstance = await GetWorkflowInstanceWithLock(delayedTransitionWorkflowMessage.WorkflowInstanceId.Value, cancellationToken).ConfigureAwait(false); if (null == workflowInstance) { throw new WorkflowException($"Workflow message [{delayedTransitionWorkflowMessage.WorkflowMessageId:D}] is referring to non existing workflow instance"); } var workflowConfiguration = await GetWorkflowConfiguration(workflowInstance.WorkflowId, cancellationToken).ConfigureAwait(false); Guard.ArgumentNotNull(workflowConfiguration, nameof(workflowConfiguration)); var stateConfiguration = workflowConfiguration.GetStateConfigurationByCode(workflowInstance.CurrentStateCode); Guard.ArgumentNotNull(stateConfiguration, nameof(stateConfiguration)); if (stateConfiguration.Type == StateTypeConfiguration.Final || stateConfiguration.Type == StateTypeConfiguration.Failed) { Log.Debug("Skipping delayed transition message [{messageId}] because {workflowInstanceId} is in [{state}]", delayedTransitionWorkflowMessage.WorkflowMessageId, delayedTransitionWorkflowMessage.WorkflowInstanceId, stateConfiguration.Code); return(new WorkflowProcessingResult(workflowInstance)); } var workflowContext = new WorkflowContext(this, WorkflowEngineBuilder, workflowInstance, workflowConfiguration); // ensure that domain entity is available for all/any coded logic for read-only purposes await TryGetDomainEntity(workflowContext, cancellationToken).ConfigureAwait(false); var stateExecutionContext = new StateExecutionContext(workflowContext, stateConfiguration); var fromStateConfiguration = workflowConfiguration.GetStateConfigurationByCode(delayedTransitionWorkflowMessage.MoveFromState); var transitionConfiguration = fromStateConfiguration.Transitions .Single(t => string.Equals(t.MoveToState, delayedTransitionWorkflowMessage.MoveToState, StringComparison.OrdinalIgnoreCase)); var transitionEvaluationResult = await _transitionProcessorLazy.Value.Evaluate(stateExecutionContext, transitionConfiguration, cancellationToken).ConfigureAwait(false); if (transitionEvaluationResult.Status == TransitionEvaluationStatus.EvaluatedTrue) { await TransitionToState(workflowContext, stateConfiguration, transitionConfiguration, cancellationToken).ConfigureAwait(false); return(new WorkflowProcessingResult(workflowInstance, workflowContext.WorkflowExecutionState)); } Log.Debug("Skipping delayed transition message [{messageId}] for {workflowInstanceId}] because transition was evaluated to {state}", delayedTransitionWorkflowMessage.WorkflowMessageId, delayedTransitionWorkflowMessage.WorkflowInstanceId, transitionEvaluationResult.Status); return(new WorkflowProcessingResult(workflowInstance, workflowContext.WorkflowExecutionState)); }
public async Task <TransitionEvaluationResult> Evaluate(StateExecutionContext stateExecutionContext, TransitionConfiguration transitionConfiguration, CancellationToken cancellationToken) { ITransition transition = null; if (!string.IsNullOrEmpty(transitionConfiguration.Code)) { transition = _workflowEngine.WorkflowEngineBuilder .TransitionFactory.CreateTransition(transitionConfiguration.Code); } if (null == transition && !string.IsNullOrEmpty(transitionConfiguration.ConditionScript) && transitionConfiguration.ConditionScriptType == ScriptTypeConfiguration.CSharp) { transition = new CSharpScriptTransition(stateExecutionContext.StateConfiguration.Code, transitionConfiguration.ConditionScriptType, transitionConfiguration.ConditionScript); } if (null == transition) { // there is neither code nor condition for transition // ==> assume transition always evaluates to true return(new TransitionEvaluationResult(TransitionEvaluationStatus.EvaluatedTrue)); } Log.Verbose("Starting evaluation of transition [{code}] [{condition}] for [{workflowInstanceId}]", transitionConfiguration.Code, transitionConfiguration.ConditionScriptType, stateExecutionContext.WorkflowContext.WorkflowInstance.Id); try { var transitionEvaluationContext = new TransitionEvaluationContext(stateExecutionContext, transitionConfiguration); var transitionEvaluationResult = await transition.Evaluate(transitionEvaluationContext, cancellationToken).ConfigureAwait(false); if (transitionEvaluationResult.Status == TransitionEvaluationStatus.EvaluationFailed) { throw new WorkflowException(string.Format(CultureInfo.InvariantCulture, "Evaluation of an transition [code={0}], [condition={1}] returns [EvaluationFailed] for workflow instance [{2:D}]", transitionConfiguration.Code, transitionConfiguration.ConditionScript, stateExecutionContext.WorkflowContext.WorkflowInstance.Id)); } return(transitionEvaluationResult); } catch (Exception ex) { throw new WorkflowException(string.Format(CultureInfo.InvariantCulture, "An error has occurred during evaluation of an transition [code={0}], [condition={1}] for workflow instance [{2:D}]", transitionConfiguration.Code, transitionConfiguration.ConditionScript, stateExecutionContext.WorkflowContext.WorkflowInstance.Id), ex); } }
public void Setup() { _workflowInstance = new Mock <IWorkflowInstance>(); _runtimeWorkflowEngine = new Mock <IRuntimeWorkflowEngine>(); _endpointConfiguration = new Mock <IEndpointConfiguration>(); _workflowMessageTransport = new Mock <IWorkflowMessageTransport>(); _workflowMessageTransportFactory = new Mock <IWorkflowMessageTransportFactory>(); _workflowMessageTransportFactoryProvider = new Mock <IWorkflowMessageTransportFactoryProvider>(); _workflowEngineBuilder = new WorkflowEngineBuilder().WithMessageTransportFactoryProvider(_workflowMessageTransportFactoryProvider.Object); _endpointConfiguration.Setup(f => f.Code).Returns("activity"); _endpointConfiguration.Setup(f => f.Address).Returns(new Uri("rabbitmq://localhost/Diadem.Workflow.Core.Model:IActivityRequestWorkflowMessage")); var workflowId = Guid.NewGuid(); _workflowInstance.Setup(f => f.Id).Returns(workflowId); _workflowConfiguration = new WorkflowConfiguration(workflowId, "unit.test", "unit.test", "Unit Test", new Version(0, 0, 1)) { RuntimeConfiguration = new WorkflowRuntimeConfiguration(workflowId, "unit.test", "unit.test") { EndpointConfiguration = _endpointConfiguration.Object, EndpointConfigurations = new[] { new KeyValuePair <string, IEndpointConfiguration>("remote.activity.*", _endpointConfiguration.Object) } } }; _activityResponseWorkflowMessage = new ActivityResponseWorkflowMessage { ActivityExecutionResult = new ActivityExecutionResult(ActivityExecutionStatus.Completed) }; _workflowMessageTransport .Setup(f => f.Request <IActivityRequestWorkflowMessage, IActivityResponseWorkflowMessage>( It.IsAny <IEndpointConfiguration>(), It.IsAny <IActivityRequestWorkflowMessage>(), It.IsAny <CancellationToken>())) .Returns <IEndpointConfiguration, IActivityRequestWorkflowMessage, CancellationToken>((epc, wm, ct) => Task.FromResult(_activityResponseWorkflowMessage)); _workflowMessageTransportFactoryProvider .Setup(f => f.CreateMessageTransportFactory(It.IsAny <EndpointConfigurationType>())) .Returns <EndpointConfigurationType>(uri => _workflowMessageTransportFactory.Object); _workflowMessageTransportFactory .Setup(f => f.CreateMessageTransport(It.IsAny <Uri>())) .Returns <Uri>(uri => _workflowMessageTransport.Object); _workflowContext = new WorkflowContext(_runtimeWorkflowEngine.Object, _workflowEngineBuilder, _workflowInstance.Object, _workflowConfiguration); _stateExecutionContext = new StateExecutionContext(_workflowContext, new StateConfiguration()); _activityExecutionContext = new ActivityExecutionContext(_stateExecutionContext, new ActivityConfiguration()); }
public TransitionEvaluationContext(StateExecutionContext stateExecutionContext, TransitionConfiguration transitionConfiguration) { TransitionConfiguration = transitionConfiguration; StateExecutionContext = stateExecutionContext; }
public ActivityExecutionContext(StateExecutionContext stateExecutionContext, ActivityConfiguration activityConfiguration) { ActivityConfiguration = activityConfiguration; StateExecutionContext = stateExecutionContext; }
private async Task ProcessState(WorkflowContext workflowContext, StateConfiguration stateConfiguration, CancellationToken cancellationToken = default) { if (stateConfiguration.Type == StateTypeConfiguration.Initial || stateConfiguration.Type == StateTypeConfiguration.Application) { var cycleDetected = DetectCycle(workflowContext, stateConfiguration); if (cycleDetected) { await TransitionToState(workflowContext, workflowContext.WorkflowConfiguration.GetFailedStateConfiguration(), null, cancellationToken) .ConfigureAwait(false); Log.Warning("Cycle has been detected for {workflowInstanceId} moving to failed state", workflowContext.WorkflowInstance.Id); return; } } StateExecutionResult stateExecutionResult; var stateExecutionContext = new StateExecutionContext(workflowContext, stateConfiguration); var started = DateTime.UtcNow; var stopwatch = Stopwatch.StartNew(); try { stateExecutionResult = await _stateProcessorLazy.Value.Process(stateExecutionContext, cancellationToken).ConfigureAwait(false); } finally { stopwatch.Stop(); Log.Debug("Finished execution of {state}, {duration}, {workflowInstanceId}", stateConfiguration.Code, stopwatch.Elapsed, workflowContext.WorkflowInstance.Id); var workflowInstanceStateLog = new WorkflowInstanceStateLog(workflowContext.WorkflowInstance.Id, stateConfiguration.Code, started, (int)stopwatch.ElapsedMilliseconds); await WorkflowEngineBuilder.WorkflowStore.SaveWorkflowInstanceStateLog(workflowInstanceStateLog, cancellationToken).ConfigureAwait(false); } if (stateExecutionResult.Status == StateExecutionStatus.Finished) { Log.Debug("{workflowInstanceId} has been finished processing", workflowContext.WorkflowInstance.Id); // if (stateExecutionContext.StateConfiguration.Type == StateTypeConfiguration.Final) // { // await PulseFinalizedToParentWorkflow(workflowContext, cancellationToken).ConfigureAwait(false); // } // else if (stateExecutionContext.StateConfiguration.Type == StateTypeConfiguration.Failed) { await PulseFailedToParentWorkflow(workflowContext, cancellationToken).ConfigureAwait(false); await PulseFailedToNestedWorkflows(workflowContext, cancellationToken).ConfigureAwait(false); } return; } if (stateExecutionResult.Status == StateExecutionStatus.Failed || stateExecutionResult.Status == StateExecutionStatus.Undefined) { // check if current state is already 'Failed' then just ignore state activity execution failure if (stateConfiguration.Type == StateTypeConfiguration.Failed) { Log.Error("Activities execution error for failed state of {workflowInstanceId} has occurred, ignoring failure...", workflowContext.WorkflowInstance.Id); return; } // state/activity failure could have custom transition to use in case of failure if (null != stateExecutionResult.Transition.TransitionConfiguration) { await TransitionAfterStateExecution(workflowContext, stateExecutionResult, cancellationToken).ConfigureAwait(false); Log.Verbose("{workflowInstanceId} has been moved to {state}", workflowContext.WorkflowInstance.Id, stateExecutionResult.Transition.TransitionConfiguration.MoveToState); return; } await TransitionToState(workflowContext, workflowContext.WorkflowConfiguration.GetFailedStateConfiguration(), null, cancellationToken) .ConfigureAwait(false); Log.Verbose("{workflowInstanceId} has been moved to failed state", workflowContext.WorkflowInstance.Id); return; } if (stateExecutionResult.Status == StateExecutionStatus.Completed) { await TransitionAfterStateExecution(workflowContext, stateExecutionResult, cancellationToken).ConfigureAwait(false); } }