diff --git a/doc/Documentation/articles/expressions.md b/doc/Documentation/articles/expressions.md index 2bec541e8..8fb4adf89 100644 --- a/doc/Documentation/articles/expressions.md +++ b/doc/Documentation/articles/expressions.md @@ -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(); -``` +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"
-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(); ``` -> [!IMPORTANT] -> A invalid expression will throw a exception in the application - -> [!WARNING] -> For Protheus calls apply JJxFun patch and configure http connection in Protheus -> -## \ No newline at end of file +[!include[Readme](../../../../src/Plugins/NCalc/README.MD)] \ No newline at end of file diff --git a/doc/Documentation/articles/plugins/hangfire.md b/doc/Documentation/articles/plugins/hangfire.md index 62b7ede1d..018a279ea 100644 --- a/doc/Documentation/articles/plugins/hangfire.md +++ b/doc/Documentation/articles/plugins/hangfire.md @@ -1,2 +1,2 @@ -[!include[Readme](../../../../src/Plugins/JJMasterData.Hangfire/README.MD)] +[!include[Readme](../../../../src/Plugins/Hangfire/README.MD)] diff --git a/doc/Documentation/articles/plugins/mongodb.md b/doc/Documentation/articles/plugins/mongodb.md index 59a75c193..173b9dd42 100644 --- a/doc/Documentation/articles/plugins/mongodb.md +++ b/doc/Documentation/articles/plugins/mongodb.md @@ -1,2 +1,2 @@ -[!include[Readme](../../../../src/Plugins/JJMasterData.MongoDB/README.MD)] +[!include[Readme](../../../../src/Plugins/MongoDB/README.MD)] diff --git a/doc/Documentation/articles/plugins/python.md b/doc/Documentation/articles/plugins/python.md index 8c2cf54fa..c32ebc7c2 100644 --- a/doc/Documentation/articles/plugins/python.md +++ b/doc/Documentation/articles/plugins/python.md @@ -1,2 +1,2 @@ -[!include[Readme](../../../../src/Plugins/JJMasterData.Python/README.MD)] +[!include[Readme](../../../../src/Plugins/Python/README.MD)] diff --git a/doc/Documentation/articles/plugins/sourcecontrol.md b/doc/Documentation/articles/plugins/sourcecontrol.md index fee9baa48..d95188312 100644 --- a/doc/Documentation/articles/plugins/sourcecontrol.md +++ b/doc/Documentation/articles/plugins/sourcecontrol.md @@ -1,3 +1,3 @@ -[!include[Readme](../../../../src/Plugins/JJMasterData.SourceControl/README.MD)] +[!include[Readme](../../../../src/Plugins/SourceControl/README.MD)] diff --git a/src/Core/Configuration/ExpressionsServiceExtensions.cs b/src/Core/Configuration/ExpressionsServiceExtensions.cs index 81c2586f3..20c3c4b48 100644 --- a/src/Core/Configuration/ExpressionsServiceExtensions.cs +++ b/src/Core/Configuration/ExpressionsServiceExtensions.cs @@ -14,7 +14,7 @@ public static IServiceCollection AddExpressionServices(this IServiceCollection s services.AddTransient(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); return services; diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index c5d96db9a..96b9a2a8c 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -117,4 +117,5 @@ + diff --git a/src/Core/DataDictionary/Services/ActionsService.cs b/src/Core/DataDictionary/Services/ActionsService.cs index 7c829cf6e..f16766840 100644 --- a/src/Core/DataDictionary/Services/ActionsService.cs +++ b/src/Core/DataDictionary/Services/ActionsService.cs @@ -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 _expressionProviders; private readonly IEnumerable _pluginHandlers; public ActionsService( IValidationDictionary validationDictionary, IStringLocalizer stringLocalizer, IDataDictionaryRepository dataDictionaryRepository, + IEnumerable expressionProviders, IEnumerable pluginHandlers) : base(validationDictionary, dataDictionaryRepository,stringLocalizer) { + _expressionProviders = expressionProviders; _pluginHandlers = pluginHandlers; } @@ -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) diff --git a/src/Core/DataDictionary/Services/BaseService.cs b/src/Core/DataDictionary/Services/BaseService.cs index 11a7c9026..1138f923d 100644 --- a/src/Core/DataDictionary/Services/BaseService.cs +++ b/src/Core/DataDictionary/Services/BaseService.cs @@ -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); } diff --git a/src/Core/DataDictionary/Services/FieldService.cs b/src/Core/DataDictionary/Services/FieldService.cs index 33b052283..7a76a840a 100644 --- a/src/Core/DataDictionary/Services/FieldService.cs +++ b/src/Core/DataDictionary/Services/FieldService.cs @@ -6,6 +6,8 @@ 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; @@ -13,15 +15,18 @@ namespace JJMasterData.Core.DataDictionary.Services; public class FieldService : BaseService { + private readonly IEnumerable _expressionProviders; private readonly IMemoryCache _memoryCache; public FieldService( IValidationDictionary validationDictionary, IDataDictionaryRepository dataDictionaryRepository, + IEnumerable expressionProviders, IMemoryCache memoryCache, IStringLocalizer stringLocalizer) : base(validationDictionary, dataDictionaryRepository,stringLocalizer) { + _expressionProviders = expressionProviders; _memoryCache = memoryCache; } @@ -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"]); } } diff --git a/src/Core/DataDictionary/Services/PanelService.cs b/src/Core/DataDictionary/Services/PanelService.cs index 5812a82f9..0231b3993 100644 --- a/src/Core/DataDictionary/Services/PanelService.cs +++ b/src/Core/DataDictionary/Services/PanelService.cs @@ -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 stringLocalizer) + private IEnumerable ExpressionProviders { get; } + + public PanelService( + IValidationDictionary validationDictionary, + IEnumerable expressionProviders, + IDataDictionaryRepository dataDictionaryRepository, + IStringLocalizer stringLocalizer) : base(validationDictionary, dataDictionaryRepository,stringLocalizer) { + ExpressionProviders = expressionProviders; } public async Task SavePanelAsync(string elementName, FormElementPanel panel, string[]? selectedFields) @@ -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; diff --git a/src/Core/DataManager/Expressions/ExpressionHelper.cs b/src/Core/DataManager/Expressions/ExpressionHelper.cs index a5b4fcce7..2753304fa 100644 --- a/src/Core/DataManager/Expressions/ExpressionHelper.cs +++ b/src/Core/DataManager/Expressions/ExpressionHelper.cs @@ -1,4 +1,5 @@ #nullable enable +using System; using System.Collections.Generic; namespace JJMasterData.Core.DataManager.Expressions; diff --git a/src/Core/DataManager/Expressions/ExpressionsService.cs b/src/Core/DataManager/Expressions/ExpressionsService.cs index c76412f91..f871dbc0f 100644 --- a/src/Core/DataManager/Expressions/ExpressionsService.cs +++ b/src/Core/DataManager/Expressions/ExpressionsService.cs @@ -42,7 +42,7 @@ public ExpressionsService( return await GetExpressionValueAsync(field.DefaultValue, field, formStateData); } - public string? ParseExpression( + public string? ReplaceExpressionWithParsedValues( string? expression, FormStateData formStateData) { diff --git a/src/Core/DataManager/Expressions/Extensions/ExpressionProvideEnumerableExtensions.cs b/src/Core/DataManager/Expressions/Extensions/ExpressionProvideEnumerableExtensions.cs new file mode 100644 index 000000000..303db3a08 --- /dev/null +++ b/src/Core/DataManager/Expressions/Extensions/ExpressionProvideEnumerableExtensions.cs @@ -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 expressionProviders) + { + return expressionProviders.Where(p => p is IAsyncExpressionProvider).Select(p => p.Prefix).ToArray(); + } + + public static string[] GetBooleanProvidersPrefixes(this IEnumerable expressionProviders) + { + return expressionProviders.Where(p => p is IBooleanExpressionProvider).Select(p => p.Prefix).ToArray(); + } +} \ No newline at end of file diff --git a/src/Core/DataManager/Expressions/Providers/InMemoryExpressionProvider.cs b/src/Core/DataManager/Expressions/Providers/DataTableExpressionProvider.cs similarity index 88% rename from src/Core/DataManager/Expressions/Providers/InMemoryExpressionProvider.cs rename to src/Core/DataManager/Expressions/Providers/DataTableExpressionProvider.cs index f27db22e7..2fbb9f2b9 100644 --- a/src/Core/DataManager/Expressions/Providers/InMemoryExpressionProvider.cs +++ b/src/Core/DataManager/Expressions/Providers/DataTableExpressionProvider.cs @@ -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(); diff --git a/src/Core/DataManager/Expressions/Providers/SqlExpressionProvider.cs b/src/Core/DataManager/Expressions/Providers/SqlExpressionProvider.cs index bd5ad895d..e4b9179da 100644 --- a/src/Core/DataManager/Expressions/Providers/SqlExpressionProvider.cs +++ b/src/Core/DataManager/Expressions/Providers/SqlExpressionProvider.cs @@ -20,7 +20,7 @@ public SqlExpressionProvider(IEntityRepository entityRepository) } public string Prefix => "sql"; - public string Title => "T-SQL"; + public string Title => "SQL"; public async Task EvaluateAsync(string expression, IDictionary parsedValues) { diff --git a/src/Core/DataManager/Expressions/Providers/ValueExpressionProvider.cs b/src/Core/DataManager/Expressions/Providers/ValueExpressionProvider.cs index 52870bca8..7b031ede9 100644 --- a/src/Core/DataManager/Expressions/Providers/ValueExpressionProvider.cs +++ b/src/Core/DataManager/Expressions/Providers/ValueExpressionProvider.cs @@ -15,8 +15,8 @@ private static object EvalutateObject(string expression, IDictionary parsedValues) diff --git a/src/Core/DataManager/Importation/DataImportationWorker.cs b/src/Core/DataManager/Importation/DataImportationWorker.cs index 85574e461..1dafb5e59 100644 --- a/src/Core/DataManager/Importation/DataImportationWorker.cs +++ b/src/Core/DataManager/Importation/DataImportationWorker.cs @@ -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)); } @@ -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!)); } diff --git a/src/Core/DataManager/Services/ElementMapService.cs b/src/Core/DataManager/Services/ElementMapService.cs index 154dde90c..35d5eec65 100644 --- a/src/Core/DataManager/Services/ElementMapService.cs +++ b/src/Core/DataManager/Services/ElementMapService.cs @@ -49,7 +49,7 @@ private IDictionary 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; } } diff --git a/src/Core/UI/Components/Actions/ActionScripts.cs b/src/Core/UI/Components/Actions/ActionScripts.cs index b673ffe5b..d80d4135f 100644 --- a/src/Core/UI/Components/Actions/ActionScripts.cs +++ b/src/Core/UI/Components/Actions/ActionScripts.cs @@ -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) }; diff --git a/src/Core/UI/Components/Controls/Lookup/LookupParameters.cs b/src/Core/UI/Components/Controls/Lookup/LookupParameters.cs index a51ee1748..01212feec 100644 --- a/src/Core/UI/Components/Controls/Lookup/LookupParameters.cs +++ b/src/Core/UI/Components/Controls/Lookup/LookupParameters.cs @@ -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('='); diff --git a/src/Core/UI/Components/DataPanel/JJDataPanel.cs b/src/Core/UI/Components/DataPanel/JJDataPanel.cs index f47a2938e..cc2b92c3b 100644 --- a/src/Core/UI/Components/DataPanel/JJDataPanel.cs +++ b/src/Core/UI/Components/DataPanel/JJDataPanel.cs @@ -324,7 +324,7 @@ internal async Task GetUrlRedirectResult(ActionMap actionMa var urlRedirectAction = actionMap.GetAction(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 { diff --git a/src/Core/UI/Components/FormView/JJFormView.cs b/src/Core/UI/Components/FormView/JJFormView.cs index d5247390b..4b9dd41a3 100644 --- a/src/Core/UI/Components/FormView/JJFormView.cs +++ b/src/Core/UI/Components/FormView/JJFormView.cs @@ -658,7 +658,7 @@ private async Task GetSqlCommandActionResult() var sqlAction = CurrentActionMap!.GetAction(FormElement); try { - var sqlCommand = ExpressionsService.ParseExpression(sqlAction.CommandSql, await GetFormStateDataAsync()); + var sqlCommand = ExpressionsService.ReplaceExpressionWithParsedValues(sqlAction.CommandSql, await GetFormStateDataAsync()); await EntityRepository.SetCommandAsync(new DataAccessCommand(sqlCommand!)); } diff --git a/src/Core/UI/Components/GridView/GridSqlCommandAction.cs b/src/Core/UI/Components/GridView/GridSqlCommandAction.cs index 861cd1bc0..fe3cd70a7 100644 --- a/src/Core/UI/Components/GridView/GridSqlCommandAction.cs +++ b/src/Core/UI/Components/GridView/GridSqlCommandAction.cs @@ -63,7 +63,7 @@ private async Task ExecuteOnList(SqlCommandAction sqlCommandAction, List().BindConfiguration("JJMasterData:MongoDB"); + builder.Services.AddOptions().BindConfiguration("JJMasterData:MongoDB"); builder.Services.Replace(ServiceDescriptor.Transient()); return builder; } - public static MasterDataServiceBuilder WithMongoDbDataDictionary(this MasterDataServiceBuilder builder, Action options) + public static MasterDataServiceBuilder WithMongoDbDataDictionary(this MasterDataServiceBuilder builder, Action options) { ConfigureMappers(); builder.Services.Configure(options); @@ -32,7 +32,7 @@ public static MasterDataServiceBuilder WithMongoDbDataDictionary(this MasterData public static MasterDataServiceBuilder WithMongoDbDataDictionary(this MasterDataServiceBuilder builder, IConfiguration configuration) { ConfigureMappers(); - builder.Services.AddOptions().Bind(configuration); + builder.Services.AddOptions().Bind(configuration); builder.Services.Replace(ServiceDescriptor.Transient()); return builder; } diff --git a/src/Plugins/MongoDB/Models/JJMasterDataMongoDBOptions.cs b/src/Plugins/MongoDB/Models/MasterDataMongoDBOptions.cs similarity index 84% rename from src/Plugins/MongoDB/Models/JJMasterDataMongoDBOptions.cs rename to src/Plugins/MongoDB/Models/MasterDataMongoDBOptions.cs index 08f0d4092..dc9f2706d 100644 --- a/src/Plugins/MongoDB/Models/JJMasterDataMongoDBOptions.cs +++ b/src/Plugins/MongoDB/Models/MasterDataMongoDBOptions.cs @@ -1,6 +1,6 @@ namespace JJMasterData.MongoDB.Models; -public class JJMasterDataMongoDBOptions +public class MasterDataMongoDBOptions { public string ConnectionString { get; set; } = null!; diff --git a/src/Plugins/MongoDB/README.MD b/src/Plugins/MongoDB/README.MD index ca6611bac..bec02326b 100644 --- a/src/Plugins/MongoDB/README.MD +++ b/src/Plugins/MongoDB/README.MD @@ -11,10 +11,10 @@ makes more sense to store metadata in JSON format in a NoSQL database than in a relational database.
In the future it is also planned to implement the forms data storage (`IEntityRepository`). -## Usage -Add the following method to `JJServiceBuilder`: +## Configuration +Add the following method to `MasterDataServiceBuilder`: ```cs -builder.AddJJMasterDataWeb(settingsPath) +builder.AddJJMasterDataWeb() .WithMongoDBDataDictionaryRepository(mongo => { mongo.ConnectionString = "mongodb://localhost:27017"; diff --git a/src/Plugins/MongoDB/Repository/MongoDbDataDictionaryRepository.cs b/src/Plugins/MongoDB/Repository/MongoDbDataDictionaryRepository.cs index 625d0143e..b2e110e2a 100644 --- a/src/Plugins/MongoDB/Repository/MongoDbDataDictionaryRepository.cs +++ b/src/Plugins/MongoDB/Repository/MongoDbDataDictionaryRepository.cs @@ -13,7 +13,7 @@ public class MongoDBDataDictionaryRepository : IDataDictionaryRepository { private readonly IMongoCollection _formElementCollection; - public MongoDBDataDictionaryRepository(IOptions options) + public MongoDBDataDictionaryRepository(IOptions options) { var mongoClient = new MongoClient( options.Value.ConnectionString); diff --git a/src/Plugins/NCalc/Configuration/MasterDataServiceBuilderExtensions.cs b/src/Plugins/NCalc/Configuration/MasterDataServiceBuilderExtensions.cs new file mode 100644 index 000000000..000879c2e --- /dev/null +++ b/src/Plugins/NCalc/Configuration/MasterDataServiceBuilderExtensions.cs @@ -0,0 +1,13 @@ +using JJMasterData.Commons.Configuration; +using JJMasterData.Core.Configuration; + +namespace JJMasterData.NCalc.Configuration; + +public static class MasterDataServiceBuilderExtensions +{ + public static MasterDataServiceBuilder WithNCalcExpressionProvider(this MasterDataServiceBuilder builder) + { + builder.WithExpressionProvider(); + return builder; + } +} \ No newline at end of file diff --git a/src/Plugins/NCalc/NCalc.csproj b/src/Plugins/NCalc/NCalc.csproj new file mode 100644 index 000000000..064b55b52 --- /dev/null +++ b/src/Plugins/NCalc/NCalc.csproj @@ -0,0 +1,21 @@ + + + + net7.0 + enable + enable + JJMasterData.NCalc + JJMasterData.NCalc + + + + + + + + + + + + + diff --git a/src/Plugins/NCalc/NCalcExpressionProvider.cs b/src/Plugins/NCalc/NCalcExpressionProvider.cs new file mode 100644 index 000000000..549d00258 --- /dev/null +++ b/src/Plugins/NCalc/NCalcExpressionProvider.cs @@ -0,0 +1,26 @@ +using JJMasterData.Core.DataManager.Expressions; +using JJMasterData.Core.DataManager.Expressions.Abstractions; +using NCalc; +using AsyncExpression = NCalcAsync.Expression; + +namespace JJMasterData.NCalc; + +public class NCalcExpressionProvider : IAsyncExpressionProvider, IBooleanExpressionProvider +{ + public string Prefix => "ncalc"; + public string Title => "NCalc"; + + public bool Evaluate(string expression, IDictionary parsedValues) + { + var replacedExpression = ExpressionHelper.ReplaceExpression(expression, parsedValues); + var ncalcExpression = new Expression(replacedExpression, EvaluateOptions.IgnoreCase); + return (bool)ncalcExpression.Evaluate(); + } + + public async Task EvaluateAsync(string expression, IDictionary parsedValues) + { + var replacedExpression = ExpressionHelper.ReplaceExpression(expression, parsedValues); + var ncalcAsyncExpression = new AsyncExpression(replacedExpression, NCalcAsync.EvaluateOptions.IgnoreCase); + return await ncalcAsyncExpression.EvaluateAsync(); + } +} \ No newline at end of file diff --git a/src/Plugins/NCalc/README.MD b/src/Plugins/NCalc/README.MD new file mode 100644 index 000000000..e8263f868 --- /dev/null +++ b/src/Plugins/NCalc/README.MD @@ -0,0 +1,22 @@ +# JJMasterData.NCalc + +## Configuration +Add the following method to `MasterDataServiceBuilder`: +```cs +builder.AddJJMasterDataWeb().WithNCalcExpressionProvider(); +``` + +## Usage +Expressions are parsed just like the Value and DataTable providers. Using {}. + +NCalc support more operators than DataTable.Compute, like "if" statements. + +``` +if('{MyColumn}',1,0) +``` + +Check [NCalc wiki](https://github.com/ncalc/ncalc/wiki) for more information. + +## Differences from DataTable.Compute +- NCalc is a mathematical expressions evaluator not a SQL-like syntax parser +- Better performance is expected \ No newline at end of file diff --git a/src/Web/Scripts/CodeMirrorWrapper.ts b/src/Web/Scripts/CodeMirrorWrapper.ts index 92f4e917a..70726b7d4 100644 --- a/src/Web/Scripts/CodeMirrorWrapper.ts +++ b/src/Web/Scripts/CodeMirrorWrapper.ts @@ -28,7 +28,7 @@ class CodeMirrorWrapper{ indentWithTabs: true, smartIndent: true, lineNumbers: true, - autofocus: true, + autofocus: false, autohint: true, extraKeys: { "Ctrl-Space": "autocomplete" } }); diff --git a/src/Web/Scripts/ExpressionTagHelper.ts b/src/Web/Scripts/ExpressionTagHelper.ts index eff97a1f4..decb8fe72 100644 --- a/src/Web/Scripts/ExpressionTagHelper.ts +++ b/src/Web/Scripts/ExpressionTagHelper.ts @@ -4,16 +4,7 @@ function listenExpressionType(name, hintList, isBoolean) { const expressionValueInput = document.getElementById(name + '-ExpressionValue') as HTMLInputElement; const expressionValueEditor = document.getElementById(name + '-ExpressionValueEditor') as HTMLInputElement; - if (selectedType === 'sql' || selectedType == 'exp') { - const textArea = document.createElement('textarea'); - textArea.setAttribute('name', name + '-ExpressionValue'); - textArea.setAttribute('id', name + '-ExpressionValue'); - textArea.setAttribute('class', 'form-control'); - textArea.innerText = expressionValueInput.value; - expressionValueEditor.innerHTML = textArea.outerHTML; - CodeMirrorWrapper.setupCodeMirror(name + '-ExpressionValue', { mode: 'text/x-sql', singleLine: true, hintList: hintList, hintKey: '{' }); - } - else if(selectedType ==='val' && isBoolean === true){ + if(selectedType ==='val' && isBoolean === true){ const div = document.createElement('div'); div.classList.add('form-switch', 'form-switch-md', 'form-check'); @@ -42,22 +33,13 @@ function listenExpressionType(name, hintList, isBoolean) { expressionValueEditor.innerHTML = div.outerHTML; } else { - const input = document.createElement('input'); - input.setAttribute('type', 'text'); - input.setAttribute('class', 'form-control'); - input.setAttribute('name', name + '-ExpressionValue'); - input.setAttribute('id', name + '-ExpressionValue'); - input.value = expressionValueInput.value; - - // @ts-ignore - if (expressionValueInput.codeMirrorInstance) { - // @ts-ignore - expressionValueInput.codeMirrorInstance.setOption('mode', 'text/x-csrc'); - // @ts-ignore - expressionValueInput.codeMirrorInstance.getWrapperElement().parentNode.removeChild(expressionValueInput.codeMirrorInstance.getWrapperElement()); - } - - expressionValueEditor.innerHTML = input.outerHTML; + const textArea = document.createElement('textarea'); + textArea.setAttribute('name', name + '-ExpressionValue'); + textArea.setAttribute('id', name + '-ExpressionValue'); + textArea.setAttribute('class', 'form-control'); + textArea.innerText = expressionValueInput.value; + expressionValueEditor.innerHTML = textArea.outerHTML; + CodeMirrorWrapper.setupCodeMirror(name + '-ExpressionValue', { mode: 'text/x-sql', singleLine: true, hintList: hintList, hintKey: '{' }); } }); } \ No newline at end of file diff --git a/src/Web/TagHelpers/ExpressionTagHelper.cs b/src/Web/TagHelpers/ExpressionTagHelper.cs index 4b0c9d764..efed43cea 100644 --- a/src/Web/TagHelpers/ExpressionTagHelper.cs +++ b/src/Web/TagHelpers/ExpressionTagHelper.cs @@ -189,7 +189,7 @@ private HtmlBuilder GetEditorHtml(string name, string? selectedExpressionType, s }); } - else if (selectedExpressionType is "sql" or "exp") + else { div.Append(HtmlTag.TextArea, textArea => { @@ -200,15 +200,6 @@ private HtmlBuilder GetEditorHtml(string name, string? selectedExpressionType, s div.AppendScript( @$"onDOMReady(()=>{{CodeMirrorWrapper.setupCodeMirror('{name}-ExpressionValue',{{mode: 'text/x-sql',singleLine:true, hintList: {codeMirrorHintList}, hintKey: '{{'}});}})"); } - else - { - div.Append(HtmlTag.Input, input => - { - input.WithNameAndId(name + "-ExpressionValue"); - input.WithValue(selectedExpressionValue); - input.WithCssClass("form-control"); - }); - } }); return div; } diff --git a/src/Web/wwwroot/js/jjmasterdata/jjmasterdata.js b/src/Web/wwwroot/js/jjmasterdata/jjmasterdata.js index 613ca4895..0c85509d8 100644 --- a/src/Web/wwwroot/js/jjmasterdata/jjmasterdata.js +++ b/src/Web/wwwroot/js/jjmasterdata/jjmasterdata.js @@ -258,7 +258,7 @@ class CodeMirrorWrapper { indentWithTabs: true, smartIndent: true, lineNumbers: true, - autofocus: true, + autofocus: false, autohint: true, extraKeys: { "Ctrl-Space": "autocomplete" } }); @@ -826,16 +826,7 @@ function listenExpressionType(name, hintList, isBoolean) { const selectedType = this.value; const expressionValueInput = document.getElementById(name + '-ExpressionValue'); const expressionValueEditor = document.getElementById(name + '-ExpressionValueEditor'); - if (selectedType === 'sql' || selectedType == 'exp') { - const textArea = document.createElement('textarea'); - textArea.setAttribute('name', name + '-ExpressionValue'); - textArea.setAttribute('id', name + '-ExpressionValue'); - textArea.setAttribute('class', 'form-control'); - textArea.innerText = expressionValueInput.value; - expressionValueEditor.innerHTML = textArea.outerHTML; - CodeMirrorWrapper.setupCodeMirror(name + '-ExpressionValue', { mode: 'text/x-sql', singleLine: true, hintList: hintList, hintKey: '{' }); - } - else if (selectedType === 'val' && isBoolean === true) { + if (selectedType === 'val' && isBoolean === true) { const div = document.createElement('div'); div.classList.add('form-switch', 'form-switch-md', 'form-check'); const expressionValueInputName = name + '-ExpressionValue'; @@ -857,17 +848,13 @@ function listenExpressionType(name, hintList, isBoolean) { expressionValueEditor.innerHTML = div.outerHTML; } else { - const input = document.createElement('input'); - input.setAttribute('type', 'text'); - input.setAttribute('class', 'form-control'); - input.setAttribute('name', name + '-ExpressionValue'); - input.setAttribute('id', name + '-ExpressionValue'); - input.value = expressionValueInput.value; - if (expressionValueInput.codeMirrorInstance) { - expressionValueInput.codeMirrorInstance.setOption('mode', 'text/x-csrc'); - expressionValueInput.codeMirrorInstance.getWrapperElement().parentNode.removeChild(expressionValueInput.codeMirrorInstance.getWrapperElement()); - } - expressionValueEditor.innerHTML = input.outerHTML; + const textArea = document.createElement('textarea'); + textArea.setAttribute('name', name + '-ExpressionValue'); + textArea.setAttribute('id', name + '-ExpressionValue'); + textArea.setAttribute('class', 'form-control'); + textArea.innerText = expressionValueInput.value; + expressionValueEditor.innerHTML = textArea.outerHTML; + CodeMirrorWrapper.setupCodeMirror(name + '-ExpressionValue', { mode: 'text/x-sql', singleLine: true, hintList: hintList, hintKey: '{' }); } }); }