From 52bf42725f00452d606008d271f39db28fabfe39 Mon Sep 17 00:00:00 2001 From: Kevin Joy Date: Thu, 25 Jul 2024 15:40:31 +0100 Subject: [PATCH] Prevent resubmitting trn request for same email --- .../AuthorizeAccessLinkGenerator.cs | 3 + .../Pages/RequestTrn/CheckAnswers.cshtml.cs | 3 +- .../Pages/RequestTrn/Email.cshtml.cs | 12 ++- .../Pages/RequestTrn/EmailInUse.cshtml | 15 +++ .../Pages/RequestTrn/EmailInUse.cshtml.cs | 21 +++++ .../Dqt/Models/GeneratedCode.cs | 21 +++++ .../Dqt/Queries/CreateTrnRequestTaskQuery.cs | 1 + .../GetOpenTasksForEmailAddressQuery.cs | 3 + .../CreateTrnRequestTaskHandler.cs | 3 +- .../GetOpenTasksForEmailAddressHandler.cs | 28 ++++++ .../PageTests/RequestTrn/EmailInUseTests.cs | 22 +++++ .../PageTests/RequestTrn/EmailTests.cs | 31 ++++++- .../QueryTests/CreateTrnRequestTaskTests.cs | 5 +- .../GetOpenTasksForEmailAddressTests.cs | 93 +++++++++++++++++++ .../TestData.CreateCrmTask.cs | 15 ++- crm_attributes.json | 3 +- tools/coretools/CrmSvcUtil.exe.config | 2 +- 17 files changed, 273 insertions(+), 8 deletions(-) create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/EmailInUse.cshtml create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/EmailInUse.cshtml.cs create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/GetOpenTasksForEmailAddressQuery.cs create mode 100644 TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/GetOpenTasksForEmailAddressHandler.cs create mode 100644 TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/RequestTrn/EmailInUseTests.cs create mode 100644 TeachingRecordSystem/tests/TeachingRecordSystem.Core.Dqt.CrmIntegrationTests/QueryTests/GetOpenTasksForEmailAddressTests.cs diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/AuthorizeAccessLinkGenerator.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/AuthorizeAccessLinkGenerator.cs index 573866095a..58d80a2e94 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/AuthorizeAccessLinkGenerator.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/AuthorizeAccessLinkGenerator.cs @@ -62,6 +62,9 @@ public string RequestTrnAddress(JourneyInstanceId journeyInstanceId, bool? fromC public string RequestTrnCheckAnswers(JourneyInstanceId journeyInstanceId) => GetRequiredPathByPage("/RequestTrn/CheckAnswers", journeyInstanceId: journeyInstanceId); + public string RequestTrnEmailInUse(JourneyInstanceId journeyInstanceId) => + GetRequiredPathByPage("/RequestTrn/EmailInUse", journeyInstanceId: journeyInstanceId); + public string RequestTrnSubmitted(JourneyInstanceId journeyInstanceId) => GetRequiredPathByPage("/RequestTrn/Submitted", journeyInstanceId: journeyInstanceId); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/CheckAnswers.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/CheckAnswers.cshtml.cs index 5700abe424..701c660a13 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/CheckAnswers.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/CheckAnswers.cshtml.cs @@ -103,7 +103,8 @@ await crmQueryDispatcher.ExecuteQuery( Description = description, EvidenceFileName = JourneyInstance!.State.EvidenceFileName!, EvidenceFileContent = stream, - EvidenceFileMimeType = evidenceFileMimeType + EvidenceFileMimeType = evidenceFileMimeType, + EmailAddress = JourneyInstance!.State.Email! }); await JourneyInstance!.UpdateStateAsync(state => state.HasPendingTrnRequest = true); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/Email.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/Email.cshtml.cs index 7137fa3a65..90355d76a2 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/Email.cshtml.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/Email.cshtml.cs @@ -2,13 +2,15 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.RazorPages; +using TeachingRecordSystem.Core.Dqt; +using TeachingRecordSystem.Core.Dqt.Queries; using TeachingRecordSystem.UiCommon.FormFlow; using EmailAddress = TeachingRecordSystem.AuthorizeAccess.DataAnnotations.EmailAddressAttribute; namespace TeachingRecordSystem.AuthorizeAccess.Pages.RequestTrn; [Journey(RequestTrnJourneyState.JourneyName), RequireJourneyInstance] -public class EmailModel(AuthorizeAccessLinkGenerator linkGenerator) : PageModel +public class EmailModel(AuthorizeAccessLinkGenerator linkGenerator, ICrmQueryDispatcher crmQueryDispatcher) : PageModel { public JourneyInstance? JourneyInstance { get; set; } @@ -30,6 +32,14 @@ public async Task OnPost() await JourneyInstance!.UpdateStateAsync(state => state.Email = Email); + var openTasks = await crmQueryDispatcher.ExecuteQuery( + new GetOpenTasksForEmailAddressQuery(EmailAddress: JourneyInstance!.State.Email!)); + + if (openTasks.Any()) + { + return Redirect(linkGenerator.RequestTrnEmailInUse(JourneyInstance!.InstanceId)); + } + return FromCheckAnswers == true ? Redirect(linkGenerator.RequestTrnCheckAnswers(JourneyInstance!.InstanceId)) : Redirect(linkGenerator.RequestTrnName(JourneyInstance.InstanceId)); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/EmailInUse.cshtml b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/EmailInUse.cshtml new file mode 100644 index 0000000000..128c9d652c --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/EmailInUse.cshtml @@ -0,0 +1,15 @@ +@page "/request-trn/emailinuse" +@model TeachingRecordSystem.AuthorizeAccess.Pages.RequestTrn.EmailInUseModel +@{ + ViewBag.Title = "Email Address in use"; +} + +@section BeforeContent { + +} + +
+
+

You've already submitted a request for a TRN

+
+
diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/EmailInUse.cshtml.cs b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/EmailInUse.cshtml.cs new file mode 100644 index 0000000000..c17af751a7 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.AuthorizeAccess/Pages/RequestTrn/EmailInUse.cshtml.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.AspNetCore.Mvc.RazorPages; +using TeachingRecordSystem.UiCommon.FormFlow; + +namespace TeachingRecordSystem.AuthorizeAccess.Pages.RequestTrn; + +[Journey(RequestTrnJourneyState.JourneyName), RequireJourneyInstance] +public class EmailInUseModel(AuthorizeAccessLinkGenerator linkGenerator) : PageModel +{ + public JourneyInstance? JourneyInstance { get; set; } + + public override void OnPageHandlerExecuting(PageHandlerExecutingContext context) + { + var state = JourneyInstance!.State; + if (state.Email is null) + { + context.Result = Redirect(linkGenerator.RequestTrnEmail(JourneyInstance.InstanceId)); + } + } +} + diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Models/GeneratedCode.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Models/GeneratedCode.cs index 9fb4a9755b..975dbf1c48 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Models/GeneratedCode.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Models/GeneratedCode.cs @@ -23294,6 +23294,7 @@ public static class Fields public const string Id = "activityid"; public const string Category = "category"; public const string Description = "description"; + public const string dfeta_EmailAddress = "dfeta_emailaddress"; public const string dfeta_potentialduplicateid = "dfeta_potentialduplicateid"; public const string ModifiedBy = "modifiedby"; public const string ModifiedOn = "modifiedon"; @@ -23441,6 +23442,26 @@ public string Description } } + /// + /// + /// + [Microsoft.Xrm.Sdk.AttributeLogicalNameAttribute("dfeta_emailaddress")] + public string dfeta_EmailAddress + { + [System.Diagnostics.DebuggerNonUserCode()] + get + { + return this.GetAttributeValue("dfeta_emailaddress"); + } + [System.Diagnostics.DebuggerNonUserCode()] + set + { + this.OnPropertyChanging("dfeta_EmailAddress"); + this.SetAttributeValue("dfeta_emailaddress", value); + this.OnPropertyChanged("dfeta_EmailAddress"); + } + } + /// /// Unique identifier for Person associated with Task. /// diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/CreateTrnRequestTaskQuery.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/CreateTrnRequestTaskQuery.cs index 58bc496e1f..04702d25b5 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/CreateTrnRequestTaskQuery.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/CreateTrnRequestTaskQuery.cs @@ -6,4 +6,5 @@ public record CreateTrnRequestTaskQuery : ICrmQuery public required string EvidenceFileName { get; init; } public required Stream EvidenceFileContent { get; init; } public required string EvidenceFileMimeType { get; init; } + public required string EmailAddress { get; init; } } diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/GetOpenTasksForEmailAddressQuery.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/GetOpenTasksForEmailAddressQuery.cs new file mode 100644 index 0000000000..18e4f85eae --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Queries/GetOpenTasksForEmailAddressQuery.cs @@ -0,0 +1,3 @@ +namespace TeachingRecordSystem.Core.Dqt.Queries; + +public record GetOpenTasksForEmailAddressQuery(string EmailAddress) : ICrmQuery; diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/CreateTrnRequestTaskHandler.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/CreateTrnRequestTaskHandler.cs index 5da0902cd5..f1fd507356 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/CreateTrnRequestTaskHandler.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/CreateTrnRequestTaskHandler.cs @@ -12,7 +12,8 @@ public async Task Execute(CreateTrnRequestTaskQuery query, IOrganizationSe { Id = Guid.NewGuid(), Subject = "Notification for TRA Support Team - TRN request", - Description = query.Description + Description = query.Description, + dfeta_EmailAddress = query.EmailAddress }; var annotationBody = await StreamHelper.GetBase64EncodedFileContent(query.EvidenceFileContent); diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/GetOpenTasksForEmailAddressHandler.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/GetOpenTasksForEmailAddressHandler.cs new file mode 100644 index 0000000000..14f8299a1e --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/QueryHandlers/GetOpenTasksForEmailAddressHandler.cs @@ -0,0 +1,28 @@ +using Microsoft.PowerPlatform.Dataverse.Client; +using Microsoft.Xrm.Sdk.Messages; +using Microsoft.Xrm.Sdk.Query; +using TeachingRecordSystem.Core.Dqt.Queries; + +namespace TeachingRecordSystem.Core.Dqt.QueryHandlers; +public class GetOpenTasksForEmailAddressHandler : ICrmQueryHandler +{ + public async Task Execute(GetOpenTasksForEmailAddressQuery query, IOrganizationServiceAsync organizationService) + { + var queryExpression = new QueryExpression() + { + EntityName = CrmTask.EntityLogicalName, + ColumnSet = new ColumnSet( + CrmTask.Fields.dfeta_EmailAddress, CrmTask.Fields.StateCode) + }; + queryExpression.Criteria.AddCondition(CrmTask.Fields.dfeta_EmailAddress, ConditionOperator.Equal, query.EmailAddress); + queryExpression.Criteria.AddCondition(CrmTask.Fields.StateCode, ConditionOperator.Equal, (int)TaskState.Open); + + var request = new RetrieveMultipleRequest() + { + Query = queryExpression + }; + + var response = await organizationService.RetrieveMultipleAsync(queryExpression); + return response.Entities.Select(e => e.ToEntity()).ToArray(); + } +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/RequestTrn/EmailInUseTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/RequestTrn/EmailInUseTests.cs new file mode 100644 index 0000000000..6ec551b246 --- /dev/null +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/RequestTrn/EmailInUseTests.cs @@ -0,0 +1,22 @@ +namespace TeachingRecordSystem.AuthorizeAccess.Tests.PageTests.RequestTrn; + +public class EmailInUseTests(HostFixture hostFixture) : TestBase(hostFixture) +{ + [Fact] + public async Task Get_WithoutEmailAddress_RedirectsToEmail() + { + // Arrange + var state = CreateNewState(); + state.HasPendingTrnRequest = true; + var journeyInstance = await CreateJourneyInstance(state); + + var request = new HttpRequestMessage(HttpMethod.Get, $"/request-trn/emailinuse?{journeyInstance.GetUniqueIdQueryParameter()}"); + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/request-trn/email?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/RequestTrn/EmailTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/RequestTrn/EmailTests.cs index 9d7d8c2bfe..e2db6837b8 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/RequestTrn/EmailTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.AuthorizeAccess.Tests/PageTests/RequestTrn/EmailTests.cs @@ -116,7 +116,6 @@ public async Task Post_InvalidFormatEmailAddress_ReturnsError() await AssertEx.HtmlResponseHasError(response, "Email", "Enter an email address in the correct format, like name@example.com"); } - [Fact] public async Task Post_ValidRequestWithValidData_RedirectsToNamePage() { @@ -140,4 +139,34 @@ public async Task Post_ValidRequestWithValidData_RedirectsToNamePage() Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); Assert.Equal($"/request-trn/name?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); } + + [Fact] + public async Task Post_RequestForEmailWithOpenTasks_RedirectsToEmailInUse() + { + // Arrange + var email = Faker.Internet.Email(); + var state = CreateNewState(); + var journeyInstance = await CreateJourneyInstance(state); + var person = await TestData.CreatePerson(); + await TestData.CreateCrmTask(x => + { + x.WithPersonId(person.ContactId); + x.WithEmailAddress(email); + }); + + var request = new HttpRequestMessage(HttpMethod.Post, $"/request-trn/email?{journeyInstance.GetUniqueIdQueryParameter()}") + { + Content = new FormUrlEncodedContent(new Dictionary + { + { "Email", email } + }) + }; + + // Act + var response = await HttpClient.SendAsync(request); + + // Assert + Assert.Equal(StatusCodes.Status302Found, (int)response.StatusCode); + Assert.Equal($"/request-trn/emailinuse?{journeyInstance.GetUniqueIdQueryParameter()}", response.Headers.Location?.OriginalString); + } } diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Dqt.CrmIntegrationTests/QueryTests/CreateTrnRequestTaskTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Dqt.CrmIntegrationTests/QueryTests/CreateTrnRequestTaskTests.cs index fd408c0807..999254cabf 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Dqt.CrmIntegrationTests/QueryTests/CreateTrnRequestTaskTests.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Dqt.CrmIntegrationTests/QueryTests/CreateTrnRequestTaskTests.cs @@ -24,13 +24,15 @@ public async Task QueryExecutesSuccessfully() var evidenceFileName = $"evidence-{uniqueId}.jpg"; var evidenceFileContent = new MemoryStream(TestCommon.TestData.JpegImage); var evidenceFileMimeType = "image/jpeg"; + var email = Faker.Internet.Email(); var query = new CreateTrnRequestTaskQuery() { Description = description, EvidenceFileName = evidenceFileName, EvidenceFileContent = evidenceFileContent, - EvidenceFileMimeType = evidenceFileMimeType + EvidenceFileMimeType = evidenceFileMimeType, + EmailAddress = email }; // Act @@ -43,6 +45,7 @@ public async Task QueryExecutesSuccessfully() Assert.NotNull(createdCrmTask); Assert.Equal("Notification for TRA Support Team - TRN request", createdCrmTask.Subject); Assert.Equal(description, createdCrmTask.Description); + Assert.Equal(createdCrmTask.dfeta_EmailAddress, email); var createdAnnotation = ctx.AnnotationSet.SingleOrDefault(i => i.GetAttributeValue(Annotation.Fields.FileName) == evidenceFileName); Assert.NotNull(createdAnnotation); diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Dqt.CrmIntegrationTests/QueryTests/GetOpenTasksForEmailAddressTests.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Dqt.CrmIntegrationTests/QueryTests/GetOpenTasksForEmailAddressTests.cs new file mode 100644 index 0000000000..4b8dc8afe8 --- /dev/null +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.Core.Dqt.CrmIntegrationTests/QueryTests/GetOpenTasksForEmailAddressTests.cs @@ -0,0 +1,93 @@ +namespace TeachingRecordSystem.Core.Dqt.CrmIntegrationTests.QueryTests; + +public class GetOpenTasksForEmailAddressTests : IAsyncLifetime +{ + private readonly CrmClientFixture.TestDataScope _dataScope; + private readonly CrmQueryDispatcher _crmQueryDispatcher; + + public GetOpenTasksForEmailAddressTests(CrmClientFixture crmClientFixture) + { + _dataScope = crmClientFixture.CreateTestDataScope(); + _crmQueryDispatcher = crmClientFixture.CreateQueryDispatcher(); + } + + public Task InitializeAsync() => Task.CompletedTask; + + public async Task DisposeAsync() => await _dataScope.DisposeAsync(); + + [Fact] + public async Task WhenCalled_WithEmailWithNoOpenTasks_ReturnsEmpty() + { + // Arrange + var emailWithNoOpenTasks = Faker.Internet.Email(); + + // Act + var result = await _crmQueryDispatcher.ExecuteQuery(new GetOpenTasksForEmailAddressQuery(emailWithNoOpenTasks)); + + // Assert + Assert.Empty(result); + } + + [Fact] + public async Task WhenCalled_WithEmailWithOpenTasks_ReturnsTasks() + { + // Arrange + var person = await _dataScope.TestData.CreatePerson(); + var emailWithOpenTasks = Faker.Internet.Email(); + await _dataScope.TestData.CreateCrmTask(x => + { + x.WithPersonId(person.ContactId); + x.WithEmailAddress(emailWithOpenTasks); + }); + + // Act + var result = await _crmQueryDispatcher.ExecuteQuery(new GetOpenTasksForEmailAddressQuery(emailWithOpenTasks)); + + // Assert + Assert.NotEmpty(result); + Assert.Collection(result, item1 => + { + Assert.Equal(emailWithOpenTasks, item1.dfeta_EmailAddress); + }); + } + + [Fact] + public async Task WhenCalled_WithEmailWithCompletedTasks_ReturnsEmpty() + { + // Arrange + var person = await _dataScope.TestData.CreatePerson(); + var emailWithOpenTasks = Faker.Internet.Email(); + await _dataScope.TestData.CreateCrmTask(x => + { + x.WithPersonId(person.ContactId); + x.WithEmailAddress(emailWithOpenTasks); + x.WithCompletedStatus(); + }); + + // Act + var result = await _crmQueryDispatcher.ExecuteQuery(new GetOpenTasksForEmailAddressQuery(emailWithOpenTasks)); + + // Assert + Assert.Empty(result); + } + + [Fact] + public async Task WhenCalled_WithEmailWithCancelledTasks_ReturnsEmpty() + { + // Arrange + var person = await _dataScope.TestData.CreatePerson(); + var emailWithOpenTasks = Faker.Internet.Email(); + await _dataScope.TestData.CreateCrmTask(x => + { + x.WithPersonId(person.ContactId); + x.WithEmailAddress(emailWithOpenTasks); + x.WithCanceledStatus(); + }); + + // Act + var result = await _crmQueryDispatcher.ExecuteQuery(new GetOpenTasksForEmailAddressQuery(emailWithOpenTasks)); + + // Assert + Assert.Empty(result); + } +} diff --git a/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreateCrmTask.cs b/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreateCrmTask.cs index 929d4f4a93..43681f88d7 100644 --- a/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreateCrmTask.cs +++ b/TeachingRecordSystem/tests/TeachingRecordSystem.TestCommon/TestData.CreateCrmTask.cs @@ -23,6 +23,7 @@ public class CreateCrmTaskBuilder private string? _description = null; private DateTime? _dueDate = null; private TaskState? _stateCode = null; + private string? _emailAddress = null; public CreateCrmTaskBuilder WithPersonId(Guid personId) { @@ -46,6 +47,17 @@ public CreateCrmTaskBuilder WithCategory(string category) return this; } + public CreateCrmTaskBuilder WithEmailAddress(string email) + { + if (_emailAddress is not null && _emailAddress != email) + { + throw new InvalidOperationException("WithEmailAddress has already been set"); + } + + _emailAddress = email; + return this; + } + public CreateCrmTaskBuilder WithSubject(string subject) { if (_subject is not null && _subject != subject) @@ -132,7 +144,8 @@ public async Task Execute(TestData crmTestData) Subject = subject, Description = description, ScheduledEnd = _dueDate, - StateCode = stateCode + StateCode = stateCode, + dfeta_EmailAddress = _emailAddress }; var txnRequestBuilder = RequestBuilder.CreateTransaction(crmTestData.OrganizationService); diff --git a/crm_attributes.json b/crm_attributes.json index 14c6a978e4..9bb1143879 100644 --- a/crm_attributes.json +++ b/crm_attributes.json @@ -333,6 +333,7 @@ "regardingobjectid", "scheduledend", "statecode", - "subject" + "subject", + "dfeta_EmailAddress" ] } diff --git a/tools/coretools/CrmSvcUtil.exe.config b/tools/coretools/CrmSvcUtil.exe.config index 24a60b63bb..2370eb6a8c 100644 --- a/tools/coretools/CrmSvcUtil.exe.config +++ b/tools/coretools/CrmSvcUtil.exe.config @@ -20,7 +20,7 @@ - +