Skip to content

Commit 83bd45c

Browse files
authored
Merge pull request #328 from delegateas/copilot/update-entitylogicalname-requirements
Enforce entitylogicalname requirements on CreateMultiple, UpdateMultiple, UpsertMultiple; implement DeleteMultipleRequestHandler
2 parents 4581a8b + 6a55f1b commit 83bd45c

10 files changed

Lines changed: 257 additions & 1 deletion

src/XrmMockup365/Core.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ private void InitializeDB()
439439
new InstantiateTemplateRequestHandler(this, db, metadata, security),
440440
new CreateMultipleRequestHandler(this, db, metadata, security),
441441
new UpdateMultipleRequestHandler(this, db, metadata, security),
442+
new DeleteMultipleRequestHandler(this, db, metadata, security),
442443
new UpsertMultipleRequestHandler(this, db, metadata, security),
443444
new ExecuteTransactionRequestHandler(this, db, metadata, security),
444445
new WinQuoteRequestHandler(this, db, metadata, security),

src/XrmMockup365/Mappings.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ internal static class Mappings
1717
{ typeof(AssignRequest), nameof(AssignRequest.Target) },
1818
{ typeof(AssociateRequest), nameof(AssociateRequest.Target) },
1919
{ typeof(CreateMultipleRequest), nameof(CreateMultipleRequest.Targets) },
20+
{ typeof(DeleteMultipleRequest), nameof(DeleteMultipleRequest.Targets) },
2021
{ typeof(CreateRequest), nameof(CreateRequest.Target) },
2122
{ typeof(DeleteRequest), nameof(DeleteRequest.Target) },
2223
{ typeof(DeliverIncomingEmailRequest), nameof(DeliverIncomingEmailRequest.MessageId) },
@@ -154,7 +155,6 @@ public static Type EventOperationToRequest(EventOperation operation)
154155
{ "yesterday", ConditionOperator.Yesterday }
155156
};
156157

157-
158158
public static EntityReference GetPrimaryEntityReferenceFromRequest(OrganizationRequest request)
159159
{
160160
switch (request)

src/XrmMockup365/Requests/CreateMultipleRequestHandler.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.Xrm.Sdk;
22
using Microsoft.Xrm.Sdk.Messages;
33
using System.Linq;
4+
using System.ServiceModel;
45
using DG.Tools.XrmMockup.Database;
56
using XrmPluginCore.Enums;
67

@@ -17,6 +18,17 @@ internal override OrganizationResponse Execute(OrganizationRequest orgRequest, E
1718
{
1819
var request = MakeRequest<CreateMultipleRequest>(orgRequest);
1920

21+
if (string.IsNullOrEmpty(request.Targets.EntityName))
22+
{
23+
throw new FaultException("The required field 'EntityName' is missing.");
24+
}
25+
26+
var mismatchedEntity = request.Targets.Entities.FirstOrDefault(e => e.LogicalName != request.Targets.EntityName);
27+
if (mismatchedEntity != null)
28+
{
29+
throw new FaultException($"The entity logical name '{mismatchedEntity.LogicalName}' does not match the expected entity logical name '{request.Targets.EntityName}'.");
30+
}
31+
2032
var ids =
2133
request.Targets.Entities.Select(entity =>
2234
{
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System.Linq;
2+
using System.ServiceModel;
3+
using Microsoft.Xrm.Sdk;
4+
using Microsoft.Xrm.Sdk.Messages;
5+
using DG.Tools.XrmMockup.Database;
6+
7+
namespace DG.Tools.XrmMockup
8+
{
9+
internal class DeleteMultipleRequestHandler : RequestHandler
10+
{
11+
internal DeleteMultipleRequestHandler(Core core, XrmDb db, MetadataSkeleton metadata, Security security)
12+
: base(core, db, metadata, security, "DeleteMultiple")
13+
{
14+
}
15+
16+
internal override OrganizationResponse Execute(OrganizationRequest orgRequest, EntityReference userRef)
17+
{
18+
var request = MakeRequest<DeleteMultipleRequest>(orgRequest);
19+
20+
var invalidRef = request.Targets.FirstOrDefault(e => string.IsNullOrEmpty(e.LogicalName));
21+
if (invalidRef != null)
22+
{
23+
throw new FaultException("The required field 'EntityName' is missing.");
24+
}
25+
26+
var distinctLogicalNames = request.Targets.Select(e => e.LogicalName).Distinct().ToList();
27+
if (distinctLogicalNames.Count > 1)
28+
{
29+
throw new FaultException("All entity references in a DeleteMultipleRequest must have the same entity logical name.");
30+
}
31+
32+
foreach (var entityRef in request.Targets)
33+
{
34+
var deleteRequest = new DeleteRequest
35+
{
36+
Target = entityRef
37+
};
38+
core.Execute(deleteRequest, userRef);
39+
}
40+
41+
return new OrganizationResponse { ResponseName = "DeleteMultiple" };
42+
}
43+
}
44+
}

src/XrmMockup365/Requests/UpdateMultipleRequestHandler.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.ServiceModel;
35
using Microsoft.Xrm.Sdk;
46
using Microsoft.Xrm.Sdk.Messages;
57
using DG.Tools.XrmMockup.Database;
@@ -18,6 +20,17 @@ internal override OrganizationResponse Execute(OrganizationRequest orgRequest, E
1820
{
1921
var request = MakeRequest<UpdateMultipleRequest>(orgRequest);
2022

23+
if (string.IsNullOrEmpty(request.Targets.EntityName))
24+
{
25+
throw new FaultException("The required field 'EntityName' is missing.");
26+
}
27+
28+
var mismatchedEntity = request.Targets.Entities.FirstOrDefault(e => e.LogicalName != request.Targets.EntityName);
29+
if (mismatchedEntity != null)
30+
{
31+
throw new FaultException($"The entity logical name '{mismatchedEntity.LogicalName}' does not match the expected entity logical name '{request.Targets.EntityName}'.");
32+
}
33+
2134
var seenIds = new HashSet<Guid>();
2235
foreach (var entity in request.Targets.Entities)
2336
{

src/XrmMockup365/Requests/UpsertMultipleRequestHandler.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ internal UpsertMultipleRequestHandler(Core core, XrmDb db, MetadataSkeleton meta
1919
internal override OrganizationResponse Execute(OrganizationRequest orgRequest, EntityReference userRef)
2020
{
2121
var request = MakeRequest<UpsertMultipleRequest>(orgRequest);
22+
23+
if (string.IsNullOrEmpty(request.Targets.EntityName))
24+
{
25+
throw new FaultException("The required field 'EntityName' is missing.");
26+
}
27+
28+
var mismatchedEntity = request.Targets.Entities.FirstOrDefault(e => e.LogicalName != request.Targets.EntityName);
29+
if (mismatchedEntity != null)
30+
{
31+
throw new FaultException($"The entity logical name '{mismatchedEntity.LogicalName}' does not match the expected entity logical name '{request.Targets.EntityName}'.");
32+
}
33+
2234
var seenIds = new HashSet<Guid>();
2335

2436
var results = request.Targets.Entities.Select(entity =>

tests/XrmMockup365Test/TestCreateMultipleRequestHandler.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.ServiceModel;
34
using Microsoft.Xrm.Sdk;
45
using Microsoft.Xrm.Sdk.Messages;
56
using DG.XrmFramework.BusinessDomain.ServiceContext;
@@ -36,5 +37,40 @@ public void TestCreateMultipleEntities()
3637
Assert.Equal(contact1.FirstName, createdContact1.FirstName);
3738
Assert.Equal(contact2.FirstName, createdContact2.FirstName);
3839
}
40+
41+
[Fact]
42+
public void TestCreateMultipleThrowsWhenEntityNameMissing()
43+
{
44+
var contact = new Contact { FirstName = "John", LastName = "Doe" };
45+
46+
var createMultipleRequest = new CreateMultipleRequest
47+
{
48+
Targets = new EntityCollection
49+
{
50+
Entities = { contact }
51+
}
52+
};
53+
54+
var exception = Assert.Throws<FaultException>(() => orgAdminService.Execute(createMultipleRequest));
55+
Assert.Equal("The required field 'EntityName' is missing.", exception.Message);
56+
}
57+
58+
[Fact]
59+
public void TestCreateMultipleThrowsWhenEntityLogicalNameMismatch()
60+
{
61+
var account = new Account { Name = "Acme" };
62+
63+
var createMultipleRequest = new CreateMultipleRequest
64+
{
65+
Targets = new EntityCollection
66+
{
67+
EntityName = Contact.EntityLogicalName,
68+
Entities = { account }
69+
}
70+
};
71+
72+
var exception = Assert.Throws<FaultException>(() => orgAdminService.Execute(createMultipleRequest));
73+
Assert.Equal($"The entity logical name '{Account.EntityLogicalName}' does not match the expected entity logical name '{Contact.EntityLogicalName}'.", exception.Message);
74+
}
3975
}
4076
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
using System;
2+
using System.ServiceModel;
3+
using Microsoft.Xrm.Sdk;
4+
using Microsoft.Xrm.Sdk.Messages;
5+
using DG.XrmFramework.BusinessDomain.ServiceContext;
6+
using Xunit;
7+
8+
namespace DG.XrmMockupTest
9+
{
10+
public class TestDeleteMultipleRequestHandler : UnitTestBase
11+
{
12+
public TestDeleteMultipleRequestHandler(XrmMockupFixture fixture) : base(fixture) { }
13+
14+
[Fact]
15+
public void TestDeleteMultipleEntities()
16+
{
17+
var contactId1 = orgGodService.Create(new Contact { FirstName = "John", LastName = "Doe" });
18+
var contactId2 = orgGodService.Create(new Contact { FirstName = "Jane", LastName = "Doe" });
19+
20+
var deleteMultipleRequest = new DeleteMultipleRequest
21+
{
22+
Targets = new EntityReferenceCollection
23+
{
24+
new EntityReference(Contact.EntityLogicalName, contactId1),
25+
new EntityReference(Contact.EntityLogicalName, contactId2)
26+
}
27+
};
28+
29+
orgAdminService.Execute(deleteMultipleRequest);
30+
31+
Assert.Throws<FaultException>(() => orgAdminService.Retrieve(Contact.EntityLogicalName, contactId1, new Microsoft.Xrm.Sdk.Query.ColumnSet(true)));
32+
Assert.Throws<FaultException>(() => orgAdminService.Retrieve(Contact.EntityLogicalName, contactId2, new Microsoft.Xrm.Sdk.Query.ColumnSet(true)));
33+
}
34+
35+
[Fact]
36+
public void TestDeleteMultipleThrowsWhenEntityNameMissing()
37+
{
38+
var contactId = orgGodService.Create(new Contact { FirstName = "John", LastName = "Doe" });
39+
40+
var deleteMultipleRequest = new DeleteMultipleRequest
41+
{
42+
Targets = new EntityReferenceCollection
43+
{
44+
new EntityReference(string.Empty, contactId)
45+
}
46+
};
47+
48+
var exception = Assert.Throws<FaultException>(() => orgAdminService.Execute(deleteMultipleRequest));
49+
Assert.Equal("The required field 'EntityName' is missing.", exception.Message);
50+
}
51+
52+
[Fact]
53+
public void TestDeleteMultipleThrowsWhenEntityLogicalNamesMismatch()
54+
{
55+
var contactId = orgGodService.Create(new Contact { FirstName = "John", LastName = "Doe" });
56+
var accountId = orgGodService.Create(new Account { Name = "Acme" });
57+
58+
var deleteMultipleRequest = new DeleteMultipleRequest
59+
{
60+
Targets = new EntityReferenceCollection
61+
{
62+
new EntityReference(Contact.EntityLogicalName, contactId),
63+
new EntityReference(Account.EntityLogicalName, accountId)
64+
}
65+
};
66+
67+
var exception = Assert.Throws<FaultException>(() => orgAdminService.Execute(deleteMultipleRequest));
68+
Assert.Equal("All entity references in a DeleteMultipleRequest must have the same entity logical name.", exception.Message);
69+
}
70+
}
71+
}

tests/XrmMockup365Test/TestUpdateMultipleRequestHandler.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.ServiceModel;
34
using Microsoft.Xrm.Sdk;
45
using Microsoft.Xrm.Sdk.Messages;
56
using DG.XrmFramework.BusinessDomain.ServiceContext;
@@ -62,5 +63,40 @@ public void TestUpdateMultipleSameKeyIgnoresSubsequent()
6263
var updatedContact = Contact.Retrieve(orgAdminService, contactId);
6364
Assert.Equal(updateContact1.FirstName, updatedContact.FirstName);
6465
}
66+
67+
[Fact]
68+
public void TestUpdateMultipleThrowsWhenEntityNameMissing()
69+
{
70+
var contactId = orgGodService.Create(new Contact { FirstName = "John", LastName = "Doe" });
71+
72+
var updateMultipleRequest = new UpdateMultipleRequest
73+
{
74+
Targets = new EntityCollection
75+
{
76+
Entities = { new Contact(contactId) { FirstName = "Johny" } }
77+
}
78+
};
79+
80+
var exception = Assert.Throws<FaultException>(() => orgAdminService.Execute(updateMultipleRequest));
81+
Assert.Equal("The required field 'EntityName' is missing.", exception.Message);
82+
}
83+
84+
[Fact]
85+
public void TestUpdateMultipleThrowsWhenEntityLogicalNameMismatch()
86+
{
87+
var contactId = orgGodService.Create(new Contact { FirstName = "John", LastName = "Doe" });
88+
89+
var updateMultipleRequest = new UpdateMultipleRequest
90+
{
91+
Targets = new EntityCollection
92+
{
93+
EntityName = Account.EntityLogicalName,
94+
Entities = { new Contact(contactId) { FirstName = "Johny" } }
95+
}
96+
};
97+
98+
var exception = Assert.Throws<FaultException>(() => orgAdminService.Execute(updateMultipleRequest));
99+
Assert.Equal($"The entity logical name '{Contact.EntityLogicalName}' does not match the expected entity logical name '{Account.EntityLogicalName}'.", exception.Message);
100+
}
65101
}
66102
}

tests/XrmMockup365Test/TestUpsertMultiple.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,5 +85,36 @@ public void TestUpsertMultipleThrowsFaultOnMultipleWithSameKey()
8585
var exception = Assert.Throws<FaultException>(() => orgAdminUIService.Execute(req));
8686
Assert.Equal($"Duplicate Ids are not allowed in the Target list of an UpsertMultipleRequest: {_account1id}.", exception.Message);
8787
}
88+
89+
[Fact]
90+
public void TestUpsertMultipleThrowsWhenEntityNameMissing()
91+
{
92+
var req = new UpsertMultipleRequest
93+
{
94+
Targets = new EntityCollection
95+
{
96+
Entities = { new Account { Name = "Acme" } }
97+
}
98+
};
99+
100+
var exception = Assert.Throws<FaultException>(() => orgAdminUIService.Execute(req));
101+
Assert.Equal("The required field 'EntityName' is missing.", exception.Message);
102+
}
103+
104+
[Fact]
105+
public void TestUpsertMultipleThrowsWhenEntityLogicalNameMismatch()
106+
{
107+
var req = new UpsertMultipleRequest
108+
{
109+
Targets = new EntityCollection
110+
{
111+
EntityName = Account.EntityLogicalName,
112+
Entities = { new Contact { FirstName = "John" } }
113+
}
114+
};
115+
116+
var exception = Assert.Throws<FaultException>(() => orgAdminUIService.Execute(req));
117+
Assert.Equal($"The entity logical name '{Contact.EntityLogicalName}' does not match the expected entity logical name '{Account.EntityLogicalName}'.", exception.Message);
118+
}
88119
}
89120
}

0 commit comments

Comments
 (0)