Add ExecuteWorkflow activity (#6134)

* Add ExecuteWorkflow activity and enable multitenancy

Introduced the ExecuteWorkflow activity to create and execute workflow instances, and updated runtime settings to use ProtoActor for distributed caching transport. Enabled multitenancy for the Elsa Server Web application.

* Namespace renames (#6135)

* Create ExecuteWorkflows tests and workflows

Add unit tests for executing workflows, including MainWorkflow and SubroutineWorkflow. Also, rename ExecutedWorkflowResult to ExecuteWorkflowResult in the runtime module for consistency.

* Add output definitions to WorkflowBuilder

Refactored the `WithInput` method for clarity and added multiple `WithOutput` methods to support different ways of defining workflow outputs. These changes enhance the flexibility and readability of the workflow configuration process by providing a consistent API for input and output definitions.

* Refactor namespaces in component tests

Updated namespaces from Helpers to specific contexts like Fixtures, Abstractions, Consumers, Decorators, etc., to improve code organization and readability. This change affects multiple files across different test scenarios and modules.

* Disable RabbitMQ and multitenancy, rename test class

Commented out RabbitMQ usage in WorkflowServer.cs to focus on other transports. Changed multitenancy flag to false in Program.cs. Renamed DispatchWorkflowsTests to ExecuteWorkflowsTests for clarity.
This commit is contained in:
Sipke Schoorstra 2024-11-21 16:44:25 +01:00 committed by GitHub
parent 49a7f22543
commit 1ed6695872
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 324 additions and 42 deletions

View file

@ -69,7 +69,7 @@ const bool useKafka = false;
const bool useReadOnlyMode = false;
const bool useSignalR = false; // Disabled until Elsa Studio sends authenticated requests.
const WorkflowRuntime workflowRuntime = WorkflowRuntime.ProtoActor;
const DistributedCachingTransport distributedCachingTransport = DistributedCachingTransport.MassTransit;
const DistributedCachingTransport distributedCachingTransport = DistributedCachingTransport.ProtoActor;
const MassTransitBroker massTransitBroker = MassTransitBroker.Memory;
const bool useMultitenancy = false;
const bool useAgents = false;

View file

@ -123,14 +123,7 @@ public class WorkflowBuilder(IActivityVisitor activityVisitor, IIdentityGraphSer
/// <inheritdoc />
public InputDefinition WithInput<T>(string name, string? description = default)
{
return WithInput(inputDefinition =>
{
inputDefinition.Name = name;
inputDefinition.Type = typeof(T);
if (description != null)
inputDefinition.Description = description;
});
return WithInput(name, typeof(T), description);
}
/// <inheritdoc />
@ -173,6 +166,46 @@ public class WorkflowBuilder(IActivityVisitor activityVisitor, IIdentityGraphSer
return this;
}
public OutputDefinition WithOutput<T>(string name, string? description = default)
{
return WithOutput(name, typeof(T), description);
}
public OutputDefinition WithOutput(string name, Type type, string? description = default)
{
return WithOutput(outputDefinition =>
{
outputDefinition.Name = name;
outputDefinition.Type = type;
if (description != null)
outputDefinition.Description = description;
});
}
public OutputDefinition WithOutput(string name, Type type, Action<OutputDefinition>? setup = default)
{
return WithOutput(outputDefinition =>
{
outputDefinition.Name = name;
outputDefinition.Type = type;
setup?.Invoke(outputDefinition);
});
}
public OutputDefinition WithOutput(Action<OutputDefinition> setup)
{
var outputDefinition = new OutputDefinition();
setup(outputDefinition);
return WithOutput(outputDefinition);
}
public OutputDefinition WithOutput(OutputDefinition outputDefinition)
{
Outputs.Add(outputDefinition);
return outputDefinition;
}
/// <inheritdoc />
public IWorkflowBuilder WithCustomProperty(string name, object value)
{

View file

@ -156,6 +156,31 @@ public interface IWorkflowBuilder
/// A fluent method for adding an input to <see cref="Inputs"/>.
/// </summary>
IWorkflowBuilder WithInput(InputDefinition inputDefinition);
/// <summary>
/// A fluent method for adding an output to <see cref="Outputs"/>.
/// </summary>
OutputDefinition WithOutput<T>(string name, string? description = default);
/// <summary>
/// A fluent method for adding an output to <see cref="Outputs"/>.
/// </summary>
OutputDefinition WithOutput(string name, Type type, string? description = default);
/// <summary>
/// A fluent method for adding an output to <see cref="Outputs"/>.
/// </summary>
OutputDefinition WithOutput(string name, Type type, Action<OutputDefinition>? setup = default);
/// <summary>
/// A fluent method for adding an output to <see cref="Outputs"/>.
/// </summary>
OutputDefinition WithOutput(Action<OutputDefinition> setup);
/// <summary>
/// A fluent method for adding an output to <see cref="Outputs"/>.
/// </summary>
OutputDefinition WithOutput(OutputDefinition outputDefinition);
/// <summary>
/// A fluent method for adding a property to <see cref="CustomProperties"/>.

View file

@ -18,15 +18,15 @@ public abstract class ArgumentDefinition
/// <summary>
/// A user friendly name of the input.
/// </summary>
public string DisplayName { get; set; } = default!;
public string DisplayName { get; set; } = string.Empty;
/// <summary>
/// A description of the input.
/// </summary>
public string Description { get; set; } = default!;
public string Description { get; set; } = string.Empty;
/// <summary>
/// The category to which this input belongs.
/// </summary>
public string Category { get; set; } = default!;
public string Category { get; set; } = string.Empty;
}

View file

@ -0,0 +1,90 @@
using System.Runtime.CompilerServices;
using Elsa.Common.Models;
using Elsa.Extensions;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Management;
using Elsa.Workflows.Models;
using Elsa.Workflows.Options;
using Elsa.Workflows.UIHints;
using JetBrains.Annotations;
namespace Elsa.Workflows.Runtime.Activities;
/// <summary>
/// Creates a new workflow instance of the specified workflow and dispatches it for execution.
/// </summary>
[Activity("Elsa", "Composition", "Create a new workflow instance of the specified workflow and execute it.", Kind = ActivityKind.Task)]
[UsedImplicitly]
public class ExecuteWorkflow : Activity<ExecuteWorkflowResult>
{
/// <inheritdoc />
public ExecuteWorkflow([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line)
{
}
/// <summary>
/// The definition ID of the workflow to execute.
/// </summary>
[Input(
DisplayName = "Workflow Definition",
Description = "The definition ID of the workflow to execute.",
UIHint = InputUIHints.WorkflowDefinitionPicker
)]
public Input<string> WorkflowDefinitionId { get; set; } = default!;
/// <summary>
/// The correlation ID to associate the workflow with.
/// </summary>
[Input(
DisplayName = "Correlation ID",
Description = "The correlation ID to associate the workflow with."
)]
public Input<string?> CorrelationId { get; set; } = default!;
/// <summary>
/// The input to send to the workflow.
/// </summary>
[Input(Description = "The input to send to the workflow.")]
public Input<IDictionary<string, object>?> Input { get; set; } = default!;
/// <inheritdoc />
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
{
var result = await ExecuteWorkflowAsync(context);
context.SetResult(result);
await context.CompleteActivityAsync();
}
private async ValueTask<ExecuteWorkflowResult> ExecuteWorkflowAsync(ActivityExecutionContext context)
{
var workflowDefinitionId = WorkflowDefinitionId.Get(context);
var input = Input.GetOrDefault(context) ?? new Dictionary<string, object>();
var correlationId = CorrelationId.GetOrDefault(context);
var workflowInvoker = context.GetRequiredService<IWorkflowInvoker>();
var identityGenerator = context.GetRequiredService<IIdentityGenerator>();
var workflowDefinitionService = context.GetRequiredService<IWorkflowDefinitionService>();
var workflowGraph = await workflowDefinitionService.FindWorkflowGraphAsync(workflowDefinitionId, VersionOptions.Published, context.CancellationToken);
if (workflowGraph == null)
throw new Exception($"No published version of workflow definition with ID {workflowDefinitionId} found.");
var options = new RunWorkflowOptions
{
ParentWorkflowInstanceId = context.WorkflowExecutionContext.Id,
Input = input,
CorrelationId = correlationId,
WorkflowInstanceId = identityGenerator.GenerateId()
};
var workflowResult = await workflowInvoker.InvokeAsync(workflowGraph, options, context.CancellationToken);
var info = new ExecuteWorkflowResult
{
WorkflowInstanceId = options.WorkflowInstanceId,
Status = workflowResult.WorkflowState.Status,
SubStatus = workflowResult.WorkflowState.SubStatus,
Output = workflowResult.WorkflowState.Output
};
return info;
}
}

View file

@ -0,0 +1,14 @@
namespace Elsa.Workflows.Runtime;
/// <summary>
/// Represents the result of executing a workflow.
/// </summary>
public class ExecuteWorkflowResult
{
public string WorkflowDefinitionVersionId { get; set; } = default!;
public string WorkflowInstanceId { get; set; } = default!;
public string? CorrelationId { get; set; }
public WorkflowStatus Status { get; set; }
public WorkflowSubStatus SubStatus { get; set; }
public IDictionary<string, object>? Output { get; set; }
}

View file

@ -1,6 +1,7 @@
using Elsa.Workflows.ComponentTests.Fixtures;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Workflows.ComponentTests.Helpers;
namespace Elsa.Workflows.ComponentTests.Abstractions;
[Collection(nameof(AppCollection))]
public abstract class AppComponentTest(App app) : IDisposable

View file

@ -4,7 +4,7 @@ using Elsa.Testing.Shared.Services;
using Hangfire.Annotations;
using MassTransit;
namespace Elsa.Workflows.ComponentTests.Helpers;
namespace Elsa.Workflows.ComponentTests.Consumers;
[UsedImplicitly]
public class WorkflowDefinitionEventConsumer(WorkflowDefinitionEvents workflowDefinitionEvents) : IConsumer<WorkflowDefinitionDeleted>

View file

@ -4,7 +4,7 @@ using Elsa.Testing.Shared.Services;
using Hangfire.Annotations;
using Microsoft.Extensions.Primitives;
namespace Elsa.Workflows.ComponentTests.Helpers;
namespace Elsa.Workflows.ComponentTests.Decorators;
/// <summary>
/// Provides a decorator for the `IChangeTokenSignaler` interface that triggers change token events.

View file

@ -1,6 +1,6 @@
using Hangfire.Annotations;
namespace Elsa.Workflows.ComponentTests.Helpers;
namespace Elsa.Workflows.ComponentTests.Fixtures;
[UsedImplicitly]
public class App : IAsyncLifetime

View file

@ -1,4 +1,4 @@
namespace Elsa.Workflows.ComponentTests.Helpers;
namespace Elsa.Workflows.ComponentTests.Fixtures;
[CollectionDefinition(nameof(AppCollection))]
public class AppCollection : ICollectionFixture<App>

View file

@ -1,6 +1,6 @@
using Hangfire.Annotations;
namespace Elsa.Workflows.ComponentTests.Helpers;
namespace Elsa.Workflows.ComponentTests.Fixtures;
[UsedImplicitly]
public class Cluster(Infrastructure infrastructure) : IAsyncDisposable

View file

@ -1,7 +1,7 @@
using Testcontainers.PostgreSql;
using Testcontainers.RabbitMq;
namespace Elsa.Workflows.ComponentTests.Helpers;
namespace Elsa.Workflows.ComponentTests.Fixtures;
public class Infrastructure : IAsyncLifetime
{

View file

@ -3,7 +3,6 @@ using System.Reflection;
using Elsa.Agents;
using Elsa.Alterations.Extensions;
using Elsa.Caching;
using Elsa.EntityFrameworkCore;
using Elsa.EntityFrameworkCore.Extensions;
using Elsa.EntityFrameworkCore.Modules.Alterations;
using Elsa.EntityFrameworkCore.Modules.Identity;
@ -14,6 +13,10 @@ using Elsa.Identity.Providers;
using Elsa.MassTransit.Extensions;
using Elsa.Testing.Shared.Handlers;
using Elsa.Testing.Shared.Services;
using Elsa.Workflows.ComponentTests.Consumers;
using Elsa.Workflows.ComponentTests.Decorators;
using Elsa.Workflows.ComponentTests.Materializers;
using Elsa.Workflows.ComponentTests.WorkflowProviders;
using Elsa.Workflows.Management;
using Elsa.Workflows.Runtime.Distributed.Extensions;
using FluentStorage;
@ -25,7 +28,7 @@ using Microsoft.Extensions.DependencyInjection;
using Refit;
using static Elsa.Api.Client.RefitSettingsHelper;
namespace Elsa.Workflows.ComponentTests.Helpers;
namespace Elsa.Workflows.ComponentTests.Fixtures;
[UsedImplicitly]
public class WorkflowServer(Infrastructure infrastructure, string url) : WebApplicationFactory<Program>
@ -73,7 +76,7 @@ public class WorkflowServer(Infrastructure infrastructure, string url) : WebAppl
});
elsa.UseMassTransit(massTransit =>
{
massTransit.UseRabbitMq(rabbitMqConnectionString);
//massTransit.UseRabbitMq(rabbitMqConnectionString);
massTransit.AddConsumer<WorkflowDefinitionEventConsumer>("elsa-test-workflow-definition-updates", true);
});
elsa.UseIdentity(identity => identity.UseEntityFrameworkCore(ef => ef.UsePostgreSql(dbConnectionString)));

View file

@ -1,9 +1,10 @@
using Elsa.Workflows.Activities;
using Elsa.Workflows.ComponentTests.WorkflowProviders;
using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Entities;
using Elsa.Workflows.Runtime;
namespace Elsa.Workflows.ComponentTests.Helpers;
namespace Elsa.Workflows.ComponentTests.Materializers;
/// <summary>
/// A workflow materializer that deserializes workflows created from <see cref="TestWorkflowProvider"/>.

View file

@ -1,6 +1,6 @@
using Elsa.Workflows.Runtime;
namespace Elsa.Workflows.ComponentTests.Helpers;
namespace Elsa.Workflows.ComponentTests.WorkflowProviders;
public class TestWorkflowProvider : IWorkflowsProvider
{

View file

@ -1,4 +1,5 @@
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Workflows.ComponentTests.Scenarios.Activities.FlowJoins;

View file

@ -2,7 +2,8 @@
using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
using Elsa.Testing.Shared.Extensions;
using Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Execute;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
namespace Elsa.Workflows.ComponentTests.Scenarios.BasicWorkflows;

View file

@ -1,7 +1,8 @@
using Elsa.Common.Models;
using Elsa.Testing.Shared;
using Elsa.Testing.Shared.Services;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.ComponentTests.Scenarios.BulkDispatchWorkflows.Workflows;
using Elsa.Workflows.Models;
using Elsa.Workflows.Runtime;

View file

@ -1,4 +1,5 @@
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Filters;
using Microsoft.Extensions.DependencyInjection;

View file

@ -1,4 +1,5 @@
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Models;
using Elsa.Workflows.Models;

View file

@ -1,7 +1,8 @@
using Elsa.Common.Models;
using Elsa.Testing.Shared;
using Elsa.Testing.Shared.Services;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.ComponentTests.Scenarios.DispatchWorkflows.Workflows;
using Elsa.Workflows.Models;
using Elsa.Workflows.Runtime;

View file

@ -0,0 +1,31 @@
using Elsa.Common.Models;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.ComponentTests.Scenarios.ExecuteWorkflows.Workflows;
using Elsa.Workflows.Models;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Messages;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Workflows.ComponentTests.Scenarios.ExecuteWorkflows;
public class ExecuteWorkflowsTests : AppComponentTest
{
private readonly IWorkflowRuntime _workflowRuntime;
public ExecuteWorkflowsTests(App app) : base(app)
{
_workflowRuntime = Scope.ServiceProvider.GetRequiredService<IWorkflowRuntime>();
}
[Fact]
public async Task ExecuteWorkflow_ShouldExecuteWorkflow()
{
var workflowClient = await _workflowRuntime.CreateClientAsync();
await workflowClient.CreateInstanceAsync(new CreateWorkflowInstanceRequest
{
WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(MainWorkflow.DefinitionId, VersionOptions.Published)
});
await workflowClient.RunInstanceAsync(RunWorkflowInstanceRequest.Empty);
}
}

View file

@ -0,0 +1,33 @@
using System.Text.Json;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Activities;
namespace Elsa.Workflows.ComponentTests.Scenarios.ExecuteWorkflows.Workflows;
public class MainWorkflow : WorkflowBase
{
public static readonly string DefinitionId = Guid.NewGuid().ToString();
protected override void Build(IWorkflowBuilder builder)
{
builder.WithDefinitionId(DefinitionId);
var workflowResult = builder.WithVariable<ExecuteWorkflowResult>();
builder.Root = new Sequence
{
Activities =
{
new ExecuteWorkflow
{
WorkflowDefinitionId = new(SubroutineWorkflow.DefinitionId),
Input = new(new Dictionary<string, object>
{
["Value"] = 21
}),
Result = new(workflowResult)
},
new WriteLine(context => $"Subroutine output: {JsonSerializer.Serialize(workflowResult.Get(context))}")
}
};
}
}

View file

@ -0,0 +1,31 @@
using Elsa.Extensions;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Management.Activities.SetOutput;
using Hangfire.Annotations;
namespace Elsa.Workflows.ComponentTests.Scenarios.ExecuteWorkflows.Workflows;
[UsedImplicitly]
public class SubroutineWorkflow : WorkflowBase
{
public static readonly string DefinitionId = Guid.NewGuid().ToString();
protected override void Build(IWorkflowBuilder builder)
{
builder.WithDefinitionId(DefinitionId);
var valueInput = builder.WithInput<double>("Value");
var output = builder.WithOutput<double>("Output");
builder.Root = new Sequence
{
Activities =
{
new WriteLine(context => $"Running subroutine on value {context.GetInput<double>(valueInput)}..."),
new SetOutput
{
OutputName = new(output.Name),
OutputValue = new(context => context.GetInput<double>(valueInput) * 2)
}
}
};
}
}

View file

@ -1,4 +1,5 @@
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
namespace Elsa.Workflows.ComponentTests.Scenarios.HttpWorkflows;

View file

@ -1,5 +1,6 @@
using System.Net;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Workflows.ComponentTests.Scenarios.HttpWorkflows;

View file

@ -4,7 +4,8 @@ using Elsa.Api.Client.Resources.WorkflowInstances.Models;
using Elsa.Common.Entities;
using Elsa.Testing.Shared.Extensions;
using Elsa.Workflows.Activities;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.Helpers;
using Elsa.Workflows.Runtime;
using Elsa.Workflows.Runtime.Entities;

View file

@ -1,5 +1,6 @@
using Elsa.Common.Models;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Filters;
using Microsoft.Extensions.DependencyInjection;

View file

@ -1,6 +1,7 @@
using Elsa.Expressions.Helpers;
using Elsa.Extensions;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.ComponentTests.Scenarios.Variables.Workflows;
using Elsa.Workflows.Management;
using Elsa.Workflows.Models;

View file

@ -1,7 +1,8 @@
using Elsa.Http;
using Elsa.Testing.Shared;
using Elsa.Testing.Shared.Services;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Filters;
using Elsa.Workflows.Runtime.Filters;

View file

@ -1,6 +1,7 @@
using Elsa.Testing.Shared;
using Elsa.Testing.Shared.Services;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.ComponentTests.Scenarios.WorkflowActivities.Workflows;
using Elsa.Workflows.Management;
using Microsoft.Extensions.DependencyInjection;

View file

@ -1,4 +1,5 @@
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.Management;
using Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity;
using Elsa.Workflows.Management.Models;

View file

@ -2,7 +2,8 @@
using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
using Elsa.Testing.Shared.Extensions;
using Elsa.Workflows.Api.Endpoints.WorkflowDefinitions.Execute;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
namespace Elsa.Workflows.ComponentTests.Scenarios.WorkflowCompletion;

View file

@ -1,6 +1,7 @@
using System.Net;
using Elsa.Testing.Shared.Services;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.Runtime;
using Microsoft.Extensions.DependencyInjection;

View file

@ -1,7 +1,10 @@
using System.Net;
using Elsa.Common.Models;
using Elsa.Workflows.Activities;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
using Elsa.Workflows.ComponentTests.Materializers;
using Elsa.Workflows.ComponentTests.WorkflowProviders;
using Elsa.Workflows.Management;
using Elsa.Workflows.Runtime;
using Humanizer;

View file

@ -1,5 +1,6 @@
using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
using Elsa.Workflows.ComponentTests.Helpers;
using Elsa.Workflows.ComponentTests.Abstractions;
using Elsa.Workflows.ComponentTests.Fixtures;
namespace Elsa.Workflows.ComponentTests.Scenarios.WorkflowJsonStructures;