Skip to content

Commit

Permalink
Support passing a verified One Login user to a TRN request (#1669)
Browse files Browse the repository at this point in the history
  • Loading branch information
gunndabad authored Nov 13, 2024
1 parent ad0d6b4 commit 56cea6f
Show file tree
Hide file tree
Showing 31 changed files with 1,283 additions and 259 deletions.
4 changes: 3 additions & 1 deletion TeachingRecordSystem/src/TeachingRecordSystem.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using TeachingRecordSystem.Core.Dqt;
using TeachingRecordSystem.Core.Infrastructure;
using TeachingRecordSystem.Core.Services.Certificates;
using TeachingRecordSystem.Core.Services.DqtOutbox;
using TeachingRecordSystem.Core.Services.GetAnIdentityApi;
using TeachingRecordSystem.Core.Services.NameSynonyms;
using TeachingRecordSystem.Core.Services.TrnGenerationApi;
Expand Down Expand Up @@ -210,7 +211,8 @@ public static void Main(string[] args)
.AddBlobStorage()
.AddDistributedLocks()
.AddIdentityApi()
.AddNameSynonyms();
.AddNameSynonyms()
.AddDqtOutboxMessageSerializer();

services.AddAccessYourTeachingQualificationsOptions(configuration, env);
services.AddCertificateGeneration();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public async Task<TrnRequestInfo> Handle(GetOrCreateTrnRequest request, Cancella

if (trnRequest is not null)
{
var teacher = await _dataverseAdapter.GetTeacher(trnRequest.ContactId, columnNames: [Contact.Fields.dfeta_TRN, Contact.Fields.dfeta_QTSDate]);
var teacher = trnRequest.Contact;

wasCreated = false;
trn = teacher?.dfeta_TRN;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using TeachingRecordSystem.Api.V2.Responses;
using TeachingRecordSystem.Core.DataStore.Postgres;
using TeachingRecordSystem.Core.Dqt;
using TeachingRecordSystem.Core.Dqt.Models;

namespace TeachingRecordSystem.Api.V2.Handlers;

Expand Down Expand Up @@ -41,21 +40,10 @@ public async Task<TrnRequestInfo> Handle(GetTrnRequest request, CancellationToke
return null;
}

var teacher = await _dataverseAdapter.GetTeacher(
trnRequest.ContactId,
columnNames:
[
Contact.Fields.dfeta_TRN,
Contact.Fields.dfeta_QTSDate
]);
var contact = trnRequest.Contact;

if (teacher is null)
{
throw new Exception($"Failed retrieving contact '{trnRequest.ContactId}' for request ID '{request.RequestId}'.");
}

var trn = teacher.dfeta_TRN;
var qtsDate = teacher.dfeta_QTSDate.ToDateOnlyWithDqtBstFix(isLocalTime: true);
var trn = contact.dfeta_TRN;
var qtsDate = contact.dfeta_QTSDate.ToDateOnlyWithDqtBstFix(isLocalTime: true);
var status = trn != null ? TrnRequestStatus.Completed : TrnRequestStatus.Pending;

return new TrnRequestInfo()
Expand All @@ -65,8 +53,10 @@ public async Task<TrnRequestInfo> Handle(GetTrnRequest request, CancellationToke
Trn = trn,
QtsDate = qtsDate,
PotentialDuplicate = status == TrnRequestStatus.Pending,
SlugId = teacher.dfeta_SlugId,
AccessYourTeachingQualificationsLink = trnRequest.TrnToken is not null ? Option.Some($"{_accessYourTeachingQualificationsOptions.BaseAddress}{_accessYourTeachingQualificationsOptions.StartUrlPath}?trn_token={trnRequest.TrnToken}") : default
SlugId = contact.dfeta_SlugId,
AccessYourTeachingQualificationsLink = trnRequest.TrnToken is not null ?
Option.Some($"{_accessYourTeachingQualificationsOptions.BaseAddress}{_accessYourTeachingQualificationsOptions.StartUrlPath}?trn_token={trnRequest.TrnToken}") :
default
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using TeachingRecordSystem.Core.Dqt;
using TeachingRecordSystem.Core.Dqt.Models;
using TeachingRecordSystem.Core.Dqt.Queries;
using TeachingRecordSystem.Core.Services.DqtOutbox;
using TeachingRecordSystem.Core.Services.DqtOutbox.Messages;
using TeachingRecordSystem.Core.Services.NameSynonyms;
using TeachingRecordSystem.Core.Services.TrnGenerationApi;

Expand All @@ -19,6 +21,7 @@ public record CreateTrnRequestCommand
public required DateOnly DateOfBirth { get; init; }
public required IReadOnlyCollection<string> EmailAddresses { get; init; }
public required string? NationalInsuranceNumber { get; init; }
public required string? VerifiedOneLoginUserSubject { get; init; }
}

public class CreateTrnRequestHandler(
Expand All @@ -27,7 +30,8 @@ public class CreateTrnRequestHandler(
TrnRequestHelper trnRequestHelper,
ICurrentUserProvider currentUserProvider,
ITrnGenerationApiClient trnGenerationApiClient,
INameSynonymProvider nameSynonymProvider)
INameSynonymProvider nameSynonymProvider,
MessageSerializer messageSerializer)
{
public async Task<TrnRequestInfo> Handle(CreateTrnRequestCommand command)
{
Expand Down Expand Up @@ -110,6 +114,17 @@ await dbContext.TpsEmployments

var emailAddress = command.EmailAddresses?.FirstOrDefault();

var outboxMessages = new List<dfeta_TrsOutboxMessage>();
if (command.VerifiedOneLoginUserSubject is string oneLoginUserId)
{
outboxMessages.Add(messageSerializer.CreateCrmOutboxMessage(new TrnRequestMetadataMessage()
{
ApplicationUserId = currentApplicationUserId,
RequestId = command.RequestId,
VerifiedOneLoginUserSubject = oneLoginUserId
}));
}

await crmQueryDispatcher.ExecuteQuery(new CreateContactQuery()
{
FirstName = firstName,
Expand All @@ -124,6 +139,7 @@ await crmQueryDispatcher.ExecuteQuery(new CreateContactQuery()
PotentialDuplicates = potentialDuplicates.Select(d => (Duplicate: d, HasActiveAlert: resultsWithActiveAlerts.Contains(d.ContactId))).ToArray(),
Trn = trn,
TrnRequestId = TrnRequestHelper.GetCrmTrnRequestId(currentApplicationUserId, command.RequestId),
OutboxMessages = outboxMessages
});

var status = trn is not null ? TrnRequestStatus.Completed : TrnRequestStatus.Pending;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
using Microsoft.Xrm.Sdk.Query;
using TeachingRecordSystem.Api.Infrastructure.Security;
using TeachingRecordSystem.Api.V3.Core.SharedModels;
using TeachingRecordSystem.Core.Dqt;
using TeachingRecordSystem.Core.Dqt.Models;
using TeachingRecordSystem.Core.Dqt.Queries;

namespace TeachingRecordSystem.Api.V3.Core.Operations;

public record GetTrnRequestCommand(string RequestId);

public class GetTrnRequestHandler(
ICrmQueryDispatcher crmQueryDispatcher,
TrnRequestHelper trnRequestHelper,
ICurrentUserProvider currentUserProvider)
public class GetTrnRequestHandler(TrnRequestHelper trnRequestHelper, ICurrentUserProvider currentUserProvider)
{
public async Task<TrnRequestInfo?> Handle(GetTrnRequestCommand command)
{
Expand All @@ -24,30 +18,18 @@ public class GetTrnRequestHandler(
return null;
}

var contact = (await crmQueryDispatcher.ExecuteQuery(
new GetContactWithMergeResolutionQuery(
trnRequest.ContactId,
new ColumnSet(
Contact.Fields.dfeta_TRN,
Contact.Fields.FirstName,
Contact.Fields.MiddleName,
Contact.Fields.LastName,
Contact.Fields.dfeta_StatedFirstName,
Contact.Fields.dfeta_StatedMiddleName,
Contact.Fields.dfeta_StatedLastName,
Contact.Fields.EMailAddress1,
Contact.Fields.dfeta_NINumber,
Contact.Fields.BirthDate,
Contact.Fields.Merged,
Contact.Fields.MasterId))))!;

var status = !string.IsNullOrEmpty(contact.dfeta_TRN) ? TrnRequestStatus.Completed : TrnRequestStatus.Pending;
var contact = trnRequest.Contact;

// If we have metadata for the One Login user, ensure they're added to the OneLoginUsers table.
// FUTURE: when TRN requests are handled exclusively in TRS this should be done at the point the task is resolved instead of here.
if (status == TrnRequestStatus.Completed)
if (trnRequest.Completed)
{
await trnRequestHelper.EnsureOneLoginUserIsConnected(trnRequest, contact);
var metadata = await trnRequestHelper.GetRequestMetadata(trnRequest.ApplicationUserId, command.RequestId);

if (metadata?.VerifiedOneLoginUserSubject is string oneLoginUserId)
{
await trnRequestHelper.EnsureOneLoginUserIsConnected(trnRequest, oneLoginUserId);
}
}

return new TrnRequestInfo()
Expand All @@ -63,7 +45,7 @@ public class GetTrnRequestHandler(
DateOfBirth = contact.BirthDate!.Value.ToDateOnlyWithDqtBstFix(isLocalTime: false)
},
Trn = contact.dfeta_TRN,
Status = status,
Status = trnRequest.Completed ? TrnRequestStatus.Completed : TrnRequestStatus.Pending,
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public async Task<IActionResult> CreateTrnRequest(
LastName = request.Person.LastName,
DateOfBirth = request.Person.DateOfBirth,
EmailAddresses = request.Person.Email is string emailAddress ? [emailAddress] : [],
NationalInsuranceNumber = request.Person.NationalInsuranceNumber
NationalInsuranceNumber = request.Person.NationalInsuranceNumber,
VerifiedOneLoginUserSubject = null
};
var result = await handler.Handle(command);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ public async Task<IActionResult> CreateTrnRequest(
LastName = request.Person.LastName,
DateOfBirth = request.Person.DateOfBirth,
EmailAddresses = request.Person.EmailAddresses ?? [],
NationalInsuranceNumber = request.Person.NationalInsuranceNumber
NationalInsuranceNumber = request.Person.NationalInsuranceNumber,
VerifiedOneLoginUserSubject = null
};
var result = await handler.Handle(command);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using TeachingRecordSystem.Api.Infrastructure.Security;
using TeachingRecordSystem.Api.V3.Core.Operations;
using TeachingRecordSystem.Api.V3.V20240606.ApiModels;
using TeachingRecordSystem.Api.V3.VNext.Requests;

namespace TeachingRecordSystem.Api.V3.VNext.Controllers;

[Route("trn-requests")]
[Authorize(Policy = AuthorizationPolicies.ApiKey, Roles = ApiRoles.CreateTrn)]
public class TrnRequestsController(IMapper mapper) : ControllerBase
{
[HttpPost("")]
[SwaggerOperation(
OperationId = "CreateTrnRequest",
Summary = "Creates a TRN request",
Description = """
Creates a new TRN request using the personally identifiable information in the request body.
If the request can be fulfilled immediately the response's status property will be 'Completed' and a TRN will also be returned.
Otherwise, the response's status property will be 'Pending' and the GET endpoint should be polled until a 'Completed' status is returned.
""")]
[ProducesResponseType(typeof(TrnRequestInfo), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)]
[MapError(10029, statusCode: StatusCodes.Status409Conflict)]
public async Task<IActionResult> CreateTrnRequest(
[FromBody] CreateTrnRequestRequest request,
[FromServices] CreateTrnRequestHandler handler)
{
var command = new CreateTrnRequestCommand()
{
RequestId = request.RequestId,
FirstName = request.Person.FirstName,
MiddleName = request.Person.MiddleName,
LastName = request.Person.LastName,
DateOfBirth = request.Person.DateOfBirth,
EmailAddresses = request.Person.EmailAddresses ?? [],
NationalInsuranceNumber = request.Person.NationalInsuranceNumber,
VerifiedOneLoginUserSubject = request.VerifiedOneLoginUserSubject
};
var result = await handler.Handle(command);

var response = mapper.Map<TrnRequestInfo>(result);
return Ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TeachingRecordSystem.Api.V3.VNext.Requests;

[GenerateVersionedDto(typeof(V20240606.Requests.CreateTrnRequestRequest))]
public partial record CreateTrnRequestRequest
{
public string? VerifiedOneLoginUserSubject { get; init; }
}
Loading

0 comments on commit 56cea6f

Please sign in to comment.