From a22eb164a3137f49bee80dfb4aeaed7f205a9618 Mon Sep 17 00:00:00 2001 From: "Sander.Bosman" Date: Fri, 26 Jul 2024 17:10:05 +0200 Subject: [PATCH 1/4] Added initial version for support of AboveOrEqual --- CHANGELOG.md | 6 +++ src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj | 14 +++--- ...ditionExpressionExtensions.AboveOrEqual.cs | 47 +++++++++++++++++++ .../Query/ConditionExpressionExtensions.cs | 7 +++ .../FakeXrmEasy.Core.Tests.csproj | 26 +++++----- ...FakeContextTestTranslateQueryExpression.cs | 37 +++++++++++++++ 6 files changed, 117 insertions(+), 20 deletions(-) create mode 100644 src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index aec5cde1..b677ca76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## [2.5.2] + +### Added + +- **Alpha**: Initial support for AboveOrEqual in ConditionExpressions + ## [2.5.1] ### Changed diff --git a/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj b/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj index dfc0c1ca..f0bce7b7 100644 --- a/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj +++ b/src/FakeXrmEasy.Core/FakeXrmEasy.Core.csproj @@ -8,7 +8,7 @@ net452 net452 FakeXrmEasy.Core - 2.5.1 + 2.5.2 Jordi Montaña Dynamics Value FakeXrmEasy Core @@ -102,22 +102,22 @@ - + - + - + - + - + - + diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs new file mode 100644 index 00000000..362cc6a9 --- /dev/null +++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs @@ -0,0 +1,47 @@ +using FakeXrmEasy.Abstractions; +using FakeXrmEasy.Extensions; +using FakeXrmEasy.Query; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Query; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace FakeXrmEasy.Core.Query +{ + internal static partial class ConditionExpressionExtensions + { + internal static Expression ToAboveOrEqualExpression(this TypedConditionExpression tc, Expression getAttributeValueExpr, Expression containsAttributeExpr, IXrmFakedContext context) + { + var c = tc.CondExpression; + var entityLogicalName = !string.IsNullOrEmpty(c.EntityName) ? c.EntityName : tc.QueryExpression.EntityName; + + if(!context.Relationships.Any(r => r.IsHierarchical && r.Entity1LogicalName.Equals(entityLogicalName) && r.Entity1Attribute.Equals(c.AttributeName))) + { + return tc.ToEqualExpression(context, getAttributeValueExpr, containsAttributeExpr); + } + + //TODO recursive magic to get parent records from hierarchie + var hierarchicalRelationship = context.Relationships.FirstOrDefault(r => r.IsHierarchical && r.Entity1LogicalName.Equals(entityLogicalName) && r.Entity1Attribute.Equals(c.AttributeName)); + + var currentRecord = context.CreateQuery(entityLogicalName).FirstOrDefault(e => ((Guid)e.Attributes[c.AttributeName]) == ((Guid)c.Values[0])); + RetrieveParentEntity(context, hierarchicalRelationship, currentRecord); + + return tc.ToEqualExpression(context, getAttributeValueExpr, containsAttributeExpr); + } + + private static void RetrieveParentEntity(IXrmFakedContext context, XrmFakedRelationship hierarchicalRelationship, Entity currentRecord) + { + if (currentRecord == null || !currentRecord.Attributes.ContainsKey(hierarchicalRelationship.Entity2Attribute) || currentRecord.Attributes[hierarchicalRelationship.Entity2Attribute] == null) + { + return; + } + + var parentRecord = context.CreateQuery(hierarchicalRelationship.Entity1LogicalName).FirstOrDefault(e => ((Guid)e.Attributes[hierarchicalRelationship.Entity1Attribute]) == ((EntityReference)currentRecord.Attributes[hierarchicalRelationship.Entity2Attribute]).Id); + RetrieveParentEntity(context, hierarchicalRelationship, parentRecord); + } + } +} diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs index 8a5fa6ba..157e8aee 100644 --- a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs +++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs @@ -7,6 +7,7 @@ using FakeXrmEasy.Extensions; using System; using FakeXrmEasy.Abstractions.Exceptions; +using FakeXrmEasy.Core.Query; namespace FakeXrmEasy.Query { @@ -288,6 +289,12 @@ internal static Expression ToExpression(this TypedConditionExpression c, QueryEx break; #endif +#if FAKE_XRM_EASY_365 || FAKE_XRM_EASY_9 + case ConditionOperator.AboveOrEqual: + operatorExpression = c.ToAboveOrEqualExpression(getNonBasicValueExpr, containsAttributeExpression, context); + break; +#endif + default: throw UnsupportedExceptionFactory.New(context.LicenseContext.Value, string.Format("Operator {0} not yet implemented for condition expression", c.CondExpression.Operator.ToString())); diff --git a/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj b/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj index 2ab2cc4f..7974fb3c 100644 --- a/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj +++ b/tests/FakeXrmEasy.Core.Tests/FakeXrmEasy.Core.Tests.csproj @@ -11,7 +11,7 @@ true FakeXrmEasy.CoreTests - 2.5.1 + 2.5.2 Jordi Montaña Dynamics Value S.L. Internal Unit test suite for FakeXrmEasy.Core package @@ -114,22 +114,22 @@ - + - + - + - + - + - + @@ -137,22 +137,22 @@ - + - + - + - + - + - + diff --git a/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs b/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs index 12c3f711..74e9468c 100644 --- a/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs +++ b/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs @@ -875,5 +875,42 @@ public void Should_Not_Fail_On_Conditions_In_Link_Entities_Multiple() Assert.Single(invoiceDetails.Entities); Assert.Equal(invoicedetail02.Id, invoiceDetails.Entities[0].Id); } + + [Fact] + public void When_excecuting_a_queryexpression_with_aboveorequal_all_records_inhierarchy_are_returned () + { + Entity parentAccount = new Entity("account"); + parentAccount.Id = Guid.NewGuid(); + parentAccount.Attributes.Add("name", "Parent Account"); + + Entity childAccount = new Entity("account"); + childAccount.Id = Guid.NewGuid(); + childAccount.Attributes.Add("name", "Child Account"); + childAccount.Attributes.Add("parentaccountid", new EntityReference("account", parentAccount.Id)); + + _context.AddRelationship("account_parent_account", new FakeXrmEasy.Abstractions.XrmFakedRelationship + { + RelationshipType = XrmFakedRelationship.FakeRelationshipType.OneToMany, + IsHierarchical = true, + Entity1LogicalName = "account", + Entity1Attribute = "accountid", + Entity2LogicalName = "account", + Entity2Attribute = "parentaccountid" + }); + + _service.Create(parentAccount); + _service.Create(childAccount); + + QueryExpression query = new QueryExpression { EntityName = "account", ColumnSet = new ColumnSet(true) }; + query.Criteria.AddCondition("accountid", ConditionOperator.AboveOrEqual, childAccount.Id); + + var result = _service.RetrieveMultiple(query); + + foreach (var item in result.Entities) + { + Assert.NotNull(item.LogicalName); + } + } + } } \ No newline at end of file From 2bbaf14a881206b6e3f3ba689a4d324661181667 Mon Sep 17 00:00:00 2001 From: "Sander.Bosman" Date: Fri, 26 Jul 2024 22:11:12 +0200 Subject: [PATCH 2/4] Finished the AboveOrEqual condition expression Added a test for the above or equal expression --- ...ditionExpressionExtensions.AboveOrEqual.cs | 25 +++++++++++++------ ...FakeContextTestTranslateQueryExpression.cs | 20 +++++++++++---- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs index 362cc6a9..1372afc9 100644 --- a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs +++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs @@ -24,24 +24,35 @@ internal static Expression ToAboveOrEqualExpression(this TypedConditionExpressio return tc.ToEqualExpression(context, getAttributeValueExpr, containsAttributeExpr); } - //TODO recursive magic to get parent records from hierarchie + //Retrieve the hierarchical relationship to determine the parent attribute var hierarchicalRelationship = context.Relationships.FirstOrDefault(r => r.IsHierarchical && r.Entity1LogicalName.Equals(entityLogicalName) && r.Entity1Attribute.Equals(c.AttributeName)); + + //Retrieve initial record from the hierarchical tree + var currentRecord = context.CreateQuery(entityLogicalName).FirstOrDefault(e => ((Guid)e.Attributes[c.AttributeName]) == ((Guid)c.Values[0])); + if(currentRecord == null) + { + return tc.ToEqualExpression(context, getAttributeValueExpr, containsAttributeExpr); + } - var currentRecord = context.CreateQuery(entityLogicalName).FirstOrDefault(e => ((Guid)e.Attributes[c.AttributeName]) == ((Guid)c.Values[0])); - RetrieveParentEntity(context, hierarchicalRelationship, currentRecord); + //Iterrate through parents via the relationship attributes to walk through the entire hierarchy and build a list of identifiers of the nodes in the hierarchy. + RetrieveParentEntity(context, hierarchicalRelationship, currentRecord, c.Values); - return tc.ToEqualExpression(context, getAttributeValueExpr, containsAttributeExpr); + return tc.ToInExpression(getAttributeValueExpr, containsAttributeExpr); } - private static void RetrieveParentEntity(IXrmFakedContext context, XrmFakedRelationship hierarchicalRelationship, Entity currentRecord) + private static void RetrieveParentEntity(IXrmFakedContext context, XrmFakedRelationship hierarchicalRelationship, Entity currentRecord, DataCollection values) { - if (currentRecord == null || !currentRecord.Attributes.ContainsKey(hierarchicalRelationship.Entity2Attribute) || currentRecord.Attributes[hierarchicalRelationship.Entity2Attribute] == null) + if (!currentRecord.Attributes.ContainsKey(hierarchicalRelationship.Entity2Attribute) || currentRecord.Attributes[hierarchicalRelationship.Entity2Attribute] == null) { return; } var parentRecord = context.CreateQuery(hierarchicalRelationship.Entity1LogicalName).FirstOrDefault(e => ((Guid)e.Attributes[hierarchicalRelationship.Entity1Attribute]) == ((EntityReference)currentRecord.Attributes[hierarchicalRelationship.Entity2Attribute]).Id); - RetrieveParentEntity(context, hierarchicalRelationship, parentRecord); + if (parentRecord != null) + { + values.Add(parentRecord.Id); + RetrieveParentEntity(context, hierarchicalRelationship, parentRecord, values); + } } } } diff --git a/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs b/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs index 74e9468c..9155abf9 100644 --- a/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs +++ b/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs @@ -877,18 +877,24 @@ public void Should_Not_Fail_On_Conditions_In_Link_Entities_Multiple() } [Fact] - public void When_excecuting_a_queryexpression_with_aboveorequal_all_records_inhierarchy_are_returned () + public void When_executing_a_queryexpression_with_aboveorequal_all_records_in_hierarchy_are_returned() { + Entity parentParentAccount = new Entity("account"); + parentParentAccount.Id = Guid.NewGuid(); + parentParentAccount.Attributes.Add("name", "Parent Parent Account"); + Entity parentAccount = new Entity("account"); parentAccount.Id = Guid.NewGuid(); parentAccount.Attributes.Add("name", "Parent Account"); + parentAccount.Attributes.Add("parentaccountid", new EntityReference("account", parentParentAccount.Id)); Entity childAccount = new Entity("account"); childAccount.Id = Guid.NewGuid(); childAccount.Attributes.Add("name", "Child Account"); childAccount.Attributes.Add("parentaccountid", new EntityReference("account", parentAccount.Id)); - _context.AddRelationship("account_parent_account", new FakeXrmEasy.Abstractions.XrmFakedRelationship + //Important step to specify the hierarchical relation ship. + _context.AddRelationship("account_parent_account", new XrmFakedRelationship { RelationshipType = XrmFakedRelationship.FakeRelationshipType.OneToMany, IsHierarchical = true, @@ -898,6 +904,7 @@ public void When_excecuting_a_queryexpression_with_aboveorequal_all_records_inhi Entity2Attribute = "parentaccountid" }); + _service.Create(parentParentAccount); _service.Create(parentAccount); _service.Create(childAccount); @@ -906,11 +913,14 @@ public void When_excecuting_a_queryexpression_with_aboveorequal_all_records_inhi var result = _service.RetrieveMultiple(query); + Assert.NotNull(result); + Assert.NotNull(result.Entities); + Assert.True(result.Entities.Count == 3); + foreach (var item in result.Entities) { - Assert.NotNull(item.LogicalName); - } + Assert.True(item.Id == childAccount.Id || item.Id == parentAccount.Id || item.Id == parentParentAccount.Id); + } } - } } \ No newline at end of file From 01fd51709b17d33fb070001455839606e4b30182 Mon Sep 17 00:00:00 2001 From: "Sander.Bosman" Date: Fri, 26 Jul 2024 23:05:58 +0200 Subject: [PATCH 3/4] Fixed some issues with building for other target configurations theb 365 or v9 Fixed namespace --- .../ConditionExpressionExtensions.AboveOrEqual.cs | 11 ++++------- .../Query/ConditionExpressionExtensions.cs | 1 - 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs index 1372afc9..13b71b8c 100644 --- a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs +++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.AboveOrEqual.cs @@ -1,16 +1,12 @@ -using FakeXrmEasy.Abstractions; -using FakeXrmEasy.Extensions; +#if FAKE_XRM_EASY_365 || FAKE_XRM_EASY_9 +using FakeXrmEasy.Abstractions; using FakeXrmEasy.Query; using Microsoft.Xrm.Sdk; -using Microsoft.Xrm.Sdk.Query; using System; -using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; -using System.Text; -using System.Threading.Tasks; -namespace FakeXrmEasy.Core.Query +namespace FakeXrmEasy.Query { internal static partial class ConditionExpressionExtensions { @@ -56,3 +52,4 @@ private static void RetrieveParentEntity(IXrmFakedContext context, XrmFakedRelat } } } +#endif \ No newline at end of file diff --git a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs index 157e8aee..d5cf8eda 100644 --- a/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs +++ b/src/FakeXrmEasy.Core/Query/ConditionExpressionExtensions.cs @@ -7,7 +7,6 @@ using FakeXrmEasy.Extensions; using System; using FakeXrmEasy.Abstractions.Exceptions; -using FakeXrmEasy.Core.Query; namespace FakeXrmEasy.Query { From 03ccd9fd2b16fa2b9c29253fad487b748e4f2e93 Mon Sep 17 00:00:00 2001 From: "Sander.Bosman" Date: Fri, 26 Jul 2024 23:12:14 +0200 Subject: [PATCH 4/4] Fixed Test project as well for other build targets --- .../FakeContextTestTranslateQueryExpression.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs b/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs index 9155abf9..37beabd2 100644 --- a/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs +++ b/tests/FakeXrmEasy.Core.Tests/Query/TranslateQueryExpressionTests/FakeContextTestTranslateQueryExpression.cs @@ -876,6 +876,7 @@ public void Should_Not_Fail_On_Conditions_In_Link_Entities_Multiple() Assert.Equal(invoicedetail02.Id, invoiceDetails.Entities[0].Id); } +#if FAKE_XRM_EASY_365 || FAKE_XRM_EASY_9 [Fact] public void When_executing_a_queryexpression_with_aboveorequal_all_records_in_hierarchy_are_returned() { @@ -922,5 +923,6 @@ public void When_executing_a_queryexpression_with_aboveorequal_all_records_in_hi Assert.True(item.Id == childAccount.Id || item.Id == parentAccount.Id || item.Id == parentParentAccount.Id); } } +#endif } } \ No newline at end of file