Skip to content

Commit

Permalink
create and verify controlling member update invite (#1397)
Browse files Browse the repository at this point in the history
# Description

This PR includes the following proposed change(s):

- spdbt-3055: be: modify the create Controlling member invitation
endpoint to support "update cm" invite.
- spdbt-3056: be: when verifying the invite, need pass back invitetype,
also contact id.
  • Loading branch information
esdd1995 authored Sep 13, 2024
2 parents 1e7b65c + dc6ac90 commit 3033654
Show file tree
Hide file tree
Showing 11 changed files with 11,445 additions and 817 deletions.
3 changes: 2 additions & 1 deletion src/Spd.Manager.Licence/BizMemberContract.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public interface IBizMemberManager
public Task<ControllingMemberAppInviteVerifyResponse> Handle(VerifyBizControllingMemberInviteCommand command, CancellationToken ct);
}

public record BizControllingMemberNewInviteCommand(Guid BizContactId, Guid UserId, string HostUrl) : IRequest<ControllingMemberInvitesCreateResponse>;
public record BizControllingMemberNewInviteCommand(Guid BizContactId, Guid UserId, string HostUrl, ControllingMemberAppInviteTypeCode InviteTypeCode = ControllingMemberAppInviteTypeCode.New) : IRequest<ControllingMemberInvitesCreateResponse>;
public record VerifyBizControllingMemberInviteCommand(string InviteEncryptedCode) : IRequest<ControllingMemberAppInviteVerifyResponse>;
public record GetBizMembersQuery(Guid BizId, Guid? AppId = null) : IRequest<Members>;
public record GetNonSwlBizMemberCommand(Guid BizContactId) : IRequest<NonSwlContactInfo>;
Expand Down Expand Up @@ -52,6 +52,7 @@ public record ControllingMemberAppInviteVerifyResponse()
{
public Guid BizContactId { get; set; }
public Guid? BizLicAppId { get; set; }
public Guid? ContactId { get; set; }
public Guid BizId { get; set; }
public Guid? ControllingMemberCrcAppId { get; set; }
public ApplicationPortalStatusCode? ControllingMemberCrcAppPortalStatusCode { get; set; }
Expand Down
11 changes: 7 additions & 4 deletions src/Spd.Manager.Licence/BizMemberManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using AutoMapper;
using MediatR;
using Microsoft.AspNetCore.Http.HttpResults;
using Spd.Resource.Repository;
using Spd.Resource.Repository.Application;
using Spd.Resource.Repository.Biz;
Expand Down Expand Up @@ -105,16 +104,20 @@ public async Task<ControllingMemberInvitesCreateResponse> Handle(BizControllingM

//get info from bizContactId
BizContactResp contactResp = await _bizContactRepository.GetBizContactAsync(cmd.BizContactId, cancellationToken);
if (contactResp == null)
throw new ApiException(HttpStatusCode.BadRequest, "Cannot find the non-swl controlling member.");
if (contactResp.BizContactRoleCode != BizContactRoleEnum.ControllingMember)
throw new ApiException(HttpStatusCode.BadRequest, "Cannot send out invitation for non-controlling member.");
throw new ApiException(HttpStatusCode.BadRequest, "Cannot send out invitation for non-swl controlling member.");
if (string.IsNullOrWhiteSpace(contactResp.EmailAddress))
throw new ApiException(HttpStatusCode.BadRequest, "Cannot send out invitation when there is no email address provided.");
if (contactResp.LatestControllingMemberCrcAppPortalStatusEnum != null)
if (contactResp.LatestControllingMemberCrcAppPortalStatusEnum != null &&
contactResp.LatestControllingMemberCrcAppPortalStatusEnum != ApplicationPortalStatusEnum.CompletedCleared)
throw new ApiException(HttpStatusCode.BadRequest, "This business contact already has a CRC application");

var createCmd = _mapper.Map<ControllingMemberInviteCreateCmd>(contactResp);
createCmd.CreatedByUserId = cmd.UserId;
createCmd.HostUrl = cmd.HostUrl;
createCmd.InviteTypeCode = Enum.Parse<ControllingMemberAppInviteTypeEnum>(cmd.InviteTypeCode.ToString());
await _cmInviteRepository.ManageAsync(createCmd, cancellationToken);

return new ControllingMemberInvitesCreateResponse(cmd.BizContactId) { CreateSuccess = true };
Expand Down Expand Up @@ -200,7 +203,7 @@ public async Task<Unit> Handle(UpsertBizMembersCommand cmd, CancellationToken ct
return default;
}

public async Task<NonSwlContactInfo> Handle(GetNonSwlBizMemberCommand cmd, CancellationToken ct)
public async Task<NonSwlContactInfo> Handle(GetNonSwlBizMemberCommand cmd, CancellationToken ct)
{
var result = await _bizContactRepository.GetBizContactAsync(cmd.BizContactId, ct);
if (result == null) throw new ApiException(HttpStatusCode.BadRequest, $"bizContact with id {cmd.BizContactId} not found");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,21 +146,24 @@ public async Task<ActionResult> DeleteBizMember([FromRoute] Guid bizId, [FromRou

/// <summary>
/// Create controlling member crc invitation for this biz contact
/// Example: http://localhost:5114/api/business-licence-application/controlling-member-invitation/123?inviteType=Update
/// </summary>
/// <param name="bizContactId"></param>
/// <param name="inviteType"></param>
/// <returns></returns>
[Route("api/business-licence-application/controlling-member-invitation/{bizContactId}")]
[Authorize(Policy = "OnlyBceid", Roles = "PrimaryBusinessManager,BusinessManager")]
[HttpGet]
public async Task<ControllingMemberInvitesCreateResponse> CreateControllingMemberCrcAppInvitation([FromRoute][Required] Guid bizContactId, CancellationToken ct)
public async Task<ControllingMemberInvitesCreateResponse> CreateControllingMemberCrcAppInvitation([FromRoute][Required] Guid bizContactId,
[FromQuery] ControllingMemberAppInviteTypeCode inviteType = ControllingMemberAppInviteTypeCode.New)
{
var userIdStr = _currentUser.GetUserId();
if (userIdStr == null) throw new ApiException(System.Net.HttpStatusCode.Unauthorized, "Unauthorized");
string? hostUrl = _configuration.GetValue<string>("HostUrl");
if (hostUrl == null)
throw new ConfigurationErrorsException("HostUrl is not set correctly in configuration.");
var inviteCreateCmd = new BizControllingMemberNewInviteCommand(bizContactId, Guid.Parse(userIdStr), hostUrl);
return await _mediator.Send(inviteCreateCmd, ct);
var inviteCreateCmd = new BizControllingMemberNewInviteCommand(bizContactId, Guid.Parse(userIdStr), hostUrl, inviteType);
return await _mediator.Send(inviteCreateCmd);
}

/// <summary>
Expand Down
8 changes: 5 additions & 3 deletions src/Spd.Resource.Repository/BizContact/Mappings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public Mappings()
.ForMember(d => d.LatestControllingMemberCrcAppPortalStatusEnum, opt => opt.MapFrom(s => GetLastestControllingMemberCrcApp(s.spd_businesscontact_spd_application.ToList()).PortalStatus));
}

private (Guid? AppId, ApplicationPortalStatusEnum? PortalStatus) GetLastestControllingMemberCrcApp(IEnumerable<spd_application> apps)
private static (Guid? AppId, ApplicationPortalStatusEnum? PortalStatus) GetLastestControllingMemberCrcApp(IEnumerable<spd_application> apps)
{
Guid? cmServiceTypeGuid = DynamicsContextLookupHelpers.GetServiceTypeGuid(ServiceTypeEnum.SECURITY_BUSINESS_LICENCE_CONTROLLING_MEMBER_CRC.ToString());
spd_application? app = apps
Expand All @@ -52,9 +52,11 @@ public Mappings()
}
}

private (Guid? InviteId, ApplicationInviteStatusEnum? InviteStatus) GetLastestControllingMemberInvite(IEnumerable<spd_portalinvitation> invites)
private static (Guid? InviteId, ApplicationInviteStatusEnum? InviteStatus) GetLastestControllingMemberInvite(IEnumerable<spd_portalinvitation> invites)
{
spd_portalinvitation? invite = invites.OrderByDescending(app => app.createdon).FirstOrDefault();
spd_portalinvitation? invite = invites.Where(i => i.spd_invitationtype == (int)InvitationTypeOptionSet.ControllingMemberCRC)
.OrderByDescending(app => app.createdon)
.FirstOrDefault();
if (invite == null) return (null, null);
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public record ControllingMemberInviteVerifyResp
public Guid BizContactId { get; set; }
public Guid BizId { get; set; }
public Guid InviteId { get; set; }
public ControllingMemberAppInviteTypeEnum InviteTypeCode { get; set; }
}

public record ControllingMemberInvite
Expand All @@ -47,5 +48,12 @@ public record ControllingMemberInvite
public Guid BizId { get; set; }
public Guid CreatedByUserId { get; set; }
public Guid BizContactId { get; set; }
public ControllingMemberAppInviteTypeEnum InviteTypeCode { get; set; } = ControllingMemberAppInviteTypeEnum.New;
}

public enum ControllingMemberAppInviteTypeEnum
{
New,
Update
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ private async Task UpdateApplicationInvitesAsync(ControllingMemberInviteUpdateCm
{
spd_portalinvitation? invite = await _dynaContext.spd_portalinvitations
.Where(i => i.statecode == DynamicsConstants.StateCode_Active)
.Where(i => i.spd_invitationtype != null && i.spd_invitationtype == (int)InvitationTypeOptionSet.ControllingMemberCRC)
.Where(i => i.spd_invitationtype != null && (i.spd_invitationtype == (int)InvitationTypeOptionSet.ControllingMemberCRC || i.spd_invitationtype == (int)InvitationTypeOptionSet.ControllingMemberCRCUpdate))
.Where(i => i.spd_portalinvitationid == cmInviteUpdateCmd.ControllingMemberInviteId)
.FirstOrDefaultAsync(cancellationToken);
if (invite == null)
Expand Down Expand Up @@ -95,7 +95,7 @@ public async Task<ControllingMemberInviteVerifyResp> VerifyControllingMemberInvi
var invite = await _dynaContext.spd_portalinvitations
.Expand(i => i.spd_OrganizationId)
.Where(i => i.spd_portalinvitationid == inviteId)
.Where(i => i.spd_invitationtype == (int)InvitationTypeOptionSet.ControllingMemberCRC)
.Where(i => i.spd_invitationtype == (int)InvitationTypeOptionSet.ControllingMemberCRC || i.spd_invitationtype == (int)InvitationTypeOptionSet.ControllingMemberCRCUpdate)
.Where(i => i.statecode != DynamicsConstants.StateCode_Inactive)
.FirstOrDefaultAsync(ct);
if (invite == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@ public Mappings()
.ForMember(d => d.spd_firstname, opt => opt.MapFrom(s => StringHelper.ToTitleCase(s.GivenName)))
.ForMember(d => d.spd_surname, opt => opt.MapFrom(s => StringHelper.ToTitleCase(s.Surname)))
.ForMember(d => d.spd_email, opt => opt.MapFrom(s => s.EmailAddress))
.ForMember(d => d.spd_invitationtype, opt => opt.MapFrom(s => InvitationTypeOptionSet.ControllingMemberCRC))
.ForMember(d => d.spd_invitationtype, opt => opt.MapFrom(s => s.InviteTypeCode == ControllingMemberAppInviteTypeEnum.New ? (int)InvitationTypeOptionSet.ControllingMemberCRC : (int)InvitationTypeOptionSet.ControllingMemberCRCUpdate))
.ForMember(d => d.spd_views, opt => opt.MapFrom(s => 0))
.ForMember(d => d.spd_payeetype, opt => opt.MapFrom(s => PayerPreferenceOptionSet.Applicant))
.ReverseMap()
.ForMember(d => d.GivenName, opt => opt.MapFrom(s => s.spd_firstname))
.ForMember(d => d.Surname, opt => opt.MapFrom(s => s.spd_surname));
.ForMember(d => d.Surname, opt => opt.MapFrom(s => s.spd_surname))
.ForMember(d => d.InviteTypeCode, opt => opt.MapFrom(s => s.spd_invitationtype == (int)InvitationTypeOptionSet.ControllingMemberCRC ? ControllingMemberAppInviteTypeEnum.New : ControllingMemberAppInviteTypeEnum.Update));

_ = CreateMap<spd_portalinvitation, ControllingMemberInviteResp>()
.IncludeBase<spd_portalinvitation, ControllingMemberInvite>()
Expand All @@ -33,7 +34,8 @@ public Mappings()
_ = CreateMap<spd_portalinvitation, ControllingMemberInviteVerifyResp>()
.ForMember(d => d.BizId, opt => opt.MapFrom(s => s._spd_organizationid_value))
.ForMember(d => d.BizContactId, opt => opt.MapFrom(s => s._spd_businesscontact_value))
.ForMember(d => d.InviteId, opt => opt.MapFrom(s => s.spd_portalinvitationid));
.ForMember(d => d.InviteId, opt => opt.MapFrom(s => s.spd_portalinvitationid))
.ForMember(d => d.InviteTypeCode, opt => opt.MapFrom(s => s.spd_invitationtype == (int)InvitationTypeOptionSet.ControllingMemberCRC ? ControllingMemberAppInviteTypeEnum.New : ControllingMemberAppInviteTypeEnum.Update));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"MakeTypesInternal": false,
"OpenGeneratedFilesInIDE": false,
"GenerateMultipleFiles": false,
"CustomHttpHeaders": "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ims0Y3BkRmdybU0yMHJhcThYVVRxMVlpaXNISSIsImtpZCI6Ims0Y3BkRmdybU0yMHJhcThYVVRxMVlpaXNISSJ9.eyJhdWQiOiJodHRwczovL3NwZC1zcGFyYy5kZXYuamFnLmdvdi5iYy5jYS9hcGkvZGF0YS92OS4wLyIsImlzcyI6Imh0dHA6Ly9zdHN0ZXN0Lmdvdi5iYy5jYS9hZGZzL3NlcnZpY2VzL3RydXN0IiwiaWF0IjoxNzI0NzExNzczLCJuYmYiOjE3MjQ3MTE3NzMsImV4cCI6MTcyNDcxNTM3MywidXBuIjoic3BkX29zYWRAZ292LmJjLmNhIiwidW5pcXVlX25hbWUiOiJJRElSXFxTUERfT1NBRCIsImFwcHR5cGUiOiJDb25maWRlbnRpYWwiLCJhcHBpZCI6IjAxNzRiMjAzLWNkY2YtNDE2NC04NTMyLTBhNGM0ZmRmZGY2MiIsImF1dGhtZXRob2QiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydCIsImF1dGhfdGltZSI6IjIwMjQtMDgtMjZUMjI6MzY6MTMuNDYzWiIsInZlciI6IjEuMCIsInNjcCI6Im9wZW5pZCJ9.eqDSxilaKPjHWF8mdmPGjhpTGTE_fAwgFrfHcPXf-N_JOY09-ZXRnoDexUqMtSRutpNeYFEB_VcES24krXc1rsBrVoAs__od9hh39bHzX46MpHoStK9-JUgp92X2s-ugPy28vmDojA_de7bv4CoHR9ygNWBKvjdAsd4Y19mkhxbtYsQFIStnZZSVQTDf6nBtbUhidcTuVehpiZV4GN8TZzbPkgq6L8Eyk1XSiLNjUnhSUM-Qh83ECS8Rk2B6eSiCpytzTBTP_BdwK0hf7ao4XC9-5OI91hMQHWMKPDm2eNxTuYBGrn-v_9kUwsX2jV-730k2UkBUYFzsYCzT10kMLQ",
"CustomHttpHeaders": "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ims0Y3BkRmdybU0yMHJhcThYVVRxMVlpaXNISSIsImtpZCI6Ims0Y3BkRmdybU0yMHJhcThYVVRxMVlpaXNISSJ9.eyJhdWQiOiJodHRwczovL3NwZC1zcGFyYy5kZXYuamFnLmdvdi5iYy5jYS9hcGkvZGF0YS92OS4wLyIsImlzcyI6Imh0dHA6Ly9zdHN0ZXN0Lmdvdi5iYy5jYS9hZGZzL3NlcnZpY2VzL3RydXN0IiwiaWF0IjoxNzI2MTg2ODQ1LCJuYmYiOjE3MjYxODY4NDUsImV4cCI6MTcyNjE5MDQ0NSwidXBuIjoic3BkX29zYWRAZ292LmJjLmNhIiwidW5pcXVlX25hbWUiOiJJRElSXFxTUERfT1NBRCIsImFwcHR5cGUiOiJDb25maWRlbnRpYWwiLCJhcHBpZCI6IjAxNzRiMjAzLWNkY2YtNDE2NC04NTMyLTBhNGM0ZmRmZGY2MiIsImF1dGhtZXRob2QiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZFByb3RlY3RlZFRyYW5zcG9ydCIsImF1dGhfdGltZSI6IjIwMjQtMDktMTNUMDA6MjA6NDUuODM5WiIsInZlciI6IjEuMCIsInNjcCI6Im9wZW5pZCJ9.VozwSbBLZrsMZ9Oi6obzwqMLnz8_kawQmi7CWU0nyJTo8T1JqxkPFnCu2jSt-kJMnA9RtcNR2-O0cSjUOpgG6gP-s9i-loh_ubsA8f5-kqOJOfNFVhOVrUmD3s-sKSiqjTljCyZICzHtGiP2w3fpEQ4sz_wMe2gXEtvNttBXrdDv3BJbbS30c-Ix4QoAl8eSDWwV1if-gOoUQH3u-YZ_kpXjvvjHLYF5qS9bLgVKW_Ow6CkHBO9W_TBL7-P157VFNRqLyFSuLCEmljs_nwjd8wyTeaFt7xrrARkcf1N0_j0YJz7_Y-RzkjiFCs97cMzdAJrsWm6Om3PxPRyAB4a9fA",
"IncludeWebProxy": false,
"WebProxyHost": null,
"IncludeWebProxyNetworkCredentials": false,
Expand Down
Loading

0 comments on commit 3033654

Please sign in to comment.