Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NCalc Plugin for Expressions #179

Merged
merged 2 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 4 additions & 23 deletions doc/Documentation/articles/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,31 +79,12 @@ field.DefaultValue = "sql:select field2 from table1 where field1 = '{field1}'";

## Implementing your own expression provider

Implement the [IExpressionProvider](https://portal.jjconsulting.com.br/jjdoc/lib/JJMasterData.Core.Expressions.Abstractions.IExpressionProvider.html) interface and add to your services your custom provider.
You can implement both [IBooleanExpressionProvider] (used at visible and enable expressions) or [IAsyncExpressionProvider] (used at triggers and default values.)
```cs
builder.Services.AddJJMasterDataWeb().WithExpressionProvider<TMyCustomProvider>();
```
You can implement both [IBooleanExpressionProvider] (used at visible and enable expressions) or [IAsyncExpressionProvider] (used at triggers and default values.).

## Protheus Plugin

Add to your project:
```cs
builder.Services.AddJJMasterDataWeb().WithProtheusServices();
```
Example using [protheus:] + "UrlProtheus", "NameFunction", "Parameters" <br>
1. protheus:"http://localhost/jjmain.apw","u_test","";
2. protheus:"http://localhost/jjmain.apw","u_test","{field1};parm2";
You can call [ExpressionHelper] method to replace values {} with runtime values.
```cs
var field = new ElementField();
field.DefaultValue = "protheus:'http://10.0.0.6:8181/websales/jjmain.apw', 'u_vldpan', '1;2'";
builder.Services.AddJJMasterDataWeb().WithExpressionProvider<TMyCustomProvider>();
```

> [!IMPORTANT]
> A invalid expression will throw a exception in the application

> [!WARNING]
> For Protheus calls apply JJxFun patch and configure http connection in Protheus
>

##
[!include[Readme](../../../../src/Plugins/NCalc/README.MD)]
2 changes: 1 addition & 1 deletion doc/Documentation/articles/plugins/hangfire.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[!include[Readme](../../../../src/Plugins/JJMasterData.Hangfire/README.MD)]
[!include[Readme](../../../../src/Plugins/Hangfire/README.MD)]

2 changes: 1 addition & 1 deletion doc/Documentation/articles/plugins/mongodb.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[!include[Readme](../../../../src/Plugins/JJMasterData.MongoDB/README.MD)]
[!include[Readme](../../../../src/Plugins/MongoDB/README.MD)]

2 changes: 1 addition & 1 deletion doc/Documentation/articles/plugins/python.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
[!include[Readme](../../../../src/Plugins/JJMasterData.Python/README.MD)]
[!include[Readme](../../../../src/Plugins/Python/README.MD)]

2 changes: 1 addition & 1 deletion doc/Documentation/articles/plugins/sourcecontrol.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[!include[Readme](../../../../src/Plugins/JJMasterData.SourceControl/README.MD)]
[!include[Readme](../../../../src/Plugins/SourceControl/README.MD)]


2 changes: 1 addition & 1 deletion src/Core/Configuration/ExpressionsServiceExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public static IServiceCollection AddExpressionServices(this IServiceCollection s
services.AddTransient<ExpressionParser>();

services.AddScoped<IExpressionProvider, ValueExpressionProvider>();
services.AddScoped<IExpressionProvider, InMemoryExpressionProvider>();
services.AddScoped<IExpressionProvider, DataTableExpressionProvider>();
services.AddScoped<IExpressionProvider, SqlExpressionProvider>();

return services;
Expand Down
1 change: 1 addition & 0 deletions src/Core/Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,5 @@




</Project>
9 changes: 7 additions & 2 deletions src/Core/DataDictionary/Services/ActionsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,26 @@
using JJMasterData.Core.DataDictionary.Models;
using JJMasterData.Core.DataDictionary.Models.Actions;
using JJMasterData.Core.DataDictionary.Repository.Abstractions;
using JJMasterData.Core.DataManager.Expressions.Abstractions;
using JJMasterData.Core.DataManager.Expressions.Extensions;
using Microsoft.Extensions.Localization;

namespace JJMasterData.Core.DataDictionary.Services;

public class ActionsService : BaseService
{
private readonly IEnumerable<IExpressionProvider> _expressionProviders;
private readonly IEnumerable<IPluginHandler> _pluginHandlers;

public ActionsService(
IValidationDictionary validationDictionary,
IStringLocalizer<MasterDataResources> stringLocalizer,
IDataDictionaryRepository dataDictionaryRepository,
IEnumerable<IExpressionProvider> expressionProviders,
IEnumerable<IPluginHandler> pluginHandlers)
: base(validationDictionary, dataDictionaryRepository,stringLocalizer)
{
_expressionProviders = expressionProviders;
_pluginHandlers = pluginHandlers;
}

Expand Down Expand Up @@ -159,12 +164,12 @@ private void ValidateAction(FormElement formElement, BasicAction action, [CanBeN
{
if (string.IsNullOrWhiteSpace(action.VisibleExpression))
AddError(nameof(action.VisibleExpression), StringLocalizer["Required [VisibleExpression] field"]);
else if (!ValidateExpression(action.VisibleExpression, "val:", "exp:", "sql:"))
else if (!ValidateExpression(action.VisibleExpression, _expressionProviders.GetBooleanProvidersPrefixes()))
AddError(nameof(action.VisibleExpression), StringLocalizer["Invalid [VisibleExpression] field"]);

if (string.IsNullOrWhiteSpace(action.EnableExpression))
AddError(nameof(action.EnableExpression), "Required [EnableExpression] field");
else if (!ValidateExpression(action.EnableExpression, "val:", "exp:", "sql:"))
else if (!ValidateExpression(action.EnableExpression, _expressionProviders.GetBooleanProvidersPrefixes()))
AddError(nameof(action.EnableExpression), "Invalid [EnableExpression] field");

switch (action)
Expand Down
3 changes: 1 addition & 2 deletions src/Core/DataDictionary/Services/BaseService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ public bool ValidateName(string name)
return _validationDictionary.IsValid;
}

//todo: "Expressions can start with anything now."
public bool ValidateExpression(string value, params string[] args)
protected static bool ValidateExpression(string value, params string[] args)
{
return args.Any(value.StartsWith);
}
Expand Down
14 changes: 10 additions & 4 deletions src/Core/DataDictionary/Services/FieldService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,27 @@
using JJMasterData.Commons.Localization;
using JJMasterData.Core.DataDictionary.Models;
using JJMasterData.Core.DataDictionary.Repository.Abstractions;
using JJMasterData.Core.DataManager.Expressions.Abstractions;
using JJMasterData.Core.DataManager.Expressions.Extensions;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Localization;

namespace JJMasterData.Core.DataDictionary.Services;

public class FieldService : BaseService
{
private readonly IEnumerable<IExpressionProvider> _expressionProviders;
private readonly IMemoryCache _memoryCache;

public FieldService(
IValidationDictionary validationDictionary,
IDataDictionaryRepository dataDictionaryRepository,
IEnumerable<IExpressionProvider> expressionProviders,
IMemoryCache memoryCache,
IStringLocalizer<MasterDataResources> stringLocalizer)
: base(validationDictionary, dataDictionaryRepository,stringLocalizer)
{
_expressionProviders = expressionProviders;
_memoryCache = memoryCache;
}

Expand Down Expand Up @@ -193,23 +198,24 @@ private void ValidateExpressions(FormElementField field)
{
if (string.IsNullOrWhiteSpace(field.VisibleExpression))
AddError(nameof(field.VisibleExpression), StringLocalizer["Required [VisibleExpression] field"]);
else if (!ValidateExpression(field.VisibleExpression, "val:", "exp:"))

else if (!ValidateExpression(field.VisibleExpression, _expressionProviders.GetBooleanProvidersPrefixes()))
AddError(nameof(field.VisibleExpression), StringLocalizer["Invalid [VisibleExpression] field"]);

if (string.IsNullOrWhiteSpace(field.EnableExpression))
AddError(nameof(field.EnableExpression), StringLocalizer["Required [EnableExpression] field"]);
else if (!ValidateExpression(field.EnableExpression, "val:", "exp:"))
else if (!ValidateExpression(field.EnableExpression, _expressionProviders.GetBooleanProvidersPrefixes()))
AddError(nameof(field.EnableExpression), StringLocalizer["Invalid [EnableExpression] field"]);

if (!string.IsNullOrEmpty(field.DefaultValue))
{
if (!ValidateExpression(field.DefaultValue, "val:", "exp:", "sql:", "protheus:"))
if (!ValidateExpression(field.DefaultValue, _expressionProviders.GetAsyncProvidersPrefixes()))
AddError(nameof(field.DefaultValue), StringLocalizer["Invalid [DefaultValue] field"]);
}

if (!string.IsNullOrEmpty(field.TriggerExpression))
{
if (!ValidateExpression(field.TriggerExpression, "val:", "exp:", "sql:", "protheus:"))
if (!ValidateExpression(field.TriggerExpression, _expressionProviders.GetAsyncProvidersPrefixes()))
AddError(nameof(field.TriggerExpression), StringLocalizer["Invalid [TriggerExpression] field"]);
}
}
Expand Down
15 changes: 12 additions & 3 deletions src/Core/DataDictionary/Services/PanelService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,24 @@
using JJMasterData.Commons.Localization;
using JJMasterData.Core.DataDictionary.Models;
using JJMasterData.Core.DataDictionary.Repository.Abstractions;
using JJMasterData.Core.DataManager.Expressions.Abstractions;
using JJMasterData.Core.DataManager.Expressions.Extensions;
using Microsoft.Extensions.Localization;

namespace JJMasterData.Core.DataDictionary.Services;

public class PanelService : BaseService
{
public PanelService(IValidationDictionary validationDictionary, IDataDictionaryRepository dataDictionaryRepository, IStringLocalizer<MasterDataResources> stringLocalizer)
private IEnumerable<IExpressionProvider> ExpressionProviders { get; }

public PanelService(
IValidationDictionary validationDictionary,
IEnumerable<IExpressionProvider> expressionProviders,
IDataDictionaryRepository dataDictionaryRepository,
IStringLocalizer<MasterDataResources> stringLocalizer)
: base(validationDictionary, dataDictionaryRepository,stringLocalizer)
{
ExpressionProviders = expressionProviders;
}

public async Task SavePanelAsync(string elementName, FormElementPanel panel, string[]? selectedFields)
Expand Down Expand Up @@ -70,11 +79,11 @@ public bool ValidatePanel(FormElementPanel panel)
{
if (string.IsNullOrWhiteSpace(panel.VisibleExpression))
AddError(nameof(panel.VisibleExpression), "Required [VisibleExpression] panel");
else if (!ValidateExpression(panel.VisibleExpression, "val:", "exp:"))
else if (!ValidateExpression(panel.VisibleExpression, ExpressionProviders.GetBooleanProvidersPrefixes()))
AddError(nameof(panel.VisibleExpression), "Invalid [VisibleExpression] panel");
if (string.IsNullOrWhiteSpace(panel.EnableExpression))
AddError(nameof(panel.EnableExpression), "Required [VisibleExpression] panel");
else if (!ValidateExpression(panel.EnableExpression, "val:", "exp:"))
else if (!ValidateExpression(panel.EnableExpression, ExpressionProviders.GetBooleanProvidersPrefixes()))
AddError(nameof(panel.EnableExpression), "Invalid [VisibleExpression] panel");

return IsValid;
Expand Down
1 change: 1 addition & 0 deletions src/Core/DataManager/Expressions/ExpressionHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
using System;
using System.Collections.Generic;

namespace JJMasterData.Core.DataManager.Expressions;
Expand Down
2 changes: 1 addition & 1 deletion src/Core/DataManager/Expressions/ExpressionsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public ExpressionsService(
return await GetExpressionValueAsync(field.DefaultValue, field, formStateData);
}

public string? ParseExpression(
public string? ReplaceExpressionWithParsedValues(
string? expression,
FormStateData formStateData)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Collections.Generic;
using System.Linq;
using JJMasterData.Core.DataManager.Expressions.Abstractions;

namespace JJMasterData.Core.DataManager.Expressions.Extensions;

public static class ExpressionProvideEnumerableExtensions
{
public static string[] GetAsyncProvidersPrefixes(this IEnumerable<IExpressionProvider> expressionProviders)
{
return expressionProviders.Where(p => p is IAsyncExpressionProvider).Select(p => p.Prefix).ToArray();
}

public static string[] GetBooleanProvidersPrefixes(this IEnumerable<IExpressionProvider> expressionProviders)
{
return expressionProviders.Where(p => p is IBooleanExpressionProvider).Select(p => p.Prefix).ToArray();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

namespace JJMasterData.Core.DataManager.Expressions.Providers;

internal class InMemoryExpressionProvider : IBooleanExpressionProvider, IAsyncExpressionProvider
internal class DataTableExpressionProvider : IBooleanExpressionProvider, IAsyncExpressionProvider
{
public string Prefix => "exp";
public string Title => "Expression";
public string Title => "DataTable.Compute";
private static object EvaluateObject(string replacedExpression)
{
using var dt = new DataTable();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public SqlExpressionProvider(IEntityRepository entityRepository)
}

public string Prefix => "sql";
public string Title => "T-SQL";
public string Title => "SQL";

public async Task<object?> EvaluateAsync(string expression, IDictionary<string,object?> parsedValues)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ private static object EvalutateObject(string expression, IDictionary<string,obje
{
if (expression.Contains(ExpressionHelper.Begin.ToString()))
return ExpressionHelper.ReplaceExpression(expression, parsedValues);
return expression.Replace("val:", string.Empty).Trim();

return expression;
}

public bool Evaluate(string expression, IDictionary<string,object?> parsedValues)
Expand Down
4 changes: 2 additions & 2 deletions src/Core/DataManager/Importation/DataImportationWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ private async Task RunWorker(DataImportationReporter currentProcess, Cancellatio
if (currentProcess.TotalRecords > 0 &&
!string.IsNullOrEmpty(ProcessOptions?.CommandBeforeProcess))
{
var parsedSql = ExpressionsService.ParseExpression(ProcessOptions.CommandBeforeProcess, formData);
var parsedSql = ExpressionsService.ReplaceExpressionWithParsedValues(ProcessOptions.CommandBeforeProcess, formData);
await EntityRepository.SetCommandAsync(new DataAccessCommand(parsedSql));
}

Expand Down Expand Up @@ -214,7 +214,7 @@ private async Task RunWorker(DataImportationReporter currentProcess, Cancellatio
if (currentProcess.TotalRecords > 0 &&
!string.IsNullOrEmpty(ProcessOptions?.CommandAfterProcess))
{
string parsedSql = ExpressionsService.ParseExpression(ProcessOptions.CommandAfterProcess, formData);
string parsedSql = ExpressionsService.ReplaceExpressionWithParsedValues(ProcessOptions.CommandAfterProcess, formData);
await EntityRepository.SetCommandAsync(new DataAccessCommand(parsedSql!));
}

Expand Down
2 changes: 1 addition & 1 deletion src/Core/DataManager/Services/ElementMapService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ private IDictionary<string, object> GetFilters(DataElementMap elementMap, object
foreach (var filter in elementMap.Filters)
{
var filterParsed =
ExpressionsService.ParseExpression(filter.Value.ToString(), formStateData) ?? string.Empty;
ExpressionsService.ReplaceExpressionWithParsedValues(filter.Value.ToString(), formStateData) ?? string.Empty;
filters[filter.Key] = filterParsed;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Core/UI/Components/Actions/ActionScripts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ internal string GetUserActionScript(
{
UrlRedirectAction urlRedirectAction => GetUrlRedirectScript(urlRedirectAction, actionContext, actionSource),
SqlCommandAction => GetSqlCommandScript(actionContext, actionSource),
ScriptAction jsAction => HttpUtility.HtmlAttributeEncode(ExpressionsService.ParseExpression(jsAction.OnClientClick, formStateData) ?? string.Empty),
ScriptAction jsAction => HttpUtility.HtmlAttributeEncode(ExpressionsService.ReplaceExpressionWithParsedValues(jsAction.OnClientClick, formStateData) ?? string.Empty),
InternalAction internalAction => GetInternalUrlScript(internalAction, formStateData.Values),
_ => GetFormActionScript(actionContext, actionSource)
};
Expand Down
2 changes: 1 addition & 1 deletion src/Core/UI/Components/Controls/Lookup/LookupParameters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public string ToQueryString(ExpressionsService expressionsService, FormStateData
{
foreach (var filter in Filters)
{
string filterParsed = expressionsService.ParseExpression(filter.Value.ToString(), formStateData);
string filterParsed = expressionsService.ReplaceExpressionWithParsedValues(filter.Value.ToString(), formStateData);
queryString.Append('&');
queryString.Append(filter.Key);
queryString.Append('=');
Expand Down
2 changes: 1 addition & 1 deletion src/Core/UI/Components/DataPanel/JJDataPanel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ internal async Task<JsonComponentResult> GetUrlRedirectResult(ActionMap actionMa
var urlRedirectAction = actionMap.GetAction<UrlRedirectAction>(FormElement);
var values = await FormValuesService.GetFormValuesWithMergedValuesAsync(FormElement,PageState,actionMap.PkFieldValues, true, FieldNamePrefix);
var formStateData = new FormStateData(values, PageState);
var parsedUrl = ExpressionsService.ParseExpression(urlRedirectAction.UrlRedirect, formStateData);
var parsedUrl = ExpressionsService.ReplaceExpressionWithParsedValues(urlRedirectAction.UrlRedirect, formStateData);

var model = new UrlRedirectModel
{
Expand Down
2 changes: 1 addition & 1 deletion src/Core/UI/Components/FormView/JJFormView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ private async Task<ComponentResult> GetSqlCommandActionResult()
var sqlAction = CurrentActionMap!.GetAction<SqlCommandAction>(FormElement);
try
{
var sqlCommand = ExpressionsService.ParseExpression(sqlAction.CommandSql, await GetFormStateDataAsync());
var sqlCommand = ExpressionsService.ReplaceExpressionWithParsedValues(sqlAction.CommandSql, await GetFormStateDataAsync());

await EntityRepository.SetCommandAsync(new DataAccessCommand(sqlCommand!));
}
Expand Down
4 changes: 2 additions & 2 deletions src/Core/UI/Components/GridView/GridSqlCommandAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private async Task ExecuteOnList(SqlCommandAction sqlCommandAction, List<IDictio
foreach (var row in selectedRows)
{
var formData = new FormStateData(row, _gridView.UserValues, PageState.List);
var sql = _gridView.ExpressionsService.ParseExpression(sqlCommandAction.CommandSql, formData);
var sql = _gridView.ExpressionsService.ReplaceExpressionWithParsedValues(sqlCommandAction.CommandSql, formData);
commandList.Add(new DataAccessCommand(sql!));
}

Expand All @@ -84,7 +84,7 @@ private async Task ExecuteOnRecord(ActionMap map, SqlCommandAction sqlCommandAct
}

var formStateData = new FormStateData(formValues, _gridView.UserValues, PageState.List);
var sqlCommand = _gridView.ExpressionsService.ParseExpression(sqlCommandAction.CommandSql, formStateData);
var sqlCommand = _gridView.ExpressionsService.ReplaceExpressionWithParsedValues(sqlCommandAction.CommandSql, formStateData);

await _gridView.EntityRepository.SetCommandAsync(new DataAccessCommand(sqlCommand!));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ public static class MasterDataServiceBuilderExtensions
public static MasterDataServiceBuilder WithMongoDbDataDictionary(this MasterDataServiceBuilder builder)
{
ConfigureMappers();
builder.Services.AddOptions<JJMasterDataMongoDBOptions>().BindConfiguration("JJMasterData:MongoDB");
builder.Services.AddOptions<MasterDataMongoDBOptions>().BindConfiguration("JJMasterData:MongoDB");
builder.Services.Replace(ServiceDescriptor.Transient<IDataDictionaryRepository, MongoDBDataDictionaryRepository>());
return builder;
}

public static MasterDataServiceBuilder WithMongoDbDataDictionary(this MasterDataServiceBuilder builder, Action<JJMasterDataMongoDBOptions> options)
public static MasterDataServiceBuilder WithMongoDbDataDictionary(this MasterDataServiceBuilder builder, Action<MasterDataMongoDBOptions> options)
{
ConfigureMappers();
builder.Services.Configure(options);
Expand All @@ -32,7 +32,7 @@ public static MasterDataServiceBuilder WithMongoDbDataDictionary(this MasterData
public static MasterDataServiceBuilder WithMongoDbDataDictionary(this MasterDataServiceBuilder builder, IConfiguration configuration)
{
ConfigureMappers();
builder.Services.AddOptions<JJMasterDataMongoDBOptions>().Bind(configuration);
builder.Services.AddOptions<MasterDataMongoDBOptions>().Bind(configuration);
builder.Services.Replace(ServiceDescriptor.Transient<IDataDictionaryRepository, MongoDBDataDictionaryRepository>());
return builder;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace JJMasterData.MongoDB.Models;

public class JJMasterDataMongoDBOptions
public class MasterDataMongoDBOptions
{
public string ConnectionString { get; set; } = null!;

Expand Down
Loading
Loading