From 52ba6ee606b95be4b632240a2835ef95b7884bae Mon Sep 17 00:00:00 2001 From: Colin Dawson Date: Thu, 12 Feb 2026 00:40:16 +0000 Subject: [PATCH] Added the taskMetadata workflow endpoint --- .../IWorkflowTemplateManager.cs | 8 +++ .../Controllers/WorkflowTemplateController.cs | 20 +++++++ .../WorkflowTemplateManager.cs | 55 ++++++++++++++++++- .../Extensions/StageExtensions.cs | 51 +++++++++++++++-- .../Extensions/TaskExtensions.cs | 4 +- e-suite.Workflow.Core/Tasks/BasicTask.cs | 3 +- 6 files changed, 132 insertions(+), 9 deletions(-) diff --git a/e-suite.API.Common/e-suite.API.Common/IWorkflowTemplateManager.cs b/e-suite.API.Common/e-suite.API.Common/IWorkflowTemplateManager.cs index 87da8e9..d2e1f10 100644 --- a/e-suite.API.Common/e-suite.API.Common/IWorkflowTemplateManager.cs +++ b/e-suite.API.Common/e-suite.API.Common/IWorkflowTemplateManager.cs @@ -94,6 +94,13 @@ public class CreateWorkflowTemplateVersion public string ActivityNameTemplate { get; set; } = String.Empty; } +public class TaskMetadata +{ + public string TaskType { get; set; } + public string DisplayName { get; set; } + public List Capabilities { get; set; } = []; +} + public interface IWorkflowTemplateManager { Task> GetWorkflowTemplatesAsync(Paging paging, CancellationToken cancellationToken); @@ -104,4 +111,5 @@ public interface IWorkflowTemplateManager Task PatchTemplateVersion(AuditUserDetails auditUserDetails, IGeneralIdRef templateId, PatchWorkflowTemplateVersion patchTemplate, CancellationToken cancellationToken); Task PostTemplateVersion(AuditUserDetails auditUserDetails, CreateWorkflowTemplateVersion template, CancellationToken cancellationToken); Task DeleteTemplateVersion(AuditUserDetails auditUserDetails, IGeneralIdRef templateId, CancellationToken cancellationToken); + Task> GetAllowedTaskMetadataList(string taskType, CancellationToken cancellationToken); } \ No newline at end of file diff --git a/e-suite.API/eSuite.API/Controllers/WorkflowTemplateController.cs b/e-suite.API/eSuite.API/Controllers/WorkflowTemplateController.cs index ecdb8a5..d1221a7 100644 --- a/e-suite.API/eSuite.API/Controllers/WorkflowTemplateController.cs +++ b/e-suite.API/eSuite.API/Controllers/WorkflowTemplateController.cs @@ -155,4 +155,24 @@ public class WorkflowTemplateController : ESuiteControllerBase await _workflowTemplateManager.DeleteTemplateVersion(AuditUserDetails, templateId, cancellationToken); return Ok(); } + + /// + /// Returns a list of Task Metadata + /// + /// + [Route("taskMetadata")] + [HttpGet] + [ProducesResponseType(StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ProblemDetails))] + [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))] + [AccessKey(SecurityAccess.DeleteWorkflowTemplate)] + public async Task GetAllowedTaskMetadataList( + [FromQuery] string taskType, + CancellationToken cancellationToken = default! + ) + { + var result = await _workflowTemplateManager.GetAllowedTaskMetadataList(taskType, cancellationToken); + return Ok(result); + } + } \ No newline at end of file diff --git a/e-suite.Modules.WorkflowTemplatesManager/WorkflowTemplateManager.cs b/e-suite.Modules.WorkflowTemplatesManager/WorkflowTemplateManager.cs index 0aa1636..819f2dd 100644 --- a/e-suite.Modules.WorkflowTemplatesManager/WorkflowTemplateManager.cs +++ b/e-suite.Modules.WorkflowTemplatesManager/WorkflowTemplateManager.cs @@ -3,10 +3,13 @@ using e_suite.API.Common.exceptions; using e_suite.API.Common.repository; using e_suite.Database.Audit; using e_suite.Database.Core.Extensions; +using e_suite.Modules.WorkflowTemplatesManager.Repository; using e_suite.Utilities.Pagination; +using e_suite.Workflow.Core.Attributes; using eSuite.Core.Miscellaneous; using System.Linq.Expressions; -using e_suite.Modules.WorkflowTemplatesManager.Repository; +using e_suite.Workflow.Core.Extensions; +using e_suite.Workflow.Core.Interfaces; //using WorkflowVersion = e_suite.Workflow.Core.WorkflowVersion; @@ -132,7 +135,7 @@ public class WorkflowTemplateManager : IWorkflowTemplateManager version.Deleted = true; }, cancellationToken); } - + private async Task AlterWorkflowTemplateVersionAsync( AuditUserDetails auditUserDetails, IGeneralIdRef generalIdRef, @@ -149,4 +152,52 @@ public class WorkflowTemplateManager : IWorkflowTemplateManager await _workflowTemplateRepository.EditWorkflowVersionAsync(auditUserDetails, workflowVersion, cancellationToken); }); } + + private static string FormatInterfaceName(Type interfaceType) + { + if (!interfaceType.IsGenericType) + return interfaceType.Name; + + var genericName = interfaceType.Name; + var tickIndex = genericName.IndexOf('`'); + if (tickIndex > 0) + genericName = genericName.Substring(0, tickIndex); + + var genericArgs = interfaceType.GetGenericArguments(); + var formattedArgs = string.Join(", ", genericArgs.Select(a => a.Name)); + + return $"{genericName}<{formattedArgs}>"; + } + + public Task> GetAllowedTaskMetadataList(string taskType, CancellationToken cancellationToken) + { + var taskTypeAttribute = StageExtensions.GetTaskAttributeType(taskType); + + var allowedTypes = StageExtensions.GetAllowedTaskTypes(taskTypeAttribute); + + var result = allowedTypes + .Select(t => + { + var interfaces = t.GetInterfaces(); + var capabilities = interfaces + .Where(i => + i != typeof(ITask) && + i.Namespace?.StartsWith("e_suite.Workflow") == true // avoid system interfaces + ) + .Select(FormatInterfaceName) + .ToList(); + + var newTaskMetadata = new TaskMetadata + { + TaskType = t.FullName!, + DisplayName = t.Name, + }; + newTaskMetadata.Capabilities.AddRange(capabilities); + + return newTaskMetadata; + }) + .ToList(); + + return Task.FromResult(result); + } } \ No newline at end of file diff --git a/e-suite.Workflow.Core/Extensions/StageExtensions.cs b/e-suite.Workflow.Core/Extensions/StageExtensions.cs index 5d249fa..823a62f 100644 --- a/e-suite.Workflow.Core/Extensions/StageExtensions.cs +++ b/e-suite.Workflow.Core/Extensions/StageExtensions.cs @@ -14,6 +14,43 @@ public static class StageExtensions // x.IsCompleted()); //} + private static readonly Dictionary TaskAttributeMap = + AppDomain.CurrentDomain + .GetAssemblies() + .SelectMany(a => + { + try { return a.GetTypes(); } + catch { return Array.Empty(); } + }) + .Where(t => + t.IsClass && + !t.IsAbstract && + typeof(TaskTypeAttribute).IsAssignableFrom(t)) + .ToDictionary( + t => t.Name, // e.g. "GeneralTaskAttribute" + t => t, + StringComparer.OrdinalIgnoreCase + ); + + public static Type GetTaskAttributeType(string taskType) + { + if (taskType == null) + throw new ArgumentNullException(nameof(taskType)); + + var key = taskType.Trim(); + + // Try exact match first + if (TaskAttributeMap.TryGetValue(key, out var type)) + return type; + + // Try with "Attribute" suffix added + if (TaskAttributeMap.TryGetValue(key + "Attribute", out type)) + return type; + + throw new ArgumentOutOfRangeException(nameof(taskType), $"Unknown task type '{taskType}'"); + } + + private static readonly ConcurrentDictionary> AllowedTasksCache = new(); [System.Diagnostics.CodeAnalysis.SuppressMessage( @@ -22,11 +59,10 @@ public static class StageExtensions [System.Diagnostics.CodeAnalysis.SuppressMessage( "Style", "IDE0060:Removed unused parameter 'stage' if it is not part of a shipped public API", Justification = "parameter required for extension method definition")] - public static IEnumerable AllowedTasks(this IStage stage) - where T : TaskTypeAttribute - { - var attributeType = typeof(T); + + public static IEnumerable GetAllowedTaskTypes(Type attributeType) + { return AllowedTasksCache.GetOrAdd(attributeType, _ => { return AppDomain.CurrentDomain @@ -43,4 +79,11 @@ public static class StageExtensions .ToArray(); }); } + + public static IEnumerable AllowedTasks(this IStage stage) + where T : TaskTypeAttribute + { + var attributeType = typeof(T); + return GetAllowedTaskTypes(attributeType); + } } \ No newline at end of file diff --git a/e-suite.Workflow.Core/Extensions/TaskExtensions.cs b/e-suite.Workflow.Core/Extensions/TaskExtensions.cs index 93f8077..c6b3f09 100644 --- a/e-suite.Workflow.Core/Extensions/TaskExtensions.cs +++ b/e-suite.Workflow.Core/Extensions/TaskExtensions.cs @@ -78,7 +78,7 @@ public static class TaskExtensions return new TaskDefinition { - Type = task.GetType().AssemblyQualifiedName, + Type = task.GetType().FullName, Config = task.ToConfigDictionary() }; } @@ -95,7 +95,7 @@ public static class TaskExtensions // return new TaskDefinition // { - // Type = task.GetType().AssemblyQualifiedName!, + // Type = task.GetType().FullName!, // Config = ExtractConfig(task) // }; //} diff --git a/e-suite.Workflow.Core/Tasks/BasicTask.cs b/e-suite.Workflow.Core/Tasks/BasicTask.cs index ae18d19..f66f90f 100644 --- a/e-suite.Workflow.Core/Tasks/BasicTask.cs +++ b/e-suite.Workflow.Core/Tasks/BasicTask.cs @@ -1,4 +1,5 @@ -using e_suite.Workflow.Core.Attributes; +using System.ComponentModel; +using e_suite.Workflow.Core.Attributes; namespace e_suite.Workflow.Core.Tasks;