diff --git a/GetIntoTeachingApi/Jobs/ApplyCandidateSyncJob.cs b/GetIntoTeachingApi/Jobs/ApplyCandidateSyncJob.cs index 8fb10aac7..69b7d6e89 100644 --- a/GetIntoTeachingApi/Jobs/ApplyCandidateSyncJob.cs +++ b/GetIntoTeachingApi/Jobs/ApplyCandidateSyncJob.cs @@ -1,9 +1,10 @@ using System; -using GetIntoTeachingApi.Models.Apply; +using GetIntoTeachingApi.Models.Crm; using GetIntoTeachingApi.Services; using GetIntoTeachingApi.Utils; using Hangfire; using Microsoft.Extensions.Logging; +using ApplyCandidate = GetIntoTeachingApi.Models.Apply.Candidate; namespace GetIntoTeachingApi.Jobs { @@ -29,7 +30,7 @@ public ApplyCandidateSyncJob( _appSettings = appSettings; } - public void Run(Candidate applyCandidate) + public void Run(ApplyCandidate applyCandidate) { if (_appSettings.IsCrmIntegrationPaused) { @@ -41,23 +42,25 @@ public void Run(Candidate applyCandidate) _logger.LogInformation("ApplyCandidateSyncJob - Succeeded - {Id}", applyCandidate.Id); } - public void SyncCandidate(Candidate applyCandidate) + public void SyncCandidate(ApplyCandidate applyCandidate) { - var candidate = applyCandidate.ToCrmModel(); - var match = _crm.MatchCandidate(candidate.Email, applyCandidate.Id); + ContactChannelCandidateWrapper wrappedCandidate = new(applyCandidate.ToCrmModel()) + { + CreationChannelSourceId = (int?) ContactChannelCreation.CreationChannelSource.Apply + }; + + var match = _crm.MatchCandidate(wrappedCandidate.ScopedCandidate.Email, applyCandidate.Id); _logger.LogInformation("ApplyCandidateSyncJob - {Status} - {Id}", match == null ? "Miss" : "Hit", applyCandidate.Id); if (match != null) { - UpdateCandidateWithMatch(candidate, match); - } - else - { - candidate.ChannelId = (int)Models.Crm.Candidate.Channel.ApplyForTeacherTraining; + UpdateCandidateWithMatch(wrappedCandidate.ScopedCandidate, match); } - - string json = candidate.SerializeChangeTracked(); + + wrappedCandidate.ScopedCandidate.ConfigureChannel(wrappedCandidate, wrappedCandidate.ScopedCandidate.Id); + + string json = wrappedCandidate.ScopedCandidate.SerializeChangeTracked(); _jobClient.Enqueue((x) => x.Run(json, null)); } diff --git a/GetIntoTeachingApi/Jobs/ContactChannelCandidateWrapper.cs b/GetIntoTeachingApi/Jobs/ContactChannelCandidateWrapper.cs new file mode 100644 index 000000000..8c602506a --- /dev/null +++ b/GetIntoTeachingApi/Jobs/ContactChannelCandidateWrapper.cs @@ -0,0 +1,41 @@ +using GetIntoTeachingApi.Models.Crm; + +namespace GetIntoTeachingApi.Jobs +{ + public class ContactChannelCandidateWrapper : ICreateContactChannel + { + /// + /// Provides the default read-only contact creation channel integer value. + /// + public int? DefaultContactCreationChannel => + (int?)Candidate.Channel.ApplyForTeacherTraining; + + /// + /// Provides the ability to assign and retrieve the channel source creation identifier. + /// + public int? CreationChannelSourceId { get; set; } + + /// + /// Provides the ability to assign and retrieve the channel service creation identifier. + /// + public int? CreationChannelServiceId { get; set; } + + /// + /// Provides the ability to assign and retrieve the channel activity creation identifier. + /// + public int? CreationChannelActivityId { get; set; } + + /// + /// Provides the ability to retrieve the underlying CRM candidate record. + /// + public Candidate ScopedCandidate { get; } + + /// + /// Initialises the instance with an underlying CRM candidate record. + /// + public ContactChannelCandidateWrapper(Candidate candidate) + { + ScopedCandidate = candidate; + } + } +} \ No newline at end of file diff --git a/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_application_form.json b/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_application_form.json index 5b9605bb6..7564e1f67 100644 --- a/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_application_form.json +++ b/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_application_form.json @@ -29,9 +29,7 @@ }, { "Key": "dfe_channelcreation", - "Value": { - "Value": 222750025 - } + "Value": null }, { "Key": "dfe_newregistrant", @@ -126,5 +124,38 @@ "RelatedEntities": [], "RowVersion": null, "KeyAttributes": [] + }, + { + "LogicalName" : "dfe_contactchannelcreation", + "Id" : "00000000-0000-0000-0000-000000000000", + "Attributes" : [ { + "Key" : "dfe_contactid", + "Value" : { + "Id" : "00000000-0000-0000-0000-000000000000", + "LogicalName" : "contact", + "Name" : null, + "KeyAttributes" : [ ], + "RowVersion" : null + } + }, { + "Key" : "dfe_creationchannel", + "Value" : true + }, { + "Key" : "dfe_creationchannelactivities", + "Value" : null + }, { + "Key" : "dfe_creationchannelservice", + "Value" : null + }, { + "Key" : "dfe_creationchannelsource", + "Value" : { + "Value" : 222750000 + } + } ], + "EntityState" : 1, + "FormattedValues" : [ ], + "RelatedEntities" : [ ], + "RowVersion" : null, + "KeyAttributes" : [ ] } ] \ No newline at end of file diff --git a/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_fully_populated_application_form.json b/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_fully_populated_application_form.json index efb5e99f6..4985e09f8 100644 --- a/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_fully_populated_application_form.json +++ b/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_fully_populated_application_form.json @@ -29,9 +29,7 @@ }, { "Key": "dfe_channelcreation", - "Value": { - "Value": 222750025 - } + "Value": null }, { "Key": "dfe_newregistrant", @@ -277,5 +275,38 @@ "RelatedEntities": [], "RowVersion": null, "KeyAttributes": [] + }, + { + "LogicalName" : "dfe_contactchannelcreation", + "Id" : "00000000-0000-0000-0000-000000000000", + "Attributes" : [ { + "Key" : "dfe_contactid", + "Value" : { + "Id" : "00000000-0000-0000-0000-000000000000", + "LogicalName" : "contact", + "Name" : null, + "KeyAttributes" : [ ], + "RowVersion" : null + } + }, { + "Key" : "dfe_creationchannel", + "Value" : true + }, { + "Key" : "dfe_creationchannelactivities", + "Value" : null + }, { + "Key" : "dfe_creationchannelservice", + "Value" : null + }, { + "Key" : "dfe_creationchannelsource", + "Value" : { + "Value" : 222750000 + } + } ], + "EntityState" : 1, + "FormattedValues" : [ ], + "RelatedEntities" : [ ], + "RowVersion" : null, + "KeyAttributes" : [ ] } ] \ No newline at end of file diff --git a/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_no_application_form.json b/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_no_application_form.json index 35f4284a9..88a6bbae1 100644 --- a/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_no_application_form.json +++ b/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_a_new_candidate_and_no_application_form.json @@ -23,9 +23,7 @@ }, { "Key": "dfe_channelcreation", - "Value": { - "Value": 222750025 - } + "Value": null }, { "Key": "dfe_newregistrant", @@ -45,5 +43,38 @@ "RelatedEntities": [], "RowVersion": null, "KeyAttributes": [] + }, + { + "LogicalName" : "dfe_contactchannelcreation", + "Id" : "00000000-0000-0000-0000-000000000000", + "Attributes" : [ { + "Key" : "dfe_contactid", + "Value" : { + "Id" : "00000000-0000-0000-0000-000000000000", + "LogicalName" : "contact", + "Name" : null, + "KeyAttributes" : [ ], + "RowVersion" : null + } + }, { + "Key" : "dfe_creationchannel", + "Value" : true + }, { + "Key" : "dfe_creationchannelactivities", + "Value" : null + }, { + "Key" : "dfe_creationchannelservice", + "Value" : null + }, { + "Key" : "dfe_creationchannelsource", + "Value" : { + "Value" : 222750000 + } + } ], + "EntityState" : 1, + "FormattedValues" : [ ], + "RelatedEntities" : [ ], + "RowVersion" : null, + "KeyAttributes" : [ ] } ] \ No newline at end of file diff --git a/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_an_existing_candidate_and_application_form.json b/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_an_existing_candidate_and_application_form.json index 1945bd9b3..1f2aeff71 100644 --- a/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_an_existing_candidate_and_application_form.json +++ b/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_an_existing_candidate_and_application_form.json @@ -120,5 +120,38 @@ "RelatedEntities": [], "RowVersion": null, "KeyAttributes": [] + }, + { + "LogicalName" : "dfe_contactchannelcreation", + "Id" : "00000000-0000-0000-0000-000000000000", + "Attributes" : [ { + "Key" : "dfe_contactid", + "Value" : { + "Id" : "00000000-0000-0000-0000-000000000000", + "LogicalName" : "contact", + "Name" : null, + "KeyAttributes" : [ ], + "RowVersion" : null + } + }, { + "Key" : "dfe_creationchannel", + "Value" : true + }, { + "Key" : "dfe_creationchannelactivities", + "Value" : null + }, { + "Key" : "dfe_creationchannelservice", + "Value" : null + }, { + "Key" : "dfe_creationchannelsource", + "Value" : { + "Value" : 222750000 + } + } ], + "EntityState" : 1, + "FormattedValues" : [ ], + "RelatedEntities" : [ ], + "RowVersion" : null, + "KeyAttributes" : [ ] } ] \ No newline at end of file diff --git a/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_an_existing_candidate_and_fully_populated_application_form.json b/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_an_existing_candidate_and_fully_populated_application_form.json index fef20a28b..39efb435c 100644 --- a/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_an_existing_candidate_and_fully_populated_application_form.json +++ b/GetIntoTeachingApiTests/Contracts/Output/ApplyCandidateApi/sync_with_an_existing_candidate_and_fully_populated_application_form.json @@ -271,5 +271,38 @@ "RelatedEntities": [], "RowVersion": null, "KeyAttributes": [] + }, + { + "LogicalName" : "dfe_contactchannelcreation", + "Id" : "00000000-0000-0000-0000-000000000000", + "Attributes" : [ { + "Key" : "dfe_contactid", + "Value" : { + "Id" : "00000000-0000-0000-0000-000000000000", + "LogicalName" : "contact", + "Name" : null, + "KeyAttributes" : [ ], + "RowVersion" : null + } + }, { + "Key" : "dfe_creationchannel", + "Value" : true + }, { + "Key" : "dfe_creationchannelactivities", + "Value" : null + }, { + "Key" : "dfe_creationchannelservice", + "Value" : null + }, { + "Key" : "dfe_creationchannelsource", + "Value" : { + "Value" : 222750000 + } + } ], + "EntityState" : 1, + "FormattedValues" : [ ], + "RelatedEntities" : [ ], + "RowVersion" : null, + "KeyAttributes" : [ ] } ] \ No newline at end of file diff --git a/GetIntoTeachingApiTests/Jobs/ApplyCandidateSyncJobTests.cs b/GetIntoTeachingApiTests/Jobs/ApplyCandidateSyncJobTests.cs index b3263619b..8bdedb123 100644 --- a/GetIntoTeachingApiTests/Jobs/ApplyCandidateSyncJobTests.cs +++ b/GetIntoTeachingApiTests/Jobs/ApplyCandidateSyncJobTests.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using System.Linq; using FluentAssertions; using GetIntoTeachingApi.Jobs; using GetIntoTeachingApi.Models.Apply; +using GetIntoTeachingApi.Models.Crm; using GetIntoTeachingApi.Services; using GetIntoTeachingApi.Utils; using GetIntoTeachingApiTests.Helpers; @@ -12,6 +14,9 @@ using Microsoft.Extensions.Logging; using Moq; using Xunit; +using ApplicationChoice = GetIntoTeachingApi.Models.Apply.ApplicationChoice; +using ApplicationForm = GetIntoTeachingApi.Models.Apply.ApplicationForm; +using Candidate = GetIntoTeachingApi.Models.Apply.Candidate; namespace GetIntoTeachingApiTests.Jobs { @@ -138,7 +143,7 @@ public void Run_OnSuccessWithExistingCandidateHavingSecondaryEmail_SetsIdAndQueu _mockJobClient.Verify(x => x.Create( It.Is(job => job.Type == typeof(UpsertCandidateJob) && job.Method.Name == "Run" && - IsMatch(candidate, (string)job.Args[0])), + IsMatchWithNewContactChannelCreation(candidate, (string)job.Args[0])), It.IsAny())); _mockLogger.VerifyInformationWasCalled($"ApplyCandidateSyncJob - Started - {_candidate.Id}"); @@ -198,7 +203,7 @@ public void Run_OnSuccessWithNewCandidate_SetsChannelAndQueuesUpsertJobForCandid _mockJobClient.Verify(x => x.Create( It.Is(job => job.Type == typeof(UpsertCandidateJob) && job.Method.Name == "Run" && - IsMatch(candidate, (string)job.Args[0])), + IsMatchWithNewContactChannelCreation(candidate, (string)job.Args[0])), It.IsAny())); } @@ -225,7 +230,7 @@ public void Run_MatchesBackOnApplyIdAsWellAsEmailAndWritesApplyEmailToSecondaryE _mockJobClient.Verify(x => x.Create( It.Is(job => job.Type == typeof(UpsertCandidateJob) && job.Method.Name == "Run" && - IsMatch(candidate, (string)job.Args[0])), + IsMatchWithNewContactChannelCreation(candidate, (string)job.Args[0])), It.IsAny())); _mockCrm.VerifyAll(); @@ -242,9 +247,26 @@ public void Run_WhenCrmIntegrationPaused_Aborts() .WithMessage("ApplyCandidateSyncJob - Aborting (CRM integration paused)."); } - private static bool IsMatch(GetIntoTeachingApi.Models.Crm.Candidate candidateA, string candidateBJson) + private static bool IsMatchWithNewContactChannelCreation(GetIntoTeachingApi.Models.Crm.Candidate candidateA, string candidateBJson) { var candidateB = candidateBJson.DeserializeChangeTracked(); + + // CandidateB will have an additional automatically created ContactChannelCreation record added via the job + var applyContactChannelCreationRecord = candidateB.ContactChannelCreations.Last(); + + applyContactChannelCreationRecord.CreationChannelSourceId.Should() + .Be((int?)ContactChannelCreation.CreationChannelSource.Apply); + + // We need to add the record to CandidateA to compare + candidateA.ContactChannelCreations.Add(applyContactChannelCreationRecord); + + // CandidateB will have a null ChannelId; we need to remove this from CandidateA if it is not null to compare + if (candidateA.ChannelId != null) + { + candidateB.ChannelId.Should().BeNull(); + candidateA.ChannelId = null; + } + candidateA.Should().BeEquivalentTo(candidateB); return true; } diff --git a/GetIntoTeachingApiTests/Jobs/ContactChannelCandidateWrapperTests.cs b/GetIntoTeachingApiTests/Jobs/ContactChannelCandidateWrapperTests.cs new file mode 100644 index 000000000..c05c6ed05 --- /dev/null +++ b/GetIntoTeachingApiTests/Jobs/ContactChannelCandidateWrapperTests.cs @@ -0,0 +1,24 @@ +using FluentAssertions; +using GetIntoTeachingApi.Jobs; +using GetIntoTeachingApi.Models.Crm; +using Xunit; + +namespace GetIntoTeachingApiTests.Jobs +{ + public class ContactChannelCandidateWrapperTests + { + private readonly Candidate _candidate; + + public ContactChannelCandidateWrapperTests() + { + _candidate = new Candidate(); + } + + [Fact] + public void Constructor_WithCandidate_MapsToScopedCandidate() + { + var contactChannelCandidateWrapper = new ContactChannelCandidateWrapper(_candidate); + contactChannelCandidateWrapper.ScopedCandidate.Should().Be(_candidate); + } + } +}