Fix SetOutput when used in composite activity

This commit is contained in:
Sipke Schoorstra 2023-09-11 21:36:07 +02:00
parent 9bb2c44d32
commit e8421bd148
5 changed files with 31 additions and 19 deletions

View file

@ -82,7 +82,6 @@ public abstract class Composite : Activity, IVariableContainer
/// Override this method to execute custom logic when the composite activity completes.
/// </summary>
/// <param name="context">The context of the composite activity.</param>
/// <param name="childContext">The context of the child activity that completed.</param>
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.
/// </summary>
/// <param name="context">The context of the composite activity.</param>
/// <param name="childContext">The context of the child activity that completed.</param>
protected virtual void OnCompleted(ActivityCompletedContext context)
{
}

View file

@ -20,13 +20,16 @@ public class OutputDescriptor : PropertyDescriptor
string displayName,
Type type,
Func<IActivity, object?> valueGetter,
string? description = default,
Action<IActivity, object?> valueSetter,
string? description = default,
bool? isBrowsable = default)
{
Name = name;
DisplayName = displayName;
Type = type;
ValueGetter = valueGetter;
ValueSetter = valueSetter;
Description = description;
IsBrowsable = isBrowsable;
}

View file

@ -65,7 +65,7 @@ public class ActivityDescriber : IActivityDescriber
Name = x,
DisplayName = x
}).ToDictionary(x => x.Name) ?? new Dictionary<string, Port>();
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;
}
/// <inheritdoc />
public IEnumerable<PropertyInfo> GetInputProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType) =>
public IEnumerable<PropertyInfo> GetInputProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType) =>
activityType.GetProperties().Where(x => typeof(Input).IsAssignableFrom(x.PropertyType) || x.GetCustomAttribute<InputAttribute>() != null).DistinctBy(x => x.Name);
/// <inheritdoc />
public IEnumerable<PropertyInfo> GetOutputProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType) =>
public IEnumerable<PropertyInfo> GetOutputProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType) =>
activityType.GetProperties().Where(x => typeof(Output).IsAssignableFrom(x.PropertyType)).DistinctBy(x => x.Name).ToList();
/// <inheritdoc />
@ -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
);
}
/// <inheritdoc />
public async Task<IEnumerable<InputDescriptor>> DescribeInputPropertiesAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]Type activityType, CancellationToken cancellationToken = default)
public async Task<IEnumerable<InputDescriptor>> DescribeInputPropertiesAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType, CancellationToken cancellationToken = default)
{
var properties = GetInputProperties(activityType);
return await DescribeInputPropertiesAsync(properties, cancellationToken);
}
/// <inheritdoc />
public async Task<IEnumerable<OutputDescriptor>> DescribeOutputPropertiesAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]Type activityType, CancellationToken cancellationToken = default) =>
public async Task<IEnumerable<OutputDescriptor>> DescribeOutputPropertiesAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type activityType, CancellationToken cancellationToken = default) =>
await DescribeOutputPropertiesAsync(GetOutputProperties(activityType), cancellationToken);
private async Task<IEnumerable<InputDescriptor>> DescribeInputPropertiesAsync(IEnumerable<PropertyInfo> properties, CancellationToken cancellationToken = default)

View file

@ -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)
{
}
/// <summary>
/// The name of the output to assign.
/// </summary>
@ -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);
}
}
}

View file

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