Skip to content

Commit 6ecfc10

Browse files
authored
fix: using Operators.In are not working properly with MongoDB (#111)
1 parent 953667a commit 6ecfc10

11 files changed

Lines changed: 227 additions & 31 deletions

File tree

src/Rules.Framework.Providers.InMemory/Rules.Framework.Providers.InMemory.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,6 @@
3636
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3737
</PackageReference>
3838
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.1.1" />
39-
<PackageReference Include="Rules.Framework" Version="1.6.0" />
39+
<PackageReference Include="Rules.Framework" Version="1.7.3" />
4040
</ItemGroup>
4141
</Project>

src/Rules.Framework.Providers.MongoDb/DataModel/ValueConditionNodeDataModel.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
namespace Rules.Framework.Providers.MongoDb.DataModel
32
{
43
using MongoDB.Bson.Serialization.Attributes;
@@ -13,12 +12,12 @@ internal sealed class ValueConditionNodeDataModel : ConditionNodeDataModel
1312
[BsonElement(Order = 2)]
1413
[BsonRepresentation(MongoDB.Bson.BsonType.String)]
1514
public DataTypes DataType { get; set; }
16-
15+
1716
[BsonElement(Order = 3)]
1817
[BsonRepresentation(MongoDB.Bson.BsonType.String)]
1918
public Operators Operator { get; set; }
2019

2120
[BsonElement(Order = 4)]
2221
public object Operand { get; set; }
2322
}
24-
}
23+
}

src/Rules.Framework.Providers.MongoDb/MongoDbProviderRulesDataSource.cs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ internal MongoDbProviderRulesDataSource(
4141
/// <param name="rule">The rule.</param>
4242
public async Task AddRuleAsync(Rule<TContentType, TConditionType> rule)
4343
{
44-
IMongoCollection<RuleDataModel> rulesCollection = this.mongoDatabase.GetCollection<RuleDataModel>(this.mongoDbProviderSettings.RulesCollectionName);
44+
var rulesCollection = this.mongoDatabase.GetCollection<RuleDataModel>(this.mongoDbProviderSettings.RulesCollectionName);
4545

46-
RuleDataModel ruleDataModel = this.ruleFactory.CreateRule(rule);
46+
var ruleDataModel = this.ruleFactory.CreateRule(rule);
4747

4848
await rulesCollection.InsertOneAsync(ruleDataModel).ConfigureAwait(false);
4949
}
@@ -57,7 +57,7 @@ public async Task AddRuleAsync(Rule<TContentType, TConditionType> rule)
5757
/// <returns></returns>
5858
public async Task<IEnumerable<Rule<TContentType, TConditionType>>> GetRulesAsync(TContentType contentType, DateTime dateBegin, DateTime dateEnd)
5959
{
60-
FilterDefinition<RuleDataModel> getRulesByContentTypeAndDatesInterval = MongoDbProviderRulesDataSource<TContentType, TConditionType>
60+
var getRulesByContentTypeAndDatesInterval = MongoDbProviderRulesDataSource<TContentType, TConditionType>
6161
.BuildFilterByContentTypeAndDatesInterval(contentType, dateBegin, dateEnd);
6262

6363
return await this.GetRulesAsync(getRulesByContentTypeAndDatesInterval).ConfigureAwait(false);
@@ -75,7 +75,7 @@ public Task<IEnumerable<Rule<TContentType, TConditionType>>> GetRulesByAsync(Rul
7575
throw new ArgumentNullException(nameof(rulesFilterArgs));
7676
}
7777

78-
FilterDefinition<RuleDataModel> filterDefinition = MongoDbProviderRulesDataSource<TContentType, TConditionType>
78+
var filterDefinition = MongoDbProviderRulesDataSource<TContentType, TConditionType>
7979
.BuildFilterFromRulesFilterArgs(rulesFilterArgs);
8080

8181
return this.GetRulesAsync(filterDefinition);
@@ -87,13 +87,13 @@ public Task<IEnumerable<Rule<TContentType, TConditionType>>> GetRulesByAsync(Rul
8787
/// <param name="rule">The rule.</param>
8888
public async Task UpdateRuleAsync(Rule<TContentType, TConditionType> rule)
8989
{
90-
IMongoCollection<RuleDataModel> rulesCollection = this.mongoDatabase.GetCollection<RuleDataModel>(this.mongoDbProviderSettings.RulesCollectionName);
90+
var rulesCollection = this.mongoDatabase.GetCollection<RuleDataModel>(this.mongoDbProviderSettings.RulesCollectionName);
9191

92-
RuleDataModel ruleDataModel = this.ruleFactory.CreateRule(rule);
92+
var ruleDataModel = this.ruleFactory.CreateRule(rule);
9393

94-
FilterDefinition<RuleDataModel> filterDefinition = Builders<RuleDataModel>.Filter.Eq(x => x.Name, ruleDataModel.Name);
94+
var filterDefinition = Builders<RuleDataModel>.Filter.Eq(x => x.Name, ruleDataModel.Name);
9595
FieldDefinition<RuleDataModel, object> contentField = "Content";
96-
IEnumerable<UpdateDefinition<RuleDataModel>> updateDefinitions = new UpdateDefinition<RuleDataModel>[]
96+
var updateDefinitions = new UpdateDefinition<RuleDataModel>[]
9797
{
9898
Builders<RuleDataModel>.Update.Set(contentField, (object)ruleDataModel.Content),
9999
Builders<RuleDataModel>.Update.Set(r => r.ContentType, ruleDataModel.ContentType),
@@ -104,28 +104,28 @@ public async Task UpdateRuleAsync(Rule<TContentType, TConditionType> rule)
104104
Builders<RuleDataModel>.Update.Set(r => r.RootCondition, ruleDataModel.RootCondition),
105105
};
106106

107-
UpdateDefinition<RuleDataModel> updateDefinition = Builders<RuleDataModel>.Update.Combine(updateDefinitions);
107+
var updateDefinition = Builders<RuleDataModel>.Update.Combine(updateDefinitions);
108108

109109
await rulesCollection.UpdateOneAsync(filterDefinition, updateDefinition).ConfigureAwait(false);
110110
}
111111

112112
private static FilterDefinition<RuleDataModel> BuildFilterByContentTypeAndDatesInterval(TContentType contentType, DateTime dateBegin, DateTime dateEnd)
113113
{
114-
FilterDefinition<RuleDataModel> contentTypeFilter = Builders<RuleDataModel>.Filter.Eq(x => x.ContentType, contentType.ToString());
114+
var contentTypeFilter = Builders<RuleDataModel>.Filter.Eq(x => x.ContentType, contentType.ToString());
115115

116116
// To fetch rules that begin during filtered interval but end after it.
117-
FilterDefinition<RuleDataModel> dateBeginFilter = Builders<RuleDataModel>.Filter.And(
117+
var dateBeginFilter = Builders<RuleDataModel>.Filter.And(
118118
Builders<RuleDataModel>.Filter.Gte(x => x.DateBegin, dateBegin),
119119
Builders<RuleDataModel>.Filter.Lt(x => x.DateBegin, dateEnd));
120120

121121
// To fetch rules that begun before filtered interval but end during it.
122-
FilterDefinition<RuleDataModel> dateEndFilter = Builders<RuleDataModel>.Filter.And(
122+
var dateEndFilter = Builders<RuleDataModel>.Filter.And(
123123
Builders<RuleDataModel>.Filter.Ne(x => x.DateEnd, null),
124124
Builders<RuleDataModel>.Filter.Gte(x => x.DateEnd, dateBegin),
125125
Builders<RuleDataModel>.Filter.Lt(x => x.DateEnd, dateEnd));
126126

127127
// To fetch rules that begun before and end after filtered interval.
128-
FilterDefinition<RuleDataModel> slicingFilter = Builders<RuleDataModel>.Filter.And(
128+
var slicingFilter = Builders<RuleDataModel>.Filter.And(
129129
Builders<RuleDataModel>.Filter.Lt(x => x.DateBegin, dateBegin),
130130
Builders<RuleDataModel>.Filter.Or(
131131
Builders<RuleDataModel>.Filter.Eq(x => x.DateEnd, null),
@@ -136,7 +136,7 @@ private static FilterDefinition<RuleDataModel> BuildFilterByContentTypeAndDatesI
136136

137137
private static FilterDefinition<RuleDataModel> BuildFilterFromRulesFilterArgs(RulesFilterArgs<TContentType> rulesFilterArgs)
138138
{
139-
List<FilterDefinition<RuleDataModel>> filtersToApply = new List<FilterDefinition<RuleDataModel>>(3);
139+
var filtersToApply = new List<FilterDefinition<RuleDataModel>>(3);
140140

141141
if (!object.Equals(rulesFilterArgs.ContentType, default(TContentType)))
142142
{
@@ -158,11 +158,11 @@ private static FilterDefinition<RuleDataModel> BuildFilterFromRulesFilterArgs(Ru
158158

159159
private async Task<IEnumerable<Rule<TContentType, TConditionType>>> GetRulesAsync(FilterDefinition<RuleDataModel> getRulesByContentTypeAndDatesInterval)
160160
{
161-
IMongoCollection<RuleDataModel> rulesCollection = this.mongoDatabase.GetCollection<RuleDataModel>(this.mongoDbProviderSettings.RulesCollectionName);
161+
var rulesCollection = this.mongoDatabase.GetCollection<RuleDataModel>(this.mongoDbProviderSettings.RulesCollectionName);
162162

163-
IAsyncCursor<RuleDataModel> fetchedRulesCursor = await rulesCollection.FindAsync(getRulesByContentTypeAndDatesInterval).ConfigureAwait(false);
163+
var fetchedRulesCursor = await rulesCollection.FindAsync(getRulesByContentTypeAndDatesInterval).ConfigureAwait(false);
164164

165-
List<RuleDataModel> fetchedRules = await fetchedRulesCursor.ToListAsync().ConfigureAwait(false);
165+
var fetchedRules = await fetchedRulesCursor.ToListAsync().ConfigureAwait(false);
166166

167167
return fetchedRules.Select(r => this.ruleFactory.CreateRule(r));
168168
}

src/Rules.Framework.Providers.MongoDb/RuleFactory.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,27 @@ private static IConditionNode<TConditionType> CreateValueConditionNode(IConditio
165165
.WithComparisonOperator(conditionNodeDataModel.Operator)
166166
.SetOperand(Convert.ToBoolean(conditionNodeDataModel.Operand, CultureInfo.InvariantCulture))
167167
.Build(),
168+
169+
DataTypes.ArrayInteger => conditionNodeBuilder.AsValued(conditionType)
170+
.OfDataType<IEnumerable<int>>()
171+
.WithComparisonOperator(conditionNodeDataModel.Operator)
172+
.SetOperand(conditionNodeDataModel.Operand as IEnumerable<int>)
173+
.Build(),
174+
DataTypes.ArrayDecimal => conditionNodeBuilder.AsValued(conditionType)
175+
.OfDataType<IEnumerable<decimal>>()
176+
.WithComparisonOperator(conditionNodeDataModel.Operator)
177+
.SetOperand(conditionNodeDataModel.Operand as IEnumerable<decimal>)
178+
.Build(),
179+
DataTypes.ArrayString => conditionNodeBuilder.AsValued(conditionType)
180+
.OfDataType<IEnumerable<string>>()
181+
.WithComparisonOperator(conditionNodeDataModel.Operator)
182+
.SetOperand(conditionNodeDataModel.Operand as IEnumerable<string>)
183+
.Build(),
184+
DataTypes.ArrayBoolean => conditionNodeBuilder.AsValued(conditionType)
185+
.OfDataType<IEnumerable<bool>>()
186+
.WithComparisonOperator(conditionNodeDataModel.Operator)
187+
.SetOperand(conditionNodeDataModel.Operand as IEnumerable<bool>)
188+
.Build(),
168189
_ => throw new NotSupportedException($"Unsupported data type: {conditionNodeDataModel.DataType}."),
169190
};
170191
}

src/Rules.Framework.Providers.MongoDb/Rules.Framework.Providers.MongoDb.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@
3737
</PackageReference>
3838
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
3939
<PackageReference Include="MongoDB.Driver" Version="2.13.1" />
40-
<PackageReference Include="Rules.Framework" Version="1.6.0" />
40+
<PackageReference Include="Rules.Framework" Version="1.7.3" />
4141
</ItemGroup>
4242
</Project>

src/Rules.Framework.WebUI/Rules.Framework.WebUI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
</ItemGroup>
4848

4949
<ItemGroup>
50-
<PackageReference Include="Rules.Framework" Version="1.6.0" />
50+
<PackageReference Include="Rules.Framework" Version="1.7.3" />
5151
</ItemGroup>
5252

5353
<ItemGroup>

tests/Rules.Framework.Providers.InMemory.IntegrationTests/Rules.Framework.Providers.InMemory.IntegrationTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@
2626
<ProjectReference Include="..\Rules.Framework.IntegrationTests.Common\Rules.Framework.IntegrationTests.Common.csproj" />
2727
</ItemGroup>
2828

29-
</Project>
29+
</Project>
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
namespace Rules.Framework.Providers.MongoDb.IntegrationTests.Tests.Scenario5
2+
{
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using FluentAssertions;
8+
using MongoDB.Driver;
9+
using Rules.Framework.Core;
10+
using Rules.Framework.IntegrationTests.Common.Scenarios.Scenario5;
11+
using Rules.Framework.Providers.MongoDb;
12+
using Xunit;
13+
14+
public sealed class BestServerTests : IDisposable
15+
{
16+
public static readonly IEnumerable<object[]> DataTest = new List<object[]>
17+
{
18+
new object[]
19+
{
20+
new[]
21+
{
22+
new Condition<BestServerConditions>
23+
{
24+
Type = BestServerConditions.Price,
25+
Value = 100
26+
},
27+
new Condition<BestServerConditions>
28+
{
29+
Type = BestServerConditions.Memory,
30+
Value = 12
31+
},
32+
new Condition<BestServerConditions>
33+
{
34+
Type = BestServerConditions.StoragePartionable,
35+
Value = true
36+
},
37+
new Condition<BestServerConditions>
38+
{
39+
Type = BestServerConditions.Brand,
40+
Value = "AMD"
41+
}
42+
},
43+
"Best Server Top5"
44+
},
45+
new object[]
46+
{
47+
new[]
48+
{
49+
new Condition<BestServerConditions>
50+
{
51+
Type = BestServerConditions.Price,
52+
Value = 110
53+
},
54+
new Condition<BestServerConditions>
55+
{
56+
Type = BestServerConditions.Memory,
57+
Value = 12
58+
},
59+
new Condition<BestServerConditions>
60+
{
61+
Type = BestServerConditions.StoragePartionable,
62+
Value = true
63+
},
64+
new Condition<BestServerConditions>
65+
{
66+
Type = BestServerConditions.Brand,
67+
Value = "AMD"
68+
}
69+
},
70+
"Best Server Default"
71+
}
72+
};
73+
74+
private readonly IMongoClient mongoClient;
75+
private readonly MongoDbProviderSettings mongoDbProviderSettings;
76+
77+
public BestServerTests()
78+
{
79+
this.mongoClient = CreateMongoClient();
80+
this.mongoDbProviderSettings = CreateProviderSettings();
81+
}
82+
83+
[Theory]
84+
[MemberData(nameof(DataTest))]
85+
public async Task BestServer_InEvaluation(IEnumerable<Condition<BestServerConditions>> conditions, string expectedRuleName)
86+
{
87+
// Arrange
88+
var rulesEngine = RulesEngineBuilder.CreateRulesEngine()
89+
.WithContentType<BestServerConfigurations>()
90+
.WithConditionType<BestServerConditions>()
91+
.SetMongoDbDataSource(this.mongoClient, this.mongoDbProviderSettings)
92+
.Build();
93+
94+
// Act 1 - Create rule with "in" operator
95+
var ruleBuilderResult = RuleBuilder.NewRule<BestServerConfigurations, BestServerConditions>()
96+
.WithName("Best Server Top5")
97+
.WithDatesInterval(DateTime.Parse("2021-05-29Z"), DateTime.Parse("2021-05-31Z"))
98+
.WithContentContainer(new ContentContainer<BestServerConfigurations>(BestServerConfigurations.BestServerEvaluation, t => "Top5"))
99+
.WithCondition(cnb =>
100+
cnb.AsComposed()
101+
.WithLogicalOperator(LogicalOperators.And)
102+
.AddCondition(x1 =>
103+
x1.AsValued(BestServerConditions.Price)
104+
.OfDataType<IEnumerable<decimal>>()
105+
.WithComparisonOperator(Operators.In)
106+
.SetOperand(new[] { 100m, 200m, 300m })
107+
.Build())
108+
.AddCondition(x2 =>
109+
x2.AsValued(BestServerConditions.Memory)
110+
.OfDataType<IEnumerable<int>>()
111+
.WithComparisonOperator(Operators.In)
112+
.SetOperand(new[] { 12, 16, 24, 36 })
113+
.Build())
114+
.AddCondition(x2 =>
115+
x2.AsValued(BestServerConditions.StoragePartionable)
116+
.OfDataType<IEnumerable<bool>>()
117+
.WithComparisonOperator(Operators.In)
118+
.SetOperand(new[] { true })
119+
.Build())
120+
.AddCondition(x2 =>
121+
x2.AsValued(BestServerConditions.Brand)
122+
.OfDataType<IEnumerable<string>>()
123+
.WithComparisonOperator(Operators.In)
124+
.SetOperand(new[] { "AMD", "Intel", "Cisco" })
125+
.Build())
126+
.Build())
127+
128+
.Build();
129+
130+
// Act 2 - Create rule default
131+
var ruleBuilderResultDefault = RuleBuilder.NewRule<BestServerConfigurations, BestServerConditions>()
132+
.WithName("Best Server Default")
133+
.WithDatesInterval(DateTime.Parse("2021-05-29Z"), DateTime.Parse("2021-05-31Z"))
134+
.WithContentContainer(new ContentContainer<BestServerConfigurations>(BestServerConfigurations.BestServerEvaluation, t => "Default"))
135+
.Build();
136+
137+
// Assert 1
138+
ruleBuilderResult.Should().NotBeNull();
139+
string errors = ruleBuilderResult.Errors.Any() ? ruleBuilderResult.Errors.Aggregate((s1, s2) => $"{s1}\n- {s2}") : string.Empty;
140+
ruleBuilderResult.IsSuccess.Should().BeTrue(
141+
$"errors have occurred while creating rule: \n[\n- {errors}\n]");
142+
143+
// Assert 3
144+
ruleBuilderResultDefault.Should().NotBeNull();
145+
errors = ruleBuilderResultDefault.Errors.Any() ? ruleBuilderResultDefault.Errors.Aggregate((s1, s2) => $"{s1}\n- {s2}") : string.Empty;
146+
ruleBuilderResultDefault.IsSuccess.Should().BeTrue(
147+
$"errors have occurred while creating rule default: \n[\n- {errors}\n]");
148+
149+
// Act 2 - Add new rule with "in" operator
150+
await rulesEngine.AddRuleAsync(ruleBuilderResultDefault.Rule, RuleAddPriorityOption.ByPriorityNumber(2)).ConfigureAwait(false);
151+
await rulesEngine.AddRuleAsync(ruleBuilderResult.Rule, RuleAddPriorityOption.ByPriorityNumber(1)).ConfigureAwait(false);
152+
153+
DateTime matchDateTime = DateTime.Parse("2021-05-29T12:34:52Z");
154+
155+
var actual = await rulesEngine.MatchOneAsync(BestServerConfigurations.BestServerEvaluation, matchDateTime, conditions).ConfigureAwait(false);
156+
157+
// Assert 3
158+
actual.Should().NotBeNull();
159+
actual.Name.Should().BeEquivalentTo(expectedRuleName);
160+
}
161+
162+
public void Dispose()
163+
{
164+
var mongoDatabase = this.mongoClient.GetDatabase(this.mongoDbProviderSettings.DatabaseName);
165+
mongoDatabase.DropCollection(this.mongoDbProviderSettings.RulesCollectionName);
166+
}
167+
168+
private static MongoClient CreateMongoClient() => new($"mongodb://{SettingsProvider.GetMongoDbHost()}:27017");
169+
170+
private static MongoDbProviderSettings CreateProviderSettings() => new MongoDbProviderSettings
171+
{
172+
DatabaseName = "rules-framework-tests",
173+
RulesCollectionName = "best-server"
174+
};
175+
}
176+
}

tests/Rules.Framework.WebUI.Tests/Rules.Framework.WebUI.Tests.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netcoreapp3.1</TargetFramework>
4+
<TargetFramework>net6.0</TargetFramework>
55
<LangVersion>9.0</LangVersion>
66
<DebugType>Full</DebugType>
77
<IsPackable>false</IsPackable>
@@ -12,7 +12,7 @@
1212
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
1313
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
1414
<PackageReference Include="Moq" Version="4.16.1" />
15-
<PackageReference Include="Rules.Framework" Version="1.6.0" />
15+
<PackageReference Include="Rules.Framework" Version="1.7.3" />
1616
<PackageReference Include="xunit" Version="2.4.1" />
1717
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
1818
<PrivateAssets>all</PrivateAssets>
@@ -28,4 +28,4 @@
2828
<ProjectReference Include="..\..\src\Rules.Framework.WebUI\Rules.Framework.WebUI.csproj" />
2929
</ItemGroup>
3030

31-
</Project>
31+
</Project>

0 commit comments

Comments
 (0)