111 lines
5.3 KiB
C#
111 lines
5.3 KiB
C#
using System.Dynamic;
|
|
using Elsa.Expressions.Models;
|
|
using Elsa.Extensions;
|
|
using Elsa.Scripting.Liquid.Helpers;
|
|
using Elsa.Scripting.Liquid.Notifications;
|
|
using Elsa.Scripting.Liquid.Options;
|
|
using Elsa.Mediator.Contracts;
|
|
using Elsa.Workflows.Management.Options;
|
|
using Elsa.Workflows.Memory;
|
|
using Fluid;
|
|
using Fluid.Values;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace Elsa.Scripting.Liquid.Handlers;
|
|
|
|
/// <summary>
|
|
/// Configures the liquid templating engine before evaluating a liquid expression.
|
|
/// </summary>
|
|
internal class ConfigureLiquidEngine : INotificationHandler<RenderingLiquidTemplate>
|
|
{
|
|
private readonly IConfiguration _configuration;
|
|
private readonly ManagementOptions _managementOptions;
|
|
private readonly FluidOptions _fluidOptions;
|
|
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
public ConfigureLiquidEngine(IConfiguration configuration, IOptions<FluidOptions> fluidOptions, IOptions<ManagementOptions> managementOptions)
|
|
{
|
|
_configuration = configuration;
|
|
_managementOptions = managementOptions.Value;
|
|
_fluidOptions = fluidOptions.Value;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public Task HandleAsync(RenderingLiquidTemplate notification, CancellationToken cancellationToken)
|
|
{
|
|
var context = notification.TemplateContext;
|
|
var options = context.Options;
|
|
var memberAccessStrategy = options.MemberAccessStrategy;
|
|
|
|
memberAccessStrategy.Register<ExpandoObject>();
|
|
memberAccessStrategy.Register<LiquidPropertyAccessor, FluidValue>((x, name) => x.GetValueAsync(name));
|
|
memberAccessStrategy.Register<ExpandoObject, object>((x, name) => ((IDictionary<string, object>)x!)[name]);
|
|
memberAccessStrategy.Register<ExpressionExecutionContext, LiquidPropertyAccessor>("Variables", x => new LiquidPropertyAccessor(name => GetVariable(x, name, options)));
|
|
memberAccessStrategy.Register<ExpressionExecutionContext, LiquidPropertyAccessor>("Input", x => new LiquidPropertyAccessor(name => GetInput(x, name, options)));
|
|
memberAccessStrategy.Register<ExpressionExecutionContext, string?>("CorrelationId", x => x.GetWorkflowExecutionContext().CorrelationId);
|
|
memberAccessStrategy.Register<ExpressionExecutionContext, string>("WorkflowDefinitionId", x => x.GetWorkflowExecutionContext().Workflow.Identity.DefinitionId);
|
|
memberAccessStrategy.Register<ExpressionExecutionContext, string>("WorkflowDefinitionVersionId", x => x.GetWorkflowExecutionContext().Workflow.Identity.Id);
|
|
memberAccessStrategy.Register<ExpressionExecutionContext, int>("WorkflowDefinitionVersion", x => x.GetWorkflowExecutionContext().Workflow.Identity.Version);
|
|
memberAccessStrategy.Register<ExpressionExecutionContext, string>("WorkflowInstanceId", x => x.GetActivityExecutionContext().WorkflowExecutionContext.Id);
|
|
|
|
if (_fluidOptions.AllowConfigurationAccess)
|
|
{
|
|
memberAccessStrategy.Register<ExpressionExecutionContext, LiquidPropertyAccessor>("Configuration", x => new LiquidPropertyAccessor(name => ToFluidValue(GetConfigurationValue(name), options)));
|
|
memberAccessStrategy.Register<ConfigurationSectionWrapper, ConfigurationSectionWrapper?>((source, name) => source.GetSection(name));
|
|
}
|
|
|
|
// Register all variable types.
|
|
foreach (var variableDescriptor in _managementOptions.VariableDescriptors.Where(x => x.Type is { IsClass: true, ContainsGenericParameters: false }))
|
|
memberAccessStrategy.Register(variableDescriptor.Type);
|
|
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
private ConfigurationSectionWrapper GetConfigurationValue(string name) => new(_configuration.GetSection(name));
|
|
private Task<FluidValue> ToFluidValue(object? input, TemplateOptions options) => Task.FromResult(FluidValue.Create(input, options));
|
|
|
|
private Task<FluidValue> GetVariable(ExpressionExecutionContext context, string key, TemplateOptions options)
|
|
{
|
|
var value = GetVariableInScope(context, key);
|
|
return Task.FromResult(value == null ? NilValue.Instance : FluidValue.Create(value, options));
|
|
}
|
|
|
|
private Task<FluidValue> GetInput(ExpressionExecutionContext context, string key, TemplateOptions options)
|
|
{
|
|
var workflowExecutionContext = context.GetWorkflowExecutionContext();
|
|
var input = workflowExecutionContext.Input.TryGetValue(key, out var value) ? value : default;
|
|
|
|
return Task.FromResult(input == null ? NilValue.Instance : FluidValue.Create(value, options));
|
|
}
|
|
|
|
private static object? GetVariableInScope(ExpressionExecutionContext context, string variableName)
|
|
{
|
|
var q = from variable in EnumerateVariablesInScope(context)
|
|
where variable.Name == variableName
|
|
where variable.TryGet(context, out _)
|
|
select variable.Get(context);
|
|
|
|
return q.FirstOrDefault();
|
|
}
|
|
|
|
private static IEnumerable<Variable> EnumerateVariablesInScope(ExpressionExecutionContext context)
|
|
{
|
|
var currentScope = context;
|
|
|
|
while (currentScope != null)
|
|
{
|
|
if (!currentScope.TryGetActivityExecutionContext(out var activityExecutionContext))
|
|
break;
|
|
|
|
var variables = activityExecutionContext.Variables;
|
|
|
|
foreach (var variable in variables)
|
|
yield return variable;
|
|
|
|
currentScope = currentScope.ParentContext;
|
|
}
|
|
}
|
|
} |