From e8421bd148834b26223a6770771006b7980ca2ed Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Mon, 11 Sep 2023 21:36:07 +0200 Subject: [PATCH] Fix SetOutput when used in composite activity --- .../Elsa.Workflows.Core/Activities/Composite.cs | 2 -- .../Models/OutputDescriptor.cs | 5 ++++- .../Services/ActivityDescriber.cs | 16 ++++++++-------- .../Activities/SetOutput/SetOutput.cs | 17 ++++++++++++++--- .../WorkflowDefinitionActivityProvider.cs | 10 +++++----- 5 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/modules/Elsa.Workflows.Core/Activities/Composite.cs b/src/modules/Elsa.Workflows.Core/Activities/Composite.cs index 6a045573c..e31330e3b 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/Composite.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/Composite.cs @@ -82,7 +82,6 @@ public abstract class Composite : Activity, IVariableContainer /// Override this method to execute custom logic when the composite activity completes. /// /// The context of the composite activity. - /// The context of the child activity that completed. protected virtual ValueTask OnCompletedAsync(ActivityCompletedContext context) { OnCompleted(context); @@ -93,7 +92,6 @@ public abstract class Composite : Activity, IVariableContainer /// Override this method to execute custom logic when the composite activity completes. /// /// The context of the composite activity. - /// The context of the child activity that completed. protected virtual void OnCompleted(ActivityCompletedContext context) { } diff --git a/src/modules/Elsa.Workflows.Core/Models/OutputDescriptor.cs b/src/modules/Elsa.Workflows.Core/Models/OutputDescriptor.cs index 617deeff4..6021a0d16 100644 --- a/src/modules/Elsa.Workflows.Core/Models/OutputDescriptor.cs +++ b/src/modules/Elsa.Workflows.Core/Models/OutputDescriptor.cs @@ -20,13 +20,16 @@ public class OutputDescriptor : PropertyDescriptor string displayName, Type type, Func valueGetter, - string? description = default, + Action valueSetter, + + string? description = default, bool? isBrowsable = default) { Name = name; DisplayName = displayName; Type = type; ValueGetter = valueGetter; + ValueSetter = valueSetter; Description = description; IsBrowsable = isBrowsable; } diff --git a/src/modules/Elsa.Workflows.Core/Services/ActivityDescriber.cs b/src/modules/Elsa.Workflows.Core/Services/ActivityDescriber.cs index 91117cc4f..ecb1d1c43 100644 --- a/src/modules/Elsa.Workflows.Core/Services/ActivityDescriber.cs +++ b/src/modules/Elsa.Workflows.Core/Services/ActivityDescriber.cs @@ -65,7 +65,7 @@ public class ActivityDescriber : IActivityDescriber Name = x, DisplayName = x }).ToDictionary(x => x.Name) ?? new Dictionary(); - + var allPorts = embeddedPorts.Concat(flowPorts.Values); var inputProperties = GetInputProperties(activityType).ToList(); var outputProperties = GetOutputProperties(activityType).ToList(); @@ -96,16 +96,16 @@ public class ActivityDescriber : IActivityDescriber return activity; } }; - + return descriptor; } /// - public IEnumerable GetInputProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType) => + public IEnumerable GetInputProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType) => activityType.GetProperties().Where(x => typeof(Input).IsAssignableFrom(x.PropertyType) || x.GetCustomAttribute() != null).DistinctBy(x => x.Name); /// - public IEnumerable GetOutputProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType) => + public IEnumerable GetOutputProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType) => activityType.GetProperties().Where(x => typeof(Output).IsAssignableFrom(x.PropertyType)).DistinctBy(x => x.Name).ToList(); /// @@ -122,6 +122,7 @@ public class ActivityDescriber : IActivityDescriber outputAttribute?.DisplayName ?? propertyInfo.Name.Humanize(LetterCasing.Title), wrappedPropertyType, propertyInfo.GetValue, + propertyInfo.SetValue, descriptionAttribute?.Description ?? outputAttribute?.Description, outputAttribute?.IsBrowsable ?? true )); @@ -140,7 +141,7 @@ public class ActivityDescriber : IActivityDescriber wrappedPropertyType = wrappedPropertyType.GetTypeOfNullable(); var inputOptions = await _optionsResolver.GetOptionsAsync(propertyInfo, cancellationToken); - + return new InputDescriptor ( inputAttribute?.Name ?? propertyInfo.Name, @@ -156,21 +157,20 @@ public class ActivityDescriber : IActivityDescriber inputAttribute?.Order ?? 0, _defaultValueResolver.GetDefaultValue(propertyInfo), inputAttribute?.DefaultSyntax, - //inputAttribute?.SupportedSyntaxes, TODO: Come up with a different way to specify support languages for activity inputs. By default, maybe all props should support all registered scripting languages? inputAttribute?.IsReadOnly ?? false, inputAttribute?.IsBrowsable ?? true ); } /// - public async Task> DescribeInputPropertiesAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]Type activityType, CancellationToken cancellationToken = default) + public async Task> DescribeInputPropertiesAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType, CancellationToken cancellationToken = default) { var properties = GetInputProperties(activityType); return await DescribeInputPropertiesAsync(properties, cancellationToken); } /// - public async Task> DescribeOutputPropertiesAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]Type activityType, CancellationToken cancellationToken = default) => + public async Task> DescribeOutputPropertiesAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType, CancellationToken cancellationToken = default) => await DescribeOutputPropertiesAsync(GetOutputProperties(activityType), cancellationToken); private async Task> DescribeInputPropertiesAsync(IEnumerable properties, CancellationToken cancellationToken = default) diff --git a/src/modules/Elsa.Workflows.Management/Activities/SetOutput/SetOutput.cs b/src/modules/Elsa.Workflows.Management/Activities/SetOutput/SetOutput.cs index 1b611b9c7..3a4afa5ed 100644 --- a/src/modules/Elsa.Workflows.Management/Activities/SetOutput/SetOutput.cs +++ b/src/modules/Elsa.Workflows.Management/Activities/SetOutput/SetOutput.cs @@ -1,6 +1,7 @@ using System.Runtime.CompilerServices; using Elsa.Extensions; using Elsa.Workflows.Core; +using Elsa.Workflows.Core.Activities; using Elsa.Workflows.Core.Attributes; using Elsa.Workflows.Core.Models; using JetBrains.Annotations; @@ -18,7 +19,7 @@ public class SetOutput : CodeActivity public SetOutput([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) { } - + /// /// The name of the output to assign. /// @@ -68,9 +69,19 @@ public class SetOutput : CodeActivity { // If this activity executes in a composite activity, we need to update the composite activity's output variable as well. var variable = context.ExpressionExecutionContext.GetVariable(outputName); - - if(variable != null) + + if (variable != null) context.Set(variable, outputValue); + + // Also set the output on the composite activity's output property. + var compositeActivityContext = ancestorContext.ParentActivityExecutionContext; + var compositeActivity = compositeActivityContext.Activity; + var compositeActivityDescriptor = compositeActivityContext.ActivityDescriptor; + var compositeOutputDescriptor = compositeActivityDescriptor.Outputs.FirstOrDefault(x => x.Name == outputName); + var compositeOutput = (Output?)compositeOutputDescriptor?.ValueGetter(compositeActivity); + + if(compositeOutput != null) + ancestorContext.ParentActivityExecutionContext.Set(compositeOutput, outputValue); } } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Activities/WorkflowDefinitionActivity/WorkflowDefinitionActivityProvider.cs b/src/modules/Elsa.Workflows.Management/Activities/WorkflowDefinitionActivity/WorkflowDefinitionActivityProvider.cs index d64b733b5..73f7bfc95 100644 --- a/src/modules/Elsa.Workflows.Management/Activities/WorkflowDefinitionActivity/WorkflowDefinitionActivityProvider.cs +++ b/src/modules/Elsa.Workflows.Management/Activities/WorkflowDefinitionActivity/WorkflowDefinitionActivityProvider.cs @@ -32,10 +32,10 @@ public class WorkflowDefinitionActivityProvider : IActivityProvider { var filter = new WorkflowDefinitionFilter { - UsableAsActivity = true, + UsableAsActivity = true, VersionOptions = VersionOptions.All }; - + var definitions = (await _store.FindManyAsync(filter, cancellationToken)).ToList(); var descriptors = CreateDescriptors(definitions); return descriptors; @@ -47,7 +47,7 @@ public class WorkflowDefinitionActivityProvider : IActivityProvider private ActivityDescriptor CreateDescriptor(WorkflowDefinition definition, ICollection allDefinitions) { var typeName = definition.Name!.Pascalize(); - + var latestPublishedVersion = allDefinitions .Where(x => x.DefinitionId == definition.DefinitionId && x.IsPublished) .MaxBy(x => x.Version); @@ -59,7 +59,7 @@ public class WorkflowDefinitionActivityProvider : IActivityProvider IsBrowsable = true, Type = PortType.Flow }).ToList(); - + var rootPort = new Port { Name = nameof(WorkflowDefinitionActivity.Root), @@ -67,7 +67,7 @@ public class WorkflowDefinitionActivityProvider : IActivityProvider IsBrowsable = true, Type = PortType.Embedded }; - + ports.Insert(0, rootPort); return new ActivityDescriptor