From 0729cb2762b074a0e5667241cb2ab3a8c327f6a3 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Wed, 27 Apr 2022 22:32:40 +0200 Subject: [PATCH] Type name imrovements --- .../Extensions/ActivityExtensions.cs | 7 ++- .../Elsa.Core/Extensions/TypeExtensions.cs | 22 ++++++++++ .../Helpers/ActivityTypeNameHelper.cs | 38 ++++++++++++++++ src/core/Elsa.Core/Helpers/TypeNameHelper.cs | 33 +------------- .../Implementations/IdentityGraphService.cs | 4 +- .../WorkflowStateSerializer.cs | 2 +- src/core/Elsa.Core/Models/Activity.cs | 2 +- .../Models/ExpressionExecutionContext.cs | 7 ++- src/core/Elsa.Core/Models/NamedOutput.cs | 3 ++ .../Models/WorkflowExecutionContext.cs | 6 ++- .../Implementations/ActivityDescriber.cs | 4 +- .../Activities/HttpEndpoint.cs | 2 +- .../Elsa.Modules.Http.csproj | 5 +-- .../Extensions/RouteTableExtensions.cs | 43 +++++++++++++++++++ .../Extensions/ServiceCollectionExtensions.cs | 9 +++- ...DefaultHttpEndpointWorkflowFaultHandler.cs | 1 + .../Handlers/UpdateRouteTable.cs | 33 ++++++++++++++ .../Middleware/HttpTriggerMiddleware.cs | 4 +- .../Scripting/ConfigureJavaScriptEngine.cs | 14 ++++++ .../Extensions/ServiceCollectionExtensions.cs | 1 + 20 files changed, 186 insertions(+), 54 deletions(-) create mode 100644 src/core/Elsa.Core/Extensions/TypeExtensions.cs create mode 100644 src/core/Elsa.Core/Helpers/ActivityTypeNameHelper.cs create mode 100644 src/core/Elsa.Core/Models/NamedOutput.cs create mode 100644 src/modules/Elsa.Modules.Http/Extensions/RouteTableExtensions.cs create mode 100644 src/modules/Elsa.Modules.Http/Handlers/UpdateRouteTable.cs create mode 100644 src/modules/Elsa.Modules.Http/Scripting/ConfigureJavaScriptEngine.cs diff --git a/src/core/Elsa.Core/Extensions/ActivityExtensions.cs b/src/core/Elsa.Core/Extensions/ActivityExtensions.cs index f6bc1cf5e..2c9f1619b 100644 --- a/src/core/Elsa.Core/Extensions/ActivityExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ActivityExtensions.cs @@ -30,16 +30,15 @@ public static class ActivityExtensions return (Input?)inputProp.GetValue(activity); } - public static IEnumerable GetOutputs(this IActivity activity) + public static IEnumerable GetOutputs(this IActivity activity) { var outputProps = activity.GetType().GetProperties().Where(x => typeof(Output).IsAssignableFrom(x.PropertyType)).ToList(); var query = from outputProp in outputProps - select (Output?)outputProp.GetValue(activity) - into output + let output = (Output?)outputProp.GetValue(activity) where output != null - select output; + select new NamedOutput(outputProp.Name, output); return query.Select(x => x!).ToList(); } diff --git a/src/core/Elsa.Core/Extensions/TypeExtensions.cs b/src/core/Elsa.Core/Extensions/TypeExtensions.cs new file mode 100644 index 000000000..3c2b80df0 --- /dev/null +++ b/src/core/Elsa.Core/Extensions/TypeExtensions.cs @@ -0,0 +1,22 @@ +using System.Collections.Concurrent; +using System.Reflection; + +namespace Elsa; + +public static class TypeExtensions +{ + private static readonly ConcurrentDictionary SimpleAssemblyQualifiedTypeNameCache = new(); + + /// + /// Gets the assembly-qualified name of the type, without any version info etc. + /// E.g. "System.String, System.Private.CoreLib" + /// + public static string GetSimpleAssemblyQualifiedName(this Type type) + { + if (type == null) throw new ArgumentNullException(nameof(type)); + + return SimpleAssemblyQualifiedTypeNameCache.GetOrAdd(type, GetSimpleAssemblyQualifiedNameInternal); + } + + private static string GetSimpleAssemblyQualifiedNameInternal(Type type) => $"{type.FullName}, {Assembly.GetAssembly(type)!.GetName().Name}"; +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Helpers/ActivityTypeNameHelper.cs b/src/core/Elsa.Core/Helpers/ActivityTypeNameHelper.cs new file mode 100644 index 000000000..cd0e5674a --- /dev/null +++ b/src/core/Elsa.Core/Helpers/ActivityTypeNameHelper.cs @@ -0,0 +1,38 @@ +using System.Reflection; +using Elsa.Attributes; + +namespace Elsa.Helpers; + +public static class ActivityTypeNameHelper +{ + public static string? GenerateNamespace(Type activityType) + { + var activityAttr = activityType.GetCustomAttribute(); + return activityAttr?.Namespace ?? activityType.Namespace; + } + + public static string GenerateTypeName(Type type, string? ns) + { + var activityAttr = type.GetCustomAttribute(); + var typeName = activityAttr?.TypeName ?? type.Name; + return ns != null ? $"{ns}.{typeName}" : typeName; + } + + public static string GenerateTypeName() => GenerateTypeName(typeof(T)); + + public static string GenerateTypeName(Type type) + { + var ns = GenerateNamespace(type); + return GenerateTypeName(type, ns); + } + + public static string? GetCategoryFromNamespace(string? ns) + { + if (string.IsNullOrWhiteSpace(ns)) + return null; + + var index = ns.LastIndexOf('.'); + + return index < 0 ? ns : ns[(index + 1)..]; + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Helpers/TypeNameHelper.cs b/src/core/Elsa.Core/Helpers/TypeNameHelper.cs index 184c08a5c..71042f506 100644 --- a/src/core/Elsa.Core/Helpers/TypeNameHelper.cs +++ b/src/core/Elsa.Core/Helpers/TypeNameHelper.cs @@ -1,38 +1,7 @@ -using System.Reflection; -using Elsa.Attributes; - namespace Elsa.Helpers; public static class TypeNameHelper { - public static string? GenerateNamespace(Type activityType) - { - var activityAttr = activityType.GetCustomAttribute(); - return activityAttr?.Namespace ?? activityType.Namespace; - } - - public static string GenerateTypeName(Type type, string? ns) - { - var activityAttr = type.GetCustomAttribute(); - var typeName = activityAttr?.TypeName ?? type.Name; - return ns != null ? $"{ns}.{typeName}" : typeName; - } - public static string GenerateTypeName() => GenerateTypeName(typeof(T)); - - public static string GenerateTypeName(Type type) - { - var ns = GenerateNamespace(type); - return GenerateTypeName(type, ns); - } - - public static string? GetCategoryFromNamespace(string? ns) - { - if (string.IsNullOrWhiteSpace(ns)) - return null; - - var index = ns.LastIndexOf('.'); - - return index < 0 ? ns : ns[(index + 1)..]; - } + public static string GenerateTypeName(Type type) => type.FullName!; } \ No newline at end of file diff --git a/src/core/Elsa.Core/Implementations/IdentityGraphService.cs b/src/core/Elsa.Core/Implementations/IdentityGraphService.cs index f41579ac3..827726fcc 100644 --- a/src/core/Elsa.Core/Implementations/IdentityGraphService.cs +++ b/src/core/Elsa.Core/Implementations/IdentityGraphService.cs @@ -47,11 +47,11 @@ public class IdentityGraphService : IIdentityGraphService seed = 0; var outputs = activity.GetOutputs(); - var assignedOutputs = outputs.Where(x => x.LocationReference != null! && x.LocationReference.Id == null!).ToList(); + var assignedOutputs = outputs.Where(x => x.Value.LocationReference != null! && x.Value.LocationReference.Id == null!).ToList(); foreach (var output in assignedOutputs) { - var locationReference = output.LocationReference; + var locationReference = output.Value.LocationReference; locationReference.Id = $"{activity.Id}:output-{++seed}"; } } diff --git a/src/core/Elsa.Core/Implementations/WorkflowStateSerializer.cs b/src/core/Elsa.Core/Implementations/WorkflowStateSerializer.cs index cf82ec092..283f9acf3 100644 --- a/src/core/Elsa.Core/Implementations/WorkflowStateSerializer.cs +++ b/src/core/Elsa.Core/Implementations/WorkflowStateSerializer.cs @@ -130,7 +130,7 @@ public class WorkflowStateSerializer : IWorkflowStateSerializer { var cancellationToken = workflowExecutionContext.CancellationToken; var activity = workflowExecutionContext.FindActivityById(activityExecutionContextState.ScheduledActivityId); - var register = new Register(activityExecutionContextState.Register.Locations); + var register = workflowExecutionContext.Register; var workflow = workflowExecutionContext.Workflow; var expressionInput = workflowExecutionContext.Input; var expressionExecutionContext = new ExpressionExecutionContext(_serviceProvider, register, workflow, expressionInput, workflowExecutionContext.TransientProperties, default, cancellationToken); diff --git a/src/core/Elsa.Core/Models/Activity.cs b/src/core/Elsa.Core/Models/Activity.cs index 0e4e3e9ad..2f32a4b9d 100644 --- a/src/core/Elsa.Core/Models/Activity.cs +++ b/src/core/Elsa.Core/Models/Activity.cs @@ -11,7 +11,7 @@ public abstract class Activity : IActivity, ISignalHandler protected Activity() { - TypeName = TypeNameHelper.GenerateTypeName(GetType()); + TypeName = ActivityTypeNameHelper.GenerateTypeName(GetType()); Behaviors.Add(); Behaviors.Add(); } diff --git a/src/core/Elsa.Core/Models/ExpressionExecutionContext.cs b/src/core/Elsa.Core/Models/ExpressionExecutionContext.cs index d22340e92..03f3cd62f 100644 --- a/src/core/Elsa.Core/Models/ExpressionExecutionContext.cs +++ b/src/core/Elsa.Core/Models/ExpressionExecutionContext.cs @@ -6,7 +6,8 @@ public class ExpressionExecutionContext { private readonly IServiceProvider _serviceProvider; - public ExpressionExecutionContext(IServiceProvider serviceProvider, + public ExpressionExecutionContext( + IServiceProvider serviceProvider, Register register, Workflow workflow, IDictionary input, @@ -23,7 +24,11 @@ public class ExpressionExecutionContext CancellationToken = cancellationToken; } + /// + /// A register of computer memory. + /// public Register Register { get; } + public Workflow Workflow { get; } public IDictionary Input { get; } public IDictionary TransientProperties { get; } diff --git a/src/core/Elsa.Core/Models/NamedOutput.cs b/src/core/Elsa.Core/Models/NamedOutput.cs new file mode 100644 index 000000000..77981565d --- /dev/null +++ b/src/core/Elsa.Core/Models/NamedOutput.cs @@ -0,0 +1,3 @@ +namespace Elsa.Models; + +public record NamedOutput(string Name, Output Value); \ No newline at end of file diff --git a/src/core/Elsa.Core/Models/WorkflowExecutionContext.cs b/src/core/Elsa.Core/Models/WorkflowExecutionContext.cs index d26d4d0db..b07efbdfb 100644 --- a/src/core/Elsa.Core/Models/WorkflowExecutionContext.cs +++ b/src/core/Elsa.Core/Models/WorkflowExecutionContext.cs @@ -129,7 +129,7 @@ public class WorkflowExecutionContext foreach (var bookmark in bookmarks) _bookmarks.Remove(bookmark); } - + /// /// Clears all bookmarks from all . /// @@ -142,7 +142,7 @@ public class WorkflowExecutionContext public void TransitionTo(WorkflowSubStatus subStatus) { var targetStatus = GetMainStatus(subStatus); - + if (!ValidateStatusTransition(SubStatus, subStatus)) throw new Exception($"Cannot transition from {Status} to {targetStatus}"); @@ -165,4 +165,6 @@ public class WorkflowExecutionContext var currentMainStatus = GetMainStatus(currentSubStatus); return currentMainStatus != WorkflowStatus.Finished; } + + private IEnumerable GetMergedRegistersView() => new[] { Register }.Concat(ActivityExecutionContexts.Select(x => x.ExpressionExecutionContext.Register)).ToList(); } \ No newline at end of file diff --git a/src/core/Elsa.Management/Implementations/ActivityDescriber.cs b/src/core/Elsa.Management/Implementations/ActivityDescriber.cs index cd4593a03..65120558b 100644 --- a/src/core/Elsa.Management/Implementations/ActivityDescriber.cs +++ b/src/core/Elsa.Management/Implementations/ActivityDescriber.cs @@ -28,13 +28,13 @@ public class ActivityDescriber : IActivityDescriber public ValueTask DescribeActivityAsync(Type activityType, CancellationToken cancellationToken = default) { var activityAttr = activityType.GetCustomAttribute(); - var ns = activityAttr?.Namespace ?? TypeNameHelper.GenerateNamespace(activityType); + var ns = activityAttr?.Namespace ?? ActivityTypeNameHelper.GenerateNamespace(activityType); var typeName = activityAttr?.TypeName ?? activityType.Name; var fullTypeName = $"{ns}.{typeName}"; var displayNameAttr = activityType.GetCustomAttribute(); var displayName = displayNameAttr?.DisplayName ?? activityAttr?.DisplayName ?? typeName.Humanize(LetterCasing.Title); var categoryAttr = activityType.GetCustomAttribute(); - var category = categoryAttr?.Category ?? activityAttr?.Category ?? TypeNameHelper.GetCategoryFromNamespace(ns) ?? "Miscellaneous"; + var category = categoryAttr?.Category ?? activityAttr?.Category ?? ActivityTypeNameHelper.GetCategoryFromNamespace(ns) ?? "Miscellaneous"; var descriptionAttr = activityType.GetCustomAttribute(); var description = descriptionAttr?.Description ?? activityAttr?.Description; diff --git a/src/modules/Elsa.Modules.Http/Activities/HttpEndpoint.cs b/src/modules/Elsa.Modules.Http/Activities/HttpEndpoint.cs index d861d03b8..3edb9bfb1 100644 --- a/src/modules/Elsa.Modules.Http/Activities/HttpEndpoint.cs +++ b/src/modules/Elsa.Modules.Http/Activities/HttpEndpoint.cs @@ -64,6 +64,6 @@ public class HttpEndpoint : Trigger // Generate bookmark data for path and selected methods. var path = context.Get(Path); var methods = context.Get(SupportedMethods); - return methods!.Select(x => new HttpBookmarkData(path!, x.ToLowerInvariant())).Cast().ToArray(); + return methods!.Select(x => new HttpEndpointBookmarkData(path!, x.ToLowerInvariant())).Cast().ToArray(); } } \ No newline at end of file diff --git a/src/modules/Elsa.Modules.Http/Elsa.Modules.Http.csproj b/src/modules/Elsa.Modules.Http/Elsa.Modules.Http.csproj index a53b476b7..55c66a872 100644 --- a/src/modules/Elsa.Modules.Http/Elsa.Modules.Http.csproj +++ b/src/modules/Elsa.Modules.Http/Elsa.Modules.Http.csproj @@ -9,6 +9,7 @@ + @@ -20,8 +21,4 @@ - - - - diff --git a/src/modules/Elsa.Modules.Http/Extensions/RouteTableExtensions.cs b/src/modules/Elsa.Modules.Http/Extensions/RouteTableExtensions.cs new file mode 100644 index 000000000..7bb4265bf --- /dev/null +++ b/src/modules/Elsa.Modules.Http/Extensions/RouteTableExtensions.cs @@ -0,0 +1,43 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text.Json; +using Elsa.Helpers; +using Elsa.Modules.Http.Models; +using Elsa.Modules.Http.Services; +using Elsa.Persistence.Entities; + +namespace Elsa.Modules.Http.Extensions; + +public static class RouteTableExtensions +{ + + public static void AddRoutes(this IRouteTable routeTable, IEnumerable triggers) + { + var paths = Filter(triggers).Select(Deserialize).Select(x => x.Path).ToList(); + routeTable.AddRange(paths); + } + + public static void AddRoutes(this IRouteTable routeTable, IEnumerable bookmarks) + { + var paths = Filter(bookmarks).Select(Deserialize).Select(x => x.Path).ToList(); + routeTable.AddRange(paths); + } + + public static void RemoveRoutes(this IRouteTable routeTable, IEnumerable triggers) + { + var paths = Filter(triggers).Select(Deserialize).Select(x => x.Path).ToList(); + routeTable.RemoveRange(paths); + } + + public static void RemoveRoutes(this IRouteTable routeTable, IEnumerable bookmarks) + { + var paths = Filter(bookmarks).Select(Deserialize).Select(x => x.Path).ToList(); + routeTable.RemoveRange(paths); + } + + private static IEnumerable Filter(IEnumerable triggers) => triggers.Where(x => x.Name == ActivityTypeNameHelper.GenerateTypeName()); + private static IEnumerable Filter(IEnumerable triggers) => triggers.Where(x => x.Name == ActivityTypeNameHelper.GenerateTypeName()); + private static HttpEndpointBookmarkData Deserialize(WorkflowTrigger trigger) => Deserialize(trigger.Data!); + private static HttpEndpointBookmarkData Deserialize(WorkflowBookmark bookmark) => Deserialize(bookmark.Data!); + private static HttpEndpointBookmarkData Deserialize(string model) => JsonSerializer.Deserialize(model)!; +} \ No newline at end of file diff --git a/src/modules/Elsa.Modules.Http/Extensions/ServiceCollectionExtensions.cs b/src/modules/Elsa.Modules.Http/Extensions/ServiceCollectionExtensions.cs index 868e936a0..16d532cf2 100644 --- a/src/modules/Elsa.Modules.Http/Extensions/ServiceCollectionExtensions.cs +++ b/src/modules/Elsa.Modules.Http/Extensions/ServiceCollectionExtensions.cs @@ -1,7 +1,9 @@ - +using Elsa.Mediator.Extensions; +using Elsa.Modules.Http.Handlers; +using Elsa.Modules.Http.Implementations; +using Elsa.Modules.Http.Services; // ReSharper disable once CheckNamespace - namespace Microsoft.Extensions.DependencyInjection; public static class ServiceCollectionExtensions @@ -9,6 +11,9 @@ public static class ServiceCollectionExtensions public static IServiceCollection AddHttpActivityServices(this IServiceCollection services) { return services + .AddSingleton() + .AddSingleton() + .AddNotificationHandlersFrom() .AddHttpContextAccessor(); } } \ No newline at end of file diff --git a/src/modules/Elsa.Modules.Http/Handlers/DefaultHttpEndpointWorkflowFaultHandler.cs b/src/modules/Elsa.Modules.Http/Handlers/DefaultHttpEndpointWorkflowFaultHandler.cs index 0842052c6..4c65a2bb4 100644 --- a/src/modules/Elsa.Modules.Http/Handlers/DefaultHttpEndpointWorkflowFaultHandler.cs +++ b/src/modules/Elsa.Modules.Http/Handlers/DefaultHttpEndpointWorkflowFaultHandler.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Http; namespace Elsa.Modules.Http.Handlers; +// ReSharper disable once ClassWithVirtualMembersNeverInherited.Global public class DefaultHttpEndpointWorkflowFaultHandler : IHttpEndpointWorkflowFaultHandler { public virtual async ValueTask HandleAsync(HttpEndpointFaultedWorkflowContext context) diff --git a/src/modules/Elsa.Modules.Http/Handlers/UpdateRouteTable.cs b/src/modules/Elsa.Modules.Http/Handlers/UpdateRouteTable.cs new file mode 100644 index 000000000..1274524b2 --- /dev/null +++ b/src/modules/Elsa.Modules.Http/Handlers/UpdateRouteTable.cs @@ -0,0 +1,33 @@ +using System.Threading; +using System.Threading.Tasks; +using Elsa.Mediator.Services; +using Elsa.Modules.Http.Extensions; +using Elsa.Modules.Http.Services; +using Elsa.Runtime.Models; +using Elsa.Runtime.Notifications; + +namespace Elsa.Modules.Http.Handlers +{ + public class UpdateRouteTable : + INotificationHandler, + INotificationHandler + { + private readonly IRouteTable _routeTable; + public UpdateRouteTable(IRouteTable routeTable) => _routeTable = routeTable; + + public Task HandleAsync(WorkflowTriggersIndexed notification, CancellationToken cancellationToken) + { + _routeTable.RemoveRoutes(notification.IndexedWorkflowTriggers.RemovedTriggers); + _routeTable.AddRoutes(notification.IndexedWorkflowTriggers.AddedTriggers); + _routeTable.AddRoutes(notification.IndexedWorkflowTriggers.UnchangedTriggers); + return Task.CompletedTask; + } + + public Task HandleAsync(WorkflowBookmarksIndexed notification, CancellationToken cancellationToken) + { + _routeTable.RemoveRoutes(notification.IndexedWorkflowBookmarks.RemovedBookmarks); + _routeTable.AddRoutes(notification.IndexedWorkflowBookmarks.AddedBookmarks); + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Modules.Http/Middleware/HttpTriggerMiddleware.cs b/src/modules/Elsa.Modules.Http/Middleware/HttpTriggerMiddleware.cs index d7f7f76d0..a68e09f1d 100644 --- a/src/modules/Elsa.Modules.Http/Middleware/HttpTriggerMiddleware.cs +++ b/src/modules/Elsa.Modules.Http/Middleware/HttpTriggerMiddleware.cs @@ -35,8 +35,8 @@ public class HttpTriggerMiddleware var request = httpContext.Request; var method = request.Method!.ToLowerInvariant(); var abortToken = httpContext.RequestAborted; - var hash = _hasher.Hash(new HttpBookmarkData(path, method)); - var activityTypeName = TypeNameHelper.GenerateTypeName(); + var hash = _hasher.Hash(new HttpEndpointBookmarkData(path, method)); + var activityTypeName = ActivityTypeNameHelper.GenerateTypeName(); var routeData = GetRouteData(httpContext, routeMatcher, path); var requestModel = new HttpRequestModel( diff --git a/src/modules/Elsa.Modules.Http/Scripting/ConfigureJavaScriptEngine.cs b/src/modules/Elsa.Modules.Http/Scripting/ConfigureJavaScriptEngine.cs new file mode 100644 index 000000000..e46604ba9 --- /dev/null +++ b/src/modules/Elsa.Modules.Http/Scripting/ConfigureJavaScriptEngine.cs @@ -0,0 +1,14 @@ +using System.Threading; +using System.Threading.Tasks; +using Elsa.Mediator.Services; +using Elsa.Scripting.JavaScript.Notifications; + +namespace Elsa.Modules.Http.Scripting; + +public class ConfigureJavaScriptEngine : INotificationHandler +{ + public Task HandleAsync(EvaluatingJavaScript notification, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/runtime/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs b/src/runtime/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs index 4b8477c98..d72ae439a 100644 --- a/src/runtime/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs +++ b/src/runtime/Elsa.Runtime/Extensions/ServiceCollectionExtensions.cs @@ -75,6 +75,7 @@ public static class ServiceCollectionExtensions .AddExpressionHandler() .AddExpressionHandler() .AddExpressionHandler() + .AddExpressionHandler() .AddExpressionHandler() .AddExpressionHandler();