Am now able to complete a workflow consisting of a milestone task only.

This commit is contained in:
Colin Dawson 2026-03-12 14:51:32 +00:00
parent e24c8e68fe
commit 88d6a1f136
3 changed files with 128 additions and 8 deletions

View File

@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using System.Text;
using System.Text.Json.Serialization;
namespace eSuite.Core.Miscellaneous;
@ -39,5 +40,30 @@ public class GeneralIdRef : IGeneralIdRef, IEquatable<GeneralIdRef>
public override int GetHashCode()
{
return HashCode.Combine(Id, Guid);
}
public override string ToString()
{
var needsComma = false;
var output = new StringBuilder();
output.Append("GeneralIdRef(");
if (Id != null)
{
output.Append("Id:");
output.Append(Id);
needsComma = true;
}
if (Guid != null)
{
if (needsComma)
output.Append(", ");
output.Append("Guid:");
output.Append(Guid);
}
output.Append(")");
return output.ToString();
}
}

View File

@ -4,6 +4,7 @@ using e_suite.Database.Core.Tables.Activity;
using e_suite.Messaging.Common;
using e_suite.Workflow.Core;
using e_suite.Workflow.Core.Extensions;
using e_suite.Workflow.Core.Interfaces;
using eSuite.Core.Clock;
using eSuite.Core.Enums;
using eSuite.Core.Miscellaneous;
@ -74,9 +75,15 @@ public class WorkflowProcessor : IWorkflowProcessor
break;
}
case ActivityState.Active:
throw new NotImplementedException("don't know how to progress a running instance.");
case ActivityState.ReadyToComplete:
throw new NotImplementedException("don't know how to progress a ReadyToComplete instance.");
hasCompletableTask = await ProgressRunningActivity(auditUserDetails, cancellationToken, activityInstance,
workflowVersion);
if (activityInstance.Tasks.All(x => x.ActivityState == ActivityState.Completed))
{
activityInstance.ActivityState = ActivityState.Completed;
await _activityRepository.UpdateActivityInstanceAsync(auditUserDetails, activityInstance, cancellationToken);
}
break;
}
});
@ -86,6 +93,23 @@ public class WorkflowProcessor : IWorkflowProcessor
}
}
private async Task<bool> ProgressRunningActivity(AuditUserDetails auditUserDetails, CancellationToken cancellationToken, Activity activityInstance, WorkflowVersion workflowVersion)
{
bool hasCompletableTask = false;
foreach (var task in activityInstance.Tasks)
{
var taskDefinition = workflowVersion.FindTask(task.TaskGuid)!;
if (await taskDefinition.ProgressTask(task, activityInstance.Tasks, workflowVersion, _clock))
{
hasCompletableTask = true;
}
}
await _activityRepository.UpdateActivityTasksAsync(auditUserDetails, activityInstance.Tasks, cancellationToken);
return hasCompletableTask;
}
private async Task<bool> StartActivity(
AuditUserDetails auditUserDetails,
CancellationToken cancellationToken,

View File

@ -125,7 +125,7 @@ public static class TaskExtensions
};
}
public IEnumerable<Guid> GetTargetGuids()
private (Type outcomeType, IDictionary dict)? GetOutcomeDictionary()
{
var outcomeInterface = task.GetType()
.GetInterfaces()
@ -133,12 +133,53 @@ public static class TaskExtensions
i.GetGenericTypeDefinition() == typeof(IOutcome<>));
if (outcomeInterface == null)
return null;
var outcomeType = outcomeInterface.GetGenericArguments()[0];
var prop = outcomeInterface.GetProperty("OutcomeActions");
var dict = (IDictionary)prop!.GetValue(task)!;
return (outcomeType, dict);
}
public IEnumerable<Guid> GetTargetGuids()
{
var info = task.GetOutcomeDictionary();
if (info == null)
return Enumerable.Empty<Guid>();
var prop = outcomeInterface.GetProperty("OutcomeActions");
var dict = (IDictionary)prop.GetValue(task);
return info.Value.dict.Values.Cast<Guid>();
}
return dict!.Values.Cast<Guid>();
public IEnumerable<Guid> GetTargetGuids(IEnumerable<string> outcomes)
{
var info = task.GetOutcomeDictionary();
if (info == null)
return Enumerable.Empty<Guid>();
var (outcomeType, dict) = info.Value;
var results = new List<Guid>();
foreach (var outcomeString in outcomes)
{
object? typedKey;
if (outcomeType.IsEnum)
{
typedKey = Enum.Parse(outcomeType, outcomeString);
}
else
{
typedKey = Convert.ChangeType(outcomeString, outcomeType);
}
if (dict.Contains(typedKey))
{
results.Add((Guid)dict[typedKey]!);
}
}
return results;
}
public async Task StartTask(ActivityTask activityTask, IClock clock)
@ -149,6 +190,35 @@ public static class TaskExtensions
await task.OnStartedAsync(activityTask);
}
public async Task<bool> ProgressTask(ActivityTask activityTask, ICollection<ActivityTask> tasks, IStage stage, IClock clock)
{
var hasCompletableTask = false;
if (activityTask.ActivityState == ActivityState.ReadyToComplete)
{
activityTask.FinishDateTime = clock.GetNow;
activityTask.SetState(ActivityState.Completed);
await task.OnCompleteAsync(activityTask);
var targetGuids = task.GetTargetGuids(activityTask.Outcomes).ToList();
foreach (var t in tasks)
{
if (targetGuids.Contains(t.TaskGuid))
{
var taskDefinition = stage.FindTask(t.TaskGuid)!;
await taskDefinition.StartTask(t, clock);
if (t.ActivityState == ActivityState.ReadyToComplete)
{
hasCompletableTask = true;
}
}
}
}
return hasCompletableTask;
}
}