internal static bool HasScriptWithEntityUse(this WorkflowConfiguration workflowConfiguration)
        {
            var hasActivityScriptWithEntityUse = workflowConfiguration.States
                                                 .SelectMany(s => s.Activities).Any(a => !string.IsNullOrEmpty(a.Script) && a.Script.Contains("entity"));

            if (hasActivityScriptWithEntityUse)
            {
                return(true);
            }

            var hasEventHandlerScriptWithEntityUse = workflowConfiguration.States
                                                     .SelectMany(s => s.Events).Any(e => !string.IsNullOrEmpty(e.Script) && e.Script.Contains("entity"));

            if (hasEventHandlerScriptWithEntityUse)
            {
                return(true);
            }

            var hasTransitionScriptWithEntityUse = workflowConfiguration.States
                                                   .SelectMany(s => s.Transitions).Any(t => !string.IsNullOrEmpty(t.ConditionScript) && t.ConditionScript.Contains("entity"));

            if (hasTransitionScriptWithEntityUse)
            {
                return(true);
            }

            return(false);
        }
        internal static StateConfiguration GetStateConfigurationByCode(this WorkflowConfiguration workflowConfiguration, string code)
        {
            var stateConfiguration = workflowConfiguration.States.SingleOrDefault(s => string.Equals(s.Code, code, StringComparison.OrdinalIgnoreCase));

            if (null == stateConfiguration)
            {
                throw new WorkflowException($"State [{code}] was not found in workflow [{workflowConfiguration.Code}]");
            }

            return(stateConfiguration);
        }
        internal static IEndpointConfiguration FindEndpointConfiguration(this WorkflowConfiguration workflowConfiguration, string code)
        {
            Guard.ArgumentNotNull(workflowConfiguration, nameof(workflowConfiguration));
            Guard.ArgumentNotNull(workflowConfiguration.RuntimeConfiguration, nameof(workflowConfiguration.RuntimeConfiguration));
            Guard.ArgumentNotNull(workflowConfiguration.RuntimeConfiguration.EndpointConfigurations, nameof(workflowConfiguration.RuntimeConfiguration.EndpointConfigurations));

            foreach (var endpointConfigurationKvp in workflowConfiguration.RuntimeConfiguration.EndpointConfigurations)
            {
                var regex = new Regex(endpointConfigurationKvp.Key, RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
                if (regex.Match(code).Success)
                {
                    return(endpointConfigurationKvp.Value);
                }
            }

            throw new WorkflowException($"Endpoint configuration for code [${code}] was not found for workflow [ID=${workflowConfiguration.Id}]");
        }
 internal static StateConfiguration GetStateConfigurationByType(this WorkflowConfiguration workflowConfiguration, StateTypeConfiguration stateType)
 {
     return(workflowConfiguration.States.Single(s => s.Type == stateType));
 }
 internal static EventConfiguration GetEventConfigurationByType(this WorkflowConfiguration workflowConfiguration, EventTypeConfiguration eventType)
 {
     return(workflowConfiguration.Events.FirstOrDefault(e => e.Type == eventType));
 }
 internal static EventConfiguration GetEventConfigurationByCode(this WorkflowConfiguration workflowConfiguration, string code)
 {
     return(workflowConfiguration.Events.FirstOrDefault(e => string.Equals(code, e.Code, StringComparison.OrdinalIgnoreCase)));
 }
 internal static StateConfiguration GetFailedStateConfiguration(this WorkflowConfiguration workflowConfiguration)
 {
     return(workflowConfiguration.States.Single(s => s.Type == StateTypeConfiguration.Failed));
 }
        /// <summary>
        ///     Validate workflow configuration against a number of invariants
        /// </summary>
        internal static void ValidateWorkflowConfiguration(WorkflowConfiguration workflowConfiguration)
        {
            WorkflowConfigurationException workflowConfigurationException = null;

            // 1. make sure that workflow has exactly one initial state
            var initialStateCount = workflowConfiguration.States.Count(s => s.Type == StateTypeConfiguration.Initial);

            if (initialStateCount != 1)
            {
                AddValidationMessage(ref workflowConfigurationException, $"Workflow [{workflowConfiguration.Code}] must have exactly one initial state");
            }

            // 2. make sure that workflow has exactly one failed state
            var failedStateCount = workflowConfiguration.States.Count(s => s.Type == StateTypeConfiguration.Initial);

            if (failedStateCount != 1)
            {
                AddValidationMessage(ref workflowConfigurationException, $"Workflow [{workflowConfiguration.Code}] must have exactly one failed state");
            }

            // 3. there are no state code duplicates
            var hasDuplicateState = workflowConfiguration.States
                                    .Any(s => workflowConfiguration.States.Any(si => si != s && string.Equals(si.Code, s.Code, StringComparison.OrdinalIgnoreCase)));

            if (hasDuplicateState)
            {
                AddValidationMessage(ref workflowConfigurationException, $"Workflow [{workflowConfiguration.Code}] must have unique states");
            }

            // 4. make sure that workflow has zero or exactly one parentToNestedInitial event
            var parentToNestedInitialEventCount = workflowConfiguration.Events.Count(e => e.Type == EventTypeConfiguration.ParentToNestedInitial);

            if (parentToNestedInitialEventCount > 1)
            {
                AddValidationMessage(ref workflowConfigurationException,
                                     $"Workflow [{workflowConfiguration.Code}] must have zero or exactly one [{EventTypeConfiguration.ParentToNestedInitial:G}] event");
            }

            // 5. there are no event code duplicates
            var hasDuplicateEvents = workflowConfiguration.Events
                                     .Any(e => workflowConfiguration.Events.Any(ei => ei != e && string.Equals(ei.Code, e.Code, StringComparison.OrdinalIgnoreCase)));

            if (hasDuplicateEvents)
            {
                AddValidationMessage(ref workflowConfigurationException, $"Workflow [{workflowConfiguration.Code}] must have unique events");
            }

            // 6. do not allow state to have events and incoming asynchronousImmediate transitions simultaneously
            var statesWithEvents             = workflowConfiguration.States.Where(s => s.Events.Any()).ToList();
            var transitionsToStateWithEvents = workflowConfiguration.States.SelectMany(s => s.Transitions)
                                               .Where(t => t.Type == TransitionTypeConfiguration.AsynchronousImmediate)
                                               .Where(t => statesWithEvents.Any(s => s.Code == t.MoveToState));

            if (transitionsToStateWithEvents.Any())
            {
                AddValidationMessage(ref workflowConfigurationException,
                                     $"Workflow [{workflowConfiguration.Code}] has state(s) with events and incoming async transitions");
            }

            // 7. do not allow duplicated event declaration for the same state
            var hasStatesWithDuplicatedEvent = statesWithEvents
                                               .Any(s => s.Events.Any(e => s.Events
                                                                      .Any(ei => ei != e && string.Equals(ei.Code, e.Code, StringComparison.OrdinalIgnoreCase))));

            if (hasStatesWithDuplicatedEvent)
            {
                AddValidationMessage(ref workflowConfigurationException, $"Workflow [{workflowConfiguration.Code}] has state(s) with duplicated events");
            }

            // 8. make sure that activity -> retryPolicy -> onFailureTransitionToUse refers to an existing transition within the state
            var statesWithCustomFailureTransitions = workflowConfiguration.States
                                                     .Where(s => s.Activities.Any(a => !string.IsNullOrEmpty(a.RetryPolicy.OnFailureTransitionToUse))).ToList();

            if (statesWithCustomFailureTransitions.Any())
            {
                var hasMissConfiguredCustomFailureTransitions = statesWithCustomFailureTransitions
                                                                .Any(s => s.Activities
                                                                     .Any(a => !string.IsNullOrEmpty(a.RetryPolicy.OnFailureTransitionToUse) && !s.Transitions
                                                                          .Any(t => string.Equals(t.MoveToState, a.RetryPolicy.OnFailureTransitionToUse))));
                if (hasMissConfiguredCustomFailureTransitions)
                {
                    AddValidationMessage(ref workflowConfigurationException,
                                         $"Workflow [{workflowConfiguration.Code}] has activity(s) with configured 'onFailureTransitionToUse' but state has no such transition");
                }
            }

            if (null != workflowConfigurationException)
            {
                throw workflowConfigurationException;
            }
        }
コード例 #9
0
        public WorkflowConfiguration Parse(string content)
        {
            try
            {
                using (var textReader = new StringReader(content))
                    using (var xmlReader = XmlReader.Create(textReader))
                    {
                        var xDocument = XDocument.Load(xmlReader);
                        Guard.ArgumentNotNull(xDocument.Root, "document");

                        var idString      = ParseAttributeString(xDocument.Root, "id");
                        var @class        = ParseAttributeString(xDocument.Root, "class");
                        var code          = ParseAttributeString(xDocument.Root, "code");
                        var name          = ParseAttributeString(xDocument.Root, "name");
                        var versionString = ParseAttributeString(xDocument.Root, "version");

                        if (!Guid.TryParse(idString, out var id))
                        {
                            throw new Exception($"Can not convert [{idString}] to a Guid");
                        }

                        if (!Version.TryParse(versionString, out var version))
                        {
                            throw new Exception($"Can not convert [{versionString}] to a version");
                        }

                        Log.Verbose("Starting reading workflow [{code}], [{name}], [{version}] from XML", code, name, version);
                        var workflowConfiguration = new WorkflowConfiguration(id, @class, code, name, version);

                        var events = xDocument.Root.Element("events");
                        if (events != null)
                        {
                            foreach (var eventElement in events.Elements("event"))
                            {
                                var @event = ParseEvent(eventElement);
                                workflowConfiguration.Events.Add(@event);
                            }
                        }

                        var states = xDocument.Root.Element("states");
                        if (null != states)
                        {
                            foreach (var stateElement in states.Elements("state"))
                            {
                                var state = ParseState(stateElement);
                                workflowConfiguration.States.Add(state);
                            }
                        }

                        Log.Verbose("Workflow [{code}], [{name}], [{version}] has been read from XML", code, name, version);
                        WorkflowConfigurationValidator.ValidateWorkflowConfiguration(workflowConfiguration);
                        return(workflowConfiguration);
                    }
            }
            catch (WorkflowConfigurationException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new WorkflowConfigurationException("An error has occurred during reading XML workflow", ex);
            }
        }