Merge remote-tracking branch 'origin/blueberry'

This commit is contained in:
Sipke Schoorstra 2024-10-23 16:07:32 +02:00
commit 3208be7b89
8 changed files with 149 additions and 10 deletions

View file

@ -8,7 +8,12 @@ public class SignalManager
public async Task<T> WaitAsync<T>(object signal, int millisecondsTimeout = 8000)
{
return await WaitAsync(signal, millisecondsTimeout) is T result ? result : throw new InvalidCastException($"Signal '{signal}' was not of type '{typeof(T).Name}'.");
var result = await WaitAsync(signal, millisecondsTimeout);
if(result is not T typedResult)
throw new InvalidCastException($"Signal '{signal}' was not of type '{typeof(T).Name}'.");
return typedResult;
}
public async Task<object?> WaitAsync(object signal, int millisecondsTimeout = 8000)

View file

@ -40,7 +40,7 @@ public class ClrWorkflowMaterializer : IWorkflowMaterializer
public async ValueTask<Workflow> MaterializeAsync(WorkflowDefinition definition, CancellationToken cancellationToken = default)
{
var providerContext = _payloadSerializer.Deserialize<ClrWorkflowMaterializerContext>(definition.MaterializerContext!);
var workflowBuilderType = providerContext.WorkflowBuilderType;
var workflowBuilderType = providerContext.WorkflowBuilderType == null! ? typeof(NotFoundWorkflowbuilder) : providerContext.WorkflowBuilderType;
var workflowBuilder = (IWorkflow)ActivatorUtilities.GetServiceOrCreateInstance(_serviceProvider, workflowBuilderType);
var workflowDefinitionBuilder = _workflowBuilderFactory.CreateBuilder();
var workflow = await workflowDefinitionBuilder.BuildWorkflowAsync(workflowBuilder, cancellationToken);
@ -56,4 +56,11 @@ public class ClrWorkflowMaterializer : IWorkflowMaterializer
/// Provides context for the CLR workflow materializer.
/// </summary>
/// <param name="WorkflowBuilderType">The type of the workflow builder.</param>
public record ClrWorkflowMaterializerContext(Type WorkflowBuilderType);
public record ClrWorkflowMaterializerContext(Type WorkflowBuilderType);
/// <summary>
/// A workflow builder that is used when the workflow builder type is not found.
/// </summary>
public class NotFoundWorkflowbuilder : WorkflowBase
{
}

View file

@ -162,7 +162,7 @@ public class BulkDispatchWorkflows : Activity
Arguments = itemDictionary
};
var inputDictionary = item as IDictionary<string, object> ?? new Dictionary<string, object>();
var inputDictionary = item as IDictionary<string, object> ?? itemDictionary;
input["ParentInstanceId"] = parentInstanceId;
input.Merge(inputDictionary);

View file

@ -1,3 +1,5 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helpers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helpers_005Ccontracts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=helpers_005Ceventargs/@EntryIndexedValue">True</s:Boolean>
</wpf:ResourceDictionary>

View file

@ -0,0 +1,55 @@
using System.Runtime.CompilerServices;
using System.Text.Json.Serialization;
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
namespace Elsa.Workflows.ComponentTests.Activities;
public class TriggerSignal : CodeActivity
{
/// <inheritdoc />
[JsonConstructor]
private TriggerSignal(string? source = default, int? line = default) : base(source, line)
{
}
/// <inheritdoc />
public TriggerSignal(string eventName, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(new Literal<string>(eventName), source, line)
{
}
/// <inheritdoc />
public TriggerSignal(Func<string> eventName, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default)
: this(Expression.DelegateExpression(eventName), source, line)
{
}
/// <inheritdoc />
public TriggerSignal(Func<ExpressionExecutionContext, string?> eventName, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default)
: this(Expression.DelegateExpression(eventName), source, line)
{
}
/// <inheritdoc />
public TriggerSignal(Variable<string> variable, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(source, line) => EventName = new Input<string>(variable);
/// <inheritdoc />
public TriggerSignal(Literal<string> literal, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(source, line) => EventName = new Input<string>(literal);
/// <inheritdoc />
public TriggerSignal(Expression expression, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(source, line) => EventName = new Input<string>(expression, new MemoryBlockReference());
/// <inheritdoc />
public TriggerSignal(Input<string> eventName, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(source, line) => EventName = eventName;
public Input<string> EventName { get; set; }
protected override void Execute(ActivityExecutionContext context)
{
var testEventManager = context.GetRequiredService<ISignalManager>();
var eventName = EventName.Get(context);
testEventManager.Trigger(eventName);
}
}

View file

@ -1,4 +1,4 @@
using Elsa.Common.Models;
using Elsa.Common.Models;
using Elsa.Testing.Shared;
using Elsa.Testing.Shared.Services;
using Elsa.Workflows.ComponentTests.Helpers;
@ -15,7 +15,7 @@ public class BulkDispatchWorkflowsTests : AppComponentTest
private readonly WorkflowEvents _workflowEvents;
private readonly SignalManager _signalManager;
private readonly IWorkflowRuntime _workflowRuntime;
private static readonly object ParentWorkflowCompletedSignal = new();
private static readonly object GreetEmployeesWorkflowCompletedSignal = new();
public BulkDispatchWorkflowsTests(App app) : base(app)
{
@ -35,10 +35,22 @@ public class BulkDispatchWorkflowsTests : AppComponentTest
WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(GreetEmployeesWorkflow.DefinitionId, VersionOptions.Published)
});
await workflowClient.RunInstanceAsync(RunWorkflowInstanceRequest.Empty);
var parentWorkflowInstanceArgs = await _signalManager.WaitAsync<WorkflowInstanceSavedEventArgs>(ParentWorkflowCompletedSignal);
var parentWorkflowInstanceArgs = await _signalManager.WaitAsync<WorkflowInstanceSavedEventArgs>(GreetEmployeesWorkflowCompletedSignal);
Assert.Equal(WorkflowStatus.Finished, parentWorkflowInstanceArgs.WorkflowInstance.Status);
}
/// <summary>
/// Individual items are sent as input to child workflows.
/// </summary>
[Fact]
public async Task DispatchWorkflows_ChildWorkflowsShouldReceiveCurrentItem()
{
await _workflowRuntime.StartWorkflowAsync(MixFruitsWorkflow.DefinitionId);
await _signalManager.WaitAsync("Apple");
await _signalManager.WaitAsync("Banana");
await _signalManager.WaitAsync("Cherry");
}
private void OnWorkflowInstanceSaved(object? sender, WorkflowInstanceSavedEventArgs e)
{
@ -46,7 +58,7 @@ public class BulkDispatchWorkflowsTests : AppComponentTest
return;
if(e.WorkflowInstance.DefinitionId == GreetEmployeesWorkflow.DefinitionId)
_signalManager.Trigger(ParentWorkflowCompletedSignal, e);
_signalManager.Trigger(GreetEmployeesWorkflowCompletedSignal, e);
}
protected override void OnDispose()

View file

@ -0,0 +1,27 @@
using Elsa.Extensions;
using Elsa.Workflows.Activities;
using Elsa.Workflows.ComponentTests.Activities;
using Elsa.Workflows.Contracts;
using Hangfire.Annotations;
namespace Elsa.Workflows.ComponentTests.Scenarios.BulkDispatchWorkflows.Workflows;
[UsedImplicitly]
public class FruitWorkflow : WorkflowBase
{
public static readonly string DefinitionId = Guid.NewGuid().ToString();
protected override void Build(IWorkflowBuilder builder)
{
builder.WithDefinitionId(DefinitionId);
var item = builder.WithInput<string>("Item");
builder.Root = new Sequence
{
Activities =
{
new WriteLine(x => $"Mixing {x.GetInput<string>(item)}"),
new TriggerSignal(x => x.GetInput<string>(item))
}
};
}
}

View file

@ -0,0 +1,31 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.Contracts;
namespace Elsa.Workflows.ComponentTests.Scenarios.BulkDispatchWorkflows.Workflows;
public class MixFruitsWorkflow : WorkflowBase
{
public static readonly string DefinitionId = Guid.NewGuid().ToString();
protected override void Build(IWorkflowBuilder builder)
{
var fruits = new[]
{
"Apple", "Banana", "Cherry"
};
builder.WithDefinitionId(DefinitionId);
builder.Root = new Sequence
{
Activities =
{
new Runtime.Activities.BulkDispatchWorkflows
{
WorkflowDefinitionId = new(FruitWorkflow.DefinitionId),
Items = new(fruits),
WaitForCompletion = new(true)
}
}
};
}
}