feat(liquid): delete json filter and add filters already provided by fluid library (#5188)
Co-authored-by: Jean-Baptiste Dalle <jean-baptiste.dalle@stereograph.fr>
This commit is contained in:
parent
406469825c
commit
09cda8ecc7
|
|
@ -1,6 +1,7 @@
|
|||
using Elsa.Liquid.Contracts;
|
||||
using Elsa.Liquid.Options;
|
||||
using Fluid;
|
||||
using Fluid.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
|
|
@ -18,5 +19,6 @@ internal static class TemplateContextExtensions
|
|||
return filter.ProcessAsync(input, arguments, ctx);
|
||||
});
|
||||
}
|
||||
options.FluidFiltersDelegate(templateContext);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ using Elsa.Liquid.Handlers;
|
|||
using Elsa.Liquid.Options;
|
||||
using Elsa.Liquid.Providers;
|
||||
using Elsa.Liquid.Services;
|
||||
using Fluid.Filters;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Elsa.Liquid.Features;
|
||||
|
|
@ -31,21 +32,27 @@ public class LiquidFeature : FeatureBase
|
|||
/// <summary>
|
||||
/// Configures the Fluid options.
|
||||
/// </summary>
|
||||
public Action<FluidOptions> FluidOptions { get; set; } = _ => { };
|
||||
public Action<FluidOptions> FluidOptions { get; set; } = options =>
|
||||
{
|
||||
options.FluidFiltersDelegate = context => context.Options.Filters
|
||||
.WithArrayFilters()
|
||||
.WithStringFilters()
|
||||
.WithNumberFilters()
|
||||
.WithMiscFilters();
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Apply()
|
||||
{
|
||||
Services.Configure(FluidOptions);
|
||||
|
||||
|
||||
Services
|
||||
.AddHandlersFrom<ConfigureLiquidEngine>()
|
||||
.AddScoped<ILiquidTemplateManager, LiquidTemplateManager>()
|
||||
.AddScoped<LiquidParser>()
|
||||
.AddExpressionDescriptorProvider<LiquidExpressionDescriptorProvider>()
|
||||
.AddLiquidFilter<JsonFilter>("json")
|
||||
.AddLiquidFilter<Base64Filter>("base64")
|
||||
.AddLiquidFilter<DictionaryKeysFilter>("keys")
|
||||
;
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
using System.Text.Json;
|
||||
using Elsa.Liquid.Contracts;
|
||||
using Fluid;
|
||||
using Fluid.Values;
|
||||
|
||||
namespace Elsa.Liquid.Filters;
|
||||
|
||||
/// <summary>
|
||||
/// A liquid filter that converts a value into a JSON string representation.
|
||||
/// </summary>
|
||||
public class JsonFilter : ILiquidFilter
|
||||
{
|
||||
public ValueTask<FluidValue> ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext context)
|
||||
{
|
||||
switch (input.Type)
|
||||
{
|
||||
case FluidValues.Array:
|
||||
return new ValueTask<FluidValue>(new StringValue(JsonSerializer.Serialize(input.Enumerate(context).Select(o => o.ToObjectValue()))));
|
||||
|
||||
case FluidValues.Boolean:
|
||||
return new ValueTask<FluidValue>(new StringValue(JsonSerializer.Serialize(input.ToBooleanValue())));
|
||||
|
||||
case FluidValues.Nil:
|
||||
return new ValueTask<FluidValue>(FluidValue.Create("null", context.Options));
|
||||
|
||||
case FluidValues.Number:
|
||||
return new ValueTask<FluidValue>(new StringValue(JsonSerializer.Serialize(input.ToNumberValue())));
|
||||
|
||||
case FluidValues.DateTime:
|
||||
case FluidValues.Dictionary:
|
||||
case FluidValues.Object:
|
||||
return new ValueTask<FluidValue>(new StringValue(JsonSerializer.Serialize(input.ToObjectValue())));
|
||||
|
||||
case FluidValues.String:
|
||||
var stringValue = input.ToStringValue();
|
||||
|
||||
return string.IsNullOrWhiteSpace(stringValue)
|
||||
? new ValueTask<FluidValue>(input)
|
||||
: new ValueTask<FluidValue>(new StringValue(JsonSerializer.Serialize(stringValue)));
|
||||
}
|
||||
|
||||
throw new NotSupportedException("Unrecognized FluidValue");
|
||||
}
|
||||
}
|
||||
|
|
@ -12,13 +12,13 @@ public class FluidOptions
|
|||
/// <summary>
|
||||
/// A dictionary of filter registrations.
|
||||
/// </summary>
|
||||
public Dictionary<string, Type> FilterRegistrations { get; } = new();
|
||||
|
||||
public Dictionary<string, Type> FilterRegistrations { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// A list of parser configurations.
|
||||
/// </summary>
|
||||
public IList<Action<LiquidParser>> ParserConfiguration { get; } = new List<Action<LiquidParser>>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to allow access to the configuration object.
|
||||
/// </summary>
|
||||
|
|
@ -28,4 +28,9 @@ public class FluidOptions
|
|||
/// Gets or sets the default encoder to use when rendering a template.
|
||||
/// </summary>
|
||||
public TextEncoder Encoder { get; set; } = NullEncoder.Default;
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the fluid filters enabled in Elsa.
|
||||
/// </summary>
|
||||
public Action<TemplateContext> FluidFiltersDelegate { get; set; } = _ => { };
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\src\common\Elsa.Testing.Shared\Elsa.Testing.Shared.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Http\Elsa.Http.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.JavaScript\Elsa.JavaScript.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.ProtoActor\Elsa.ProtoActor.csproj" />
|
||||
<ProjectReference Include="..\..\..\src\modules\Elsa.Scheduling\Elsa.Scheduling.csproj" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
using Elsa.Expressions.Models;
|
||||
using Elsa.JavaScript.Activities;
|
||||
using Elsa.Workflows.Activities;
|
||||
using Elsa.Workflows.Contracts;
|
||||
using Elsa.Workflows.Memory;
|
||||
using Elsa.Workflows.Services;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Scenarios.LiquidLists;
|
||||
|
||||
/// <summary>
|
||||
/// A workflow that use javascript to get some data, use them with some liquid expressions
|
||||
/// </summary>
|
||||
public class JavascriptAndLiquidWorkflow : WorkflowBase
|
||||
{
|
||||
protected override void Build(IWorkflowBuilder builder)
|
||||
{
|
||||
var products = new Variable<object> { Name = "Products", StorageDriverType = typeof(WorkflowStorageDriver) };
|
||||
var product = new Variable<object> { Name = "Product", StorageDriverType = typeof(WorkflowStorageDriver) };
|
||||
|
||||
builder.Root = new Sequence
|
||||
{
|
||||
Variables = { products, product },
|
||||
Activities =
|
||||
{
|
||||
new RunJavaScript
|
||||
{
|
||||
Script = new(@"setProducts([{""id"":1, ""price"":12.99}, {""id"":2, ""price"":10}, {""id"":3, ""price"":1}])")
|
||||
},
|
||||
new WriteLine(new Expression("Liquid", "First product id: {{ Variables.Products[0].id }}")),
|
||||
new WriteLine(new Expression("Liquid", "First product price rounded: {{ Variables.Products[0].price | round }}")),
|
||||
new WriteLine(new Expression("Liquid", "First product as json: {{ Variables.Products[0] | json }}")),
|
||||
new WriteLine(new Expression("Liquid", "Second product id: {{ Variables.Products[1].id }}")),
|
||||
new RunJavaScript
|
||||
{
|
||||
Script = new(@"setProduct({""id"":2, ""price"":10})")
|
||||
},
|
||||
new WriteLine(new Expression("Liquid", "Single product id: {{ Variables.Product.id }}")),
|
||||
new WriteLine(new Expression("Liquid", "Single product as json: {{ Variables.Product | json }}")),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
using Elsa.Extensions;
|
||||
using Elsa.Testing.Shared;
|
||||
using Elsa.Workflows.Contracts;
|
||||
using Elsa.Workflows.Models;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
namespace Elsa.Workflows.IntegrationTests.Scenarios.LiquidLists;
|
||||
|
||||
public sealed class Tests : IDisposable
|
||||
{
|
||||
private readonly IWorkflowRunner _workflowRunner;
|
||||
private readonly CapturingTextWriter _capturingTextWriter = new();
|
||||
private readonly IServiceProvider _services;
|
||||
|
||||
public Tests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
_services = new TestApplicationBuilder(testOutputHelper)
|
||||
.WithCapturingTextWriter(_capturingTextWriter)
|
||||
.ConfigureElsa(configure => configure.UseHttp())
|
||||
.Build();
|
||||
_workflowRunner = _services.GetRequiredService<IWorkflowRunner>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetProducts()
|
||||
{
|
||||
await _services.PopulateRegistriesAsync();
|
||||
RunWorkflowResult result = await _workflowRunner.RunAsync(new JavascriptAndLiquidWorkflow());
|
||||
Assert.Equal(WorkflowStatus.Finished, result.WorkflowState.Status);
|
||||
Assert.Empty(result.WorkflowState.Incidents);
|
||||
Assert.Equal(WorkflowSubStatus.Finished, result.WorkflowState.SubStatus);
|
||||
|
||||
var lines = _capturingTextWriter.Lines.ToList();
|
||||
Assert.Contains("First product id: 1", lines);
|
||||
Assert.Contains("First product price rounded: 13", lines);
|
||||
Assert.Contains("First product as json: {\"id\":1,\"price\":12.99}", lines);
|
||||
Assert.Contains("Second product id: 2", lines);
|
||||
|
||||
Assert.Contains("Single product id: 2", lines);
|
||||
Assert.Contains("Single product as json: {\"id\":2,\"price\":10}", lines);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_capturingTextWriter.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue