diff --git a/src/apps/Elsa.Studio.Web/appsettings.json b/src/apps/Elsa.Studio.Web/appsettings.json
new file mode 100644
index 000000000..7e26f0ec0
--- /dev/null
+++ b/src/apps/Elsa.Studio.Web/appsettings.json
@@ -0,0 +1,15 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
+ }
+ },
+ "ElsaServer": {
+ "Url": "https://localhost:5001/elsa/api"
+ },
+ "Hosting": {
+ "BasePath": ""
+ }
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Expressions.JavaScript/Providers/VariableTypeDefinitionProvider.cs b/src/modules/Elsa.Expressions.JavaScript/Providers/VariableTypeDefinitionProvider.cs
index f6bcd0b85..25f81738f 100644
--- a/src/modules/Elsa.Expressions.JavaScript/Providers/VariableTypeDefinitionProvider.cs
+++ b/src/modules/Elsa.Expressions.JavaScript/Providers/VariableTypeDefinitionProvider.cs
@@ -6,34 +6,32 @@ using Elsa.Expressions.JavaScript.TypeDefinitions.Models;
using Elsa.Workflows.Management.Options;
using JetBrains.Annotations;
using Microsoft.Extensions.Options;
-
namespace Elsa.Expressions.JavaScript.Providers;
-
///
/// Produces s for variable types.
///
-internal class VariableTypeDefinitionProvider(ITypeDescriber typeDescriber) : TypeDefinitionProvider
+[UsedImplicitly]
+internal class VariableTypeDefinitionProvider(ITypeDescriber typeDescriber, IOptions options) : TypeDefinitionProvider
{
protected override IEnumerable GetTypeDefinitions(TypeDefinitionContext context)
{
var excludedTypes = new Func[]
{
type => type == typeof(ExpandoObject),
- type => typeof(IDictionary).IsAssignableFrom(type),
- type => type == typeof(object)
+ type => type.IsPrimitive,
+ type => type.ContainsGenericParameters,
+ type => type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IDictionary<,>),
+ type => type == typeof(object),
+ type => type == typeof(string)
};
- var variables = context.WorkflowGraph.Workflow.Variables;
-
- var variableTypeQuery =
- from variable in variables
- let variableType = variable.GetVariableType()
- where (variableType.IsClass || variableType.IsInterface || variableType.IsEnum) && !variableType.IsPrimitive && !excludedTypes.Any(x => x(variableType))
+ var variableTypes =
+ from variableDescriptor in options.Value.VariableDescriptors
+ let variableType = variableDescriptor.Type
+ where (variableType.IsClass || variableType.IsInterface || variableType.IsEnum) && !excludedTypes.Any(x => x(variableType))
select variableType;
- var variableTypes = variableTypeQuery.Distinct();
-
- foreach (var variableType in variableTypes)
+ foreach (var variableType in variableTypes.Distinct())
{
yield return typeDescriber.DescribeType(variableType);
}
diff --git a/src/modules/Elsa.Expressions.JavaScript/TypeDefinitions/Services/TypeDefinitionDocumentRenderer.cs b/src/modules/Elsa.Expressions.JavaScript/TypeDefinitions/Services/TypeDefinitionDocumentRenderer.cs
index fcc66000a..443886c65 100644
--- a/src/modules/Elsa.Expressions.JavaScript/TypeDefinitions/Services/TypeDefinitionDocumentRenderer.cs
+++ b/src/modules/Elsa.Expressions.JavaScript/TypeDefinitions/Services/TypeDefinitionDocumentRenderer.cs
@@ -1,9 +1,7 @@
using System.Text;
using Elsa.Expressions.JavaScript.TypeDefinitions.Contracts;
using Elsa.Expressions.JavaScript.TypeDefinitions.Models;
-
namespace Elsa.Expressions.JavaScript.TypeDefinitions.Services;
-
///
public class TypeDefinitionDocumentRenderer : ITypeDefinitionDocumentRenderer
{
@@ -11,19 +9,14 @@ public class TypeDefinitionDocumentRenderer : ITypeDefinitionDocumentRenderer
public string Render(TypeDefinitionsDocument document)
{
var stringBuilder = new StringBuilder();
-
foreach (var functionDefinition in document.Functions)
Render(functionDefinition, stringBuilder);
-
foreach (var typeDefinition in document.Types)
Render(typeDefinition, stringBuilder);
-
foreach (var variableDefinition in document.Variables)
Render(variableDefinition, stringBuilder);
-
return stringBuilder.ToString();
}
-
private void Render(FunctionDefinition functionDefinition, StringBuilder output)
{
var returnType = functionDefinition.ReturnType != null ? $": {functionDefinition.ReturnType}" : "";
@@ -35,11 +28,9 @@ public class TypeDefinitionDocumentRenderer : ITypeDefinitionDocumentRenderer
var returnType = functionDefinition.ReturnType != null ? $" => {functionDefinition.ReturnType}" : "";
output.AppendLine($"{functionDefinition.Name}: ({RenderParameters(functionDefinition.Parameters)}){returnType};");
}
-
private void Render(TypeDefinition typeDefinition, StringBuilder output)
{
output.AppendLine($"declare {typeDefinition.DeclarationKeyword} {typeDefinition.Name} {{");
-
if (typeDefinition.DeclarationKeyword == "enum")
{
foreach (var property in typeDefinition.Properties)
@@ -50,15 +41,13 @@ public class TypeDefinitionDocumentRenderer : ITypeDefinitionDocumentRenderer
foreach (var property in typeDefinition.Properties)
Render(property, output);
}
-
foreach (var method in typeDefinition.Methods)
RenderMethod(method, output);
-
output.AppendLine("}");
}
private void Render(PropertyDefinition property, StringBuilder output) => output.AppendLine($"{property.Name}{(property.IsOptional ? "?" : "")}: {property.Type};");
- private void RenderEnumMember(PropertyDefinition property, StringBuilder output) => output.AppendLine($"{property.Name} = \"{property.Name}\";");
+ private void RenderEnumMember(PropertyDefinition property, StringBuilder output) => output.AppendLine($"{property.Name} = \"{property.Name}\",");
private void Render(VariableDefinition variable, StringBuilder output) => output.AppendLine($"declare var {variable.Name}: {variable.Type};");
string RenderParameter(ParameterDefinition parameter) => $"{parameter.Name}{(parameter.IsOptional ? "?" : "")}: {parameter.Type}";
string RenderParameters(IEnumerable parameters) => string.Join(", ", parameters.Select(RenderParameter));
diff --git a/src/modules/Elsa.Expressions.JavaScript/TypeDefinitions/Services/TypeDescriber.cs b/src/modules/Elsa.Expressions.JavaScript/TypeDefinitions/Services/TypeDescriber.cs
index fc7079e98..9f4a141fb 100644
--- a/src/modules/Elsa.Expressions.JavaScript/TypeDefinitions/Services/TypeDescriber.cs
+++ b/src/modules/Elsa.Expressions.JavaScript/TypeDefinitions/Services/TypeDescriber.cs
@@ -1,17 +1,15 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
+using System.Runtime.CompilerServices;
using Elsa.Extensions;
using Elsa.Expressions.JavaScript.Contracts;
using Elsa.Expressions.JavaScript.TypeDefinitions.Contracts;
using Elsa.Expressions.JavaScript.TypeDefinitions.Models;
-
namespace Elsa.Expressions.JavaScript.TypeDefinitions.Services;
-
///
public class TypeDescriber : ITypeDescriber
{
private readonly ITypeAliasRegistry _typeAliasRegistry;
-
///
/// Constructor.
///
@@ -30,17 +28,18 @@ public class TypeDescriber : ITypeDescriber
Properties = GetPropertyDefinitions(type).DistinctBy(x => x.Name).ToList(),
Methods = GetMethodDefinitions(type).DistinctBy(x => x.Name).ToList()
};
-
return typeDefinition;
}
-
private IEnumerable GetMethodDefinitions(Type type)
{
if(type.IsEnum)
yield break;
-
+
#pragma warning disable IL2070
- var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static).Where(x => !x.IsSpecialName).ToList();
+ var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
+ .Where(x => !x.IsSpecialName)
+ .Where(x => x.GetCustomAttribute() == null)
+ .ToList();
#pragma warning restore IL2070
foreach (var method in methods)
@@ -53,11 +52,9 @@ public class TypeDescriber : ITypeDescriber
};
}
}
-
private IEnumerable GetMethodParameters(MethodInfo method)
{
var parameters = method.GetParameters();
-
foreach (var parameter in parameters)
{
yield return new ParameterDefinition
@@ -68,7 +65,6 @@ public class TypeDescriber : ITypeDescriber
};
}
}
-
private IEnumerable GetPropertyDefinitions([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type)
{
// If the type is an enum, enumerate its members.
@@ -83,12 +79,10 @@ public class TypeDescriber : ITypeDescriber
IsOptional = false,
};
}
-
yield break;
}
var properties = type.GetProperties();
-
foreach (var property in properties)
{
yield return new PropertyDefinition
@@ -99,7 +93,6 @@ public class TypeDescriber : ITypeDescriber
};
}
}
-
private static string GetDeclarationKeyword(Type type) =>
type switch
{
diff --git a/src/modules/Elsa.Http/Activities/SendHttpRequest.cs b/src/modules/Elsa.Http/Activities/SendHttpRequest.cs
index 7b27571c4..6a3b150fe 100644
--- a/src/modules/Elsa.Http/Activities/SendHttpRequest.cs
+++ b/src/modules/Elsa.Http/Activities/SendHttpRequest.cs
@@ -1,9 +1,7 @@
using System.Runtime.CompilerServices;
using Elsa.Workflows;
using Elsa.Workflows.Attributes;
-
namespace Elsa.Http;
-
///
/// Send an HTTP request.
///
@@ -14,7 +12,6 @@ public class SendHttpRequest : SendHttpRequestBase
public SendHttpRequest([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line)
{
}
-
///
/// A list of expected status codes to handle and the corresponding activity to execute when the status code matches.
///
@@ -23,21 +20,21 @@ public class SendHttpRequest : SendHttpRequestBase
UIHint = "http-status-codes"
)]
public ICollection ExpectedStatusCodes { get; set; } = new List();
-
///
/// The activity to execute when the HTTP status code does not match any of the expected status codes.
///
[Port]
public IActivity? UnmatchedStatusCode { get; set; }
-
///
/// The activity to execute when the HTTP request fails to connect.
///
+ [Port]
public IActivity? FailedToConnect { get; set; }
-
+
///
/// The activity to execute when the HTTP request times out.
///
+ [Port]
public IActivity? Timeout { get; set; }
///
@@ -47,22 +44,18 @@ public class SendHttpRequest : SendHttpRequestBase
var statusCode = (int)response.StatusCode;
var matchingCase = expectedStatusCodes.FirstOrDefault(x => x.StatusCode == statusCode);
var activity = matchingCase != null ? matchingCase.Activity : UnmatchedStatusCode;
-
await context.ScheduleActivityAsync(activity, OnChildActivityCompletedAsync);
}
-
///
protected override async ValueTask HandleRequestExceptionAsync(ActivityExecutionContext context, HttpRequestException exception)
{
await context.ScheduleActivityAsync(FailedToConnect, OnChildActivityCompletedAsync);
}
-
///
protected override async ValueTask HandleTaskCanceledExceptionAsync(ActivityExecutionContext context, TaskCanceledException exception)
{
await context.ScheduleActivityAsync(Timeout, OnChildActivityCompletedAsync);
}
-
private async ValueTask OnChildActivityCompletedAsync(ActivityCompletedContext context)
{
await context.TargetContext.CompleteActivityAsync();
diff --git a/src/modules/Elsa.Http/Features/HttpFeature.cs b/src/modules/Elsa.Http/Features/HttpFeature.cs
index 3940688e3..a12022186 100644
--- a/src/modules/Elsa.Http/Features/HttpFeature.cs
+++ b/src/modules/Elsa.Http/Features/HttpFeature.cs
@@ -26,9 +26,7 @@ using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
-
namespace Elsa.Http.Features;
-
///
/// Installs services related to HTTP services and activities.
///
@@ -38,32 +36,26 @@ public class HttpFeature(IModule module) : FeatureBase(module)
{
private Func _httpEndpointRouteProvider = sp => sp.GetRequiredService();
private Func _httpEndpointBasePathProvider = sp => sp.GetRequiredService();
-
///
/// A delegate to configure .
///
public Action? ConfigureHttpOptions { get; set; }
-
///
/// A delegate to configure .
///
public Action? ConfigureHttpFileCacheOptions { get; set; }
-
///
/// A delegate that is invoked when authorizing an inbound HTTP request.
///
public Func HttpEndpointAuthorizationHandler { get; set; } = sp => sp.GetRequiredService();
-
///
/// A delegate that is invoked when an HTTP workflow faults.
///
public Func HttpEndpointWorkflowFaultHandler { get; set; } = sp => sp.GetRequiredService();
-
///
/// A delegate to configure the .
///
public Func ContentTypeProvider { get; set; } = _ => new FileExtensionContentTypeProvider();
-
///
/// A delegate to configure the .
///
@@ -75,7 +67,7 @@ public class HttpFeature(IModule module) : FeatureBase(module)
};
///
- /// A delegate to configure the used when by the activity.
+ /// A delegate to configure the used when by the and activities.
///
public Action HttpClient { get; set; } = (_, _) => { };
@@ -83,7 +75,6 @@ public class HttpFeature(IModule module) : FeatureBase(module)
/// A delegate to configure the for .
///
public Action HttpClientBuilder { get; set; } = _ => { };
-
///
/// A list of types to register with the service collection.
///
@@ -92,7 +83,6 @@ public class HttpFeature(IModule module) : FeatureBase(module)
typeof(HeaderHttpCorrelationIdSelector),
typeof(QueryStringHttpCorrelationIdSelector)
};
-
///
/// A list of types to register with the service collection.
///
@@ -101,12 +91,10 @@ public class HttpFeature(IModule module) : FeatureBase(module)
typeof(HeaderHttpWorkflowInstanceIdSelector),
typeof(QueryStringHttpWorkflowInstanceIdSelector)
};
-
public HttpFeature WithHttpEndpointRoutesProvider() where T : IHttpEndpointRoutesProvider
{
return WithHttpEndpointRoutesProvider(sp => sp.GetRequiredService());
}
-
public HttpFeature WithHttpEndpointRoutesProvider(Func httpEndpointRouteProvider)
{
_httpEndpointRouteProvider = httpEndpointRouteProvider;
@@ -124,7 +112,6 @@ public class HttpFeature(IModule module) : FeatureBase(module)
_httpEndpointBasePathProvider = httpEndpointBasePathProvider;
return this;
}
-
///
public override void Configure()
{
@@ -140,13 +127,10 @@ public class HttpFeature(IModule module) : FeatureBase(module)
typeof(HttpFile),
typeof(Downloadable)
], "HTTP");
-
management.AddActivitiesFrom();
});
-
Module.UseResilience(resilience => resilience.AddResilienceStrategyType());
}
-
///
public override void Apply()
{
@@ -155,15 +139,11 @@ public class HttpFeature(IModule module) : FeatureBase(module)
options.BasePath = "/workflows";
options.BaseUrl = new Uri("http://localhost");
});
-
var configureFileCacheOptions = ConfigureHttpFileCacheOptions ?? (options => { options.TimeToLive = TimeSpan.FromDays(7); });
-
Services.Configure(configureOptions);
Services.Configure(configureFileCacheOptions);
-
var httpClientBuilder = Services.AddHttpClient(HttpClient);
HttpClientBuilder(httpClientBuilder);
-
Services
.AddScoped()
.AddScoped()
@@ -172,31 +152,25 @@ public class HttpFeature(IModule module) : FeatureBase(module)
.AddScoped()
.AddScoped(ContentTypeProvider)
.AddHttpContextAccessor()
-
// Handlers.
.AddNotificationHandler()
-
// Content parsers.
.AddSingleton()
.AddSingleton()
.AddSingleton()
.AddSingleton()
.AddSingleton()
-
// HTTP content factories.
.AddScoped()
.AddScoped()
.AddScoped()
.AddScoped()
-
// Activity property options providers.
.AddScoped()
.AddScoped()
.AddScoped(_httpEndpointBasePathProvider)
-
// Port resolvers.
.AddScoped()
-
// HTTP endpoint handlers.
.AddScoped()
.AddScoped()
@@ -209,7 +183,6 @@ public class HttpFeature(IModule module) : FeatureBase(module)
// Startup tasks.
.AddStartupTask()
-
// Downloadable content handlers.
.AddScoped()
.AddScoped()
@@ -220,28 +193,21 @@ public class HttpFeature(IModule module) : FeatureBase(module)
.AddScoped()
.AddScoped()
.AddScoped()
-
//Trigger payload validators.
.AddTriggerPayloadValidator()
-
// File caches.
.AddScoped(FileCache)
.AddScoped()
-
// AuthenticationBasedHttpEndpointAuthorizationHandler requires Authorization services.
// We could consider creating a separate module for installing authorization services.
.AddAuthorization();
-
// HTTP clients.
Services.AddHttpClient();
-
// Add selectors.
foreach (var httpCorrelationIdSelectorType in HttpCorrelationIdSelectorTypes)
Services.AddScoped(typeof(IHttpCorrelationIdSelector), httpCorrelationIdSelectorType);
-
foreach (var httpWorkflowInstanceIdSelectorType in HttpWorkflowInstanceIdSelectorTypes)
Services.AddScoped(typeof(IHttpWorkflowInstanceIdSelector), httpWorkflowInstanceIdSelectorType);
-
Services.Configure(options =>
{
options.AddTypeAlias("HttpRequest");
diff --git a/src/modules/Elsa.Http/PortResolvers/SendHttpRequestActivityPortResolver.cs b/src/modules/Elsa.Http/PortResolvers/SendHttpRequestActivityPortResolver.cs
index 92dcd991d..decd7e90d 100644
--- a/src/modules/Elsa.Http/PortResolvers/SendHttpRequestActivityPortResolver.cs
+++ b/src/modules/Elsa.Http/PortResolvers/SendHttpRequestActivityPortResolver.cs
@@ -1,8 +1,6 @@
using Elsa.Workflows;
using Elsa.Workflows.Models;
-
namespace Elsa.Http.PortResolvers;
-
///
/// Returns a list of outbound activities for a given activity's expected status codes.
///
@@ -10,32 +8,25 @@ public class SendHttpRequestActivityResolver : IActivityResolver
{
///
public int Priority => 0;
-
///
public bool GetSupportsActivity(IActivity activity) => activity is SendHttpRequest;
-
-
///
public ValueTask> GetActivityPortsAsync(IActivity activity, CancellationToken cancellationToken = default)
{
- IEnumerable ports = GetPortsInternal(activity);
- return new ValueTask>(ports);
+ var ports = GetPortsInternal(activity);
+ return new(ports);
}
private IEnumerable GetPortsInternal(IActivity activity)
{
var sendHttpRequest = (SendHttpRequest)activity;
var cases = sendHttpRequest.ExpectedStatusCodes.Where(x => x.Activity != null);
-
foreach (var @case in cases)
yield return ActivityPort.FromActivity(@case.Activity!, @case.StatusCode.ToString());
-
if (sendHttpRequest.Timeout != null)
yield return ActivityPort.FromActivity(sendHttpRequest.Timeout, nameof(SendHttpRequest.Timeout));
-
if (sendHttpRequest.FailedToConnect != null)
yield return ActivityPort.FromActivity(sendHttpRequest.FailedToConnect, nameof(SendHttpRequest.FailedToConnect));
-
if (sendHttpRequest.UnmatchedStatusCode != null)
yield return ActivityPort.FromActivity(sendHttpRequest.UnmatchedStatusCode, nameof(SendHttpRequest.UnmatchedStatusCode));
}
diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/Bookmarks/Resume/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/Bookmarks/Resume/Endpoint.cs
index 832b60f59..2583e180b 100644
--- a/src/modules/Elsa.Workflows.Api/Endpoints/Bookmarks/Resume/Endpoint.cs
+++ b/src/modules/Elsa.Workflows.Api/Endpoints/Bookmarks/Resume/Endpoint.cs
@@ -4,14 +4,12 @@ using Elsa.Workflows.Runtime;
using FastEndpoints;
using JetBrains.Annotations;
using Microsoft.AspNetCore.Http;
-
namespace Elsa.Workflows.Api.Endpoints.Bookmarks.Resume;
-
///
/// Resumes a bookmarked workflow instance with the bookmark ID specified in the provided SAS token.
///
[PublicAPI]
-internal class Resume(ITokenService tokenService, IBookmarkQueue bookmarkQueue, IPayloadSerializer serializer) : ElsaEndpoint
+internal class Resume(ITokenService tokenService, IWorkflowResumer workflowResumer, IBookmarkQueue bookmarkQueue, IPayloadSerializer serializer) : ElsaEndpoint
{
///
public override void Configure()
@@ -20,25 +18,27 @@ internal class Resume(ITokenService tokenService, IBookmarkQueue bookmarkQueue,
Verbs(Http.GET, Http.POST);
AllowAnonymous();
}
-
///
public override async Task HandleAsync(Request request, CancellationToken cancellationToken)
{
var token = Query("t")!;
+ var asynchronous = Query("async", false);
if (!tokenService.TryDecryptToken(token, out var payload))
AddError("Invalid token.");
-
var input = HttpContext.Request.Method == HttpMethods.Post ? request.Input : GetInputFromQueryString();
-
if (ValidationFailed)
{
await Send.ErrorsAsync(cancellation: cancellationToken);
return;
}
-
- await ResumeBookmarkedWorkflowAsync(payload, input, cancellationToken);
-
+
+ // Some clients, like Blazor, may prematurely cancel their request upon navigation away from the page.
+ // In this case, we don't want to cancel the workflow execution.
+ // We need to better understand the conditions that cause this.
+ var workflowCancellationToken = CancellationToken.None;
+ await ResumeBookmarkedWorkflowAsync(payload, input, asynchronous, workflowCancellationToken);
+
if (!HttpContext.Response.HasStarted)
await Send.OkAsync(cancellationToken);
}
@@ -48,7 +48,6 @@ internal class Resume(ITokenService tokenService, IBookmarkQueue bookmarkQueue,
var inputJson = Query("in", false);
if (string.IsNullOrWhiteSpace(inputJson))
return null;
-
try
{
return serializer.Deserialize>(inputJson);
@@ -59,21 +58,36 @@ internal class Resume(ITokenService tokenService, IBookmarkQueue bookmarkQueue,
return null;
}
}
-
- private async Task ResumeBookmarkedWorkflowAsync(BookmarkTokenPayload tokenPayload, IDictionary? input, CancellationToken cancellationToken)
+
+ private async Task ResumeBookmarkedWorkflowAsync(BookmarkTokenPayload tokenPayload, IDictionary? input, bool asynchronous, CancellationToken cancellationToken)
{
var bookmarkId = tokenPayload.BookmarkId;
var workflowInstanceId = tokenPayload.WorkflowInstanceId;
- var item = new NewBookmarkQueueItem
+
+ if (asynchronous)
+ {
+ var item = new NewBookmarkQueueItem
+ {
+ BookmarkId = bookmarkId,
+ WorkflowInstanceId = workflowInstanceId,
+ Options = new()
+ {
+ Input = input
+ }
+ };
+
+ await bookmarkQueue.EnqueueAsync(item, cancellationToken);
+ return;
+ }
+
+ var resumeRequest = new ResumeBookmarkRequest
{
BookmarkId = bookmarkId,
WorkflowInstanceId = workflowInstanceId,
- Options = new()
- {
- Input = input
- }
+ Input = input
};
- await bookmarkQueue.EnqueueAsync(item, cancellationToken);
+
+ await workflowResumer.ResumeAsync(resumeRequest, cancellationToken);
}
}
diff --git a/src/modules/Elsa.Workflows.Core/Exceptions/InputEvaluationException.cs b/src/modules/Elsa.Workflows.Core/Exceptions/InputEvaluationException.cs
new file mode 100644
index 000000000..b067f53e2
--- /dev/null
+++ b/src/modules/Elsa.Workflows.Core/Exceptions/InputEvaluationException.cs
@@ -0,0 +1,6 @@
+namespace Elsa.Workflows.Exceptions;
+
+public class InputEvaluationException(string inputName, string message, Exception exception) : Exception(message, exception)
+{
+ public string InputName { get; } = inputName;
+}
\ No newline at end of file
diff --git a/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.InputEvaluation.cs b/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.InputEvaluation.cs
index 093ede7f1..c10a9ac38 100644
--- a/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.InputEvaluation.cs
+++ b/src/modules/Elsa.Workflows.Core/Extensions/ActivityExecutionContextExtensions.InputEvaluation.cs
@@ -3,11 +3,11 @@ using Elsa.Expressions.Contracts;
using Elsa.Expressions.Helpers;
using Elsa.Expressions.Models;
using Elsa.Workflows;
+using Elsa.Workflows.Exceptions;
using Elsa.Workflows.Models;
// ReSharper disable once CheckNamespace
namespace Elsa.Extensions;
-
public static partial class ActivityExecutionContextExtensions
{
///
@@ -17,14 +17,11 @@ public static partial class ActivityExecutionContextExtensions
{
var activityDescriptor = context.ActivityDescriptor;
var inputDescriptors = activityDescriptor.Inputs.Where(x => x.AutoEvaluate).ToList();
-
// Evaluate inputs.
foreach (var inputDescriptor in inputDescriptors)
await EvaluateInputPropertyAsync(context, activityDescriptor, inputDescriptor);
-
context.SetHasEvaluatedProperties();
}
-
///
/// Evaluates the specified input property of the activity.
///
@@ -34,7 +31,6 @@ public static partial class ActivityExecutionContextExtensions
var input = await EvaluateInputPropertyAsync(context, inputName);
return input.ConvertTo();
}
-
///
/// Evaluates a specific input property of the activity.
///
@@ -44,13 +40,10 @@ public static partial class ActivityExecutionContextExtensions
var activityRegistryLookup = context.GetRequiredService();
var activityDescriptor = await activityRegistryLookup.FindAsync(activity.Type) ?? throw new Exception("Activity descriptor not found");
var inputDescriptor = activityDescriptor.GetWrappedInputPropertyDescriptor(activity, inputName);
-
if (inputDescriptor == null)
throw new Exception($"No input with name {inputName} could be found");
-
return await EvaluateInputPropertyAsync(context, activityDescriptor, inputDescriptor);
}
-
///
/// Evaluates the specified input and sets the result in the activity execution context's memory space.
///
@@ -66,19 +59,29 @@ public static partial class ActivityExecutionContextExtensions
memoryBlockReference.Set(context, value);
return value;
}
-
+
private static async Task