Type name imrovements

This commit is contained in:
Sipke Schoorstra 2022-04-27 22:32:40 +02:00
parent 556ab9c26c
commit 0729cb2762
20 changed files with 186 additions and 54 deletions

View file

@ -30,16 +30,15 @@ public static class ActivityExtensions
return (Input?)inputProp.GetValue(activity);
}
public static IEnumerable<Output> GetOutputs(this IActivity activity)
public static IEnumerable<NamedOutput> 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();
}

View file

@ -0,0 +1,22 @@
using System.Collections.Concurrent;
using System.Reflection;
namespace Elsa;
public static class TypeExtensions
{
private static readonly ConcurrentDictionary<Type, string> SimpleAssemblyQualifiedTypeNameCache = new();
/// <summary>
/// Gets the assembly-qualified name of the type, without any version info etc.
/// E.g. "System.String, System.Private.CoreLib"
/// </summary>
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}";
}

View file

@ -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<ActivityAttribute>();
return activityAttr?.Namespace ?? activityType.Namespace;
}
public static string GenerateTypeName(Type type, string? ns)
{
var activityAttr = type.GetCustomAttribute<ActivityAttribute>();
var typeName = activityAttr?.TypeName ?? type.Name;
return ns != null ? $"{ns}.{typeName}" : typeName;
}
public static string GenerateTypeName<T>() => 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)..];
}
}

View file

@ -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<ActivityAttribute>();
return activityAttr?.Namespace ?? activityType.Namespace;
}
public static string GenerateTypeName(Type type, string? ns)
{
var activityAttr = type.GetCustomAttribute<ActivityAttribute>();
var typeName = activityAttr?.TypeName ?? type.Name;
return ns != null ? $"{ns}.{typeName}" : typeName;
}
public static string GenerateTypeName<T>() => 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!;
}

View file

@ -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}";
}
}

View file

@ -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);

View file

@ -11,7 +11,7 @@ public abstract class Activity : IActivity, ISignalHandler
protected Activity()
{
TypeName = TypeNameHelper.GenerateTypeName(GetType());
TypeName = ActivityTypeNameHelper.GenerateTypeName(GetType());
Behaviors.Add<ScheduledChildCallbackBehavior>();
Behaviors.Add<AutoCompleteBehavior>();
}

View file

@ -6,7 +6,8 @@ public class ExpressionExecutionContext
{
private readonly IServiceProvider _serviceProvider;
public ExpressionExecutionContext(IServiceProvider serviceProvider,
public ExpressionExecutionContext(
IServiceProvider serviceProvider,
Register register,
Workflow workflow,
IDictionary<string, object> input,
@ -23,7 +24,11 @@ public class ExpressionExecutionContext
CancellationToken = cancellationToken;
}
/// <summary>
/// A register of computer memory.
/// </summary>
public Register Register { get; }
public Workflow Workflow { get; }
public IDictionary<string, object> Input { get; }
public IDictionary<object, object?> TransientProperties { get; }

View file

@ -0,0 +1,3 @@
namespace Elsa.Models;
public record NamedOutput(string Name, Output Value);

View file

@ -129,7 +129,7 @@ public class WorkflowExecutionContext
foreach (var bookmark in bookmarks)
_bookmarks.Remove(bookmark);
}
/// <summary>
/// Clears all bookmarks from all <see cref="ActivityExecutionContexts"/>.
/// </summary>
@ -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<Register> GetMergedRegistersView() => new[] { Register }.Concat(ActivityExecutionContexts.Select(x => x.ExpressionExecutionContext.Register)).ToList();
}

View file

@ -28,13 +28,13 @@ public class ActivityDescriber : IActivityDescriber
public ValueTask<ActivityDescriptor> DescribeActivityAsync(Type activityType, CancellationToken cancellationToken = default)
{
var activityAttr = activityType.GetCustomAttribute<ActivityAttribute>();
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<DisplayNameAttribute>();
var displayName = displayNameAttr?.DisplayName ?? activityAttr?.DisplayName ?? typeName.Humanize(LetterCasing.Title);
var categoryAttr = activityType.GetCustomAttribute<CategoryAttribute>();
var category = categoryAttr?.Category ?? activityAttr?.Category ?? TypeNameHelper.GetCategoryFromNamespace(ns) ?? "Miscellaneous";
var category = categoryAttr?.Category ?? activityAttr?.Category ?? ActivityTypeNameHelper.GetCategoryFromNamespace(ns) ?? "Miscellaneous";
var descriptionAttr = activityType.GetCustomAttribute<DescriptionAttribute>();
var description = descriptionAttr?.Description ?? activityAttr?.Description;

View file

@ -64,6 +64,6 @@ public class HttpEndpoint : Trigger<HttpRequestModel>
// 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<object>().ToArray();
return methods!.Select(x => new HttpEndpointBookmarkData(path!, x.ToLowerInvariant())).Cast<object>().ToArray();
}
}

View file

@ -9,6 +9,7 @@
<ItemGroup>
<ProjectReference Include="..\..\core\Elsa.Core\Elsa.Core.csproj" />
<ProjectReference Include="..\..\runtime\Elsa.Runtime\Elsa.Runtime.csproj" />
<ProjectReference Include="..\..\scripting\Elsa.Scripting.JavaScript\Elsa.Scripting.JavaScript.csproj" />
</ItemGroup>
<ItemGroup>
@ -20,8 +21,4 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Scripting" />
</ItemGroup>
</Project>

View file

@ -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<WorkflowTrigger> triggers)
{
var paths = Filter(triggers).Select(Deserialize).Select(x => x.Path).ToList();
routeTable.AddRange(paths);
}
public static void AddRoutes(this IRouteTable routeTable, IEnumerable<WorkflowBookmark> bookmarks)
{
var paths = Filter(bookmarks).Select(Deserialize).Select(x => x.Path).ToList();
routeTable.AddRange(paths);
}
public static void RemoveRoutes(this IRouteTable routeTable, IEnumerable<WorkflowTrigger> triggers)
{
var paths = Filter(triggers).Select(Deserialize).Select(x => x.Path).ToList();
routeTable.RemoveRange(paths);
}
public static void RemoveRoutes(this IRouteTable routeTable, IEnumerable<WorkflowBookmark> bookmarks)
{
var paths = Filter(bookmarks).Select(Deserialize).Select(x => x.Path).ToList();
routeTable.RemoveRange(paths);
}
private static IEnumerable<WorkflowTrigger> Filter(IEnumerable<WorkflowTrigger> triggers) => triggers.Where(x => x.Name == ActivityTypeNameHelper.GenerateTypeName<HttpEndpoint>());
private static IEnumerable<WorkflowBookmark> Filter(IEnumerable<WorkflowBookmark> triggers) => triggers.Where(x => x.Name == ActivityTypeNameHelper.GenerateTypeName<HttpEndpoint>());
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<HttpEndpointBookmarkData>(model)!;
}

View file

@ -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<IRouteMatcher, RouteMatcher>()
.AddSingleton<IRouteTable, RouteTable>()
.AddNotificationHandlersFrom<UpdateRouteTable>()
.AddHttpContextAccessor();
}
}

View file

@ -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)

View file

@ -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<WorkflowTriggersIndexed>,
INotificationHandler<WorkflowBookmarksIndexed>
{
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;
}
}
}

View file

@ -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<HttpEndpoint>();
var hash = _hasher.Hash(new HttpEndpointBookmarkData(path, method));
var activityTypeName = ActivityTypeNameHelper.GenerateTypeName<HttpEndpoint>();
var routeData = GetRouteData(httpContext, routeMatcher, path);
var requestModel = new HttpRequestModel(

View file

@ -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<EvaluatingJavaScript>
{
public Task HandleAsync(EvaluatingJavaScript notification, CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}

View file

@ -75,6 +75,7 @@ public static class ServiceCollectionExtensions
.AddExpressionHandler<LiteralExpressionHandler, LiteralExpression>()
.AddExpressionHandler<DelegateExpressionHandler, DelegateExpression>()
.AddExpressionHandler<VariableExpressionHandler, VariableExpression>()
.AddExpressionHandler<JsonExpressionHandler, JsonExpression>()
.AddExpressionHandler<OutputExpressionHandler, OutputExpression>()
.AddExpressionHandler<ElsaExpressionHandler, ElsaExpression>();