From 607dc73e67607389e0d691ce18c6a8532122ae27 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 13 Jun 2025 19:44:10 +0200 Subject: [PATCH 01/10] Fix ordering and pagination logic in `FindManyAsync` method for `WorkflowExecutionLogStore` Reordered query operations to ensure consistent execution of `OrderBy` before `Paginate`, improving clarity and maintaining the expected query behavior. --- .../Modules/Runtime/WorkflowExecutionLogStore.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowExecutionLogStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowExecutionLogStore.cs index 2569f479e..94ca16dc5 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowExecutionLogStore.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/WorkflowExecutionLogStore.cs @@ -52,8 +52,8 @@ public class EFCoreWorkflowExecutionLogStore(EntityStore public async Task> FindManyAsync(WorkflowExecutionLogRecordFilter filter, PageArgs pageArgs, CancellationToken cancellationToken = default) { - var count = await store.QueryAsync(queryable => Filter(queryable, filter).OrderBy(x => x.Timestamp), cancellationToken).LongCount(); - var results = await store.QueryAsync(queryable => Filter(queryable, filter).Paginate(pageArgs), OnLoadAsync, cancellationToken).ToList(); + var count = await store.QueryAsync(queryable => Filter(queryable, filter), cancellationToken).LongCount(); + var results = await store.QueryAsync(queryable => Filter(queryable, filter).OrderBy(x => x.Timestamp).Paginate(pageArgs), OnLoadAsync, cancellationToken).ToList(); return new(results, count); } From b860370d9d6320805f13514fc9188ba042bf0954 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 13 Jun 2025 20:35:19 +0200 Subject: [PATCH 02/10] Add `HealActivity` alteration and handler with workflow fault recovery support - Introduced `HealActivity` alteration type for resolving faulted activity states. - Added `HealActivityHandler` to manage execution of `HealActivity`. - Enhanced `WorkflowExecutionContextExtensions` with `FindActivityExecutionContexts` method for locating activity execution contexts based on provided handles. - Updated `AlterationHandlerContext` to enable custom commit actions during `Succeed` calls. - Registered `HealActivity` and its handler in alteration services. - Upgraded `ElsaStudioVersion` to `3.5.0-preview.1092`. --- Directory.Packages.props | 2 +- src/apps/Directory.Build.props | 4 -- .../Contexts/AlterationHandlerContext.cs | 30 ++++++++++++- .../AlterationHandlers/HealActivityHandler.cs | 42 +++++++++++++++++++ .../AlterationTypes/HealActivity.cs | 17 ++++++++ .../Extensions/ServiceCollectionExtensions.cs | 1 + .../WorkflowExecutionContextExtensions.cs | 23 ++++++++-- .../Models/ActivityHandle.cs | 35 ++++++++++++---- 8 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 src/modules/Elsa.Alterations/AlterationHandlers/HealActivityHandler.cs create mode 100644 src/modules/Elsa.Alterations/AlterationTypes/HealActivity.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index 58bd46230..a0b5e6b20 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,7 +4,7 @@ true - 3.4.0 + 3.5.0-preview.1092 diff --git a/src/apps/Directory.Build.props b/src/apps/Directory.Build.props index 9edfcd282..45f95be6a 100644 --- a/src/apps/Directory.Build.props +++ b/src/apps/Directory.Build.props @@ -7,10 +7,6 @@ $(NoWarn);CS0162;CS1591 - - 3.5.0-preview.1040 - - diff --git a/src/modules/Elsa.Alterations.Core/Contexts/AlterationHandlerContext.cs b/src/modules/Elsa.Alterations.Core/Contexts/AlterationHandlerContext.cs index f583f6cd3..9836749e8 100644 --- a/src/modules/Elsa.Alterations.Core/Contexts/AlterationHandlerContext.cs +++ b/src/modules/Elsa.Alterations.Core/Contexts/AlterationHandlerContext.cs @@ -99,13 +99,26 @@ public class AlterationContext CommitAction = commitAction; } + /// + /// Marks the alteration as succeeded. + /// + public void Succeed(Action commitAction) + { + Succeed(); + CommitAction = () => + { + commitAction(); + return Task.CompletedTask; + }; + } + /// /// Marks the alteration as succeeded. /// public void Succeed(string message) { HasSucceeded = true; - Log($"Alteration {Alteration.GetType().Name} succeeded", message, LogLevel.Information); + Log($"Alteration {Alteration.GetType().Name} succeeded", message); } /// @@ -116,12 +129,25 @@ public class AlterationContext Succeed(message); CommitAction = commitAction; } + + /// + /// Marks the alteration as succeeded. + /// + public void Succeed(string message, Action commitAction) + { + Succeed(message); + CommitAction = () => + { + commitAction(); + return Task.CompletedTask; + }; + } /// /// Marks the alteration as failed. /// /// An optional message. - public void Fail(string? message = default) + public void Fail(string? message = null) { HasFailed = true; Log($"Alteration {Alteration.GetType().Name} failed", message ?? $"{Alteration.GetType().Name} failed", LogLevel.Error); diff --git a/src/modules/Elsa.Alterations/AlterationHandlers/HealActivityHandler.cs b/src/modules/Elsa.Alterations/AlterationHandlers/HealActivityHandler.cs new file mode 100644 index 000000000..715380a3d --- /dev/null +++ b/src/modules/Elsa.Alterations/AlterationHandlers/HealActivityHandler.cs @@ -0,0 +1,42 @@ +using Elsa.Alterations.AlterationTypes; +using Elsa.Alterations.Core.Abstractions; +using Elsa.Alterations.Core.Contexts; +using Elsa.Extensions; +using Elsa.Workflows; +using JetBrains.Annotations; + +namespace Elsa.Alterations.AlterationHandlers; + +/// +/// Cancels an activity. +/// +[UsedImplicitly] +public class HealActivityHandler : AlterationHandlerBase +{ + /// + protected override ValueTask HandleAsync(AlterationContext context, HealActivity alteration) + { + var activityExecutionContexts = context.WorkflowExecutionContext.FindActivityExecutionContexts(alteration.ActivityHandle).ToList(); + + if (!activityExecutionContexts.Any()) + { + context.Fail($"Activity execution context with handle {alteration.ActivityHandle} not found"); + + return ValueTask.CompletedTask; + } + + context.Succeed(() => Heal(activityExecutionContexts)); + return ValueTask.CompletedTask; + } + + private void Heal(IEnumerable activityExecutionContexts) + { + foreach (var activityExecutionContext in activityExecutionContexts) + Heal(activityExecutionContext); + } + + private void Heal(ActivityExecutionContext activityExecutionContext) + { + activityExecutionContext.RecoverFromFault(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Alterations/AlterationTypes/HealActivity.cs b/src/modules/Elsa.Alterations/AlterationTypes/HealActivity.cs new file mode 100644 index 000000000..1afbdb8d0 --- /dev/null +++ b/src/modules/Elsa.Alterations/AlterationTypes/HealActivity.cs @@ -0,0 +1,17 @@ +using Elsa.Alterations.Core.Abstractions; +using Elsa.Workflows.Models; +using JetBrains.Annotations; + +namespace Elsa.Alterations.AlterationTypes; + +/// +/// Heals an activity from the Faulted state. +/// +[UsedImplicitly] +public class HealActivity : AlterationBase +{ + /// + /// The handle to the to be healed. + /// + public ActivityHandle ActivityHandle { get; set; } = null!; +} \ No newline at end of file diff --git a/src/modules/Elsa.Alterations/Extensions/ServiceCollectionExtensions.cs b/src/modules/Elsa.Alterations/Extensions/ServiceCollectionExtensions.cs index b930d2878..5f2c069f1 100644 --- a/src/modules/Elsa.Alterations/Extensions/ServiceCollectionExtensions.cs +++ b/src/modules/Elsa.Alterations/Extensions/ServiceCollectionExtensions.cs @@ -20,6 +20,7 @@ public static class ServiceCollectionExtensions services.AddAlteration(); services.AddAlteration(); services.AddAlteration(); + services.AddAlteration(); services.AddAlteration(); services.AddNotificationHandlersFrom(); return services; diff --git a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs index 57c01a6e6..1f683c0e6 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs @@ -50,8 +50,8 @@ public static class WorkflowExecutionContextExtensions public static ActivityWorkItem ScheduleActivityExecutionContext(this WorkflowExecutionContext workflowExecutionContext, ActivityExecutionContext activityExecutionContext, IDictionary? input = null, IEnumerable? variables = null) { var workItem = new ActivityWorkItem( - activityExecutionContext.Activity, - input: input, + activityExecutionContext.Activity, + input: input, variables: variables, existingActivityExecutionContext: activityExecutionContext); workflowExecutionContext.Scheduler.Schedule(workItem); @@ -112,7 +112,7 @@ public static class WorkflowExecutionContextExtensions // Validate that the specified activity is part of the workflow. if (!workflowExecutionContext.NodeActivityLookup.ContainsKey(activityNode.Activity)) throw new InvalidOperationException("The specified activity is not part of the workflow."); - + var scheduler = workflowExecutionContext.Scheduler; if (options?.PreventDuplicateScheduling == true) @@ -145,4 +145,21 @@ public static class WorkflowExecutionContextExtensions var outputRegister = workflowExecutionContext.GetActivityOutputRegister(); return outputRegister.FindOutputByActivityId(activityId, outputName); } + + public static IEnumerable FindActivityExecutionContexts(this WorkflowExecutionContext workflowExecutionContext, ActivityHandle activityHandle) + { + if (activityHandle.ActivityInstanceId != null) + return workflowExecutionContext.ActivityExecutionContexts.Where(x => x.Id == activityHandle.ActivityId); + if (activityHandle.ActivityNodeId != null) + return workflowExecutionContext.ActivityExecutionContexts.Where(x => x.NodeId == activityHandle.ActivityNodeId); + if (activityHandle.ActivityId != null) + return workflowExecutionContext.ActivityExecutionContexts.Where(x => x.Activity.Id == activityHandle.ActivityId); + if (activityHandle.ActivityHash != null) + { + var activity = workflowExecutionContext.FindActivityByHash(activityHandle.ActivityHash); + return activity != null ? workflowExecutionContext.ActivityExecutionContexts.Where(x => x.Activity.NodeId == activity.NodeId) : []; + } + + return []; + } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Models/ActivityHandle.cs b/src/modules/Elsa.Workflows.Core/Models/ActivityHandle.cs index ba0cb92c8..7ffefa1d2 100644 --- a/src/modules/Elsa.Workflows.Core/Models/ActivityHandle.cs +++ b/src/modules/Elsa.Workflows.Core/Models/ActivityHandle.cs @@ -5,12 +5,33 @@ namespace Elsa.Workflows.Models; /// public class ActivityHandle { - public static ActivityHandle FromActivityId(string activityId) => new() { ActivityId = activityId }; - public static ActivityHandle FromActivityNodeId(string activityNodeId) => new() { ActivityNodeId = activityNodeId }; - public static ActivityHandle FromActivityInstanceId(string activityInstanceId) => new() { ActivityInstanceId = activityInstanceId }; - public static ActivityHandle FromActivityHash(string activityHash) => new() { ActivityHash = activityHash }; + public static ActivityHandle FromActivityId(string activityId) => new() + { + ActivityId = activityId + }; + + public static ActivityHandle FromActivityNodeId(string activityNodeId) => new() + { + ActivityNodeId = activityNodeId + }; + + public static ActivityHandle FromActivityInstanceId(string activityInstanceId) => new() + { + ActivityInstanceId = activityInstanceId + }; + + public static ActivityHandle FromActivityHash(string activityHash) => new() + { + ActivityHash = activityHash + }; + public string? ActivityId { get; init; } - public string? ActivityNodeId { get; init;} - public string? ActivityInstanceId { get; init;} - public string? ActivityHash { get; init;} + public string? ActivityNodeId { get; init; } + public string? ActivityInstanceId { get; init; } + public string? ActivityHash { get; init; } + + public override string ToString() + { + return ActivityId ?? (ActivityNodeId ?? (ActivityInstanceId ?? (ActivityHash ?? ""))); + } } \ No newline at end of file From ce3a0b9ce0893fbb3598506c9aa44496f5a931dc Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 14 Jun 2025 11:38:25 +0200 Subject: [PATCH 03/10] Refactors alteration job dispatcher. Refactors the background alteration job dispatcher to use a service scope to resolve the alteration job runner. This ensures that the runner is resolved within a scope, allowing it to utilize scoped services. --- .../BackgroundAlterationJobDispatcher.cs | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/modules/Elsa.Alterations/Services/BackgroundAlterationJobDispatcher.cs b/src/modules/Elsa.Alterations/Services/BackgroundAlterationJobDispatcher.cs index 65e3b9ace..80874dc32 100644 --- a/src/modules/Elsa.Alterations/Services/BackgroundAlterationJobDispatcher.cs +++ b/src/modules/Elsa.Alterations/Services/BackgroundAlterationJobDispatcher.cs @@ -1,34 +1,25 @@ using Elsa.Alterations.Core.Contracts; using Elsa.Mediator.Contracts; +using Microsoft.Extensions.DependencyInjection; namespace Elsa.Alterations.Services; /// /// Dispatches an alteration job for execution using an in-memory channel. /// -public class BackgroundAlterationJobDispatcher : IAlterationJobDispatcher +public class BackgroundAlterationJobDispatcher(IJobQueue jobQueue, IServiceScopeFactory scopeFactory) : IAlterationJobDispatcher { - private readonly IJobQueue _jobQueue; - private readonly IAlterationJobRunner _alterationJobRunner; - - /// - /// Initializes a new instance of the class. - /// - public BackgroundAlterationJobDispatcher(IJobQueue jobQueue, IAlterationJobRunner alterationJobRunner) - { - _jobQueue = jobQueue; - _alterationJobRunner = alterationJobRunner; - } - /// public ValueTask DispatchAsync(string jobId, CancellationToken cancellationToken = default) { - _jobQueue.Enqueue(ct => ExecuteJobAsync(jobId, ct)); + jobQueue.Enqueue(ct => ExecuteJobAsync(jobId, ct)); return default; } private async Task ExecuteJobAsync(string alterationJobId, CancellationToken cancellationToken) { - await _alterationJobRunner.RunAsync(alterationJobId, cancellationToken); + using var scope = scopeFactory.CreateScope(); + var alterationJobRunner = scope.ServiceProvider.GetRequiredService(); + await alterationJobRunner.RunAsync(alterationJobId, cancellationToken); } } \ No newline at end of file From 2cbcefee836acd2c1b805be82543eb69f9acfa4f Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 14 Jun 2025 12:15:34 +0200 Subject: [PATCH 04/10] Remove `HealActivity` alteration and handler - Deleted `HealActivity` alteration type and associated `HealActivityHandler`. - Unregistered `HealActivity` from alteration services. --- .../AlterationHandlers/HealActivityHandler.cs | 42 ------------------- .../AlterationTypes/HealActivity.cs | 17 -------- .../Extensions/ServiceCollectionExtensions.cs | 1 - 3 files changed, 60 deletions(-) delete mode 100644 src/modules/Elsa.Alterations/AlterationHandlers/HealActivityHandler.cs delete mode 100644 src/modules/Elsa.Alterations/AlterationTypes/HealActivity.cs diff --git a/src/modules/Elsa.Alterations/AlterationHandlers/HealActivityHandler.cs b/src/modules/Elsa.Alterations/AlterationHandlers/HealActivityHandler.cs deleted file mode 100644 index 715380a3d..000000000 --- a/src/modules/Elsa.Alterations/AlterationHandlers/HealActivityHandler.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Elsa.Alterations.AlterationTypes; -using Elsa.Alterations.Core.Abstractions; -using Elsa.Alterations.Core.Contexts; -using Elsa.Extensions; -using Elsa.Workflows; -using JetBrains.Annotations; - -namespace Elsa.Alterations.AlterationHandlers; - -/// -/// Cancels an activity. -/// -[UsedImplicitly] -public class HealActivityHandler : AlterationHandlerBase -{ - /// - protected override ValueTask HandleAsync(AlterationContext context, HealActivity alteration) - { - var activityExecutionContexts = context.WorkflowExecutionContext.FindActivityExecutionContexts(alteration.ActivityHandle).ToList(); - - if (!activityExecutionContexts.Any()) - { - context.Fail($"Activity execution context with handle {alteration.ActivityHandle} not found"); - - return ValueTask.CompletedTask; - } - - context.Succeed(() => Heal(activityExecutionContexts)); - return ValueTask.CompletedTask; - } - - private void Heal(IEnumerable activityExecutionContexts) - { - foreach (var activityExecutionContext in activityExecutionContexts) - Heal(activityExecutionContext); - } - - private void Heal(ActivityExecutionContext activityExecutionContext) - { - activityExecutionContext.RecoverFromFault(); - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Alterations/AlterationTypes/HealActivity.cs b/src/modules/Elsa.Alterations/AlterationTypes/HealActivity.cs deleted file mode 100644 index 1afbdb8d0..000000000 --- a/src/modules/Elsa.Alterations/AlterationTypes/HealActivity.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Elsa.Alterations.Core.Abstractions; -using Elsa.Workflows.Models; -using JetBrains.Annotations; - -namespace Elsa.Alterations.AlterationTypes; - -/// -/// Heals an activity from the Faulted state. -/// -[UsedImplicitly] -public class HealActivity : AlterationBase -{ - /// - /// The handle to the to be healed. - /// - public ActivityHandle ActivityHandle { get; set; } = null!; -} \ No newline at end of file diff --git a/src/modules/Elsa.Alterations/Extensions/ServiceCollectionExtensions.cs b/src/modules/Elsa.Alterations/Extensions/ServiceCollectionExtensions.cs index 5f2c069f1..b930d2878 100644 --- a/src/modules/Elsa.Alterations/Extensions/ServiceCollectionExtensions.cs +++ b/src/modules/Elsa.Alterations/Extensions/ServiceCollectionExtensions.cs @@ -20,7 +20,6 @@ public static class ServiceCollectionExtensions services.AddAlteration(); services.AddAlteration(); services.AddAlteration(); - services.AddAlteration(); services.AddAlteration(); services.AddNotificationHandlersFrom(); return services; From 54fcd9305b07dc32b8e4f1ba9480b8156f4e4630 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 14 Jun 2025 12:15:46 +0200 Subject: [PATCH 05/10] Simplify exception handling logic in `ExceptionHandlingMiddleware` - Removed `LogExceptionAndTransition` and `FaultAncestors` methods. - Integrated exception fault handling with `ActivityExecutionContext.Fault()`. --- .../Activities/ExceptionHandlingMiddleware.cs | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/modules/Elsa.Workflows.Core/Middleware/Activities/ExceptionHandlingMiddleware.cs b/src/modules/Elsa.Workflows.Core/Middleware/Activities/ExceptionHandlingMiddleware.cs index 60f4e3fec..b7028e2b8 100644 --- a/src/modules/Elsa.Workflows.Core/Middleware/Activities/ExceptionHandlingMiddleware.cs +++ b/src/modules/Elsa.Workflows.Core/Middleware/Activities/ExceptionHandlingMiddleware.cs @@ -1,8 +1,6 @@ using Elsa.Common; using Elsa.Extensions; -using Elsa.Workflows.Models; using Elsa.Workflows.Pipelines.ActivityExecution; -using Elsa.Workflows.State; using Microsoft.Extensions.Logging; namespace Elsa.Workflows.Middleware.Activities; @@ -34,34 +32,14 @@ public class ExceptionHandlingMiddleware(ActivityMiddlewareDelegate next, IIncid catch (Exception e) { logger.LogWarning(e, "An exception was caught from a downstream middleware component"); - LogExceptionAndTransition(context, e); - FaultAncestors(context); + context.Fault(e); await HandleIncidentAsync(context); } } - private void LogExceptionAndTransition(ActivityExecutionContext context, Exception e) - { - context.Exception = e; - context.TransitionTo(ActivityStatus.Faulted); - var activity = context.Activity; - var exceptionState = ExceptionState.FromException(e); - var now = systemClock.UtcNow; - var incident = new ActivityIncident(activity.Id, activity.NodeId ,activity.Type, e.Message, exceptionState, now); - context.WorkflowExecutionContext.Incidents.Add(incident); - } - private async Task HandleIncidentAsync(ActivityExecutionContext context) { var strategy = await incidentStrategyResolver.ResolveStrategyAsync(context); strategy.HandleIncident(context); } - - private static void FaultAncestors(ActivityExecutionContext context) - { - var ancestors = context.GetAncestors(); - - foreach (var ancestor in ancestors) - ancestor.TransitionTo(ActivityStatus.Faulted); - } } \ No newline at end of file From ab6bb23df9cc92e7f467d7b66f0cf8735dd321d1 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 14 Jun 2025 13:00:45 +0200 Subject: [PATCH 06/10] Update src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../Extensions/WorkflowExecutionContextExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs index 1f683c0e6..f025ef74d 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/WorkflowExecutionContextExtensions.cs @@ -149,7 +149,7 @@ public static class WorkflowExecutionContextExtensions public static IEnumerable FindActivityExecutionContexts(this WorkflowExecutionContext workflowExecutionContext, ActivityHandle activityHandle) { if (activityHandle.ActivityInstanceId != null) - return workflowExecutionContext.ActivityExecutionContexts.Where(x => x.Id == activityHandle.ActivityId); + return workflowExecutionContext.ActivityExecutionContexts.Where(x => x.Id == activityHandle.ActivityInstanceId); if (activityHandle.ActivityNodeId != null) return workflowExecutionContext.ActivityExecutionContexts.Where(x => x.NodeId == activityHandle.ActivityNodeId); if (activityHandle.ActivityId != null) From 776630a556c286fb0615b0ddae03669dd61c3947 Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 5 Jun 2025 00:36:18 +0100 Subject: [PATCH 07/10] Create Api Client Models for upcoming RadioList UIHint. --- src/clients/Elsa.Api.Client/Elsa.Api.Client.csproj | 4 ++-- .../Shared/UIHints/RadioList/RadioList.cs | 3 +++ .../Shared/UIHints/RadioList/RadioListItem.cs | 9 +++++++++ .../Shared/UIHints/RadioList/RadioListProps.cs | 9 +++++++++ 4 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioList.cs create mode 100644 src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListItem.cs create mode 100644 src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs diff --git a/src/clients/Elsa.Api.Client/Elsa.Api.Client.csproj b/src/clients/Elsa.Api.Client/Elsa.Api.Client.csproj index f8246f150..068b8badc 100644 --- a/src/clients/Elsa.Api.Client/Elsa.Api.Client.csproj +++ b/src/clients/Elsa.Api.Client/Elsa.Api.Client.csproj @@ -1,4 +1,4 @@ - + @@ -17,4 +17,4 @@ - + \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioList.cs b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioList.cs new file mode 100644 index 000000000..3075e7be8 --- /dev/null +++ b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioList.cs @@ -0,0 +1,3 @@ +namespace Elsa.Api.Client.Shared.UIHints.RadioList; + +public record RadioList(IEnumerable Items, bool IsFlagsEnum = false); \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListItem.cs b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListItem.cs new file mode 100644 index 000000000..45ba5d2e2 --- /dev/null +++ b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListItem.cs @@ -0,0 +1,9 @@ +namespace Elsa.Api.Client.Shared.UIHints.RadioList; + +public class RadioListItem +{ +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public string Text { get; set; } + public string Value { get; set; } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. +} \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs new file mode 100644 index 000000000..fa1c6a25d --- /dev/null +++ b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs @@ -0,0 +1,9 @@ +namespace Elsa.Api.Client.Shared.UIHints.RadioList; + +public class RadioListProps +{ + /// + /// The select list. + /// + public RadioList? CheckList { get; set; } +} \ No newline at end of file From b4fd26d52376037e89a424d140251d99947f2aca Mon Sep 17 00:00:00 2001 From: Matt Date: Thu, 5 Jun 2025 01:56:44 +0100 Subject: [PATCH 08/10] Add radio list support and related UI components - Renamed `CheckList` to `RadioList` in `RadioListProps`. - Updated `WorkflowsFeature` to include `RadioListUIHintHandler` and `StaticRadioListOptionsProvider`. - Introduced `TestRadioList` class for executing radio list functionality. - Created `RadioList` and `RadioListItem` classes for managing radio list items. - Added `RadioListOptionsProviderBase` for custom radio list data logic. - Implemented `StaticRadioListOptionsProvider` for static radio list options. --- src/apps/Elsa.Server.Web/RadioListActivity.cs | 42 +++++++++++++++++ .../UIHints/RadioList/RadioListProps.cs | 2 +- .../Features/WorkflowsFeature.cs | 3 ++ .../UIHints/RadioList/RadioList.cs | 7 +++ .../UIHints/RadioList/RadioListItem.cs | 3 ++ .../RadioList/RadioListOptionsProviderBase.cs | 47 +++++++++++++++++++ .../UIHints/RadioList/RadioListProps.cs | 9 ++++ .../RadioList/RadioListUIHintHandler.cs | 16 +++++++ .../StaticRadioListOptionsProvider.cs | 29 ++++++++++++ 9 files changed, 157 insertions(+), 1 deletion(-) create mode 100644 src/apps/Elsa.Server.Web/RadioListActivity.cs create mode 100644 src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioList.cs create mode 100644 src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListItem.cs create mode 100644 src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListOptionsProviderBase.cs create mode 100644 src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListProps.cs create mode 100644 src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListUIHintHandler.cs create mode 100644 src/modules/Elsa.Workflows.Core/UIHints/RadioList/StaticRadioListOptionsProvider.cs diff --git a/src/apps/Elsa.Server.Web/RadioListActivity.cs b/src/apps/Elsa.Server.Web/RadioListActivity.cs new file mode 100644 index 000000000..2e7a38208 --- /dev/null +++ b/src/apps/Elsa.Server.Web/RadioListActivity.cs @@ -0,0 +1,42 @@ +using System.Runtime.CompilerServices; +using Elsa.Workflows; +using Elsa.Workflows.Attributes; +using Elsa.Workflows.UIHints; +using Elsa.Workflows.Models; + +// ReSharper disable once CheckNamespace +namespace Elsa.Server.Web; + +/// +/// Executes C# code. +/// +[Activity("Elsa", "TESTS", "Tests Radio List Functionality", DisplayName = "TEST")] +public class TestRadioList : CodeActivity +{ + /// + public TestRadioList([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line) + { + } + + /// + public TestRadioList(string script, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(source, line) + { + } + + /// + /// The script to run. + /// + [Input( + Description = "Choose to download one file or entire folder", + DefaultValue = "File", + Options = new[] { "File", "Folder" }, + UIHint = InputUIHints.RadioList + )] + public Input SelectedRadioOption { get; set; } = default!; + + /// + protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) + { + + } +} \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs index fa1c6a25d..a4441a15f 100644 --- a/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs +++ b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs @@ -5,5 +5,5 @@ public class RadioListProps /// /// The select list. /// - public RadioList? CheckList { get; set; } + public RadioList? RadioList { get; set; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs b/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs index 5759abed5..cc3f81aad 100644 --- a/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs +++ b/src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs @@ -24,6 +24,7 @@ using Elsa.Workflows.Services; using Elsa.Workflows.UIHints.CheckList; using Elsa.Workflows.UIHints.Dropdown; using Elsa.Workflows.UIHints.JsonEditor; +using Elsa.Workflows.UIHints.RadioList; using Microsoft.Extensions.DependencyInjection; namespace Elsa.Workflows.Features; @@ -230,10 +231,12 @@ public class WorkflowsFeature : FeatureBase // UI hints. .AddScoped() .AddScoped() + .AddScoped() .AddScoped() // UI property handlers. .AddScoped() + .AddScoped() .AddScoped() .AddScoped() diff --git a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioList.cs b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioList.cs new file mode 100644 index 000000000..0dd5e25ec --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioList.cs @@ -0,0 +1,7 @@ +namespace Elsa.Workflows.UIHints.RadioList; + +public class RadioList +{ + public IEnumerable Items { get; set; } + public bool IsFlagsEnum { get; set; } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListItem.cs b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListItem.cs new file mode 100644 index 000000000..d683b8070 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListItem.cs @@ -0,0 +1,3 @@ +namespace Elsa.Workflows.UIHints.RadioList; + +public record RadioListItem(string Text, string Value); \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListOptionsProviderBase.cs b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListOptionsProviderBase.cs new file mode 100644 index 000000000..aa09266cb --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListOptionsProviderBase.cs @@ -0,0 +1,47 @@ +using System.Reflection; +using Elsa.Extensions; + +namespace Elsa.Workflows.UIHints.RadioList; + +/// +/// A base class for providing options to populate a checklist UI component. This class is intended to be inherited to implement +/// custom radiolist data logic by overriding the `GetItemsAsync` method. +/// +public abstract class RadioListOptionsProviderBase : PropertyUIHandlerBase +{ + protected virtual bool RefreshOnChange => false; + + /// + public override async ValueTask> GetUIPropertiesAsync(PropertyInfo propertyInfo, object? context, CancellationToken cancellationToken = default) + { + var items = await GetItemsAsync(propertyInfo, context, cancellationToken); + var props = new RadioListProps + { + RadioList = new() + { + Items = items.ToList() + } + }; + + var options = new Dictionary + { + [InputUIHints.RadioList] = props + }; + + options.AddRange(GetUIPropertyAdditionalOptions()); + + return options; + } + + /// + /// Implement this to provide items to the dropdown list. + /// + protected abstract ValueTask> GetItemsAsync(PropertyInfo propertyInfo, object? context, CancellationToken cancellationToken); + + protected virtual IDictionary GetUIPropertyAdditionalOptions() + { + var options = new Dictionary(); + if (RefreshOnChange) options["Refresh"] = true; + return options; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListProps.cs b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListProps.cs new file mode 100644 index 000000000..2465cdda5 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListProps.cs @@ -0,0 +1,9 @@ +namespace Elsa.Workflows.UIHints.RadioList; + +public class RadioListProps +{ + /// + /// The select list. + /// + public RadioList? RadioList { get; set; } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListUIHintHandler.cs b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListUIHintHandler.cs new file mode 100644 index 000000000..48607267c --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListUIHintHandler.cs @@ -0,0 +1,16 @@ +using System.Reflection; + +namespace Elsa.Workflows.UIHints.RadioList; + +/// +public class RadioListUIHintHandler : IUIHintHandler +{ + /// + public string UIHint => InputUIHints.RadioList; + + /// + public ValueTask> GetPropertyUIHandlersAsync(PropertyInfo propertyInfo, CancellationToken cancellationToken) + { + return new([typeof(StaticRadioListOptionsProvider)]); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/StaticRadioListOptionsProvider.cs b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/StaticRadioListOptionsProvider.cs new file mode 100644 index 000000000..f05f31948 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/StaticRadioListOptionsProvider.cs @@ -0,0 +1,29 @@ +using System.Reflection; +using Elsa.Workflows.Attributes; + +namespace Elsa.Workflows.UIHints.RadioList; + +/// +/// Provides static drop-down options for a given property. +/// +public class StaticRadioListOptionsProvider : RadioListOptionsProviderBase +{ + public override float Priority => -1; + + /// + protected override ValueTask> GetItemsAsync(PropertyInfo propertyInfo, object? context, CancellationToken cancellationToken) + { + var inputAttribute = propertyInfo.GetCustomAttribute(); + var inputOptions = inputAttribute?.Options; + + if (inputOptions == null) + return new([]); + + var selectListItems = (inputOptions as ICollection)?.Select(x => new RadioListItem(x, x)).ToList(); + + if (selectListItems == null) + return new([]); + + return new(selectListItems); + } +} \ No newline at end of file From 4530dfd1f7c8303519ae84803f77a0c7334b95ce Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 6 Jun 2025 00:13:35 +0100 Subject: [PATCH 09/10] Refactor checklists and radio lists to use records Converted `CheckList` and `CheckListItem` to records, adding XML documentation for clarity. Updated properties in `CheckListProps`, `RadioList`, and `RadioListItem` with similar changes. Enhanced documentation in `DropDownOptionsProviderBase` and modified `RadioListOptionsProviderBase` to reflect new functionality. Overall improvements for readability and maintainability. --- .../Shared/UIHints/CheckList/CheckList.cs | 5 +++++ .../Shared/UIHints/CheckList/CheckListItem.cs | 12 ++++-------- .../Shared/UIHints/CheckList/CheckListProps.cs | 3 +++ .../Shared/UIHints/RadioList/RadioList.cs | 5 +++++ .../Shared/UIHints/RadioList/RadioListItem.cs | 11 ++++------- .../Shared/UIHints/RadioList/RadioListProps.cs | 3 +++ .../UIHints/CheckList/CheckList.cs | 10 ++++++++++ .../UIHints/CheckList/CheckListProps.cs | 3 +++ .../UIHints/Dropdown/DropDownOptionsProviderBase.cs | 3 ++- .../UIHints/RadioList/RadioList.cs | 10 ++++++++++ .../RadioList/RadioListOptionsProviderBase.cs | 2 +- .../UIHints/RadioList/RadioListProps.cs | 3 +++ 12 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckList.cs b/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckList.cs index 3caba2572..ed04df0e6 100644 --- a/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckList.cs +++ b/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckList.cs @@ -1,3 +1,8 @@ namespace Elsa.Api.Client.Shared.UIHints.CheckList; +/// +/// Represents a list of check list items. +/// +/// The items. +/// Whether the select list represents a flags enum. public record CheckList(IEnumerable Items, bool IsFlagsEnum = false); \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListItem.cs b/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListItem.cs index 53cd50bd6..62d5e6bf5 100644 --- a/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListItem.cs +++ b/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListItem.cs @@ -1,10 +1,6 @@ namespace Elsa.Api.Client.Shared.UIHints.CheckList; -public class CheckListItem -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public string Text { get; set; } - public string Value { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public bool IsChecked { get; set; } -} \ No newline at end of file +/// +/// Represents an item in a . +/// +public record CheckListItem(string Text, string Value, bool IsChecked); \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListProps.cs b/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListProps.cs index 431e9263d..4ef203dc7 100644 --- a/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListProps.cs +++ b/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListProps.cs @@ -1,5 +1,8 @@ namespace Elsa.Api.Client.Shared.UIHints.CheckList; +/// +/// Provides properties for the checklist UI hint. +/// public class CheckListProps { /// diff --git a/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioList.cs b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioList.cs index 3075e7be8..b17e00f1e 100644 --- a/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioList.cs +++ b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioList.cs @@ -1,3 +1,8 @@ namespace Elsa.Api.Client.Shared.UIHints.RadioList; +/// +/// Represents a list of radio list items. +/// +/// The items. +/// Whether the select list represents a flags enum. public record RadioList(IEnumerable Items, bool IsFlagsEnum = false); \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListItem.cs b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListItem.cs index 45ba5d2e2..3e7722287 100644 --- a/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListItem.cs +++ b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListItem.cs @@ -1,9 +1,6 @@ namespace Elsa.Api.Client.Shared.UIHints.RadioList; -public class RadioListItem -{ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public string Text { get; set; } - public string Value { get; set; } -#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. -} \ No newline at end of file +/// +/// Represents an item in a . +/// +public record RadioListItem(string Text, string Value); \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs index a4441a15f..be100b644 100644 --- a/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs +++ b/src/clients/Elsa.Api.Client/Shared/UIHints/RadioList/RadioListProps.cs @@ -1,5 +1,8 @@ namespace Elsa.Api.Client.Shared.UIHints.RadioList; +/// +/// Provides properties for the radiolist UI hint. +/// public class RadioListProps { /// diff --git a/src/modules/Elsa.Workflows.Core/UIHints/CheckList/CheckList.cs b/src/modules/Elsa.Workflows.Core/UIHints/CheckList/CheckList.cs index dcb697d7e..4380ecae1 100644 --- a/src/modules/Elsa.Workflows.Core/UIHints/CheckList/CheckList.cs +++ b/src/modules/Elsa.Workflows.Core/UIHints/CheckList/CheckList.cs @@ -1,7 +1,17 @@ namespace Elsa.Workflows.UIHints.CheckList; +/// +/// Provides properties for the UI hint. +/// public class CheckList { + /// + /// The radio list. + /// public IEnumerable Items { get; set; } + + /// + /// The name of the provider that will provide the select list. + /// public bool IsFlagsEnum { get; set; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/UIHints/CheckList/CheckListProps.cs b/src/modules/Elsa.Workflows.Core/UIHints/CheckList/CheckListProps.cs index 4d3d6e8df..f2dda59aa 100644 --- a/src/modules/Elsa.Workflows.Core/UIHints/CheckList/CheckListProps.cs +++ b/src/modules/Elsa.Workflows.Core/UIHints/CheckList/CheckListProps.cs @@ -1,5 +1,8 @@ namespace Elsa.Workflows.UIHints.CheckList; +/// +/// Provides properties for the UI hint. +/// public class CheckListProps { /// diff --git a/src/modules/Elsa.Workflows.Core/UIHints/Dropdown/DropDownOptionsProviderBase.cs b/src/modules/Elsa.Workflows.Core/UIHints/Dropdown/DropDownOptionsProviderBase.cs index e248cb557..5ee12af51 100644 --- a/src/modules/Elsa.Workflows.Core/UIHints/Dropdown/DropDownOptionsProviderBase.cs +++ b/src/modules/Elsa.Workflows.Core/UIHints/Dropdown/DropDownOptionsProviderBase.cs @@ -4,7 +4,8 @@ using Elsa.Extensions; namespace Elsa.Workflows.UIHints.Dropdown; /// -/// +/// A base class for providing options to populate a dropdown UI component. This class is intended to be inherited to implement +/// custom dropdown data logic by overriding the `GetItemsAsync` method. /// public abstract class DropDownOptionsProviderBase : IPropertyUIHandler { diff --git a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioList.cs b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioList.cs index 0dd5e25ec..ace833be9 100644 --- a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioList.cs +++ b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioList.cs @@ -1,7 +1,17 @@ namespace Elsa.Workflows.UIHints.RadioList; +/// +/// Provides properties for the UI hint. +/// public class RadioList { + /// + /// The radio list. + /// public IEnumerable Items { get; set; } + + /// + /// The name of the provider that will provide the select list. + /// public bool IsFlagsEnum { get; set; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListOptionsProviderBase.cs b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListOptionsProviderBase.cs index aa09266cb..70bae9a45 100644 --- a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListOptionsProviderBase.cs +++ b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListOptionsProviderBase.cs @@ -34,7 +34,7 @@ public abstract class RadioListOptionsProviderBase : PropertyUIHandlerBase } /// - /// Implement this to provide items to the dropdown list. + /// Implement this to provide items to the radio list. /// protected abstract ValueTask> GetItemsAsync(PropertyInfo propertyInfo, object? context, CancellationToken cancellationToken); diff --git a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListProps.cs b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListProps.cs index 2465cdda5..bdb15084a 100644 --- a/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListProps.cs +++ b/src/modules/Elsa.Workflows.Core/UIHints/RadioList/RadioListProps.cs @@ -1,5 +1,8 @@ namespace Elsa.Workflows.UIHints.RadioList; +/// +/// Provides properties for the UI hint. +/// public class RadioListProps { /// From 2dbd050e6472e156ff9175b52ab2239552559fad Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 6 Jun 2025 00:44:10 +0100 Subject: [PATCH 10/10] Revert ChecklistItem back from Record to Class and remove left over RadioListActivity. Update Nuget packages. --- src/apps/Elsa.Server.Web/RadioListActivity.cs | 42 ------------------- .../Shared/UIHints/CheckList/CheckListItem.cs | 9 +++- 2 files changed, 8 insertions(+), 43 deletions(-) delete mode 100644 src/apps/Elsa.Server.Web/RadioListActivity.cs diff --git a/src/apps/Elsa.Server.Web/RadioListActivity.cs b/src/apps/Elsa.Server.Web/RadioListActivity.cs deleted file mode 100644 index 2e7a38208..000000000 --- a/src/apps/Elsa.Server.Web/RadioListActivity.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System.Runtime.CompilerServices; -using Elsa.Workflows; -using Elsa.Workflows.Attributes; -using Elsa.Workflows.UIHints; -using Elsa.Workflows.Models; - -// ReSharper disable once CheckNamespace -namespace Elsa.Server.Web; - -/// -/// Executes C# code. -/// -[Activity("Elsa", "TESTS", "Tests Radio List Functionality", DisplayName = "TEST")] -public class TestRadioList : CodeActivity -{ - /// - public TestRadioList([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line) - { - } - - /// - public TestRadioList(string script, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(source, line) - { - } - - /// - /// The script to run. - /// - [Input( - Description = "Choose to download one file or entire folder", - DefaultValue = "File", - Options = new[] { "File", "Folder" }, - UIHint = InputUIHints.RadioList - )] - public Input SelectedRadioOption { get; set; } = default!; - - /// - protected override async ValueTask ExecuteAsync(ActivityExecutionContext context) - { - - } -} \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListItem.cs b/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListItem.cs index 62d5e6bf5..abeeee9a2 100644 --- a/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListItem.cs +++ b/src/clients/Elsa.Api.Client/Shared/UIHints/CheckList/CheckListItem.cs @@ -3,4 +3,11 @@ namespace Elsa.Api.Client.Shared.UIHints.CheckList; /// /// Represents an item in a . /// -public record CheckListItem(string Text, string Value, bool IsChecked); \ No newline at end of file +public class CheckListItem +{ +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public string Text { get; set; } + public string Value { get; set; } +#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public bool IsChecked { get; set; } +} \ No newline at end of file