2021-03-04 15:11:18 +00:00
|
|
|
using System;
|
2021-05-29 15:11:00 +00:00
|
|
|
using System.Collections.Generic;
|
2021-03-04 15:11:18 +00:00
|
|
|
using System.Globalization;
|
|
|
|
|
using System.Linq;
|
2021-05-29 15:11:00 +00:00
|
|
|
using System.Reflection;
|
|
|
|
|
using System.Text;
|
2021-03-04 15:11:18 +00:00
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
2021-05-29 15:11:00 +00:00
|
|
|
using AutoMapper.Internal;
|
|
|
|
|
using Elsa.Attributes;
|
|
|
|
|
using Elsa.Scripting.JavaScript.Events;
|
|
|
|
|
using Elsa.Scripting.JavaScript.Extensions;
|
2021-03-04 15:11:18 +00:00
|
|
|
using Elsa.Scripting.JavaScript.Messages;
|
2021-04-26 10:32:16 +00:00
|
|
|
using Elsa.Services;
|
|
|
|
|
using Elsa.Services.Models;
|
2021-03-04 15:11:18 +00:00
|
|
|
using Jint;
|
|
|
|
|
using Jint.Runtime.Interop;
|
|
|
|
|
using MediatR;
|
2021-04-13 11:17:24 +00:00
|
|
|
using Microsoft.Extensions.Configuration;
|
2021-03-04 15:11:18 +00:00
|
|
|
using NodaTime;
|
|
|
|
|
|
|
|
|
|
namespace Elsa.Scripting.JavaScript.Handlers
|
|
|
|
|
{
|
2021-05-29 15:11:00 +00:00
|
|
|
public class ConfigureJavaScriptEngine : INotificationHandler<EvaluatingJavaScriptExpression>, INotificationHandler<RenderingTypeScriptDefinitions>
|
2021-03-04 15:11:18 +00:00
|
|
|
{
|
2021-04-13 11:17:24 +00:00
|
|
|
private readonly IConfiguration _configuration;
|
2021-05-29 15:11:00 +00:00
|
|
|
private readonly IActivityTypeService _activityTypeService;
|
2021-04-13 11:17:24 +00:00
|
|
|
|
2021-05-29 15:11:00 +00:00
|
|
|
public ConfigureJavaScriptEngine(IConfiguration configuration, IActivityTypeService activityTypeService)
|
2021-04-13 11:17:24 +00:00
|
|
|
{
|
|
|
|
|
_configuration = configuration;
|
2021-05-29 15:11:00 +00:00
|
|
|
_activityTypeService = activityTypeService;
|
2021-04-13 11:17:24 +00:00
|
|
|
}
|
2021-05-29 15:11:00 +00:00
|
|
|
|
2021-03-04 15:11:18 +00:00
|
|
|
public Task Handle(EvaluatingJavaScriptExpression notification, CancellationToken cancellationToken)
|
|
|
|
|
{
|
2021-05-13 10:15:31 +00:00
|
|
|
var activityExecutionContext = notification.ActivityExecutionContext;
|
|
|
|
|
var workflowExecutionContext = activityExecutionContext.WorkflowExecutionContext;
|
2021-03-04 15:11:18 +00:00
|
|
|
var workflowBlueprint = workflowExecutionContext.WorkflowBlueprint;
|
|
|
|
|
var workflowInstance = workflowExecutionContext.WorkflowInstance;
|
|
|
|
|
var engine = notification.Engine;
|
|
|
|
|
|
|
|
|
|
// Global functions.
|
|
|
|
|
engine.SetValue("guid", (Func<string>) (() => Guid.NewGuid().ToString()));
|
2021-05-25 11:24:01 +00:00
|
|
|
engine.SetValue("parseGuid", (Func<string, Guid>) (Guid.Parse));
|
2021-05-13 10:15:31 +00:00
|
|
|
engine.SetValue("setVariable", (Action<string, object>) ((name, value) => activityExecutionContext.SetVariable(name, value)));
|
|
|
|
|
engine.SetValue("getVariable", (Func<string, object?>) (name => activityExecutionContext.GetVariable(name)));
|
2021-04-13 11:17:24 +00:00
|
|
|
engine.SetValue("getConfig", (Func<string, object?>) (name => _configuration.GetSection(name).Value));
|
2021-04-15 14:08:17 +00:00
|
|
|
engine.SetValue("isNullOrWhiteSpace", (Func<string, bool>) (string.IsNullOrWhiteSpace));
|
|
|
|
|
engine.SetValue("isNullOrEmpty", (Func<string, bool>) (string.IsNullOrEmpty));
|
2021-05-13 10:15:31 +00:00
|
|
|
engine.SetValue("getWorkflowDefinitionIdByName", (Func<string, string?>) (name => GetWorkflowDefinitionIdByName(activityExecutionContext, name)));
|
|
|
|
|
engine.SetValue("getWorkflowDefinitionIdByTag", (Func<string, string?>) (tag => GetWorkflowDefinitionIdByTag(activityExecutionContext, tag)));
|
2021-05-29 12:16:23 +00:00
|
|
|
engine.SetValue("getActivity", (Func<string, object?>) (idOrName => GetActivityModel(activityExecutionContext, idOrName)));
|
|
|
|
|
engine.SetValue("getInboundActivity", (Func<object?>) (() => GetInboundActivityModel(activityExecutionContext)));
|
2021-03-11 21:50:18 +00:00
|
|
|
|
2021-03-04 15:11:18 +00:00
|
|
|
// Global variables.
|
2021-05-13 10:15:31 +00:00
|
|
|
engine.SetValue("activityExecutionContext", activityExecutionContext);
|
|
|
|
|
engine.SetValue("workflowExecutionContext", workflowExecutionContext);
|
|
|
|
|
engine.SetValue("workflowInstance", workflowInstance);
|
|
|
|
|
engine.SetValue("input", activityExecutionContext.Input);
|
|
|
|
|
engine.SetValue("workflowInstanceId", workflowInstance.Id);
|
|
|
|
|
engine.SetValue("workflowDefinitionId", workflowInstance.DefinitionId);
|
|
|
|
|
engine.SetValue("workflowDefinitionVersion", workflowInstance.Version);
|
2021-03-04 15:11:18 +00:00
|
|
|
engine.SetValue("correlationId", workflowInstance.CorrelationId);
|
|
|
|
|
engine.SetValue("currentCulture", CultureInfo.InvariantCulture);
|
2021-05-13 10:15:31 +00:00
|
|
|
engine.SetValue("workflowContext", activityExecutionContext.GetWorkflowContext());
|
2021-03-11 21:50:18 +00:00
|
|
|
|
2021-05-29 15:11:00 +00:00
|
|
|
// Types.
|
2021-03-04 15:11:18 +00:00
|
|
|
RegisterType<Instant>(engine);
|
|
|
|
|
RegisterType<Duration>(engine);
|
|
|
|
|
RegisterType<Period>(engine);
|
|
|
|
|
RegisterType<LocalDate>(engine);
|
|
|
|
|
RegisterType<LocalTime>(engine);
|
|
|
|
|
RegisterType<LocalDateTime>(engine);
|
2021-05-29 15:11:00 +00:00
|
|
|
RegisterType<Guid>(engine);
|
|
|
|
|
RegisterType<WorkflowExecutionContext>(engine);
|
|
|
|
|
RegisterType<ActivityExecutionContext>(engine);
|
2021-03-04 15:11:18 +00:00
|
|
|
|
2021-05-29 15:11:00 +00:00
|
|
|
// Workflow variables.
|
2021-04-04 13:19:54 +00:00
|
|
|
var variables = workflowExecutionContext.GetMergedVariables();
|
2021-03-04 15:11:18 +00:00
|
|
|
|
|
|
|
|
foreach (var variable in variables.Data)
|
|
|
|
|
engine.SetValue(variable.Key, variable.Value);
|
|
|
|
|
|
2021-05-29 12:16:23 +00:00
|
|
|
// TODO: Deprecated. Remove with next breaking version.
|
2021-05-29 15:11:00 +00:00
|
|
|
// Activity outputs.
|
2021-03-11 21:50:18 +00:00
|
|
|
foreach (var activity in workflowBlueprint.Activities.Where(x => x.Name is not null and not "" && workflowInstance.ActivityOutput.ContainsKey(x.Id)))
|
|
|
|
|
{
|
|
|
|
|
var output = new { Output = workflowInstance.ActivityOutput[activity.Id!] };
|
|
|
|
|
engine.SetValue(activity.Name, output);
|
|
|
|
|
}
|
2021-03-04 15:11:18 +00:00
|
|
|
|
2021-05-29 15:11:00 +00:00
|
|
|
// Named activities.
|
|
|
|
|
foreach (var activity in workflowBlueprint.Activities.Where(x => x.Name is not null))
|
|
|
|
|
{
|
|
|
|
|
var state = activityExecutionContext.GetActivityData(activity.Id);
|
|
|
|
|
var dictionary = state.ToDictionary();
|
|
|
|
|
|
|
|
|
|
engine.SetValue(activity.Name, dictionary);
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-04 15:11:18 +00:00
|
|
|
return Task.CompletedTask;
|
|
|
|
|
}
|
2021-03-11 21:50:18 +00:00
|
|
|
|
2021-05-29 15:11:00 +00:00
|
|
|
public async Task Handle(RenderingTypeScriptDefinitions notification, CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
var output = notification.Output;
|
|
|
|
|
|
|
|
|
|
output.AppendLine("declare function guid(): string");
|
|
|
|
|
output.AppendLine("declare function parseGuid(text: string): Guid");
|
|
|
|
|
output.AppendLine("declare function setVariable(name: string, value?: any): void;");
|
|
|
|
|
output.AppendLine("declare function getVariable(name: string): any;");
|
|
|
|
|
output.AppendLine("declare function getConfig(section: string): any;");
|
|
|
|
|
output.AppendLine("declare function isNullOrWhiteSpace(text: string): boolean;");
|
|
|
|
|
output.AppendLine("declare function isNullOrEmpty(text: string): boolean;");
|
|
|
|
|
output.AppendLine("declare function getWorkflowDefinitionIdByName(name: string): string;");
|
|
|
|
|
output.AppendLine("declare function getWorkflowDefinitionIdByTag(tag: string): string;");
|
|
|
|
|
output.AppendLine("declare function getActivity(idOrName: string): any;");
|
|
|
|
|
output.AppendLine("declare function getInboundActivity(): any;");
|
|
|
|
|
|
|
|
|
|
output.AppendLine("declare const activityExecutionContext: ActivityExecutionContext;");
|
|
|
|
|
output.AppendLine("declare const workflowExecutionContext: WorkflowExecutionContext;");
|
|
|
|
|
output.AppendLine("declare const workflowInstance: WorkflowInstance;");
|
|
|
|
|
output.AppendLine("declare const workflowInstanceId: string;");
|
|
|
|
|
output.AppendLine("declare const workflowDefinitionId: string;");
|
|
|
|
|
output.AppendLine("declare const workflowDefinitionVersion: number;");
|
|
|
|
|
output.AppendLine("declare const correlationId: string;");
|
|
|
|
|
output.AppendLine("declare const currentCulture: CultureInfo;");
|
|
|
|
|
|
|
|
|
|
var workflowDefinition = notification.WorkflowDefinition;
|
|
|
|
|
|
|
|
|
|
if (workflowDefinition != null)
|
|
|
|
|
{
|
|
|
|
|
// Workflow Context
|
|
|
|
|
var contextType = workflowDefinition.ContextOptions?.ContextType;
|
|
|
|
|
|
2021-05-30 10:51:42 +00:00
|
|
|
if (contextType != null)
|
|
|
|
|
{
|
2021-05-29 15:11:00 +00:00
|
|
|
|
2021-05-30 10:51:42 +00:00
|
|
|
var workflowContextTypeScriptType = notification.GetTypeScriptType(contextType);
|
|
|
|
|
output.AppendLine($"declare const workflowContext: {workflowContextTypeScriptType}");
|
|
|
|
|
}
|
2021-05-29 15:11:00 +00:00
|
|
|
|
|
|
|
|
// Workflow Variables.
|
|
|
|
|
foreach (var variable in workflowDefinition.Variables!.Data)
|
|
|
|
|
{
|
|
|
|
|
var variableType = variable.Value?.GetType() ?? typeof(object);
|
|
|
|
|
var typeScriptType = notification.GetTypeScriptType(variableType);
|
|
|
|
|
output.AppendLine($"declare const {variable.Key}: {typeScriptType}");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Named Activities.
|
|
|
|
|
var namedActivities = workflowDefinition.Activities.Where(x => x.Name is not null).ToList();
|
|
|
|
|
var activityTypeNames = namedActivities.Select(x => x.Type).Distinct().ToList();
|
|
|
|
|
var activityTypes = await Task.WhenAll(activityTypeNames.Select(async activityTypeName => (activityTypeName, await _activityTypeService.GetActivityTypeAsync(activityTypeName, cancellationToken))));
|
|
|
|
|
var activityTypeDictionary = activityTypes.ToDictionary(x => x.activityTypeName, x => x.Item2);
|
|
|
|
|
|
|
|
|
|
foreach (var activityType in activityTypeDictionary.Values)
|
|
|
|
|
RenderActivityTypeDeclaration(activityType, output);
|
|
|
|
|
|
|
|
|
|
foreach (var activity in namedActivities)
|
|
|
|
|
{
|
|
|
|
|
var activityType = activityTypeDictionary[activity.Type];
|
|
|
|
|
var typeScriptType = activityType.TypeName;
|
|
|
|
|
output.AppendLine($"declare const {activity.Name}: {typeScriptType}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RenderActivityTypeDeclaration(ActivityType type, StringBuilder writer)
|
|
|
|
|
{
|
|
|
|
|
var typeName = type.TypeName;
|
|
|
|
|
var descriptor = type.Describe();
|
|
|
|
|
var inputProperties = descriptor.InputProperties;
|
|
|
|
|
var outputProperties = descriptor.OutputProperties;
|
|
|
|
|
|
|
|
|
|
writer.AppendLine($"declare interface {typeName} {{");
|
|
|
|
|
|
|
|
|
|
foreach (var property in inputProperties)
|
|
|
|
|
{
|
|
|
|
|
var typeScriptType = notification.GetTypeScriptType(property.Type);
|
|
|
|
|
var propertyName = property.Name;
|
|
|
|
|
writer.AppendLine($"{propertyName}: {typeScriptType};");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var property in outputProperties)
|
|
|
|
|
{
|
|
|
|
|
var typeScriptType = notification.GetTypeScriptType(property.Type);
|
|
|
|
|
var propertyName = property.Name;
|
|
|
|
|
writer.AppendLine($"{propertyName}: {typeScriptType};");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.AppendLine("}");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-26 10:32:16 +00:00
|
|
|
private string? GetWorkflowDefinitionIdByTag(ActivityExecutionContext activityExecutionContext, string tag) => GetWorkflowDefinitionId(activityExecutionContext, x => string.Equals(x.Tag, tag, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
private string? GetWorkflowDefinitionIdByName(ActivityExecutionContext activityExecutionContext, string name) => GetWorkflowDefinitionId(activityExecutionContext, x => string.Equals(x.Name, name, StringComparison.OrdinalIgnoreCase));
|
2021-05-29 15:11:00 +00:00
|
|
|
|
2021-04-26 10:32:16 +00:00
|
|
|
private string? GetWorkflowDefinitionId(ActivityExecutionContext activityExecutionContext, Func<IWorkflowBlueprint, bool> filter)
|
|
|
|
|
{
|
|
|
|
|
var workflowRegistry = activityExecutionContext.GetService<IWorkflowRegistry>();
|
|
|
|
|
var workflowBlueprint = workflowRegistry.FindAsync(filter).Result;
|
|
|
|
|
return workflowBlueprint?.Id;
|
|
|
|
|
}
|
2021-05-29 15:11:00 +00:00
|
|
|
|
2021-05-29 12:16:23 +00:00
|
|
|
private object? GetActivityModel(ActivityExecutionContext context, string idOrName)
|
|
|
|
|
{
|
|
|
|
|
var workflowExecutionContext = context.WorkflowExecutionContext;
|
|
|
|
|
var activity = workflowExecutionContext.GetActivityBlueprintByName(idOrName) ?? workflowExecutionContext.GetActivityBlueprintById(idOrName);
|
|
|
|
|
return activity == null ? null : workflowExecutionContext.WorkflowInstance.ActivityData[activity.Id];
|
|
|
|
|
}
|
2021-05-29 15:11:00 +00:00
|
|
|
|
2021-05-29 12:16:23 +00:00
|
|
|
private object? GetInboundActivityModel(ActivityExecutionContext context)
|
|
|
|
|
{
|
|
|
|
|
var inboundActivityId = context.WorkflowExecutionContext.GetInboundActivityPath(context.ActivityId).FirstOrDefault();
|
|
|
|
|
return inboundActivityId == null ? null : GetActivityModel(context, inboundActivityId);
|
|
|
|
|
}
|
2021-04-26 10:32:16 +00:00
|
|
|
|
2021-03-04 15:11:18 +00:00
|
|
|
private void RegisterType<T>(Engine engine) => engine.SetValue(typeof(T).Name, TypeReference.CreateTypeReference(engine, typeof(T)));
|
2021-05-29 15:11:00 +00:00
|
|
|
private void RegisterType(Type type, Engine engine) => engine.SetValue(type.Name, TypeReference.CreateTypeReference(engine, type));
|
2021-03-04 15:11:18 +00:00
|
|
|
}
|
|
|
|
|
}
|