From 9e981560e09b4e9016597f2888986b9529691b32 Mon Sep 17 00:00:00 2001 From: Mai An Krause Bernt Date: Tue, 13 Aug 2024 16:52:55 +0200 Subject: [PATCH] Implemented SendEmailRequestHandler --- src/XrmMockupShared/Core.cs | 1 + .../Database/DbAttributeTypeMap.cs | 8 +- .../Requests/SendEmailRequestHandler.cs | 134 +++++++ src/XrmMockupShared/XrmMockupShared.projitems | 1 + tests/SharedTests/SharedTests.projitems | 1 + tests/SharedTests/TestSendEmail.cs | 358 ++++++++++++++++++ 6 files changed, 499 insertions(+), 4 deletions(-) create mode 100644 src/XrmMockupShared/Requests/SendEmailRequestHandler.cs create mode 100644 tests/SharedTests/TestSendEmail.cs diff --git a/src/XrmMockupShared/Core.cs b/src/XrmMockupShared/Core.cs index 3bb36351..7fb55b99 100644 --- a/src/XrmMockupShared/Core.cs +++ b/src/XrmMockupShared/Core.cs @@ -211,6 +211,7 @@ private void InitializeDB() new CloseIncidentRequestHandler(this, db, metadata, security), new AddMembersTeamRequestHandler(this, db, metadata, security), new RemoveMembersTeamRequestHandler(this, db, metadata, security), + new SendEmailRequestHandler(this, db, metadata, security), #if !(XRM_MOCKUP_2011 || XRM_MOCKUP_2013) new IsValidStateTransitionRequestHandler(this, db, metadata, security), new CalculateRollupFieldRequestHandler(this, db, metadata, security), diff --git a/src/XrmMockupShared/Database/DbAttributeTypeMap.cs b/src/XrmMockupShared/Database/DbAttributeTypeMap.cs index 803e3598..0db531a4 100644 --- a/src/XrmMockupShared/Database/DbAttributeTypeMap.cs +++ b/src/XrmMockupShared/Database/DbAttributeTypeMap.cs @@ -1,7 +1,7 @@ -using Microsoft.Xrm.Sdk.Metadata; +using Microsoft.Xrm.Sdk; +using Microsoft.Xrm.Sdk.Metadata; using System; using System.Collections.Generic; -using System.Text; namespace DG.Tools.XrmMockup.Database { @@ -27,7 +27,7 @@ internal static class DbAttributeTypeHelper { AttributeTypeCode.Money, typeof(decimal) }, { AttributeTypeCode.Owner, typeof(DbRow) }, { AttributeTypeCode.Customer, typeof(DbRow) }, - { AttributeTypeCode.PartyList, typeof(DbRow[]) }, + { AttributeTypeCode.PartyList, typeof(EntityCollection) }, { AttributeTypeCode.String, typeof(string) }, { AttributeTypeCode.Uniqueidentifier, typeof(Guid) }, { AttributeTypeCode.Virtual, typeof(DbRow[]) } @@ -37,7 +37,7 @@ public static bool IsValidType(AttributeMetadata attrMetadata, object value) { if (value == null) return true; _metadataTypeMap.TryGetValue(attrMetadata.AttributeType.Value, out Type expectedType); if (expectedType == null) { - throw new NotImplementedException($"Attribute of type '{attrMetadata.AttributeType.Value}' is not implemeted in XrmMockup yet."); + throw new NotImplementedException($"Attribute of type '{attrMetadata.AttributeType.Value}' is not implemented in XrmMockup yet."); } if (expectedType.Name == "DbRow[]" && attrMetadata.AttributeType.Value == AttributeTypeCode.Virtual) return true; diff --git a/src/XrmMockupShared/Requests/SendEmailRequestHandler.cs b/src/XrmMockupShared/Requests/SendEmailRequestHandler.cs new file mode 100644 index 00000000..f5d04045 --- /dev/null +++ b/src/XrmMockupShared/Requests/SendEmailRequestHandler.cs @@ -0,0 +1,134 @@ +using DG.Tools.XrmMockup.Database; +using Microsoft.Crm.Sdk.Messages; +using Microsoft.Xrm.Sdk; +using System; +using System.ServiceModel; + +namespace DG.Tools.XrmMockup +{ + internal class SendEmailRequestHandler : RequestHandler + { + const int EMAIL_STATE_COMPLETED = 1; + const int EMAIL_STATUS_DRAFT = 1; + const int EMAIL_STATUS_PENDING_SEND = 6; + const int EMAIL_STATUS_SENT = 3; + + public SendEmailRequestHandler(Core core, XrmDb db, MetadataSkeleton metadata, Security security) : base(core, db, metadata, security, "SendEmail") { } + + internal override void CheckSecurity(OrganizationRequest orgRequest, EntityReference userRef) + { + var request = MakeRequest(orgRequest); + if (request.EmailId == Guid.Empty) + { + throw new FaultException("Required field 'EmailId' is missing"); + } + + var emailRef = new EntityReference("email", request.EmailId); + + if (!security.HasPermission(emailRef, AccessRights.ReadAccess, userRef)) + { + throw new FaultException($"Principal user (Id={userRef.Id}) is missing Read privilege for email (Id={emailRef.Id})"); + } + + if (!security.HasPermission(emailRef, AccessRights.WriteAccess, userRef)) + { + throw new FaultException($"Principal user (Id={userRef.Id}) is missing Write privilege for email (Id={emailRef.Id})"); + } + + var email = db.GetEntityOrNull(emailRef) ?? throw new FaultException($"email with Id = {request.EmailId} does not exist"); + + if (email.Contains("regardingobjectid")) + { + var regardingObject = email.GetAttributeValue("regardingobjectid"); + + if (!security.HasPermission(regardingObject, AccessRights.ReadAccess, userRef)) + { + throw new FaultException($"Principal user (Id={userRef.Id}) is missing Read privilege for {regardingObject.LogicalName} (Id={regardingObject.Id})"); + } + } + + // Remaining security checks have been omitted to reduce complexity + } + + internal override OrganizationResponse Execute(OrganizationRequest orgRequest, EntityReference userRef) + { + var request = MakeRequest(orgRequest); + + var email = db.GetEntity(new EntityReference("email", request.EmailId)); + + if (email.GetAttributeValue("statuscode").Value != EMAIL_STATUS_DRAFT) + { + throw new FaultException("Email must be in Draft status to send"); + } + + if (email.GetAttributeValue("directioncode") is false) + { + throw new FaultException("Cannot send incoming email messages"); + } + + if (email.Contains("from")) + { + var from = email.GetAttributeValue("from"); + if (from.Entities.Count != 1) + { + throw new FaultException("The email must have one and only one sender"); + } + + var activityParty = from.Entities[0]; + if (activityParty.Contains("partyid")) + { + var partyRef = activityParty.GetAttributeValue("partyid"); + if (db.GetEntityOrNull(partyRef) is null) + { + throw new FaultException($"{partyRef.LogicalName} with Id = {partyRef.Id} does not exist"); + } + } + else + { + throw new FaultException("Sender cannot be unresolved"); + } + } + else + { + throw new FaultException("The email must have a sender"); + } + + if (email.Contains("to")) + { + var to = email.GetAttributeValue("to"); + if (to.Entities.Count is 0) + { + throw new FaultException("The email must have at least one recipient before it can be sent"); + } + + foreach (Entity activityParty in to.Entities) + { + if (activityParty.Contains("partyid")) + { + var partyRef = activityParty.GetAttributeValue("partyid"); + if (db.GetEntityOrNull(partyRef) is null) + { + throw new FaultException($"{partyRef.LogicalName} with Id = {partyRef.Id} does not exist"); + } + } + else if (!activityParty.Contains("addressused")) + { + throw new FaultException("Invalid ActivityParty"); + } + } + } + else + { + throw new FaultException("The email must have at least one recipient before it can be sent"); + } + + + email["statecode"] = new OptionSetValue(EMAIL_STATE_COMPLETED); + email["statuscode"] = new OptionSetValue(request.IssueSend ? EMAIL_STATUS_PENDING_SEND : EMAIL_STATUS_SENT); + + db.Update(email); + + return new SendEmailResponse(); + } + } +} diff --git a/src/XrmMockupShared/XrmMockupShared.projitems b/src/XrmMockupShared/XrmMockupShared.projitems index b1e4ac4d..b611a7af 100644 --- a/src/XrmMockupShared/XrmMockupShared.projitems +++ b/src/XrmMockupShared/XrmMockupShared.projitems @@ -14,6 +14,7 @@ + diff --git a/tests/SharedTests/SharedTests.projitems b/tests/SharedTests/SharedTests.projitems index b1c08505..9d88ac4b 100644 --- a/tests/SharedTests/SharedTests.projitems +++ b/tests/SharedTests/SharedTests.projitems @@ -20,6 +20,7 @@ + diff --git a/tests/SharedTests/TestSendEmail.cs b/tests/SharedTests/TestSendEmail.cs new file mode 100644 index 00000000..0ce09ee4 --- /dev/null +++ b/tests/SharedTests/TestSendEmail.cs @@ -0,0 +1,358 @@ +using DG.XrmFramework.BusinessDomain.ServiceContext; +using Microsoft.Crm.Sdk.Messages; +using System; +using System.Linq; +using System.ServiceModel; +using Xunit; + +namespace DG.XrmMockupTest +{ + public class TestSendEmail : UnitTestBase + { + public TestSendEmail(XrmMockupFixture fixture) : base(fixture) { } + + [Fact] + public void TestSendEmailRequestSuccessWhenIssueSendTrue() + { + var contact = new Contact + { + FirstName = "Test", + EMailAddress1 = "test@test.com" + }; + contact.Id = orgAdminUIService.Create(contact); + + var email = new Email + { + From = new ActivityParty[] + { + new ActivityParty + { + PartyId = crm.AdminUser + } + }, + To = new ActivityParty[] + { + new ActivityParty + { + PartyId = contact.ToEntityReference() + } + }, + Subject = "Test Email", + }; + email.Id = orgAdminUIService.Create(email); + + var sendEmailRequest = new SendEmailRequest + { + EmailId = email.Id, + IssueSend = true + }; + + var response = orgAdminUIService.Execute(sendEmailRequest) as SendEmailResponse; + Assert.NotNull(response); + + using (var context = new Xrm(orgAdminUIService)) + { + var retrievedEmail = context.EmailSet.FirstOrDefault(); + Assert.Equal(EmailState.Completed, retrievedEmail.StateCode); + Assert.Equal(Email_StatusCode.PendingSend, retrievedEmail.StatusCode); + } + } + + [Fact] + public void TestSendEmailRequestSuccessWhenIssueSendFalse() + { + var contact = new Contact + { + FirstName = "Test", + EMailAddress1 = "test@test.com" + }; + contact.Id = orgAdminUIService.Create(contact); + + var email = new Email + { + From = new ActivityParty[] + { + new ActivityParty + { + PartyId = crm.AdminUser + } + }, + To = new ActivityParty[] + { + new ActivityParty + { + PartyId = contact.ToEntityReference() + } + }, + Subject = "Test Email", + }; + email.Id = orgAdminUIService.Create(email); + + var sendEmailRequest = new SendEmailRequest + { + EmailId = email.Id + }; + + var response = orgAdminUIService.Execute(sendEmailRequest) as SendEmailResponse; + Assert.NotNull(response); + + using (var context = new Xrm(orgAdminUIService)) + { + var retrievedEmail = context.EmailSet.FirstOrDefault(); + Assert.Equal(EmailState.Completed, retrievedEmail.StateCode); + Assert.Equal(Email_StatusCode.Sent, retrievedEmail.StatusCode); + } + } + + [Fact] + public void TestSendEmailRequestSuccessWithUnresolvedRecipient() + { + var email = new Email + { + From = new ActivityParty[] + { + new ActivityParty + { + PartyId = crm.AdminUser + } + }, + To = new ActivityParty[] + { + new ActivityParty + { + AddressUsed = "test@test.com" + } + }, + Subject = "Test Email", + }; + email.Id = orgAdminUIService.Create(email); + + var sendEmailRequest = new SendEmailRequest + { + EmailId = email.Id, + IssueSend = true + }; + + var response = orgAdminUIService.Execute(sendEmailRequest) as SendEmailResponse; + Assert.NotNull(response); + + using (var context = new Xrm(orgAdminUIService)) + { + var retrievedEmail = context.EmailSet.FirstOrDefault(); + Assert.Equal(EmailState.Completed, retrievedEmail.StateCode); + Assert.Equal(Email_StatusCode.PendingSend, retrievedEmail.StatusCode); + } + } + + [Fact] + public void TestSendEmailRequestFailsWhenTryingToSendAgain() + { + var contact = new Contact + { + FirstName = "Test", + EMailAddress1 = "test@test.com" + }; + contact.Id = orgAdminUIService.Create(contact); + + var email = new Email + { + From = new ActivityParty[] + { + new ActivityParty + { + PartyId = crm.AdminUser + } + }, + To = new ActivityParty[] + { + new ActivityParty + { + PartyId = contact.ToEntityReference() + } + }, + Subject = "Test Email", + }; + email.Id = orgAdminUIService.Create(email); + + var sendEmailRequest = new SendEmailRequest + { + EmailId = email.Id, + IssueSend = true + }; + + orgAdminUIService.Execute(sendEmailRequest); + + Assert.Throws(() => orgAdminUIService.Execute(sendEmailRequest)); + } + + [Fact] + public void TestSendEmailRequestFailsWhenEmailDirectionCodeIncoming() + { + var email = new Email + { + From = new ActivityParty[] + { + new ActivityParty + { + PartyId = crm.AdminUser + } + }, + To = new ActivityParty[] + { + new ActivityParty + { + AddressUsed = "test@test.com" + } + }, + Subject = "Test Email", + DirectionCode = false + }; + email.Id = orgAdminUIService.Create(email); + + var sendEmailRequest = new SendEmailRequest + { + EmailId = email.Id, + IssueSend = true + }; + + Assert.Throws(() => orgAdminUIService.Execute(sendEmailRequest)); + } + + [Fact] + public void TestSendEmailRequestFailsWhenNoSender() + { + var email = new Email(); + email.Id = orgAdminUIService.Create(email); + + var sendEmailRequest = new SendEmailRequest + { + EmailId = email.Id + }; + + Assert.Throws(() => orgAdminUIService.Execute(sendEmailRequest)); + } + + [Fact] + public void TestSendEmailRequestFailsWhenSenderNotCreated() + { + var contact = new Contact + { + FirstName = "Test", + EMailAddress1 = "test@test.com" + }; + + var email = new Email + { + From = new ActivityParty[] + { + new ActivityParty + { + PartyId = contact.ToEntityReference() + } + } + }; + email.Id = orgAdminUIService.Create(email); + + var sendEmailRequest = new SendEmailRequest + { + EmailId = email.Id + }; + + Assert.Throws(() => orgAdminUIService.Execute(sendEmailRequest)); + } + + [Fact] + public void TestSendEmailRequestFailsWhenSenderUnresolved() + { + var email = new Email + { + From = new ActivityParty[] + { + new ActivityParty() + { + AddressUsed = "test@test.com" + } + } + }; + email.Id = orgAdminUIService.Create(email); + + var sendEmailRequest = new SendEmailRequest + { + EmailId = email.Id + }; + + Assert.Throws(() => orgAdminUIService.Execute(sendEmailRequest)); + } + + [Fact] + public void TestSendEmailRequestFailsWhenMoreThanOneSender() + { + var email = new Email + { + From = new ActivityParty[] + { + new ActivityParty(), + new ActivityParty(), + } + }; + email.Id = orgAdminUIService.Create(email); + + var sendEmailRequest = new SendEmailRequest + { + EmailId = email.Id + }; + + Assert.Throws(() => orgAdminUIService.Execute(sendEmailRequest)); + } + + [Fact] + public void TestSendEmailRequestFailsWhenNoRecipients() + { + var email = new Email + { + From = new ActivityParty[] + { + new ActivityParty + { + PartyId = crm.AdminUser + } + } + }; + email.Id = orgAdminUIService.Create(email); + + var sendEmailRequest = new SendEmailRequest + { + EmailId = email.Id + }; + + Assert.Throws(() => orgAdminUIService.Execute(sendEmailRequest)); + } + + [Fact] + public void TestSendEmailRequestFailsWhenRecipientInvalid() + { + var email = new Email + { + From = new ActivityParty[] + { + new ActivityParty + { + PartyId = crm.AdminUser + } + }, + To = new ActivityParty[] + { + new ActivityParty() + } + }; + email.Id = orgAdminUIService.Create(email); + + var sendEmailRequest = new SendEmailRequest + { + EmailId = email.Id + }; + + Assert.Throws(() => orgAdminUIService.Execute(sendEmailRequest)); + } + } +}