Skip to content

Commit

Permalink
Fix liquid engine to handle JObject and JValue resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
sfmskywalker committed May 30, 2021
1 parent a8f6cf3 commit d195dfc
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Models;
Expand Down Expand Up @@ -27,38 +29,75 @@ public Task Handle(EvaluatingLiquidExpression notification, CancellationToken ca
{
var context = notification.TemplateContext;
var options = context.Options;
var memberAccessStrategy = options.MemberAccessStrategy;

options.MemberAccessStrategy.Register<ExpandoObject>();
options.MemberAccessStrategy.Register<JObject>();
options.MemberAccessStrategy.Register<JValue>(o => o.Value);
options.MemberAccessStrategy.Register<LiquidActivityModel>();
options.MemberAccessStrategy.Register<LiquidPropertyAccessor, FluidValue>((x, name) => x.GetValueAsync(name));
options.MemberAccessStrategy.Register<ActivityExecutionContext, FluidValue>("Input", x => ToFluidValue(x.Input, options));
options.MemberAccessStrategy.Register<ActivityExecutionContext, FluidValue>("WorkflowInstanceId", x => ToFluidValue(x.WorkflowInstance.Id, options));
options.MemberAccessStrategy.Register<ActivityExecutionContext, FluidValue>("CorrelationId", x => ToFluidValue(x.CorrelationId, options));
options.MemberAccessStrategy.Register<ActivityExecutionContext, FluidValue>("WorkflowDefinitionId", x => ToFluidValue(x.WorkflowInstance.DefinitionId, options));
options.MemberAccessStrategy.Register<ActivityExecutionContext, FluidValue>("WorkflowDefinitionVersion", x => ToFluidValue(x.WorkflowInstance.Version, options));
options.MemberAccessStrategy.Register<ActivityExecutionContext, LiquidPropertyAccessor>("Variables", x => new LiquidPropertyAccessor(name => ToFluidValue(x.WorkflowExecutionContext.GetMergedVariables(), name, options)));
options.MemberAccessStrategy.Register<ActivityExecutionContext, LiquidPropertyAccessor>("Activities", x => new LiquidPropertyAccessor(name => ToFluidValue(GetActivityModelAsync(x, name), options)!));
options.MemberAccessStrategy.Register<LiquidActivityModel, object?>("Output", GetActivityOutput);
options.MemberAccessStrategy.Register<LiquidObjectAccessor<object>, object>((x, name) => x.GetValueAsync(name));
options.MemberAccessStrategy.Register<ExpandoObject, object>((x, name) => ((IDictionary<string, object>) x)[name]);
options.MemberAccessStrategy.Register<JObject, object?>((source, name) => source[name]);
options.MemberAccessStrategy.Register<ActivityExecutionContext, LiquidPropertyAccessor>("Configuration", x => new LiquidPropertyAccessor(name => ToFluidValue(GetConfigurationValue(name), options)!));
options.MemberAccessStrategy.Register<ConfigurationSectionWrapper, ConfigurationSectionWrapper?>((source, name) => source.GetSection(name));
options.ValueConverters.Add(x => x is JObject o ? new ObjectValue(o) : null);
options.ValueConverters.Add(x => x is JValue v ? v.Value : null);

memberAccessStrategy.Register<ExpandoObject>();
memberAccessStrategy.Register<JObject>();
memberAccessStrategy.Register<JValue>(o => o.Value);
memberAccessStrategy.Register<LiquidActivityModel>();
memberAccessStrategy.Register<LiquidPropertyAccessor, FluidValue>((x, name) => x.GetValueAsync(name));
memberAccessStrategy.Register<ActivityExecutionContext, FluidValue>("Input", x => ToFluidValue(x.Input, options));
memberAccessStrategy.Register<ActivityExecutionContext, FluidValue>("WorkflowInstanceId", x => ToFluidValue(x.WorkflowInstance.Id, options));
memberAccessStrategy.Register<ActivityExecutionContext, FluidValue>("CorrelationId", x => ToFluidValue(x.CorrelationId, options));
memberAccessStrategy.Register<ActivityExecutionContext, FluidValue>("WorkflowDefinitionId", x => ToFluidValue(x.WorkflowInstance.DefinitionId, options));
memberAccessStrategy.Register<ActivityExecutionContext, FluidValue>("WorkflowDefinitionVersion", x => ToFluidValue(x.WorkflowInstance.Version, options));
memberAccessStrategy.Register<ActivityExecutionContext, LiquidPropertyAccessor>("Variables", x => new LiquidPropertyAccessor(name => ToFluidValue(x.WorkflowExecutionContext.GetMergedVariables(), name, options)));
memberAccessStrategy.Register<ActivityExecutionContext, LiquidPropertyAccessor>("Activities", x => new LiquidPropertyAccessor(name => ToFluidValue(GetActivityModelAsync(x, name), options)!));
memberAccessStrategy.Register<ActivityExecutionContext, LiquidActivityModel?>("InboundActivity", GetInboundActivityModelAsync);
memberAccessStrategy.Register<LiquidActivityModel, object?>(GetActivityProperty);
memberAccessStrategy.Register<LiquidObjectAccessor<JObject>, JObject>((x, name) => x.GetValueAsync(name));
memberAccessStrategy.Register<ExpandoObject, object>((x, name) => ((IDictionary<string, object>) x)[name]);
memberAccessStrategy.Register<JObject, object?>((source, name) => source.GetValue(name, StringComparison.OrdinalIgnoreCase));
memberAccessStrategy.Register<ActivityExecutionContext, LiquidPropertyAccessor>("Configuration", x => new LiquidPropertyAccessor(name => ToFluidValue(GetConfigurationValue(name), options)!));
memberAccessStrategy.Register<ConfigurationSectionWrapper, ConfigurationSectionWrapper?>((source, name) => source.GetSection(name));

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?> ToFluidValue(Variables dictionary, string key, TemplateOptions options) => Task.FromResult(!dictionary.Has(key) ? default : FluidValue.Create(dictionary.Get(key), options));
private LiquidActivityModel GetActivityModelAsync(ActivityExecutionContext context, string name) => new(context, name);
private Task<FluidValue> ToFluidValue(Variables dictionary, string key, TemplateOptions options) => Task.FromResult(!dictionary.Has(key) ? NilValue.Instance : FluidValue.Create(dictionary.Get(key), options));
private LiquidActivityModel GetActivityModelAsync(ActivityExecutionContext context, string name) => new(context, name, null);

private Task<object?> GetActivityOutput(LiquidActivityModel activityModel)
private object? GetInboundActivityModelPropertyAsync(ActivityExecutionContext context, string propertyName)
{
var output = activityModel.ActivityExecutionContext.GetOutputFrom(activityModel.ActivityName);
return Task.FromResult(output);
var activityModel = GetInboundActivityModelAsync(context);

if (activityModel == null)
return null;

return GetActivityProperty(activityModel, propertyName);
}

private LiquidActivityModel? GetInboundActivityModelAsync(ActivityExecutionContext context)
{
var inboundActivityId = context.WorkflowExecutionContext.GetInboundActivityPath(context.ActivityId).FirstOrDefault();

if (inboundActivityId == null)
return null;

return new LiquidActivityModel(context, null, inboundActivityId);
}

private object? GetActivityProperty(LiquidActivityModel activityModel, string name)
{
var activityExecutionContext = activityModel.ActivityExecutionContext;

// Deprecated.
if (name == "Output")
{
var output = activityExecutionContext.GetOutputFrom(activityModel.ActivityName!);
return output != null ? new JObject(output) : default;
}

var workflowExecutionContext = activityExecutionContext.WorkflowExecutionContext;
var activityId = activityModel.ActivityId ?? workflowExecutionContext.GetActivityBlueprintByName(activityModel.ActivityName!)!.Id;
var activityState = workflowExecutionContext.WorkflowInstance.ActivityData.GetItem(activityId, () => new JObject());
var value = activityState.GetState<object>(name);
return value;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Elsa.Scripting.Liquid.Helpers
{
public record LiquidActivityModel(ActivityExecutionContext ActivityExecutionContext, string ActivityName)
public record LiquidActivityModel(ActivityExecutionContext ActivityExecutionContext, string? ActivityName, string? ActivityId)
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Elsa.Scripting.Liquid.Helpers
/// </summary>
public class LiquidPropertyAccessor : LiquidObjectAccessor<FluidValue>
{
public LiquidPropertyAccessor(Func<string, Task<FluidValue?>> getter) : base(getter!)
public LiquidPropertyAccessor(Func<string, Task<FluidValue>> getter) : base(getter!)
{
}
}
Expand Down

0 comments on commit d195dfc

Please sign in to comment.