diff --git a/src/bundles/Elsa.ServerAndStudio.Web/appsettings.json b/src/bundles/Elsa.ServerAndStudio.Web/appsettings.json
index a8caf8fe0..6a427b4da 100644
--- a/src/bundles/Elsa.ServerAndStudio.Web/appsettings.json
+++ b/src/bundles/Elsa.ServerAndStudio.Web/appsettings.json
@@ -66,6 +66,7 @@
"DefaultSender": "noreply@crmservices.com"
},
"Http": {
+ "BaseUrl": "https://localhost:5001",
"BasePath": "/api/workflows"
},
"Webhooks": {
diff --git a/src/clients/Elsa.Api.Client/Contracts/IElsaClient.cs b/src/clients/Elsa.Api.Client/Contracts/IElsaClient.cs
deleted file mode 100644
index 1530f9190..000000000
--- a/src/clients/Elsa.Api.Client/Contracts/IElsaClient.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using Elsa.Api.Client.Resources.ActivityDescriptorOptions.Contracts;
-using Elsa.Api.Client.Resources.ActivityDescriptors.Contracts;
-using Elsa.Api.Client.Resources.Scripting.Contracts;
-using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
-using Elsa.Api.Client.Resources.WorkflowInstances.Contracts;
-
-namespace Elsa.Api.Client.Contracts;
-
-///
-/// Represents a client for the Elsa API. Each API is exposed as a property.
-///
-public interface IElsaClient
-{
- ///
- /// Gets the workflow definitions API.
- ///
- IWorkflowDefinitionsApi WorkflowDefinitions { get; }
-
- ///
- /// Gets the activity descriptors API.
- ///
- IActivityDescriptorsApi ActivityDescriptors { get; }
-
- ///
- /// Get the activity descriptor Options API
- ///
- IActivityDescriptorOptionsApi ActivityDescriptorOptions { get; }
- ///
- /// Gets the workflow instances API.
- ///
- IWorkflowInstancesApi WorkflowInstances { get; }
-
- ///
- /// Gets the javascript API.
- ///
- IJavaScriptApi JavaScript { get; }
-}
\ No newline at end of file
diff --git a/src/clients/Elsa.Api.Client/Contracts/IElsaClientFactory.cs b/src/clients/Elsa.Api.Client/Contracts/IElsaClientFactory.cs
deleted file mode 100644
index cf109850e..000000000
--- a/src/clients/Elsa.Api.Client/Contracts/IElsaClientFactory.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using Elsa.Api.Client.Options;
-
-namespace Elsa.Api.Client.Contracts;
-
-///
-/// A factory for creating instances.
-///
-public interface IElsaClientFactory
-{
- ///
- /// Creates a new instance.
- ///
- IElsaClient CreateClient(Action configureOptions, Action? configureHttpClientBuilder = default);
-}
\ No newline at end of file
diff --git a/src/clients/Elsa.Api.Client/Extensions/DependencyInjectionExtensions.cs b/src/clients/Elsa.Api.Client/Extensions/DependencyInjectionExtensions.cs
index a16717ead..416962a0b 100644
--- a/src/clients/Elsa.Api.Client/Extensions/DependencyInjectionExtensions.cs
+++ b/src/clients/Elsa.Api.Client/Extensions/DependencyInjectionExtensions.cs
@@ -1,8 +1,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
-using Elsa.Api.Client.Contracts;
using Elsa.Api.Client.Converters;
-using Elsa.Api.Client.HttpMessageHandlers;
using Elsa.Api.Client.Options;
using Elsa.Api.Client.Resources.ActivityDescriptorOptions.Contracts;
using Elsa.Api.Client.Resources.ActivityDescriptors.Contracts;
@@ -17,7 +15,6 @@ using Elsa.Api.Client.Resources.WorkflowActivationStrategies.Contracts;
using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
using Elsa.Api.Client.Resources.WorkflowExecutionContexts.Contracts;
using Elsa.Api.Client.Resources.WorkflowInstances.Contracts;
-using Elsa.Api.Client.Services;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
@@ -33,40 +30,39 @@ namespace Elsa.Api.Client.Extensions;
public static class DependencyInjectionExtensions
{
///
- /// Adds the Elsa client to the service collection.
+ /// Adds the Elsa API client configured to use an API key to the service collection.
///
- /// The service collection.
- /// The base address of the Elsa API.
- /// The API key to use for authentication.
- /// An optional delegate that can be used to configure the client options.
- /// An optional delegate that can be used to configure the client builder options.
- public static IServiceCollection AddElsaClient(this IServiceCollection services, Uri baseAddress, string apiKey, Action? configureOptions = default, Action? configureBuilderOptions = default)
+ public static IServiceCollection AddElsaApiKeyClient(this IServiceCollection services, Action configureOptions)
{
- services.AddScoped();
- return services.AddElsaClient(
- options =>
- {
- options.BaseAddress = baseAddress;
- options.ApiKey = apiKey;
- configureOptions?.Invoke(options);
- },
- configureBuilderOptions: options =>
- {
- options.ConfigureHttpClientBuilder = builder => builder.AddHttpMessageHandler();
- configureBuilderOptions?.Invoke(options);
- });
+ var options = new ElsaClientOptions();
+ configureOptions(options);
+
+ return services.AddElsaClient(client =>
+ {
+ client.BaseAddress = options.BaseAddress;
+ client.ApiKey = options.ApiKey;
+ client.ConfigureHttpClient = options.ConfigureHttpClient;
+ });
}
-
+
///
/// Adds the Elsa client to the service collection.
///
- public static IServiceCollection AddElsaClient(this IServiceCollection services, Action? configureOptions = default, Action? configureBuilderOptions = default)
+ public static IServiceCollection AddElsaClient(this IServiceCollection services, Action configureClient)
{
var builderOptions = new ElsaClientBuilderOptions();
- configureBuilderOptions?.Invoke(builderOptions);
+ configureClient.Invoke(builderOptions);
+
+ builderOptions.ConfigureHttpClientBuilder += builder => builder.AddHttpMessageHandler(sp => (DelegatingHandler)sp.GetRequiredService(builderOptions.AuthenticationHandler));
- services.Configure(configureOptions ?? (_ => { }));
- services.AddScoped();
+ services.AddScoped(builderOptions.AuthenticationHandler);
+
+ services.Configure(options =>
+ {
+ options.BaseAddress = builderOptions.BaseAddress;
+ options.ConfigureHttpClient = builderOptions.ConfigureHttpClient;
+ options.ApiKey = builderOptions.ApiKey;
+ });
services.AddApi(builderOptions);
services.AddApi(builderOptions);
services.AddApi(builderOptions);
diff --git a/src/clients/Elsa.Api.Client/Extensions/HeaderExtensions.cs b/src/clients/Elsa.Api.Client/Extensions/HeaderExtensions.cs
new file mode 100644
index 000000000..e23bafce7
--- /dev/null
+++ b/src/clients/Elsa.Api.Client/Extensions/HeaderExtensions.cs
@@ -0,0 +1,14 @@
+using Elsa.Api.Client.Shared;
+
+namespace Elsa.Api.Client.Extensions;
+
+///
+/// Contains extension methods for the class.
+///
+public static class HttpResponseMessageExtensions
+{
+ ///
+ /// Gets the workflow instance ID from the response.
+ ///
+ public static string? GetWorkflowInstanceId(this HttpResponseMessage response) => response.Headers.TryGetValues(HeaderNames.WorkflowInstanceId, out var values) ? values.FirstOrDefault() : default;
+}
\ No newline at end of file
diff --git a/src/clients/Elsa.Api.Client/Options/ElsaClientBuilderOptions.cs b/src/clients/Elsa.Api.Client/Options/ElsaClientBuilderOptions.cs
index ab406a218..998b6d499 100644
--- a/src/clients/Elsa.Api.Client/Options/ElsaClientBuilderOptions.cs
+++ b/src/clients/Elsa.Api.Client/Options/ElsaClientBuilderOptions.cs
@@ -1,3 +1,4 @@
+using Elsa.Api.Client.HttpMessageHandlers;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Api.Client.Options;
@@ -7,10 +8,29 @@ namespace Elsa.Api.Client.Options;
///
public class ElsaClientBuilderOptions
{
+ ///
+ /// Gets or sets the base address of the Elsa server.
+ ///
+ public Uri BaseAddress { get; set; } = default!;
+
+ ///
+ /// Gets or sets the API key function to use when authenticating with the Elsa server.
+ ///
+ public string? ApiKey { get; set; }
+
+ ///
+ /// A type that can be used to authenticate with the Elsa server.
+ /// Defaults to .
+ ///
+ public Type AuthenticationHandler { get; set; } = typeof(ApiKeyHttpMessageHandler);
+
+ ///
+ /// Gets or sets a delegate that can be used to configure the HTTP client.
+ ///
+ public Action? ConfigureHttpClient { get; set; }
+
///
/// Gets or sets a delegate that can be used to configure the HTTP client builder.
///
- public Action? ConfigureHttpClientBuilder { get; set; }
-
-
+ public Action ConfigureHttpClientBuilder { get; set; } = _ => { };
}
\ No newline at end of file
diff --git a/src/clients/Elsa.Api.Client/Services/ElsaClient.cs b/src/clients/Elsa.Api.Client/Services/ElsaClient.cs
deleted file mode 100644
index 3a0008512..000000000
--- a/src/clients/Elsa.Api.Client/Services/ElsaClient.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Elsa.Api.Client.Contracts;
-using Elsa.Api.Client.Resources.ActivityDescriptorOptions.Contracts;
-using Elsa.Api.Client.Resources.ActivityDescriptors.Contracts;
-using Elsa.Api.Client.Resources.Scripting.Contracts;
-using Elsa.Api.Client.Resources.WorkflowDefinitions.Contracts;
-using Elsa.Api.Client.Resources.WorkflowInstances.Contracts;
-
-namespace Elsa.Api.Client.Services;
-
-///
-public class ElsaClient : IElsaClient
-{
- ///
- /// Initializes a new instance of the class.
- ///
- public ElsaClient(IWorkflowDefinitionsApi workflowDefinitions
- , IWorkflowInstancesApi workflowInstances
- , IActivityDescriptorsApi activityDescriptors
- , IActivityDescriptorOptionsApi activityDescriptorOptions
- , IJavaScriptApi javaScript)
- {
- WorkflowDefinitions = workflowDefinitions;
- WorkflowInstances = workflowInstances;
- ActivityDescriptors = activityDescriptors;
- ActivityDescriptorOptions = activityDescriptorOptions;
- JavaScript = javaScript;
- }
-
- ///
- public IWorkflowDefinitionsApi WorkflowDefinitions { get; }
-
- ///
- public IActivityDescriptorsApi ActivityDescriptors { get; }
-
- ///
- public IActivityDescriptorOptionsApi ActivityDescriptorOptions { get; }
-
- ///
- public IWorkflowInstancesApi WorkflowInstances { get; }
-
- ///
- public IJavaScriptApi JavaScript { get; }
-}
\ No newline at end of file
diff --git a/src/clients/Elsa.Api.Client/Shared/HeaderNames.cs b/src/clients/Elsa.Api.Client/Shared/HeaderNames.cs
new file mode 100644
index 000000000..a3c26753c
--- /dev/null
+++ b/src/clients/Elsa.Api.Client/Shared/HeaderNames.cs
@@ -0,0 +1,12 @@
+namespace Elsa.Api.Client.Shared;
+
+///
+/// Contains header names used by the Elsa API.
+///
+public static class HeaderNames
+{
+ ///
+ /// The name of the header that contains the workflow instance ID.
+ ///
+ public const string WorkflowInstanceId = "x-elsa-workflow-instance-id";
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Http/Parsers/JsonHttpContentParser.cs b/src/modules/Elsa.Http/Parsers/JsonHttpContentParser.cs
index ad2906c49..4f2ee9bba 100644
--- a/src/modules/Elsa.Http/Parsers/JsonHttpContentParser.cs
+++ b/src/modules/Elsa.Http/Parsers/JsonHttpContentParser.cs
@@ -30,6 +30,9 @@ public class JsonHttpContentParser : IHttpContentParser
using var reader = new StreamReader(content, leaveOpen: true);
var json = await reader.ReadToEndAsync();
+
+ if(returnType == typeof(string))
+ return json;
if (returnType == null || returnType.IsPrimitive)
return json.ConvertTo(returnType ?? typeof(string))!;
diff --git a/src/modules/Elsa.Http/UIHints/HttpEndpointPathUIHandler.cs b/src/modules/Elsa.Http/UIHints/HttpEndpointPathUIHandler.cs
index e81a9d840..0258340be 100644
--- a/src/modules/Elsa.Http/UIHints/HttpEndpointPathUIHandler.cs
+++ b/src/modules/Elsa.Http/UIHints/HttpEndpointPathUIHandler.cs
@@ -16,8 +16,8 @@ public class HttpEndpointPathUIHandler(IOptions options) :
public ValueTask> GetUIPropertiesAsync(PropertyInfo propertyInfo, object? context, CancellationToken cancellationToken = default)
{
var baseUrl = options.Value.BaseUrl;
- var apiRoutePrefix = options.Value.ApiRoutePrefix;
- var completeBaseUrl = new Uri(baseUrl, apiRoutePrefix);
+ var basePath = options.Value.BasePath;
+ var completeBaseUrl = new Uri(baseUrl, basePath);
return new(new Dictionary
{
diff --git a/src/modules/Elsa.Python/HostedServices/PythonGlobalInterpreterManager.cs b/src/modules/Elsa.Python/HostedServices/PythonGlobalInterpreterManager.cs
index 4b75d927f..084d1ebe7 100644
--- a/src/modules/Elsa.Python/HostedServices/PythonGlobalInterpreterManager.cs
+++ b/src/modules/Elsa.Python/HostedServices/PythonGlobalInterpreterManager.cs
@@ -44,13 +44,5 @@ public class PythonGlobalInterpreterManager : IHostedService
}
///
- public Task StopAsync(CancellationToken cancellationToken)
- {
- if(_mainThreadState != IntPtr.Zero)
- {
- PythonEngine.EndAllowThreads(_mainThreadState);
- PythonEngine.Shutdown();
- }
- return Task.CompletedTask;
- }
+ public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Core/Activities/SetVariable.cs b/src/modules/Elsa.Workflows.Core/Activities/SetVariable.cs
index ad4dc9ab9..8265840d3 100644
--- a/src/modules/Elsa.Workflows.Core/Activities/SetVariable.cs
+++ b/src/modules/Elsa.Workflows.Core/Activities/SetVariable.cs
@@ -5,6 +5,7 @@ using Elsa.Extensions;
using Elsa.Workflows.Attributes;
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
+using Elsa.Workflows.UIHints;
using JetBrains.Annotations;
namespace Elsa.Workflows.Activities;
@@ -100,7 +101,10 @@ public class SetVariable : CodeActivity
///
protected override void Execute(ActivityExecutionContext context)
{
+ // Always refer to the variable by ID to ensure that the variable is resolved from the correct scope.
+ var variableId = Variable.Id;
+ var variable = context.ExpressionExecutionContext.EnumerateVariablesInScope().FirstOrDefault(x => x.Id == variableId);
var value = context.Get(Value);
- context.SetVariable(Variable.Name, value);
+ variable?.Set(context, value);
}
}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs
index 05d5d1d55..cb9dd853c 100644
--- a/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs
+++ b/src/modules/Elsa.Workflows.Core/Extensions/ExpressionExecutionContextExtensions.cs
@@ -314,6 +314,12 @@ public static class ExpressionExecutionContextExtensions
currentScope = currentScope.ParentContext;
}
+
+ if (context.TryGetWorkflowExecutionContext(out var workflowExecutionContext))
+ {
+ if (workflowExecutionContext.Workflow.ResultVariable != null)
+ yield return workflowExecutionContext.Workflow.ResultVariable;
+ }
}
///