Feature/ab#32490 updates to portal contact provider and events#2208
Merged
JamesPasta merged 5 commits intodevfrom Apr 1, 2026
Merged
Feature/ab#32490 updates to portal contact provider and events#2208JamesPasta merged 5 commits intodevfrom
JamesPasta merged 5 commits intodevfrom
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates the Applicant Portal contact model to use applicant-linked contacts (resolved via submissions/subject) rather than profile-linked contacts, and extends contact payloads to include creationTime.
Changes:
- Update
ContactInfoDataProvider/ApplicantProfileContactServiceto resolve contacts viaApplicationFormSubmission.OidcSub → ApplicantId(s) → ContactLink(RelatedEntityType="Applicant"), with editability dependent on whether a single applicant is resolved. - Update Grants Portal contact command handlers and message DTOs to carry
applicantIdand link contacts to theApplicantentity type. - Add
CreationTimetoContactInfoItemDto, update docs/examples, and deduplicate org results viaDistinct().
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| documentation/applicant-portal/applicant-profile-data-providers.md | Updates CONTACTINFO provider documentation to reflect applicant-linked resolution + editability rule and adds CreationTime mapping. |
| documentation/applicant-portal/applicant-portal-integration.md | Extends CONTACTINFO response example with creationTime (example contactType needs updating). |
| applications/Unity.GrantManager/src/Unity.GrantManager.Application/ApplicantProfile/ContactInfoDataProvider.cs | Switches from profile-based contact lookup to subject/applicant-based lookup. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Application/ApplicantProfile/ApplicantProfileContactService.cs | Implements applicant-ID resolution from submissions and returns conditional IsEditable; populates CreationTime. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/IApplicantProfileContactService.cs | Replaces profileId-based API with subject-based applicant contact retrieval. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Application.Contracts/ApplicantProfile/ProfileData/ContactInfoItemDto.cs | Adds CreationTime to the contact DTO contract. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantsPortal/Handlers/ContactCreateHandler.cs | Creates ContactLink records against Applicant using applicantId from command payload. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantsPortal/Handlers/ContactEditHandler.cs | Adds contact-link primary mutation logic keyed by applicantId (currently has behavior issues). |
| applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantsPortal/Handlers/ContactSetPrimaryHandler.cs | Updates set-primary flow to be applicant-based via applicantId in payload. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantsPortal/Messages/Commands/ContactCreateData.cs | Adds applicantId to create-contact command payload. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantsPortal/Messages/Commands/ContactEditData.cs | Adds applicantId to edit-contact command payload. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantsPortal/Messages/Commands/ContactSetPrimaryData.cs | Introduces DTO for set-primary command applicantId data. |
| applications/Unity.GrantManager/src/Unity.GrantManager.Application/ApplicantProfile/OrgInfoDataProvider.cs | Adds Distinct() to remove duplicate organizations when multiple submissions point to the same applicant/org. |
| applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/Contacts/ContactInfoServiceTests.cs | Updates tests for applicant/subject-based contact resolution and adds editability rule tests (names need updating). |
| applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/Contacts/ContactInfoDataProviderTests.cs | Updates provider tests to expect GetApplicantContactsAsync(...) usage (test name needs updating). |
| applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/GrantsPortal/ContactCreateHandlerTests.cs | Updates create-command tests for applicant-linked contact creation. |
| applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/GrantsPortal/ContactEditHandlerTests.cs | Updates edit-command tests to include applicantId and handler dependency updates. |
| applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/GrantsPortal/ContactSetPrimaryHandlerTests.cs | Updates set-primary tests to include applicantId in payload data (should better exercise new behavior). |
| applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/Applicants/OrgInfoDataProviderTests.cs | Updates expectations to reflect distinct org results. |
| applications/Unity.GrantManager/test/Unity.GrantManager.Application.Tests/Applicants/ApplicantProfileDataProviderTests.cs | Updates mocked contact service call from profile-based to applicant/subject-based. |
Comments suppressed due to low confidence (1)
applications/Unity.GrantManager/src/Unity.GrantManager.Application/GrantsPortal/Handlers/ContactCreateHandler.cs:62
ContactCreateHandlerusesinnerData.ApplicantIdto create theContactLink, but ifapplicantIdis missing from the JSON it will deserialize asGuid.Emptyand create a link to an invalid applicant. Add an explicit validation thatApplicantId != Guid.Emptyand throw a clearArgumentException(e.g., "applicantId is required") before inserting records.
var contactId = Guid.Parse(payload.ContactId ?? throw new ArgumentException("contactId is required"));
var profileId = Guid.Parse(payload.ProfileId ?? throw new ArgumentException("profileId is required"));
var innerData = payload.Data?.ToObject<ContactCreateData>()
?? throw new ArgumentException("Contact data is required");
// Idempotency: if the contact already exists, treat as success
var existing = await contactRepository.FindAsync(contactId);
if (existing != null)
{
logger.LogInformation("Contact {ContactId} already exists. Treating as idempotent success.", contactId);
return "Contact already exists";
}
logger.LogInformation("Creating contact {ContactId} for profile {ProfileId}", contactId, profileId);
var contact = new Contact
{
Name = innerData.Name,
Email = innerData.Email,
Title = innerData.Title,
HomePhoneNumber = innerData.HomePhoneNumber,
MobilePhoneNumber = innerData.MobilePhoneNumber,
WorkPhoneNumber = innerData.WorkPhoneNumber,
WorkPhoneExtension = innerData.WorkPhoneExtension
};
EntityHelper.TrySetId(contact, () => contactId);
await contactRepository.InsertAsync(contact);
// Create a contact link to track the relationship and primary status
var contactLink = new ContactLink
{
ContactId = contactId,
RelatedEntityType = "Applicant",
RelatedEntityId = innerData.ApplicantId,
Role = innerData.Role,
IsPrimary = innerData.IsPrimary,
IsActive = true
};
JamesPasta
approved these changes
Apr 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.