Enhance BulkDispatchWorkflows Activity with Support for Multiple Input Keys and Customizable Input Key (#5182)

* Add PublicAPI attribute to ActivityExecutionContext

The 'PublicAPI' attribute has been added to the ActivityExecutionContext class. This signals that this class is part of our public API and should not be modified in a way that violates backward compatibility. Furthermore, the 'RequiresUnreferencedCode' attributes were removed from the 'CompleteActivityAsync' and 'CompleteActivityWithOutcomesAsync' methods, as these methods no longer need them.

* Add merge function to DictionaryExtensions

A new merge function has been added to the DictionaryExtensions. This function combines the contents of two dictionaries, prioritizing the keys and values from the second dictionary whenever there is a collision.

* Add default item input key in BulkDispatchWorkflows

The code has been adjusted to include a default input item key in the BulkDispatchWorkflows module. This key will be used for the item input when dispatching workflows, unless the Items field contains a list of dictionaries. Additionally, modifications have been made to convert the item into an input dictionary if it isn't already one and merge this with the input dictionary.

* Update Merge method documentation in DictionaryExtensions

The commit adds a critical detail about dictionary operation in the method summary. It clearly states that during the merge operation, the value from the 'other' dictionary overwrites the value in the target dictionary when the same key exists in both.
This commit is contained in:
Sipke Schoorstra 2024-04-04 16:07:32 +02:00 committed by GitHub
parent c39f2c13fd
commit 31092f8d81
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 30 additions and 12 deletions

View file

@ -1,4 +1,3 @@
using System.Diagnostics.CodeAnalysis;
using Elsa.Extensions;
using Elsa.Workflows.Activities.Flowchart.Models;
using Elsa.Workflows.Contracts;
@ -11,7 +10,6 @@ public partial class ActivityExecutionContext
/// <summary>
/// Complete the current activity. This should only be called by activities that explicitly suppress automatic-completion.
/// </summary>
[RequiresUnreferencedCode("The activity may be serialized and executed in a different context.")]
public async ValueTask CompleteActivityAsync(object? result = default)
{
var outcomes = result as Outcomes;
@ -93,7 +91,6 @@ public partial class ActivityExecutionContext
/// <summary>
/// Complete the current activity with the specified outcomes.
/// </summary>
[RequiresUnreferencedCode("The activity may be serialized and executed in a different context.")]
public ValueTask CompleteActivityWithOutcomesAsync(params string[] outcomes)
{
return CompleteActivityAsync(new Outcomes(outcomes));

View file

@ -10,12 +10,14 @@ using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
using Elsa.Workflows.Options;
using Elsa.Workflows.Services;
using JetBrains.Annotations;
namespace Elsa.Workflows;
/// <summary>
/// Represents the context of an activity execution.
/// </summary>
[PublicAPI]
public partial class ActivityExecutionContext : IExecutionContext
{
private readonly ISystemClock _systemClock;

View file

@ -65,6 +65,16 @@ public static class DictionaryExtensions
dictionary.Add(key, value);
return dictionary;
}
/// <summary>
/// Merges the specified dictionary with the other dictionary.
/// When a key exists in both dictionaries, the value in the other dictionary will overwrite the value in the specified dictionary.
/// </summary>
public static void Merge(this IDictionary<string, object> dictionary, IDictionary<string, object> other)
{
foreach (var (key, value) in other)
dictionary[key] = value;
}
private static T? ConvertValue<T>(object? value) => value.ConvertTo<T>();
}

View file

@ -55,19 +55,25 @@ public class BulkDispatchWorkflows : Activity
[Input(Description = "The data source to use for dispatching the workflows.")]
public Input<object> Items { get; set; } = default!;
/// <summary>
/// The default key to use for the item input. Will not be used if the Items contain a list of dictionaries.
/// </summary>
[Input(Description = "The default key to use for the input name when sending the current item to the dispatched workflow. Will not be used if the Items field contain a list of dictionaries", DefaultValue = "Item")]
public Input<string> DefaultItemInputKey { get; set; } = new("Item");
/// <summary>
/// The correlation ID to associate the workflow with.
/// </summary>
[Input(
DisplayName = "Correlation ID Function",
Description = "A function to compute the correlation ID to associate a dispatched workflow with. Receives the current item as an argument called Item.",
Description = "A function to compute the correlation ID to associate a dispatched workflow with.",
AutoEvaluate = false)]
public Input<string?>? CorrelationIdFunction { get; set; }
/// <summary>
/// The input to send to the workflows.
/// </summary>
[Input(Description = """Additional input to send to the workflows being dispatched. The "Item" key is reserved and should not be used.""")]
[Input(Description = "Additional input to send to the workflows being dispatched.")]
public Input<IDictionary<string, object>?> Input { get; set; } = default!;
/// <summary>
@ -102,7 +108,6 @@ public class BulkDispatchWorkflows : Activity
public IActivity? ChildFaulted { get; set; }
/// <inheritdoc />
[RequiresUnreferencedCode("Calls Elsa.Expressions.Helpers.ObjectConverter.ConvertTo<T>(ObjectConverterOptions)")]
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var waitForCompletion = WaitForCompletion.GetOrDefault(context);
@ -162,20 +167,24 @@ public class BulkDispatchWorkflows : Activity
var parentInstanceId = context.WorkflowExecutionContext.Id;
var input = Input.GetOrDefault(context) ?? new Dictionary<string, object>();
var channelName = ChannelName.GetOrDefault(context);
var defaultInputItemKey = DefaultItemInputKey.GetOrDefault(context, () => "Item")!;
var properties = new Dictionary<string, object>
{
["ParentInstanceId"] = parentInstanceId
};
var itemAsInputDictionary = item as IDictionary<string, object> ?? new Dictionary<string, object>
{
[defaultInputItemKey] = item
};
var evaluatorOptions = new ExpressionEvaluatorOptions
{
Arguments = new Dictionary<string, object>
{
["Item"] = item
}
Arguments = itemAsInputDictionary
};
input["ParentInstanceId"] = parentInstanceId;
input["Item"] = item;
input.Merge(itemAsInputDictionary);
var workflowDispatcher = context.GetRequiredService<IWorkflowDispatcher>();
var identityGenerator = context.GetRequiredService<IIdentityGenerator>();
@ -198,7 +207,7 @@ public class BulkDispatchWorkflows : Activity
};
var dispatchResponse = await workflowDispatcher.DispatchAsync(request, options, context.CancellationToken);
if (!dispatchResponse.Succeeded)
throw new FaultException(dispatchResponse.ErrorMessage);