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

RQL (part 1) - Match and search expressions #166

Open
wants to merge 20 commits into
base: release-3.0.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 3 additions & 0 deletions .github/workflows/dotnet-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ jobs:
- name: Pack Rules.Framework.Providers.MongoDb
run: dotnet pack src/Rules.Framework.Providers.MongoDb/Rules.Framework.Providers.MongoDb.csproj --include-symbols -c Release /p:Version=$BUILD_VERSION

- name: Pack Rules.Framework.Rql
run: dotnet pack src/Rules.Framework.Rql/Rules.Framework.Rql.csproj --include-symbols -c Release /p:Version=$BUILD_VERSION

- name: Pack Rules.Framework.WebUI
run: dotnet pack src/Rules.Framework.WebUI/Rules.Framework.WebUI.csproj --include-symbols -c Release /p:Version=$BUILD_VERSION

Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -560,5 +560,5 @@ coverage-outputs/**
.env

### Rules Framework Web UI ###
!src/Rules.Framework.WebUI/node_modules/
src/Rules.Framework.WebUI/Assets/dist/
!samples/Rules.Framework.WebUI.Sample/Properties/launchSettings.json
28 changes: 28 additions & 0 deletions rules-framework.sln
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rules.Framework.WebUI.Tests
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rules.Framework.BenchmarkTests", "tests\Rules.Framework.BenchmarkTests\Rules.Framework.BenchmarkTests.csproj", "{16C9F383-3B58-4911-9D26-7FDB907DD0D2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rules.Framework.RqlReplTester", "tests\Rules.Framework.RqlReplTester\Rules.Framework.RqlReplTester.csproj", "{F21A8797-89E4-4EB3-92AF-4A051C3E579A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rules.Framework.Rql", "src\Rules.Framework.Rql\Rules.Framework.Rql.csproj", "{76298D4D-537C-4522-91AB-0084535B1FF0}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rules.Framework.Rql.Tests", "tests\Rules.Framework.Rql.Tests\Rules.Framework.Rql.Tests.csproj", "{776E54A7-9099-4EBD-9C62-A371DFED58E5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Rules.Framework.Rql.IntegrationTests", "tests\Rules.Framework.Rql.IntegrationTests\Rules.Framework.Rql.IntegrationTests.csproj", "{C24A2234-AD6A-4377-9FAA-9CC58386107C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -98,6 +106,22 @@ Global
{16C9F383-3B58-4911-9D26-7FDB907DD0D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{16C9F383-3B58-4911-9D26-7FDB907DD0D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{16C9F383-3B58-4911-9D26-7FDB907DD0D2}.Release|Any CPU.Build.0 = Release|Any CPU
{F21A8797-89E4-4EB3-92AF-4A051C3E579A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F21A8797-89E4-4EB3-92AF-4A051C3E579A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F21A8797-89E4-4EB3-92AF-4A051C3E579A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F21A8797-89E4-4EB3-92AF-4A051C3E579A}.Release|Any CPU.Build.0 = Release|Any CPU
{76298D4D-537C-4522-91AB-0084535B1FF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{76298D4D-537C-4522-91AB-0084535B1FF0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{76298D4D-537C-4522-91AB-0084535B1FF0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{76298D4D-537C-4522-91AB-0084535B1FF0}.Release|Any CPU.Build.0 = Release|Any CPU
{776E54A7-9099-4EBD-9C62-A371DFED58E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{776E54A7-9099-4EBD-9C62-A371DFED58E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{776E54A7-9099-4EBD-9C62-A371DFED58E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{776E54A7-9099-4EBD-9C62-A371DFED58E5}.Release|Any CPU.Build.0 = Release|Any CPU
{C24A2234-AD6A-4377-9FAA-9CC58386107C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C24A2234-AD6A-4377-9FAA-9CC58386107C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C24A2234-AD6A-4377-9FAA-9CC58386107C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C24A2234-AD6A-4377-9FAA-9CC58386107C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -116,6 +140,10 @@ Global
{7CE82611-FEC1-49E9-91FB-4C3ADF5ED56F} = {AEE746EC-CEAA-4892-8C29-0CAAB97A23A8}
{29DC6661-4F0C-46F7-AC91-968700D13C11} = {74E24C97-8EE4-4B69-AECD-4765FD2C751F}
{16C9F383-3B58-4911-9D26-7FDB907DD0D2} = {74E24C97-8EE4-4B69-AECD-4765FD2C751F}
{F21A8797-89E4-4EB3-92AF-4A051C3E579A} = {74E24C97-8EE4-4B69-AECD-4765FD2C751F}
{76298D4D-537C-4522-91AB-0084535B1FF0} = {AEE746EC-CEAA-4892-8C29-0CAAB97A23A8}
{776E54A7-9099-4EBD-9C62-A371DFED58E5} = {74E24C97-8EE4-4B69-AECD-4765FD2C751F}
{C24A2234-AD6A-4377-9FAA-9CC58386107C} = {74E24C97-8EE4-4B69-AECD-4765FD2C751F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FA9D4C31-972B-49C2-9F63-C56ED766DAB0}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ namespace Rules.Framework.WebUI.Sample.Engine
using global::Rules.Framework;
using global::Rules.Framework.Builder.Generic;

internal class RuleSpecificationBase<TContentType, TConditionType>
internal class RuleSpecificationBase<TRuleset, TCondition>
{
public RuleSpecificationBase(
RuleBuilderResult<TContentType, TConditionType> ruleBuilderResult,
RuleBuilderResult<TRuleset, TCondition> ruleBuilderResult,
RuleAddPriorityOption ruleAddPriorityOption)
{
this.RuleBuilderResult = ruleBuilderResult;
Expand All @@ -15,6 +15,6 @@ public RuleSpecificationBase(

public RuleAddPriorityOption RuleAddPriorityOption { get; set; }

public RuleBuilderResult<TContentType, TConditionType> RuleBuilderResult { get; set; }
public RuleBuilderResult<TRuleset, TCondition> RuleBuilderResult { get; set; }
}
}
60 changes: 34 additions & 26 deletions samples/Rules.Framework.WebUI.Sample/Program.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
namespace Rules.Framework.WebUI.Sample
{
using global::Rules.Framework.IntegrationTests.Common.Scenarios;
using global::Rules.Framework.IntegrationTests.Common.Scenarios.Scenario8;
using global::Rules.Framework.WebUI.Sample.Engine;
using global::Rules.Framework.WebUI.Sample.ReadmeExample;
using global::Rules.Framework.WebUI.Sample.Rules;
Expand All @@ -11,7 +13,32 @@ public static void Main(string[] args)
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddControllersWithViews()
.AddRulesFrameworkWebUI(registrar =>
{
registrar.AddInstance("Readme example", (_, _) => new BasicRulesEngineExample().RulesEngine)
.AddInstance("Random rules example", async (_, _) =>
{
var rulesProvider = new RulesEngineProvider(new RulesBuilder(new List<IRuleSpecificationsProvider>()
{
new RulesRandomFactory()
}));

return await rulesProvider.GetRulesEngineAsync();
})
.AddInstance("Poker combinations example", async (_, _) =>
{
var rulesEngine = RulesEngineBuilder.CreateRulesEngine()
.SetInMemoryDataSource()
.Build();

await ScenarioLoader.LoadScenarioAsync(rulesEngine, new Scenario8Data());

return rulesEngine;
});
});

builder.Logging.SetMinimumLevel(LogLevel.Trace).AddConsole();

var app = builder.Build();

Expand All @@ -24,43 +51,24 @@ public static void Main(string[] args)
app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAntiforgery();

app.UseEndpoints(endpoints => { endpoints.MapControllers(); });

AddRulesFrameworkUI(app, useReadmeExample: false);
app.UseRulesFrameworkWebUI(opt =>
{
opt.DocumentTitle = "Sample rules";
});

app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();
}

private static void AddRulesFrameworkUI(IApplicationBuilder app, bool useReadmeExample = false)
{
if (useReadmeExample)
{
app.UseRulesFrameworkWebUI(new BasicRulesEngineExample().RulesEngine);

return;
}

var rulesProvider = new RulesEngineProvider(new RulesBuilder(new List<IRuleSpecificationsProvider>()
{
new RulesRandomFactory()
}));

var rulesEngine = rulesProvider
.GetRulesEngineAsync()
.GetAwaiter()
.GetResult();

app.UseRulesFrameworkWebUI(rulesEngine);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "rules/index.html",
"launchUrl": "rules-ui/instance-selection",
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ namespace Rules.Framework.WebUI.Sample.ReadmeExample
{
using System;
using global::Rules.Framework.WebUI.Sample.Engine;
using global::Rules.Framework.WebUI.Sample.Enums;

internal class BasicRulesEngineExample
{
Expand Down Expand Up @@ -35,7 +34,7 @@ protected void AddRules(IEnumerable<RuleSpecificationBase<BasicRulesetNames, Bas

protected void CreateRulesets()
{
foreach (var rulesetName in Enum.GetValues<RulesetNames>())
foreach (var rulesetName in Enum.GetValues<BasicRulesetNames>())
{
this.RulesEngine.CreateRulesetAsync(rulesetName.ToString())
.GetAwaiter()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
Expand All @@ -17,6 +17,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Rules.Framework.WebUI\Rules.Framework.WebUI.csproj" />
<ProjectReference Include="..\..\src\Rules.Framework\Rules.Framework.csproj" />
<ProjectReference Include="..\..\tests\Rules.Framework.IntegrationTests.Common\Rules.Framework.IntegrationTests.Common.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,16 @@ public async Task AddRuleAsync(Rule rule)
/// <summary>
/// Creates a new ruleset on the data source.
/// </summary>
/// <param name="contentType">Type of the content.</param>
public async Task CreateRulesetAsync(string contentType)
/// <param name="ruleset">The ruleset name.</param>
public async Task CreateRulesetAsync(string ruleset)
{
var rulesetsCollection = this.mongoDatabase.GetCollection<RulesetDataModel>(this.mongoDbProviderSettings.RulesetsCollectionName);

var rulesetDataModel = new RulesetDataModel
{
Creation = DateTime.UtcNow,
Id = Guid.NewGuid(),
Name = contentType,
Name = ruleset,
};

await rulesetsCollection.InsertOneAsync(rulesetDataModel).ConfigureAwait(false);
Expand All @@ -74,7 +74,7 @@ public async Task CreateRulesetAsync(string contentType)
public async Task<IEnumerable<Rule>> GetRulesAsync(string ruleset, DateTime dateBegin, DateTime dateEnd)
{
var getRulesByRulesetAndDatesInterval = MongoDbProviderRulesDataSource
.BuildFilterByContentTypeAndDatesInterval(ruleset, dateBegin, dateEnd);
.BuildFilterByRulesetAndDatesInterval(ruleset, dateBegin, dateEnd);

return await this.GetRulesAsync(getRulesByRulesetAndDatesInterval).ConfigureAwait(false);
}
Expand All @@ -98,7 +98,7 @@ public Task<IEnumerable<Rule>> GetRulesByAsync(RulesFilterArgs rulesFilterArgs)
}

/// <summary>
/// Gets the content types from the data source.
/// Gets the rulesets from the data source.
/// </summary>
/// <returns></returns>
public async Task<IEnumerable<Ruleset>> GetRulesetsAsync()
Expand Down Expand Up @@ -149,7 +149,7 @@ public async Task UpdateRuleAsync(Rule rule)
await rulesCollection.UpdateOneAsync(filterDefinition, updateDefinition).ConfigureAwait(false);
}

private static FilterDefinition<RuleDataModel> BuildFilterByContentTypeAndDatesInterval(string ruleset, DateTime dateBegin, DateTime dateEnd)
private static FilterDefinition<RuleDataModel> BuildFilterByRulesetAndDatesInterval(string ruleset, DateTime dateBegin, DateTime dateEnd)
{
var rulesetFilter = Builders<RuleDataModel>.Filter.Eq(x => x.Ruleset, ruleset);

Expand Down Expand Up @@ -185,11 +185,11 @@ private static FilterDefinition<RuleDataModel> BuildFilterFromRulesFilterArgs(Ru
return filtersToApply.Any() ? Builders<RuleDataModel>.Filter.And(filtersToApply) : Builders<RuleDataModel>.Filter.Empty;
}

private async Task<IEnumerable<Rule>> GetRulesAsync(FilterDefinition<RuleDataModel> getRulesByContentTypeAndDatesInterval)
private async Task<IEnumerable<Rule>> GetRulesAsync(FilterDefinition<RuleDataModel> getRulesByRulesetAndDatesInterval)
{
var rulesCollection = this.mongoDatabase.GetCollection<RuleDataModel>(this.mongoDbProviderSettings.RulesCollectionName);

var fetchedRulesCursor = await rulesCollection.FindAsync(getRulesByContentTypeAndDatesInterval).ConfigureAwait(false);
var fetchedRulesCursor = await rulesCollection.FindAsync(getRulesByRulesetAndDatesInterval).ConfigureAwait(false);

var fetchedRules = await fetchedRulesCursor.ToListAsync().ConfigureAwait(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public MongoDbProviderSettings()
/// <summary>
/// Gets or sets the name of the rulesets collection.
/// </summary>
/// <value>The name of the content types collection.</value>
/// <value>The name of the rulesets collection.</value>
public string RulesetsCollectionName { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@ public DynamicToStrongTypeContentSerializationProvider()
System.Threading.LazyThreadSafetyMode.PublicationOnly);
}

/// <summary>
/// Gets the content serializer associated with the given <paramref name="contentType"/>.
/// </summary>
/// <param name="contentType">the content type.</param>
/// <returns>the content serializer to deal with contents for specified content type.</returns>
public IContentSerializer GetContentSerializer(string contentType) => this.contentSerializerLazy.Value;
/// <inheritdoc/>
public IContentSerializer GetContentSerializer(string ruleset) => this.contentSerializerLazy.Value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ public object Deserialize(object serializedContent, Type type)
throw new ArgumentNullException(nameof(type));
}

var serializedContentType = serializedContent.GetType();
if (serializedContentType.IsValueType || stringType.IsAssignableFrom(serializedContentType))
var serializedRuleset = serializedContent.GetType();
if (serializedRuleset.IsValueType || stringType.IsAssignableFrom(serializedRuleset))
{
return Parse(serializedContent, type);
}

if (!expandoObjectType.IsAssignableFrom(serializedContentType))
if (!expandoObjectType.IsAssignableFrom(serializedRuleset))
{
throw new NotSupportedException($"The serialized content type is not supported for deserialization: {serializedContent.GetType().FullName}");
throw new NotSupportedException($"The serialized ruleset is not supported for deserialization: {serializedContent.GetType().FullName}");
}

if (type == objectType || type == expandoObjectType)
Expand Down Expand Up @@ -98,9 +98,9 @@ private static object DeserializeToType(IDictionary<string, object> serializedCo
throw new NotSupportedException($"The target type '{type.FullName}' must define a default (no parameters) constructor.", mme);
}

foreach (string key in serializedContentDictionary.Keys)
foreach (var key in serializedContentDictionary.Keys)
{
if (reflectedProperties.TryGetValue(key, out PropertyInfo currentPropertyInfo))
if (reflectedProperties.TryGetValue(key, out var currentPropertyInfo))
{
var serializedPropertyValue = serializedContentDictionary[key];

Expand Down
5 changes: 5 additions & 0 deletions src/Rules.Framework.Rql/AssemblyMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Rules.Framework.Rql.Tests")]
[assembly: InternalsVisibleTo("Rules.Framework.Rql.IntegrationTests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
26 changes: 26 additions & 0 deletions src/Rules.Framework.Rql/Ast/Expressions/AssignmentExpression.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
namespace Rules.Framework.Rql.Ast.Expressions
{
using System;
using System.Diagnostics.CodeAnalysis;
using Rules.Framework.Rql.Tokens;

[ExcludeFromCodeCoverage]
internal class AssignmentExpression : Expression
{
public AssignmentExpression(Expression left, Token assign, Expression right)
: base(left.BeginPosition, right.EndPosition)
{
this.Left = left;
this.Assign = assign;
this.Right = right;
}

public Token Assign { get; }

public Expression Left { get; }

public Expression Right { get; }

public override T Accept<T>(IExpressionVisitor<T> visitor) => throw new NotSupportedException("To be supported in a future release.");
}
}
Loading