Added the taskMetadata workflow endpoint

This commit is contained in:
Colin Dawson 2026-02-12 00:40:16 +00:00
parent e59430c077
commit 52ba6ee606
6 changed files with 132 additions and 9 deletions

View File

@ -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<string> Capabilities { get; set; } = [];
}
public interface IWorkflowTemplateManager
{
Task<PaginatedData<GetWorkflowTemplate>> 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<List<TaskMetadata>> GetAllowedTaskMetadataList(string taskType, CancellationToken cancellationToken);
}

View File

@ -155,4 +155,24 @@ public class WorkflowTemplateController : ESuiteControllerBase
await _workflowTemplateManager.DeleteTemplateVersion(AuditUserDetails, templateId, cancellationToken);
return Ok();
}
/// <summary>
/// Returns a list of Task Metadata
/// </summary>
/// <param name="cancellationToken"></param>
[Route("taskMetadata")]
[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ProblemDetails))]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))]
[AccessKey(SecurityAccess.DeleteWorkflowTemplate)]
public async Task<IActionResult> GetAllowedTaskMetadataList(
[FromQuery] string taskType,
CancellationToken cancellationToken = default!
)
{
var result = await _workflowTemplateManager.GetAllowedTaskMetadataList(taskType, cancellationToken);
return Ok(result);
}
}

View File

@ -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;
@ -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<List<TaskMetadata>> 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);
}
}

View File

@ -14,6 +14,43 @@ public static class StageExtensions
// x.IsCompleted());
//}
private static readonly Dictionary<string, Type> TaskAttributeMap =
AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(a =>
{
try { return a.GetTypes(); }
catch { return Array.Empty<Type>(); }
})
.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<Type, IEnumerable<Type>> 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<Type> AllowedTasks<T>(this IStage<T> stage)
where T : TaskTypeAttribute
{
var attributeType = typeof(T);
public static IEnumerable<Type> GetAllowedTaskTypes(Type attributeType)
{
return AllowedTasksCache.GetOrAdd(attributeType, _ =>
{
return AppDomain.CurrentDomain
@ -43,4 +79,11 @@ public static class StageExtensions
.ToArray();
});
}
public static IEnumerable<Type> AllowedTasks<T>(this IStage<T> stage)
where T : TaskTypeAttribute
{
var attributeType = typeof(T);
return GetAllowedTaskTypes(attributeType);
}
}

View File

@ -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)
// };
//}

View File

@ -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;