Ejemplo n.º 1
0
 public static IQueryable <WorkflowConnectionEntity> PreviousConnections(this IWorkflowNodeEntity e) =>
 As.Expression(() => Database.Query <WorkflowConnectionEntity>().Where(a => a.To == e));
Ejemplo n.º 2
0
 public static IEnumerable <WorkflowConnectionEntity> PreviousConnectionsFromCache(this IWorkflowNodeEntity e)
 {
     return(GetWorkflowNodeGraph(e.Lane.Pool.Workflow.ToLite()).PreviousConnections(e));
 }
Ejemplo n.º 3
0
        private bool IsParallelGateway(IWorkflowNodeEntity a, WorkflowGatewayDirection?direction = null)
        {
            var gateway = a as WorkflowGatewayEntity;

            return(gateway != null && gateway.Type != WorkflowGatewayType.Exclusive && (direction == null || direction.Value == gateway.Direction));
        }
Ejemplo n.º 4
0
        public static IEnumerable <WorkflowConnectionEntity> NextConnectionsFromCache(this IWorkflowNodeEntity e, ConnectionType?type)
        {
            var result = GetWorkflowNodeGraph(e.Lane.Pool.Workflow.ToLite()).NextConnections(e);

            if (type == null)
            {
                return(result);
            }

            return(result.Where(a => a.Type == type));
        }
Ejemplo n.º 5
0
 public static IQueryable <WorkflowConnectionEntity> PreviousConnections(this IWorkflowNodeEntity e)
 {
     return(PreviousConnectionsExpression.Evaluate(e));
 }
Ejemplo n.º 6
0
        private static HashSet <WorkflowConnectionEntity> GetAllConnections(WorkflowNodeGraph gr, IWorkflowNodeEntity from, IWorkflowNodeEntity to)
        {
            HashSet <WorkflowConnectionEntity> result = new HashSet <WorkflowConnectionEntity>();

            Stack <WorkflowConnectionEntity> partialPath = new Stack <WorkflowConnectionEntity>();
            HashSet <IWorkflowNodeEntity>    visited     = new HashSet <IWorkflowNodeEntity>();
            Action <IWorkflowNodeEntity>     flood       = null;

            flood = node =>
            {
                if (node.Is(to))
                {
                    result.AddRange(partialPath);
                }

                if (node is WorkflowActivityEntity && !node.Is(from))
                {
                    return;
                }


                foreach (var kvp in gr.NextGraph.RelatedTo(node).ToList())
                {
                    if (!visited.Contains(kvp.Key))
                    {
                        visited.Add(kvp.Key);
                        partialPath.Push(kvp.Value);
                        flood(kvp.Key);
                        partialPath.Pop();
                        visited.Remove(kvp.Key);
                    }
                }

                if (node is WorkflowGatewayEntity g && g.Type == WorkflowGatewayType.Inclusive && g.Direction == WorkflowGatewayDirection.Split)
                {
                    var next = gr.ParallelWorkflowPairs.GetOrThrow(g);
                    if (!visited.Contains(next))
                    {
                        visited.Add(next);
                        flood(next);
                        visited.Remove(next);
                    }
                }
            };

            flood(from);

            return(result);
        }
Ejemplo n.º 7
0
 public static IEnumerable <WorkflowConnectionEntity> NextConnectionsFromCache(this IWorkflowNodeEntity e)
 {
     return(GetWorkflowNodeGraph(e.Lane.Pool.Workflow.ToLite()).NextGraph.RelatedTo(e).Values);
 }
Ejemplo n.º 8
0
        public void Validate(List <WorkflowIssue> issuesContainer, Action <WorkflowGatewayEntity, WorkflowGatewayDirection> changeDirection)
        {
            List <WorkflowIssue> issues = issuesContainer;

            if (Events.Count(a => a.Value.Type.IsStart()) == 0)
            {
                issues.AddError(null, WorkflowValidationMessage.SomeStartEventIsRequired.NiceToString());
            }

            if (Workflow.MainEntityStrategies.Any(a => a == WorkflowMainEntityStrategy.SelectByUser || a == WorkflowMainEntityStrategy.Clone))
            {
                if (Events.Count(a => a.Value.Type == WorkflowEventType.Start) == 0)
                {
                    issues.AddError(null,
                                    WorkflowValidationMessage.NormalStartEventIsRequiredWhenThe0Are1Or2.NiceToString(
                                        Workflow.MainEntityStrategies.GetType().NiceName(),
                                        WorkflowMainEntityStrategy.SelectByUser.NiceToString(),
                                        WorkflowMainEntityStrategy.Clone.NiceToString()));
                }
            }

            if (Events.Count(a => a.Value.Type == WorkflowEventType.Start) > 1)
            {
                foreach (var e in Events.Where(a => a.Value.Type == WorkflowEventType.Start))
                {
                    issues.AddError(e.Value, WorkflowValidationMessage.MultipleStartEventsAreNotAllowed.NiceToString());
                }
            }

            var finishEventCount = Events.Count(a => a.Value.Type.IsFinish());

            if (finishEventCount == 0)
            {
                issues.AddError(null, WorkflowValidationMessage.FinishEventIsRequired.NiceToString());
            }

            Events.Values.ToList().ForEach(e =>
            {
                var fanIn  = PreviousConnections(e).Count();
                var fanOut = NextConnections(e).Count();

                if (e.Type.IsStart())
                {
                    if (fanIn > 0)
                    {
                        issues.AddError(e, WorkflowValidationMessage._0HasInputs.NiceToString(e));
                    }
                    if (fanOut == 0)
                    {
                        issues.AddError(e, WorkflowValidationMessage._0HasNoOutputs.NiceToString(e));
                    }
                    if (fanOut > 1)
                    {
                        issues.AddError(e, WorkflowValidationMessage._0HasMultipleOutputs.NiceToString(e));
                    }

                    if (fanOut == 1)
                    {
                        var nextConn = NextConnections(e).SingleEx();

                        if (e.Type == WorkflowEventType.Start && !(nextConn.To is WorkflowActivityEntity))
                        {
                            issues.AddError(e, WorkflowValidationMessage.StartEventNextNodeShouldBeAnActivity.NiceToString());
                        }
                    }
                }

                if (e.Type.IsFinish())
                {
                    if (fanIn == 0)
                    {
                        issues.AddError(e, WorkflowValidationMessage._0HasNoInputs.NiceToString(e));
                    }
                    if (fanOut > 0)
                    {
                        issues.AddError(e, WorkflowValidationMessage._0HasOutputs.NiceToString(e));
                    }
                }

                if (e.Type.IsScheduledStart())
                {
                    var schedule = e.ScheduledTask();

                    if (schedule == null)
                    {
                        issues.AddError(e, WorkflowValidationMessage._0IsTimerStartAndSchedulerIsMandatory.NiceToString(e));
                    }

                    var wet = e.WorkflowEventTask();

                    if (wet == null)
                    {
                        issues.AddError(e, WorkflowValidationMessage._0IsTimerStartAndTaskIsMandatory.NiceToString(e));
                    }
                    else if (wet.TriggeredOn != TriggeredOn.Always)
                    {
                        if (wet.Condition?.Script == null || !wet.Condition.Script.Trim().HasText())
                        {
                            issues.AddError(e, WorkflowValidationMessage._0IsConditionalStartAndTaskConditionIsMandatory.NiceToString(e));
                        }
                    }
                }

                if (e.Type.IsTimer())
                {
                    var boundaryOutput = NextConnections(e).Only();

                    if (boundaryOutput == null || boundaryOutput.Type != ConnectionType.Normal)
                    {
                        if (e.Type == WorkflowEventType.IntermediateTimer)
                        {
                            issues.AddError(e, WorkflowValidationMessage.IntermediateTimer0ShouldHaveOneOutputOfType1.NiceToString(e, ConnectionType.Normal.NiceToString()));
                        }
                        else
                        {
                            var parentActivity = Activities.Values.Where(a => a.BoundaryTimers.Contains(e)).SingleEx();
                            issues.AddError(e, WorkflowValidationMessage.BoundaryTimer0OfActivity1ShouldHaveExactlyOneConnectionOfType2.NiceToString(e, parentActivity, ConnectionType.Normal.NiceToString()));
                        }
                    }

                    if (e.Type == WorkflowEventType.IntermediateTimer && !e.Name.HasText())
                    {
                        issues.AddError(e, WorkflowValidationMessage.IntermediateTimer0ShouldHaveName.NiceToString(e));
                    }
                }
            });

            Gateways.Values.ToList().ForEach(g =>
            {
                var fanIn  = PreviousConnections(g).Count();
                var fanOut = NextConnections(g).Count();
                if (fanIn == 0)
                {
                    issues.AddError(g, WorkflowValidationMessage._0HasNoInputs.NiceToString(g));
                }
                if (fanOut == 0)
                {
                    issues.AddError(g, WorkflowValidationMessage._0HasNoOutputs.NiceToString(g));
                }

                if (fanIn == 1 && fanOut == 1)
                {
                    issues.AddError(g, WorkflowValidationMessage._0HasJustOneInputAndOneOutput.NiceToString(g));
                }

                var newDirection = fanOut == 1 ? WorkflowGatewayDirection.Join : WorkflowGatewayDirection.Split;
                if (g.Direction != newDirection)
                {
                    changeDirection(g, newDirection);
                }

                if (g.Direction == WorkflowGatewayDirection.Split)
                {
                    if (g.Type == WorkflowGatewayType.Exclusive || g.Type == WorkflowGatewayType.Inclusive)
                    {
                        if (NextConnections(g).Any(c => IsDecision(c.Type)))
                        {
                            List <WorkflowActivityEntity> previousActivities = new List <WorkflowActivityEntity>();

                            PreviousGraph.DepthExploreConnections(g, (prev, conn, next) =>
                            {
                                if (next is WorkflowActivityEntity a)
                                {
                                    previousActivities.Add(a);
                                    return(false);
                                }

                                return(true);
                            });

                            foreach (var act in previousActivities.Where(a => a.Type != WorkflowActivityType.Decision))
                            {
                                issues.AddError(act, WorkflowValidationMessage.Activity0ShouldBeDecision.NiceToString(act));
                            }
                        }
                    }

                    switch (g.Type)
                    {
                    case WorkflowGatewayType.Exclusive:
                        if (NextConnections(g).OrderByDescending(a => a.Order).Skip(1).Any(c => c.Type == ConnectionType.Normal && c.Condition == null))
                        {
                            issues.AddError(g, WorkflowValidationMessage.Gateway0ShouldHasConditionOrDecisionOnEachOutputExceptTheLast.NiceToString(g));
                        }
                        break;

                    case WorkflowGatewayType.Inclusive:
                        if (NextConnections(g).Count(c => c.Type == ConnectionType.Normal && c.Condition == null) != 1)
                        {
                            issues.AddError(g, WorkflowValidationMessage.InclusiveGateway0ShouldHaveOneConnectionWithoutCondition.NiceToString(g));
                        }

                        break;

                    case WorkflowGatewayType.Parallel:
                        if (NextConnections(g).Count() == 0)
                        {
                            issues.AddError(g, WorkflowValidationMessage.ParallelSplit0ShouldHaveAtLeastOneConnection.NiceToString(g));
                        }

                        if (NextConnections(g).Any(a => a.Type != ConnectionType.Normal || a.Condition != null))
                        {
                            issues.AddError(g, WorkflowValidationMessage.ParallelSplit0ShouldHaveOnlyNormalConnectionsWithoutConditions.NiceToString(g));
                        }
                        break;

                    default:
                        break;
                    }
                }
            });

            var starts = Events.Values.Where(a => a.Type.IsStart()).ToList();

            TrackId        = starts.ToDictionary(a => (IWorkflowNodeEntity)a, a => 0);
            TrackCreatedBy = new Dictionary <int, IWorkflowNodeEntity> {
                { 0, null ! }
            };


            Queue <IWorkflowNodeEntity> queue = new Queue <IWorkflowNodeEntity>();

            queue.EnqueueRange(starts);
            while (queue.Count > 0)
            {
                IWorkflowNodeEntity node = queue.Dequeue();

                var nextConns = NextConnections(node).ToList(); //Clone;
                if (node is WorkflowActivityEntity wa && wa.BoundaryTimers.Any())
                {
                    foreach (var bt in wa.BoundaryTimers)
                    {
                        nextConns.AddRange(NextConnections(bt));
                    }
                }

                foreach (var con in nextConns)
                {
                    if (ContinueExplore(node, con, con.To))
                    {
                        queue.Enqueue(con.To);
                    }
                }
            }


            bool ContinueExplore(IWorkflowNodeEntity prev, WorkflowConnectionEntity conn, IWorkflowNodeEntity next)
            {
                var prevTrackId = TrackId.GetOrThrow(prev);
                int newTrackId;

                if (IsParallelGateway(prev, WorkflowGatewayDirection.Split))
                {
                    if (IsParallelGateway(next, WorkflowGatewayDirection.Join))
                    {
                        newTrackId = prevTrackId;
                    }
                    else
                    {
                        newTrackId = TrackCreatedBy.Count + 1;
                        TrackCreatedBy.Add(newTrackId, (WorkflowGatewayEntity)prev);
                    }
                }
                else if (prev is WorkflowActivityEntity act && act.BoundaryTimers.Any(bt => bt.Type == WorkflowEventType.BoundaryForkTimer))
                {
                    if (IsParallelGateway(next, WorkflowGatewayDirection.Join))
                    {
                        newTrackId = prevTrackId;
                    }
                    else
                    {
                        if (conn.From is WorkflowEventEntity ev && ev.Type == WorkflowEventType.BoundaryForkTimer)
                        {
                            newTrackId = TrackCreatedBy.Count + 1;
                            TrackCreatedBy.Add(newTrackId, act);
                        }
Ejemplo n.º 9
0
 public IEnumerable <WorkflowConnectionEntity> PreviousConnections(IWorkflowNodeEntity node)
 {
     return(PreviousGraph.RelatedTo(node).SelectMany(a => a.Value));
 }
Ejemplo n.º 10
0
 private string GetBpmnElementId(IWorkflowNodeEntity node)
 {
     return(this.pools.GetOrThrow(node.Lane.Pool.ToLite()).GetLaneBuilder(node.Lane.ToLite()).GetBpmnElementId(node));
 }
Ejemplo n.º 11
0
        public HashSet <WorkflowConnectionEntity> GetAllConnections(IWorkflowNodeEntity from, IWorkflowNodeEntity to, Func <Stack <WorkflowConnectionEntity>, bool> isValidPath)
        {
            HashSet <WorkflowConnectionEntity> result = new HashSet <WorkflowConnectionEntity>();

            Stack <WorkflowConnectionEntity> partialPath = new Stack <WorkflowConnectionEntity>();
            HashSet <IWorkflowNodeEntity>    visited     = new HashSet <IWorkflowNodeEntity>();

            void Flood(IWorkflowNodeEntity node)
            {
                if (node.Is(to))
                {
                    if (isValidPath(partialPath))
                    {
                        result.AddRange(partialPath);
                    }
                    return;
                }

                if (node is WorkflowActivityEntity && !node.Is(from))
                {
                    return;
                }


                if (node is WorkflowEventEntity e && !node.Is(from) && e.BoundaryOf.Is(from))
                {
                    return;
                }

                var nextConnections = this.NextConnections(node).ToList();

                foreach (var con in nextConnections)
                {
                    if (!visited.Contains(con.To))
                    {
                        visited.Add(con.To);
                        partialPath.Push(con);
                        Flood(con.To);
                        partialPath.Pop();
                        visited.Remove(con.To);
                    }
                }
            };

            Flood(from);

            return(result);
        }