From 1ff607a56ed5e7d5cf4cd22b75bc97f1e68ae972 Mon Sep 17 00:00:00 2001 From: Mai An Krause Bernt Date: Fri, 23 Aug 2024 16:03:37 +0200 Subject: [PATCH 1/4] Implement and test support for Retrieve plugins --- src/XrmMockupShared/Core.cs | 4 +++ src/XrmMockupShared/Mappings.cs | 1 + .../RetrievePlugin.cs | 26 +++++++++++++++++++ .../SharedPluginsAndCodeactivites.projitems | 1 + tests/SharedTests/TestRetrieve.cs | 14 ++++++++++ 5 files changed, 46 insertions(+) create mode 100644 tests/SharedPluginsAndCodeactivites/RetrievePlugin.cs diff --git a/src/XrmMockupShared/Core.cs b/src/XrmMockupShared/Core.cs index 3bb36351..b88e5a5e 100644 --- a/src/XrmMockupShared/Core.cs +++ b/src/XrmMockupShared/Core.cs @@ -673,6 +673,10 @@ internal OrganizationResponse Execute(OrganizationRequest request, EntityReferen pluginContext.OutputParameters["BusinessEntityCollection"] = (response as RetrieveMultipleResponse)?.EntityCollection; } + else if (request is RetrieveRequest) + { + pluginContext.OutputParameters["BusinessEntity"] = TryRetrieve((request as RetrieveRequest).Target); + } if (!string.IsNullOrEmpty(eventOp)) { diff --git a/src/XrmMockupShared/Mappings.cs b/src/XrmMockupShared/Mappings.cs index dfb4ffe9..513d8602 100644 --- a/src/XrmMockupShared/Mappings.cs +++ b/src/XrmMockupShared/Mappings.cs @@ -24,6 +24,7 @@ internal static partial class Mappings { { typeof(UpdateRequest), "Target" }, { typeof(AssociateRequest), "Target" }, { typeof(DisassociateRequest), "Target" }, + { typeof(RetrieveRequest), "Target" }, }; public static Dictionary RequestToEventOperation = new Dictionary() diff --git a/tests/SharedPluginsAndCodeactivites/RetrievePlugin.cs b/tests/SharedPluginsAndCodeactivites/RetrievePlugin.cs new file mode 100644 index 00000000..5e3ac574 --- /dev/null +++ b/tests/SharedPluginsAndCodeactivites/RetrievePlugin.cs @@ -0,0 +1,26 @@ +using DG.XrmFramework.BusinessDomain.ServiceContext; +using Microsoft.Xrm.Sdk; + + +namespace DG.Some.Namespace +{ + public class RetrievePlugin : Plugin + { + public RetrievePlugin() : base(typeof(RetrievePlugin)) + { + RegisterPluginStep( + EventOperation.Retrieve, + ExecutionStage.PostOperation, + Execute); + } + + protected void Execute(LocalPluginContext localContext) + { + var entity = localContext.PluginExecutionContext.OutputParameters["BusinessEntity"] as Entity; + if (entity.ToEntity().StateCode == AccountState.Inactive) + { + throw new InvalidPluginExecutionException("Inactive accounts cannot be retrieved."); + } + } + } +} diff --git a/tests/SharedPluginsAndCodeactivites/SharedPluginsAndCodeactivites.projitems b/tests/SharedPluginsAndCodeactivites/SharedPluginsAndCodeactivites.projitems index 11c7fc45..c351cbf6 100644 --- a/tests/SharedPluginsAndCodeactivites/SharedPluginsAndCodeactivites.projitems +++ b/tests/SharedPluginsAndCodeactivites/SharedPluginsAndCodeactivites.projitems @@ -17,6 +17,7 @@ + diff --git a/tests/SharedTests/TestRetrieve.cs b/tests/SharedTests/TestRetrieve.cs index fedd028f..b7dce926 100644 --- a/tests/SharedTests/TestRetrieve.cs +++ b/tests/SharedTests/TestRetrieve.cs @@ -301,5 +301,19 @@ public void TestRetrieveUserByFullName() var users = orgAdminService.RetrieveMultiple(q); Assert.Single(users.Entities); } + + [Fact] + public void TestRetrievePlugin() + { + var accountId = orgAdminUIService.Create(new Account()); + + orgAdminUIService.Update(new Account(accountId) + { + StateCode = AccountState.Inactive, + StatusCode = Account_StatusCode.Inactive + }); + + Assert.Throws(() => orgAdminService.Retrieve(Account.EntityLogicalName, accountId, new ColumnSet(true))); + } } } From 7fd38198594e992b7557fc43137c215146773c99 Mon Sep 17 00:00:00 2001 From: Mai An Krause Bernt Date: Fri, 30 Aug 2024 14:58:34 +0200 Subject: [PATCH 2/4] Change test entity to be Contact instead of Account --- tests/SharedPluginsAndCodeactivites/RetrievePlugin.cs | 11 ++++++----- tests/SharedTests/TestRetrieve.cs | 10 +++++----- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/tests/SharedPluginsAndCodeactivites/RetrievePlugin.cs b/tests/SharedPluginsAndCodeactivites/RetrievePlugin.cs index 5e3ac574..740bcdda 100644 --- a/tests/SharedPluginsAndCodeactivites/RetrievePlugin.cs +++ b/tests/SharedPluginsAndCodeactivites/RetrievePlugin.cs @@ -8,18 +8,19 @@ public class RetrievePlugin : Plugin { public RetrievePlugin() : base(typeof(RetrievePlugin)) { - RegisterPluginStep( + RegisterPluginStep( EventOperation.Retrieve, ExecutionStage.PostOperation, - Execute); + ExecutePostRetrieve); } - protected void Execute(LocalPluginContext localContext) + protected void ExecutePostRetrieve(LocalPluginContext localContext) { var entity = localContext.PluginExecutionContext.OutputParameters["BusinessEntity"] as Entity; - if (entity.ToEntity().StateCode == AccountState.Inactive) + + if (entity.ToEntity().StateCode == ContactState.Inactive) { - throw new InvalidPluginExecutionException("Inactive accounts cannot be retrieved."); + throw new InvalidPluginExecutionException("Inactive contacts cannot be retrieved."); } } } diff --git a/tests/SharedTests/TestRetrieve.cs b/tests/SharedTests/TestRetrieve.cs index b7dce926..5fcb4b3c 100644 --- a/tests/SharedTests/TestRetrieve.cs +++ b/tests/SharedTests/TestRetrieve.cs @@ -305,15 +305,15 @@ public void TestRetrieveUserByFullName() [Fact] public void TestRetrievePlugin() { - var accountId = orgAdminUIService.Create(new Account()); + var accountId = orgAdminService.Create(new Contact { LastName = "Test" }); - orgAdminUIService.Update(new Account(accountId) + orgAdminUIService.Update(new Contact(accountId) { - StateCode = AccountState.Inactive, - StatusCode = Account_StatusCode.Inactive + StateCode = ContactState.Inactive, + StatusCode = Contact_StatusCode.Inactive }); - Assert.Throws(() => orgAdminService.Retrieve(Account.EntityLogicalName, accountId, new ColumnSet(true))); + Assert.Throws(() => Contact.Retrieve(orgAdminService, accountId, x => x.LastName)); } } } From 5af1ea5c0943d48fd28f4fac9ba4b036a35e61b3 Mon Sep 17 00:00:00 2001 From: Mai An Krause Bernt Date: Tue, 3 Sep 2024 11:18:57 +0200 Subject: [PATCH 3/4] Add test for referencing a nonexistent attribute --- tests/SharedTests/TestRetrieveMultiple.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/SharedTests/TestRetrieveMultiple.cs b/tests/SharedTests/TestRetrieveMultiple.cs index 76fae032..4446406e 100644 --- a/tests/SharedTests/TestRetrieveMultiple.cs +++ b/tests/SharedTests/TestRetrieveMultiple.cs @@ -921,5 +921,16 @@ public void TestCaseSensitivity() Assert.Equal("MATT", res.Entities.Single().GetAttributeValue("firstname")); } + + [Fact] + public void TestRetrieveMultipleFailWithNonExistentAttribute() + { + var query = new QueryExpression("contact") + { + ColumnSet = new ColumnSet("x") + }; + + Assert.Throws(() => orgAdminService.RetrieveMultiple(query)); + } } } From 1cb429bab41a670f5d0122b8f2446a262d42e491 Mon Sep 17 00:00:00 2001 From: Mai An Krause Bernt Date: Tue, 3 Sep 2024 11:19:26 +0200 Subject: [PATCH 4/4] Add support for distinct filtering in QueryExpressions --- .../RetrieveMultipleRequestHandler.cs | 6 +++ tests/SharedTests/TestRetrieveMultiple.cs | 38 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/XrmMockupShared/Requests/RetrieveMultipleRequestHandler.cs b/src/XrmMockupShared/Requests/RetrieveMultipleRequestHandler.cs index 7d927525..9bc4a48d 100644 --- a/src/XrmMockupShared/Requests/RetrieveMultipleRequestHandler.cs +++ b/src/XrmMockupShared/Requests/RetrieveMultipleRequestHandler.cs @@ -157,6 +157,12 @@ internal override OrganizationResponse Execute(OrganizationRequest orgRequest, E colToReturn = new EntityCollection(collection.Select(x=>x.Value).ToList()); } + if (queryExpr.Distinct) + { + var uniqueIds = new HashSet(); + colToReturn = new EntityCollection(colToReturn.Entities.Where(entity => uniqueIds.Add(entity.Id)).ToList()); + } + // According to docs, should return -1 if ReturnTotalRecordCount set to false colToReturn.TotalRecordCount = queryExpr.PageInfo.ReturnTotalRecordCount ? colToReturn.Entities.Count : -1; diff --git a/tests/SharedTests/TestRetrieveMultiple.cs b/tests/SharedTests/TestRetrieveMultiple.cs index 4446406e..e0d3b249 100644 --- a/tests/SharedTests/TestRetrieveMultiple.cs +++ b/tests/SharedTests/TestRetrieveMultiple.cs @@ -773,6 +773,44 @@ public void TestQueryExpressionLinkEntityNoSetEntityName() Assert.Equal(2, res.Count()); } + [Fact] + public void RetrieveMultipleWithLinkEntitiesReturnDistinctResults() + { + var lead = new Lead() + { + Subject = "Lead", + ParentContactId = contact1.ToEntityReference() + }; + orgAdminService.Create(lead); + + var linkEntity = new LinkEntity + { + LinkToEntityName = "lead", + LinkToAttributeName = "parentcontactid", + LinkFromEntityName = "contact", + LinkFromAttributeName = "contactid", + Columns = new ColumnSet(false), + EntityAlias = "contact", + JoinOperator = JoinOperator.LeftOuter, + }; + + var filter = new FilterExpression(LogicalOperator.And); + filter.AddCondition(new ConditionExpression("lastname", ConditionOperator.Equal, contact1.LastName)); + + var query = new QueryExpression("contact") + { + Distinct = true, + ColumnSet = new ColumnSet(), + LinkEntities = { linkEntity } + }; + + var res = orgAdminService.RetrieveMultiple(query).Entities; + + var distinctIdCount = res.Select(x => x.Id).Distinct().Count(); + + Assert.Equal(distinctIdCount, res.Count); + } + #endif [Fact]