Fix HTTP activity issues
This commit is contained in:
parent
651a7a76d5
commit
0f358e72f9
|
|
@ -24,11 +24,6 @@ public static class ObjectConverter
|
|||
return value;
|
||||
|
||||
var options = serializerOptions ?? new JsonSerializerOptions();
|
||||
options.SetupExtensions().SetReferenceHandling(ReferenceHandling.Preserve);
|
||||
var registry = options.GetDiscriminatorConventionRegistry();
|
||||
registry.ClearConventions();
|
||||
registry.RegisterConvention(new DefaultDiscriminatorConvention<string>(options, "_type"));
|
||||
|
||||
options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
|
||||
options.ReferenceHandler = ReferenceHandler.Preserve;
|
||||
options.PropertyNameCaseInsensitive = true;
|
||||
|
|
|
|||
|
|
@ -38,23 +38,28 @@ public class HttpEndpoint : Trigger<HttpRequestModel>
|
|||
)]
|
||||
public Input<string?> Policy { get; set; } = new(default(string?));
|
||||
|
||||
protected override IEnumerable<object> GetTriggerPayload(TriggerIndexingContext context) => GetBookmarkPayload(context.ExpressionExecutionContext);
|
||||
/// <inheritdoc />
|
||||
protected override IEnumerable<object> GetTriggerPayloads(TriggerIndexingContext context) => GetBookmarkPayloads(context.ExpressionExecutionContext);
|
||||
|
||||
protected override void Execute(ActivityExecutionContext context)
|
||||
/// <inheritdoc />
|
||||
protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
|
||||
{
|
||||
// If we did not receive external input, it means we are just now encountering this activity and we need to block execution by creating a bookmark.
|
||||
if (!context.TryGetInput<HttpRequestModel>(InputKey, out var request))
|
||||
{
|
||||
// Create bookmarks for when we receive the expected HTTP request.
|
||||
context.CreateBookmarks(GetBookmarkPayload(context.ExpressionExecutionContext));
|
||||
context.CreateBookmarks(GetBookmarkPayloads(context.ExpressionExecutionContext));
|
||||
return;
|
||||
}
|
||||
|
||||
// Provide the received HTTP request as output.
|
||||
context.Set(Result, request);
|
||||
|
||||
// Complete.
|
||||
await context.CompleteActivityAsync();
|
||||
}
|
||||
|
||||
private IEnumerable<object> GetBookmarkPayload(ExpressionExecutionContext context)
|
||||
private IEnumerable<object> GetBookmarkPayloads(ExpressionExecutionContext context)
|
||||
{
|
||||
// Generate bookmark data for path and selected methods.
|
||||
var path = context.Get(Path);
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ using HttpRequestHeaders = Elsa.Http.Models.HttpRequestHeaders;
|
|||
|
||||
namespace Elsa.Http;
|
||||
|
||||
[Activity("Elsa", "HTTP", "Send Http Request.", DisplayName = "Send HTTP Request", Kind = ActivityKind.Task)]
|
||||
[Activity("Elsa", "HTTP", "Send Http Request.", DisplayName = "HTTP Request", Kind = ActivityKind.Task)]
|
||||
public class SendHttpRequest : Activity
|
||||
{
|
||||
[Input] public Input<Uri?> Url { get; set; } = default!;
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="6.0.11" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Http.Extensions" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Routing" Version="2.2.2" />
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ using Elsa.Http.Features;
|
|||
// ReSharper disable once CheckNamespace
|
||||
namespace Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
public static class DependencyInjectionExtensions
|
||||
public static class ModuleExtensions
|
||||
{
|
||||
public static IModule UseHttp(this IModule module, Action<HttpFeature>? configure = default)
|
||||
{
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
using Elsa.Common.Features;
|
||||
using Elsa.Features.Abstractions;
|
||||
using Elsa.Features.Attributes;
|
||||
using Elsa.Features.Services;
|
||||
using Elsa.Http.ContentWriters;
|
||||
using Elsa.Http.Handlers;
|
||||
|
|
@ -11,6 +13,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
|
||||
namespace Elsa.Http.Features;
|
||||
|
||||
[DependsOn(typeof(MemoryCacheFeature))]
|
||||
public class HttpFeature : FeatureBase
|
||||
{
|
||||
public HttpFeature(IModule module) : base(module)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Elsa.Http.Models;
|
||||
|
||||
public record HttpEndpointBookmarkPayload
|
||||
|
|
@ -5,6 +7,11 @@ public record HttpEndpointBookmarkPayload
|
|||
private readonly string _path = default!;
|
||||
private readonly string _method = default!;
|
||||
|
||||
[JsonConstructor]
|
||||
public HttpEndpointBookmarkPayload()
|
||||
{
|
||||
}
|
||||
|
||||
public HttpEndpointBookmarkPayload(string path, string method)
|
||||
{
|
||||
Path = path;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Elsa.Http.Models;
|
||||
|
||||
public record HttpRequestModel(
|
||||
|
|
@ -7,4 +9,14 @@ public record HttpRequestModel(
|
|||
IDictionary<string, string> QueryString,
|
||||
IDictionary<string, object> RouteValues,
|
||||
IDictionary<string, string> Headers
|
||||
);
|
||||
)
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Constructor used for deserialization.
|
||||
/// </summary>
|
||||
[JsonConstructor]
|
||||
public HttpRequestModel() : this(default!, default!, default!, default!, default!, default!)
|
||||
{
|
||||
}
|
||||
}
|
||||
19
src/modules/Elsa.Identity/Extensions/ModuleExtensions.cs
Normal file
19
src/modules/Elsa.Identity/Extensions/ModuleExtensions.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using Elsa.Features.Services;
|
||||
using Elsa.Identity.Features;
|
||||
|
||||
namespace Elsa.Identity.Extensions;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for <see cref="IModule"/> that installs the <see cref="IdentityFeature"/> feature.
|
||||
/// </summary>
|
||||
public static class ModuleExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Installs & configures the <see cref="IdentityFeature"/> feature.
|
||||
/// </summary>
|
||||
public static IModule UseIdentity(this IModule module, Action<IdentityFeature>? configure = default)
|
||||
{
|
||||
module.Configure(configure);
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
|
@ -213,7 +213,7 @@ public class ProtoActorWorkflowRuntime : IWorkflowRuntime
|
|||
var storeBookmarkRequest = new StoreBookmarksRequest
|
||||
{
|
||||
WorkflowInstanceId = instanceId,
|
||||
CorrelationId = correlationId
|
||||
CorrelationId = correlationId.EmptyIfNull()
|
||||
};
|
||||
|
||||
storeBookmarkRequest.BookmarkIds.AddRange(groupedBookmark.Select(x => x.Id));
|
||||
|
|
|
|||
|
|
@ -35,23 +35,5 @@ public class ScheduledChildCallbackBehavior : Behavior
|
|||
{
|
||||
await callbackEntry.CompletionCallback(activityExecutionContext, childActivityExecutionContext);
|
||||
}
|
||||
else
|
||||
{
|
||||
var ports = Owner.GetType().GetProperties().Where(x => typeof(IActivity).IsAssignableFrom(x.PropertyType)).ToList();
|
||||
|
||||
var portQuery =
|
||||
from p in ports
|
||||
let i = (IActivity)p.GetValue(Owner)
|
||||
where i == childActivity
|
||||
select new { PortProperty = p, PortActivity = i };
|
||||
|
||||
var port = portQuery.FirstOrDefault();
|
||||
|
||||
if (port == null)
|
||||
return;
|
||||
|
||||
var portName = port.PortProperty.GetCustomAttribute<PortAttribute>()?.Name ?? port.PortProperty.Name;
|
||||
await activityExecutionContext.CompleteActivityWithOutcomesAsync(portName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -218,13 +218,18 @@ public static class ActivityExecutionContextExtensions
|
|||
await context.SendSignalAsync(new ActivityCompleted(result));
|
||||
|
||||
// Remove the context.
|
||||
context.WorkflowExecutionContext.ActivityExecutionContexts.Remove(context);
|
||||
context.WorkflowExecutionContext.RemoveActivityExecutionContext(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Complete the current activity with the specified outcome.
|
||||
/// </summary>
|
||||
public static ValueTask CompleteActivityWithOutcomesAsync(this ActivityExecutionContext context, params string[] outcomes) => context.CompleteActivityAsync(new Outcomes(outcomes));
|
||||
|
||||
/// <summary>
|
||||
/// Complete the current composite activity with the specified outcome.
|
||||
/// </summary>
|
||||
public static async ValueTask CompleteCompositeAsync(this ActivityExecutionContext context, params string[] outcomes) => await context.SendSignalAsync(new CompleteCompositeSignal(new Outcomes(outcomes)));
|
||||
|
||||
/// <summary>
|
||||
/// Cancel the activity. For blocking activities, it means their bookmarks will be removed. For job activities, the background work will be cancelled.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public static class WorkflowExecutionContextExtensions
|
|||
var list = contexts.ToList();
|
||||
|
||||
// Remove each context.
|
||||
foreach (var context in list) workflowExecutionContext.ActivityExecutionContexts.Remove(context);
|
||||
foreach (var context in list) workflowExecutionContext.RemoveActivityExecutionContext(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class ActivityInvoker : IActivityInvoker
|
|||
}
|
||||
|
||||
// Add the activity context to the workflow context.
|
||||
workflowExecutionContext.ActivityExecutionContexts.Add(activityExecutionContext);
|
||||
workflowExecutionContext.AddActivityExecutionContext(activityExecutionContext);
|
||||
|
||||
// Execute the activity execution pipeline.
|
||||
await InvokeAsync(activityExecutionContext);
|
||||
|
|
|
|||
|
|
@ -47,7 +47,18 @@ public class DefaultWorkflowExecutionContextFactory : IWorkflowExecutionContextF
|
|||
var scheduler = _schedulerFactory.CreateScheduler();
|
||||
|
||||
// Setup a workflow execution context.
|
||||
var workflowExecutionContext = new WorkflowExecutionContext(_serviceProvider, instanceId, correlationId, workflow, graph, scheduler, input, executeActivityDelegate, triggerActivityId, cancellationToken);
|
||||
var workflowExecutionContext = new WorkflowExecutionContext(
|
||||
_serviceProvider,
|
||||
instanceId,
|
||||
correlationId,
|
||||
workflow,
|
||||
graph,
|
||||
scheduler,
|
||||
input,
|
||||
executeActivityDelegate,
|
||||
triggerActivityId,
|
||||
default,
|
||||
cancellationToken);
|
||||
|
||||
// Restore workflow execution context from state, if provided.
|
||||
if (workflowState != null) _workflowStateSerializer.DeserializeState(workflowExecutionContext, workflowState);
|
||||
|
|
|
|||
|
|
@ -97,14 +97,14 @@ public class WorkflowStateSerializer : IWorkflowStateSerializer
|
|||
var owner = workflowExecutionContext.ActivityExecutionContexts.First(x => x.Id == completionCallbackEntry.OwnerId);
|
||||
var child = workflowExecutionContext.FindNodeById(completionCallbackEntry.ChildId).Activity;
|
||||
var callbackName = completionCallbackEntry.MethodName;
|
||||
var callbackDelegate = owner.Activity.GetActivityCompletionCallback(callbackName);
|
||||
var callbackDelegate = !string.IsNullOrEmpty(callbackName) ? owner.Activity.GetActivityCompletionCallback(callbackName) : default;
|
||||
workflowExecutionContext.AddCompletionCallback(owner, child, callbackDelegate);
|
||||
}
|
||||
}
|
||||
|
||||
private void SerializeCompletionCallbacks(WorkflowState state, WorkflowExecutionContext workflowExecutionContext)
|
||||
{
|
||||
var completionCallbacks = workflowExecutionContext.CompletionCallbacks.Select(x => new CompletionCallbackState(x.Owner.Id, x.Child.Id, x.CompletionCallback.Method.Name));
|
||||
var completionCallbacks = workflowExecutionContext.CompletionCallbacks.Select(x => new CompletionCallbackState(x.Owner.Id, x.Child.Id, x.CompletionCallback?.Method.Name));
|
||||
state.CompletionCallbacks = completionCallbacks.ToList();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,10 @@ public class WorkflowExecutionContext
|
|||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IList<ActivityNode> _nodes;
|
||||
private readonly IList<ActivityCompletionCallbackEntry> _completionCallbackEntries = new List<ActivityCompletionCallbackEntry>();
|
||||
private IList<ActivityExecutionContext> _activityExecutionContexts;
|
||||
|
||||
public WorkflowExecutionContext(IServiceProvider serviceProvider,
|
||||
public WorkflowExecutionContext(
|
||||
IServiceProvider serviceProvider,
|
||||
string id,
|
||||
string? correlationId,
|
||||
Workflow workflow,
|
||||
|
|
@ -24,6 +26,7 @@ public class WorkflowExecutionContext
|
|||
IDictionary<string, object>? input,
|
||||
ExecuteActivityDelegate? executeDelegate,
|
||||
string? triggerActivityId,
|
||||
IEnumerable<ActivityExecutionContext>? activityExecutionContexts,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
|
|
@ -33,6 +36,7 @@ public class WorkflowExecutionContext
|
|||
Id = id;
|
||||
CorrelationId = correlationId;
|
||||
_nodes = graph.Flatten().Distinct().ToList();
|
||||
_activityExecutionContexts = activityExecutionContexts?.ToList() ?? new List<ActivityExecutionContext>();
|
||||
Scheduler = scheduler;
|
||||
Input = input ?? new Dictionary<string, object>();
|
||||
ExecuteDelegate = executeDelegate;
|
||||
|
|
@ -72,7 +76,12 @@ public class WorkflowExecutionContext
|
|||
public string? TriggerActivityId { get; set; }
|
||||
public CancellationToken CancellationToken { get; }
|
||||
public ICollection<ActivityCompletionCallbackEntry> CompletionCallbacks => new ReadOnlyCollection<ActivityCompletionCallbackEntry>(_completionCallbackEntries);
|
||||
public ICollection<ActivityExecutionContext> ActivityExecutionContexts { get; set; } = new List<ActivityExecutionContext>();
|
||||
|
||||
public IReadOnlyCollection<ActivityExecutionContext> ActivityExecutionContexts
|
||||
{
|
||||
get => _activityExecutionContexts.ToList();
|
||||
internal set => _activityExecutionContexts = value.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A volatile collection of executed activity instance IDs. This collection is reset when workflow execution starts.
|
||||
|
|
@ -148,6 +157,9 @@ public class WorkflowExecutionContext
|
|||
expressionExecutionContext.TransientProperties[ExpressionExecutionContextExtensions.ActivityExecutionContextKey] = activityExecutionContext;
|
||||
return activityExecutionContext;
|
||||
}
|
||||
|
||||
public void RemoveActivityExecutionContext(ActivityExecutionContext context) => _activityExecutionContexts.Remove(context);
|
||||
public void AddActivityExecutionContext(ActivityExecutionContext context) => _activityExecutionContexts.Add(context);
|
||||
|
||||
public async Task CancelActivityAsync(string activityId)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ public class TypeJsonConverter : JsonConverter<Type>
|
|||
public override void Write(Utf8JsonWriter writer, Type value, JsonSerializerOptions options)
|
||||
{
|
||||
// Handle collection types.
|
||||
if (value.IsGenericType)
|
||||
if (value.IsGenericType && value.GenericTypeArguments.Length == 1)
|
||||
{
|
||||
var elementType = value.GenericTypeArguments.First();
|
||||
var typedEnumerable = typeof(IEnumerable<>).MakeGenericType(elementType);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public class CompletionCallbackState
|
|||
{
|
||||
}
|
||||
|
||||
public CompletionCallbackState(string ownerId, string childId, string methodName)
|
||||
public CompletionCallbackState(string ownerId, string childId, string? methodName)
|
||||
{
|
||||
OwnerId = ownerId;
|
||||
ChildId = childId;
|
||||
|
|
@ -22,5 +22,5 @@ public class CompletionCallbackState
|
|||
|
||||
public string OwnerId { get; init; } = default!;
|
||||
public string ChildId { get; init; } = default!;
|
||||
public string MethodName { get; init; } = default!;
|
||||
public string? MethodName { get; init; } = default!;
|
||||
}
|
||||
Loading…
Reference in a new issue