diff --git a/src/apps/Elsa.Server.Web/Program.cs b/src/apps/Elsa.Server.Web/Program.cs index 4035178eb..cfb2d5a26 100644 --- a/src/apps/Elsa.Server.Web/Program.cs +++ b/src/apps/Elsa.Server.Web/Program.cs @@ -47,10 +47,12 @@ using Elsa.Tenants.Extensions; using Elsa.Workflows; using Elsa.Workflows.Api; using Elsa.Workflows.CommitStates.Strategies; +using Elsa.Workflows.IncidentStrategies; using Elsa.Workflows.LogPersistence; using Elsa.Workflows.Management; using Elsa.Workflows.Management.Compression; using Elsa.Workflows.Management.Stores; +using Elsa.Workflows.Options; using Elsa.Workflows.Runtime.Distributed.Extensions; using Elsa.Workflows.Runtime.Options; using Elsa.Workflows.Runtime.Stores; @@ -705,7 +707,7 @@ services.Configure(options => services.Configure(options => { options.InactivityThreshold = TimeSpan.FromSeconds(15); }); services.Configure(options => options.Ttl = TimeSpan.FromSeconds(10)); services.Configure(options => options.CacheDuration = TimeSpan.FromDays(1)); - +services.Configure(options => options.DefaultIncidentStrategy = typeof(ContinueWithIncidentsStrategy)); services.AddHealthChecks(); services.AddControllers(); services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().WithExposedHeaders("*"))); diff --git a/src/modules/Elsa.CSharp/Models/Globals.cs b/src/modules/Elsa.CSharp/Models/Globals.cs index f86b9693d..cc3de6dc2 100644 --- a/src/modules/Elsa.CSharp/Models/Globals.cs +++ b/src/modules/Elsa.CSharp/Models/Globals.cs @@ -1,11 +1,13 @@ using Elsa.Expressions.Models; using Elsa.Extensions; +using JetBrains.Annotations; namespace Elsa.CSharp.Models; /// /// Provides access to global objects, such as the workflow execution context. /// +[UsedImplicitly] public partial class Globals { /// @@ -15,9 +17,9 @@ public partial class Globals { ExpressionExecutionContext = expressionExecutionContext; Arguments = arguments; - ExecutionContext = new ExecutionContextProxy(expressionExecutionContext); - Output = new OutputProxy(expressionExecutionContext); - Outcome = new OutcomeProxy(expressionExecutionContext); + ExecutionContext = new(expressionExecutionContext); + Output = new(expressionExecutionContext); + Outcome = new(expressionExecutionContext); } /// @@ -48,6 +50,15 @@ public partial class Globals get => ExpressionExecutionContext.GetWorkflowExecutionContext().CorrelationId; set => ExpressionExecutionContext.GetWorkflowExecutionContext().CorrelationId = value; } + + /// + /// Gets or sets the name of the current workflow instance. + /// + public string? WorkflowInstanceName + { + get => ExpressionExecutionContext.GetWorkflowExecutionContext().Name; + set => ExpressionExecutionContext.GetWorkflowExecutionContext().Name = value; + } /// /// Gets additional arguments provided by the caller of the evaluator. diff --git a/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithCommonFunctions.cs b/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithCommonFunctions.cs index 3c9295288..0af980344 100644 --- a/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithCommonFunctions.cs +++ b/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithCommonFunctions.cs @@ -35,6 +35,8 @@ public class ConfigureEngineWithCommonFunctions(IOptions options) : engine.SetValue("getWorkflowInstanceId", (Func)(() => context.GetActivityExecutionContext().WorkflowExecutionContext.Id)); engine.SetValue("setCorrelationId", (Action)(value => context.GetActivityExecutionContext().WorkflowExecutionContext.CorrelationId = value)); engine.SetValue("getCorrelationId", (Func)(() => context.GetActivityExecutionContext().WorkflowExecutionContext.CorrelationId)); + engine.SetValue("setWorkflowInstanceName", (Action)(value => context.GetWorkflowExecutionContext().Name = value)); + engine.SetValue("getWorkflowInstanceName", (Func)(() => context.GetWorkflowExecutionContext().Name)); engine.SetValue("setVariable", (Action)((name, value) => { engine.SyncVariablesContainer(options, name, value); diff --git a/src/modules/Elsa.JavaScript/Providers/CommonFunctionsDefinitionProvider.cs b/src/modules/Elsa.JavaScript/Providers/CommonFunctionsDefinitionProvider.cs index 32f567e6a..76598428d 100644 --- a/src/modules/Elsa.JavaScript/Providers/CommonFunctionsDefinitionProvider.cs +++ b/src/modules/Elsa.JavaScript/Providers/CommonFunctionsDefinitionProvider.cs @@ -41,6 +41,14 @@ internal class CommonFunctionsDefinitionProvider(ITypeAliasRegistry typeAliasReg yield return CreateFunctionDefinition(builder => builder .Name("setCorrelationId") .Parameter("value", "string")); + + yield return CreateFunctionDefinition(builder => builder + .Name("getWorkflowInstanceName") + .ReturnType("string")); + + yield return CreateFunctionDefinition(builder => builder + .Name("setWorkflowInstanceName") + .Parameter("value", "string")); yield return CreateFunctionDefinition(builder => builder .Name("setVariable") diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/EndpointBase.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/EndpointBase.cs index e698e85ac..399fd4096 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/EndpointBase.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/EndpointBase.cs @@ -43,6 +43,7 @@ internal abstract class EndpointBase( { Workflow = workflowGraph.Workflow, CorrelationId = request.CorrelationId, + Name = request.Name, Input = request.GetInputAsDictionary(), Variables = request.GetVariablesAsDictionary(), TriggerActivityId = request.TriggerActivityId, diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/Models.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/Models.cs index 232301466..468ded31c 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/Models.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/Models.cs @@ -13,6 +13,7 @@ public interface IExecutionRequest { string DefinitionId { get; } string? CorrelationId { get; } + string? Name { get; } string? TriggerActivityId { get; } ActivityHandle? ActivityHandle { get; } VersionOptions? VersionOptions { get; } @@ -25,6 +26,7 @@ public class PostRequest : IExecutionRequest { public string DefinitionId { get; set; } = null!; public string? CorrelationId { get; set; } + public string? Name { get; set; } public string? TriggerActivityId { get; set; } public ActivityHandle? ActivityHandle { get; set; } public VersionOptions? VersionOptions { get; set; } @@ -43,6 +45,7 @@ public class GetRequest : IExecutionRequest { public string DefinitionId { get; set; } = null!; public string? CorrelationId { get; set; } + public string? Name { get; set; } public string? TriggerActivityId { get; set; } public ActivityHandle? ActivityHandle { get; set; } public VersionOptions? VersionOptions { get; set; } diff --git a/src/modules/Elsa.Workflows.Core/Activities/SetName.cs b/src/modules/Elsa.Workflows.Core/Activities/SetName.cs index 3c4fbf1df..82e565c49 100644 --- a/src/modules/Elsa.Workflows.Core/Activities/SetName.cs +++ b/src/modules/Elsa.Workflows.Core/Activities/SetName.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +using Elsa.Extensions; using Elsa.Workflows.Attributes; using Elsa.Workflows.Models; using JetBrains.Annotations; @@ -12,18 +13,13 @@ namespace Elsa.Workflows.Activities; [PublicAPI] public class SetName : CodeActivity { - /// - /// The property key name used to store the workflow instance name. - /// - public const string WorkflowInstanceNameKey = "WorkflowInstanceName"; - /// - public SetName([CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : base(source, line) + public SetName([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line) { } /// - public SetName(Input value, [CallerFilePath] string? source = default, [CallerLineNumber] int? line = default) : this(source, line) + public SetName(Input value, [CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : this(source, line) { Value = value; } @@ -36,7 +32,7 @@ public class SetName : CodeActivity /// protected override void Execute(ActivityExecutionContext context) { - var value = context.Get(Value); - context.WorkflowExecutionContext.SetProperty(WorkflowInstanceNameKey, value!); + var value = Value.GetOrDefault(context); + context.WorkflowExecutionContext.Name = value; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs b/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs index d19d232b4..ada56a486 100644 --- a/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs +++ b/src/modules/Elsa.Workflows.Core/Contexts/WorkflowExecutionContext.cs @@ -71,8 +71,8 @@ public partial class WorkflowExecutionContext : IExecutionContext _activityExecutionContexts = new List(); Scheduler = serviceProvider.GetRequiredService().CreateScheduler(); IdentityGenerator = serviceProvider.GetRequiredService(); - Input = input != null ? new Dictionary(input, StringComparer.OrdinalIgnoreCase) : new Dictionary(StringComparer.OrdinalIgnoreCase); - Properties = properties != null ? new Dictionary(properties, StringComparer.OrdinalIgnoreCase) : new Dictionary(StringComparer.OrdinalIgnoreCase); + Input = input != null ? new(input, StringComparer.OrdinalIgnoreCase) : new Dictionary(StringComparer.OrdinalIgnoreCase); + Properties = properties != null ? new(properties, StringComparer.OrdinalIgnoreCase) : new Dictionary(StringComparer.OrdinalIgnoreCase); ExecuteDelegate = executeDelegate; TriggerActivityId = triggerActivityId; CreatedAt = createdAt; @@ -193,7 +193,7 @@ public partial class WorkflowExecutionContext : IExecutionContext MemoryRegister = workflowGraph.Workflow.CreateRegister() }; - workflowExecutionContext.ExpressionExecutionContext = new ExpressionExecutionContext(serviceProvider, workflowExecutionContext.MemoryRegister, cancellationToken: cancellationToken); + workflowExecutionContext.ExpressionExecutionContext = new(serviceProvider, workflowExecutionContext.MemoryRegister, cancellationToken: cancellationToken); await workflowExecutionContext.SetWorkflowGraphAsync(workflowGraph); return workflowExecutionContext; @@ -264,6 +264,9 @@ public partial class WorkflowExecutionContext : IExecutionContext /// An application-specific identifier associated with the execution context. public string? CorrelationId { get; set; } + /// Gets or sets the name of the workflow instance. + public string? Name { get; set; } + /// The ID of the workflow instance that triggered this instance. public string? ParentWorkflowInstanceId { get; set; } diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonWorkflowStateSerializer.cs b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonWorkflowStateSerializer.cs index d23869f4a..ad664f7e8 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonWorkflowStateSerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonWorkflowStateSerializer.cs @@ -128,7 +128,7 @@ public class JsonWorkflowStateSerializer : ConfigurableSerializer, IWorkflowStat public override JsonSerializerOptions GetOptions() { var options = base.GetOptions(); - return new JsonSerializerOptions(options) + return new(options) { ReferenceHandler = new CrossScopedReferenceHandler() }; diff --git a/src/modules/Elsa.Workflows.Core/Services/WorkflowStateExtractor.cs b/src/modules/Elsa.Workflows.Core/Services/WorkflowStateExtractor.cs index 22fa4d9fa..3d3411af2 100644 --- a/src/modules/Elsa.Workflows.Core/Services/WorkflowStateExtractor.cs +++ b/src/modules/Elsa.Workflows.Core/Services/WorkflowStateExtractor.cs @@ -18,6 +18,7 @@ public class WorkflowStateExtractor : IWorkflowStateExtractor DefinitionVersionId = workflowExecutionContext.Workflow.Identity.Id, DefinitionVersion = workflowExecutionContext.Workflow.Identity.Version, CorrelationId = workflowExecutionContext.CorrelationId, + Name = workflowExecutionContext.Name, ParentWorkflowInstanceId = workflowExecutionContext.ParentWorkflowInstanceId, Status = workflowExecutionContext.Status, SubStatus = workflowExecutionContext.SubStatus, @@ -46,6 +47,7 @@ public class WorkflowStateExtractor : IWorkflowStateExtractor { workflowExecutionContext.Id = state.Id; workflowExecutionContext.CorrelationId = state.CorrelationId; + workflowExecutionContext.Name = state.Name; workflowExecutionContext.ParentWorkflowInstanceId = state.ParentWorkflowInstanceId; workflowExecutionContext.SubStatus = state.SubStatus; workflowExecutionContext.IsExecuting = state.IsExecuting; diff --git a/src/modules/Elsa.Workflows.Core/State/WorkflowState.cs b/src/modules/Elsa.Workflows.Core/State/WorkflowState.cs index 8435420ef..0ab6fc50d 100644 --- a/src/modules/Elsa.Workflows.Core/State/WorkflowState.cs +++ b/src/modules/Elsa.Workflows.Core/State/WorkflowState.cs @@ -11,17 +11,17 @@ public class WorkflowState /// /// Gets or sets the ID. /// - public string Id { get; set; } = default!; + public string Id { get; set; } = null!; /// /// The workflow definition ID. /// - public string DefinitionId { get; set; } = default!; + public string DefinitionId { get; set; } = null!; /// /// The workflow definition version ID. /// - public string DefinitionVersionId { get; set; } = default!; + public string DefinitionVersionId { get; set; } = null!; /// /// The workflow definition version. @@ -37,6 +37,11 @@ public class WorkflowState /// The correlation ID of the workflow, if any. /// public string? CorrelationId { get; set; } + + /// + /// Gets or sets the name of the workflow instance. + /// + public string? Name { get; set; } /// /// The status of the workflow. diff --git a/src/modules/Elsa.Workflows.Management/Mappers/WorkflowStateMapper.cs b/src/modules/Elsa.Workflows.Management/Mappers/WorkflowStateMapper.cs index 87c57b480..cea8e3a2a 100644 --- a/src/modules/Elsa.Workflows.Management/Mappers/WorkflowStateMapper.cs +++ b/src/modules/Elsa.Workflows.Management/Mappers/WorkflowStateMapper.cs @@ -1,5 +1,3 @@ -using Elsa.Extensions; -using Elsa.Workflows.Activities; using Elsa.Workflows.Management.Entities; using Elsa.Workflows.State; @@ -23,7 +21,7 @@ public class WorkflowStateMapper return workflowInstance; } - + /// /// Maps a workflow state to a workflow instance. /// @@ -39,14 +37,12 @@ public class WorkflowStateMapper target.SubStatus = source.SubStatus; target.IsExecuting = source.IsExecuting; target.CorrelationId = source.CorrelationId; + target.Name = source.Name; target.IncidentCount = source.Incidents.Count; target.IsSystem = source.IsSystem; target.UpdatedAt = source.UpdatedAt; target.FinishedAt = source.FinishedAt; target.WorkflowState = source; - - if (source.Properties.TryGetValue(SetName.WorkflowInstanceNameKey, out var name)) - target.Name = name; } /// @@ -68,13 +64,11 @@ public class WorkflowStateMapper workflowState.SubStatus = source.SubStatus; workflowState.IsExecuting = source.IsExecuting; workflowState.CorrelationId = source.CorrelationId; + workflowState.Name = source.Name; workflowState.UpdatedAt = source.UpdatedAt; workflowState.FinishedAt = source.FinishedAt; workflowState.IsSystem = source.IsSystem; - if (source.Name != null) - workflowState.Properties[SetName.WorkflowInstanceNameKey] = source.Name; - return workflowState; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Options/WorkflowInstanceOptions.cs b/src/modules/Elsa.Workflows.Management/Options/WorkflowInstanceOptions.cs index 9cdc133ad..76051aed2 100644 --- a/src/modules/Elsa.Workflows.Management/Options/WorkflowInstanceOptions.cs +++ b/src/modules/Elsa.Workflows.Management/Options/WorkflowInstanceOptions.cs @@ -13,6 +13,11 @@ public class WorkflowInstanceOptions /// public string? CorrelationId { get; set; } + /// + /// The name of the workflow instance. + /// + public string? Name { get; set; } + /// /// The input to the workflow instance, if any. /// diff --git a/src/modules/Elsa.Workflows.Management/Services/WorkflowInstanceFactory.cs b/src/modules/Elsa.Workflows.Management/Services/WorkflowInstanceFactory.cs index 41cae07ac..aada085a6 100644 --- a/src/modules/Elsa.Workflows.Management/Services/WorkflowInstanceFactory.cs +++ b/src/modules/Elsa.Workflows.Management/Services/WorkflowInstanceFactory.cs @@ -21,6 +21,7 @@ public class WorkflowInstanceFactory(IIdentityGenerator identityGenerator, ISyst DefinitionVersionId = workflow.Identity.Id, DefinitionVersion = workflow.Identity.Version, CorrelationId = options?.CorrelationId, + Name = options?.Name, Input = options?.Input ?? new Dictionary(), Properties = options?.Properties ?? new Dictionary(), Status = WorkflowStatus.Running, @@ -45,6 +46,7 @@ public class WorkflowInstanceFactory(IIdentityGenerator identityGenerator, ISyst DefinitionVersionId = workflowState.DefinitionVersionId, Version = workflowState.DefinitionVersion, CorrelationId = workflowState.CorrelationId, + Name = workflowState.Name, Status = workflowState.Status, SubStatus = workflowState.SubStatus, IncidentCount = workflowState.Incidents.Count, diff --git a/src/modules/Elsa.Workflows.Runtime.ProtoActor/Mappers/CreateAndRunWorkflowInstanceRequestMapper.cs b/src/modules/Elsa.Workflows.Runtime.ProtoActor/Mappers/CreateAndRunWorkflowInstanceRequestMapper.cs index 14caababa..b81c07960 100644 --- a/src/modules/Elsa.Workflows.Runtime.ProtoActor/Mappers/CreateAndRunWorkflowInstanceRequestMapper.cs +++ b/src/modules/Elsa.Workflows.Runtime.ProtoActor/Mappers/CreateAndRunWorkflowInstanceRequestMapper.cs @@ -23,6 +23,7 @@ public class CreateAndRunWorkflowInstanceRequestMapper(WorkflowDefinitionHandleM WorkflowDefinitionHandle = workflowDefinitionHandleMapper.Map(source.WorkflowDefinitionHandle), WorkflowInstanceId = workflowInstanceId.EmptyIfNull(), CorrelationId = source.CorrelationId.EmptyIfNull(), + Name = source.Name.EmptyIfNull(), ParentId = source.ParentId.EmptyIfNull(), Input = source.Input?.SerializeInput() ?? new ProtoInput(), Properties = source.Properties?.SerializeProperties() ?? new ProtoProperties(), @@ -41,8 +42,9 @@ public class CreateAndRunWorkflowInstanceRequestMapper(WorkflowDefinitionHandleM return new() { WorkflowDefinitionHandle = workflowDefinitionHandleMapper.Map(source.WorkflowDefinitionHandle), - CorrelationId = source.CorrelationId, - ParentId = source.ParentId, + CorrelationId = source.CorrelationId.NullIfEmpty(), + Name = source.Name.NullIfEmpty(), + ParentId = source.ParentId.NullIfEmpty(), Input = source.Input?.DeserializeInput(), Properties = source.Properties?.DeserializeProperties(), ActivityHandle = activityHandleMapper.Map(source.ActivityHandle), diff --git a/src/modules/Elsa.Workflows.Runtime.ProtoActor/Mappers/CreateWorkflowInstanceRequestMapper.cs b/src/modules/Elsa.Workflows.Runtime.ProtoActor/Mappers/CreateWorkflowInstanceRequestMapper.cs index 2c0358ebe..0d8ce651a 100644 --- a/src/modules/Elsa.Workflows.Runtime.ProtoActor/Mappers/CreateWorkflowInstanceRequestMapper.cs +++ b/src/modules/Elsa.Workflows.Runtime.ProtoActor/Mappers/CreateWorkflowInstanceRequestMapper.cs @@ -24,6 +24,7 @@ public class CreateWorkflowInstanceRequestMapper(WorkflowDefinitionHandleMapper WorkflowDefinitionHandle = workflowDefinitionHandleMapper.Map(source.WorkflowDefinitionHandle), WorkflowInstanceId = workflowInstanceId, CorrelationId = source.CorrelationId.EmptyIfNull(), + Name = source.Name.EmptyIfNull(), ParentId = source.ParentId.EmptyIfNull(), Input = source.Input?.SerializeInput() ?? new ProtoInput(), Properties = source.Properties?.SerializeProperties() ?? new ProtoProperties() @@ -40,8 +41,9 @@ public class CreateWorkflowInstanceRequestMapper(WorkflowDefinitionHandleMapper return new() { WorkflowDefinitionHandle = workflowDefinitionHandleMapper.Map(source.WorkflowDefinitionHandle), - CorrelationId = source.CorrelationId, - ParentId = source.ParentId, + CorrelationId = source.CorrelationId.NullIfEmpty(), + Name = source.Name.NullIfEmpty(), + ParentId = source.ParentId.NullIfEmpty(), Input = source.Input?.DeserializeInput(), Properties = source.Properties?.DeserializeProperties() }; diff --git a/src/modules/Elsa.Workflows.Runtime.ProtoActor/Proto/WorkflowInstance.Messages.proto b/src/modules/Elsa.Workflows.Runtime.ProtoActor/Proto/WorkflowInstance.Messages.proto index 719572c28..bb1683fab 100644 --- a/src/modules/Elsa.Workflows.Runtime.ProtoActor/Proto/WorkflowInstance.Messages.proto +++ b/src/modules/Elsa.Workflows.Runtime.ProtoActor/Proto/WorkflowInstance.Messages.proto @@ -45,9 +45,10 @@ message CreateWorkflowInstanceRequest{ WorkflowDefinitionHandle WorkflowDefinitionHandle = 1; string WorkflowInstanceId = 2; optional string CorrelationId = 3; - optional string ParentId = 4; - optional Input input = 5; - optional Properties properties = 6; + optional string Name = 4; + optional string ParentId = 5; + optional Input input = 6; + optional Properties properties = 7; } message CreateWorkflowInstanceResponse{ @@ -71,11 +72,12 @@ message CreateAndRunWorkflowInstanceRequest{ WorkflowDefinitionHandle WorkflowDefinitionHandle = 1; string WorkflowInstanceId = 2; optional string CorrelationId = 3; - optional string ParentId = 4; - optional Input input = 5; - optional Properties properties = 6; - optional ActivityHandle ActivityHandle = 7; - optional string TriggerActivityId = 8; + optional string Name = 4; + optional string ParentId = 5; + optional Input input = 6; + optional Properties properties = 7; + optional ActivityHandle ActivityHandle = 8; + optional string TriggerActivityId = 9; } message ExportWorkflowStateResponse { diff --git a/src/modules/Elsa.Workflows.Runtime/Messages/CreateAndRunWorkflowInstanceRequest.cs b/src/modules/Elsa.Workflows.Runtime/Messages/CreateAndRunWorkflowInstanceRequest.cs index ad6991537..f750befdd 100644 --- a/src/modules/Elsa.Workflows.Runtime/Messages/CreateAndRunWorkflowInstanceRequest.cs +++ b/src/modules/Elsa.Workflows.Runtime/Messages/CreateAndRunWorkflowInstanceRequest.cs @@ -18,6 +18,11 @@ public class CreateAndRunWorkflowInstanceRequest /// The correlation ID of the workflow, if any. /// public string? CorrelationId { get; set; } + + /// + /// The name of the workflow instance to be created. + /// + public string? Name { get; set; } /// /// The input to the workflow instance, if any. diff --git a/src/modules/Elsa.Workflows.Runtime/Messages/CreateWorkflowInstanceRequest.cs b/src/modules/Elsa.Workflows.Runtime/Messages/CreateWorkflowInstanceRequest.cs index ab55c3891..8cebf9d6f 100644 --- a/src/modules/Elsa.Workflows.Runtime/Messages/CreateWorkflowInstanceRequest.cs +++ b/src/modules/Elsa.Workflows.Runtime/Messages/CreateWorkflowInstanceRequest.cs @@ -12,13 +12,18 @@ public class CreateWorkflowInstanceRequest /// /// The ID of the workflow definition version to create an instance of. /// - public WorkflowDefinitionHandle WorkflowDefinitionHandle { get; set; } = default!; + public WorkflowDefinitionHandle WorkflowDefinitionHandle { get; set; } = null!; /// /// The correlation ID of the workflow, if any. /// public string? CorrelationId { get; set; } + /// + /// The name of the workflow instance to be created. + /// + public string? Name { get; set; } + /// /// The input to the workflow instance, if any. /// diff --git a/src/modules/Elsa.Workflows.Runtime/Requests/StartWorkflowRequest.cs b/src/modules/Elsa.Workflows.Runtime/Requests/StartWorkflowRequest.cs index 01c652dda..dc58c9309 100644 --- a/src/modules/Elsa.Workflows.Runtime/Requests/StartWorkflowRequest.cs +++ b/src/modules/Elsa.Workflows.Runtime/Requests/StartWorkflowRequest.cs @@ -23,6 +23,11 @@ public class StartWorkflowRequest /// public string? CorrelationId { get; set; } + /// + /// The name to use when starting a new workflow instance. + /// + public string? Name { get; set; } + /// /// The input to the workflow instance, if any. /// diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowStarter.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowStarter.cs index 2b65afde9..f16b98de5 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowStarter.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowStarter.cs @@ -29,6 +29,7 @@ public class DefaultWorkflowStarter(IWorkflowDefinitionService workflowDefinitio { WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionVersionId(workflow.Identity.Id), CorrelationId = request.CorrelationId, + Name = request.Name, Input = request.Input, Variables = request.Variables, TriggerActivityId = request.TriggerActivityId, diff --git a/src/modules/Elsa.Workflows.Runtime/Services/LocalWorkflowClient.cs b/src/modules/Elsa.Workflows.Runtime/Services/LocalWorkflowClient.cs index 099313340..6d9a182f8 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/LocalWorkflowClient.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/LocalWorkflowClient.cs @@ -35,6 +35,7 @@ public class LocalWorkflowClient( { WorkflowInstanceId = WorkflowInstanceId, CorrelationId = request.CorrelationId, + Name = request.Name, ParentWorkflowInstanceId = request.ParentId, Input = request.Input, Properties = request.Properties @@ -58,6 +59,7 @@ public class LocalWorkflowClient( { Properties = request.Properties, CorrelationId = request.CorrelationId, + Name = request.Name, Input = request.Input, WorkflowDefinitionHandle = request.WorkflowDefinitionHandle, ParentId = request.ParentId @@ -150,6 +152,7 @@ public class LocalWorkflowClient( { WorkflowInstanceId = WorkflowInstanceId, CorrelationId = request.CorrelationId, + Name = request.Name, ParentWorkflowInstanceId = request.ParentId, Input = request.Input, Properties = request.Properties diff --git a/test/integration/Elsa.Activities.IntegrationTests/SetNameTests.cs b/test/integration/Elsa.Activities.IntegrationTests/SetNameTests.cs index 3524b588d..91de62ddd 100644 --- a/test/integration/Elsa.Activities.IntegrationTests/SetNameTests.cs +++ b/test/integration/Elsa.Activities.IntegrationTests/SetNameTests.cs @@ -15,13 +15,13 @@ public class SetNameTests _serviceProvider = new TestApplicationBuilder(testOutputHelper).WithCapturingTextWriter(_capturingTextWriter).Build(); } - [Fact(DisplayName = "WriteLine prints the expected line to the console.")] + [Fact(DisplayName = "SetName sets the workflow instance name.")] public async Task Test1() { const string expectedName = "Foo"; var setName = new SetName(new Input(expectedName)); var result = await _serviceProvider.RunActivityAsync(setName); - var actualName = result.WorkflowState.Properties[SetName.WorkflowInstanceNameKey]; + var actualName = result.WorkflowState.Name; Assert.Equal(expectedName, actualName); } } \ No newline at end of file diff --git a/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/WorkflowCancellation/DefaultRuntimeTests.cs b/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/WorkflowCancellation/DefaultRuntimeTests.cs index 2cad71e66..704efe89c 100644 --- a/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/WorkflowCancellation/DefaultRuntimeTests.cs +++ b/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/WorkflowCancellation/DefaultRuntimeTests.cs @@ -59,7 +59,7 @@ public class DefaultRuntimeTests const string workflowDefinitionId = nameof(SimpleSuspendedWorkflow); var workflowClient = await _workflowRuntime.CreateClientAsync(); - await workflowClient.CreateInstanceAsync(new CreateWorkflowInstanceRequest + await workflowClient.CreateInstanceAsync(new() { WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(workflowDefinitionId, VersionOptions.Published) }); diff --git a/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/WorkflowInstanceName/WorkflowInstanceNameTests.cs b/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/WorkflowInstanceName/WorkflowInstanceNameTests.cs new file mode 100644 index 000000000..b7ebfb427 --- /dev/null +++ b/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/WorkflowInstanceName/WorkflowInstanceNameTests.cs @@ -0,0 +1,46 @@ +using Elsa.Common.Models; +using Elsa.Testing.Shared; +using Elsa.Workflows.IntegrationTests.Scenarios.WorkflowInstanceName.Workflows; +using Elsa.Workflows.Models; +using Elsa.Workflows.Runtime; +using Elsa.Workflows.Runtime.Messages; +using Microsoft.Extensions.DependencyInjection; +using Xunit.Abstractions; + +namespace Elsa.Workflows.IntegrationTests.Scenarios.WorkflowInstanceName; + +public class WorkflowInstanceNameTests +{ + private readonly IServiceProvider _services; + private readonly CapturingTextWriter _capturingTextWriter = new(); + private readonly IWorkflowRuntime _workflowRuntime; + + public WorkflowInstanceNameTests(ITestOutputHelper testOutputHelper) + { + _services = new TestApplicationBuilder(testOutputHelper) + .WithCapturingTextWriter(_capturingTextWriter) + .AddWorkflow() + .Build(); + + _workflowRuntime = _services.GetRequiredService(); + } + + [Fact(DisplayName = "Setting a workflow instance name keeps the workflow instance name when the workflow is executed")] + public async Task SuspendedCancelTest() + { + await _services.PopulateRegistriesAsync(); + const string workflowDefinitionId = nameof(NamedWorkflow); + var desiredName = Guid.NewGuid().ToString(); + var workflowClient = await _workflowRuntime.CreateClientAsync(); + await workflowClient.CreateInstanceAsync(new() + { + Name = desiredName, + WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionId(workflowDefinitionId, VersionOptions.Published) + }); + await workflowClient.RunInstanceAsync(RunWorkflowInstanceRequest.Empty); + var workflowState = await workflowClient.ExportStateAsync(); + + Assert.Equal([desiredName], _capturingTextWriter.Lines); + Assert.Equal(desiredName, workflowState.Name); + } +} \ No newline at end of file diff --git a/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/WorkflowInstanceName/Workflows/NamedWorkflow.cs b/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/WorkflowInstanceName/Workflows/NamedWorkflow.cs new file mode 100644 index 000000000..62a2aaff8 --- /dev/null +++ b/test/integration/Elsa.Workflows.IntegrationTests/Scenarios/WorkflowInstanceName/Workflows/NamedWorkflow.cs @@ -0,0 +1,12 @@ +using Elsa.Extensions; +using Elsa.Workflows.Activities; + +namespace Elsa.Workflows.IntegrationTests.Scenarios.WorkflowInstanceName.Workflows; + +public class NamedWorkflow : WorkflowBase +{ + protected override void Build(IWorkflowBuilder builder) + { + builder.Root = new WriteLine(x => x.GetWorkflowExecutionContext().Name); + } +} \ No newline at end of file