Skip to content

Commit

Permalink
Amend API responses for TRS alerts data model (#1506)
Browse files Browse the repository at this point in the history
  • Loading branch information
gunndabad authored Sep 12, 2024
1 parent 0b7133a commit a4df696
Show file tree
Hide file tree
Showing 44 changed files with 639 additions and 130 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# API Changelog

## Unreleased

All references to `sanctions` have been removed and replaced with `alerts`; the following endpoints are affected:
- `GET /v3/persons/<trn>`
- `GET /v3/person`
- `GET /v3/persons`
- `GET /v3/persons/find`


## 20240814

### `POST /v3/persons/find`
Expand All @@ -10,6 +19,7 @@ New endpoint added for bulk person lookup by TRN and date of birth.

`inductionStatus`, `qts` and `eyts` members have been added to align with the bulk `POST` endpoint.


## 20240606

All endpoints under `/teacher` and `/teachers` have been moved to `/person` and `/persons`, respectively.
Expand All @@ -27,6 +37,7 @@ The `person` property has been removed from the response.

The `person` property has been removed from the response.


## 20240416

### `GET /v3/teachers/<trn>`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ void AddUsing(UsingDirectiveSyntax usingSyntax)

foreach (var (property, propertyType) in referenceGeneratedTypeInfo.Properties)
{
if (excludeMembers.Contains(property.Identifier.ValueText))
{
continue;
}

AddProperty(property, propertyType);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
global using AutoMapper;
global using PostgresModels = TeachingRecordSystem.Core.DataStore.Postgres.Models;
8 changes: 6 additions & 2 deletions TeachingRecordSystem/src/TeachingRecordSystem.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,15 +166,19 @@ public static void Main(string[] args)
{
cfg.AddMaps(typeof(Program).Assembly);
cfg.CreateMap(typeof(Option<>), typeof(Option<>)).ConvertUsing(typeof(OptionMapper<,>));
})
.AddTransient(typeof(OptionMapper<,>));
});

services.Scan(scan =>
{
scan.FromAssemblyOf<Program>()
.AddClasses(filter => filter.InNamespaces("TeachingRecordSystem.Api.V3.Core.Operations").Where(type => type.Name.EndsWith("Handler")))
.AsSelf()
.WithTransientLifetime();

scan.FromAssemblyOf<Program>()
.AddClasses(filter => filter.AssignableTo(typeof(ITypeConverter<,>)))
.AsSelf()
.WithTransientLifetime();
});

services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblyContaining<Program>());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace TeachingRecordSystem.Api.V3;

public static class Constants
{
public static IReadOnlyCollection<string> ExposableSanctionCodes { get; } = new[]
public static IReadOnlyCollection<string> LegacyExposableSanctionCodes { get; } = new[]
{
"G1",
"A18",
Expand Down Expand Up @@ -37,7 +37,7 @@ public static class Constants
"A23",
};

public static IReadOnlyCollection<string> ProhibitionSanctionCodes { get; } = new[]
public static IReadOnlyCollection<string> LegacyProhibitionSanctionCodes { get; } = new[]
{
"G1",
"B1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public record FindPersonByLastNameAndDateOfBirthResultItem
public required string MiddleName { get; init; }
public required string LastName { get; init; }
public required IReadOnlyCollection<SanctionInfo> Sanctions { get; init; }
public required IReadOnlyCollection<Alert> Alerts { get; init; }
public required IReadOnlyCollection<NameInfo> PreviousNames { get; init; }
public required InductionStatusInfo? InductionStatus { get; init; }
public required QtsInfo? Qts { get; init; }
Expand Down Expand Up @@ -53,7 +54,7 @@ public async Task<FindPersonByLastNameAndDateOfBirthResult> Handle(FindPersonByL
new GetSanctionsByContactIdsQuery(
contactsById.Keys,
ActiveOnly: true,
new()));
new(dfeta_sanction.Fields.dfeta_StartDate, dfeta_sanction.Fields.dfeta_EndDate)));

var getPreviousNamesTask = crmQueryDispatcher.ExecuteQuery(new GetPreviousNamesByContactIdsQuery(contactsById.Keys));

Expand Down Expand Up @@ -86,13 +87,39 @@ public async Task<FindPersonByLastNameAndDateOfBirthResult> Handle(FindPersonByL
MiddleName = r.ResolveMiddleName(),
LastName = r.ResolveLastName(),
Sanctions = sanctions[r.Id]
.Where(s => Constants.ExposableSanctionCodes.Contains(s.SanctionCode))
.Where(s => Constants.LegacyExposableSanctionCodes.Contains(s.SanctionCode))
.Select(s => new SanctionInfo()
{
Code = s.SanctionCode,
StartDate = s.Sanction.dfeta_StartDate?.ToDateOnlyWithDqtBstFix(isLocalTime: true)
})
.AsReadOnly(),
Alerts = await sanctions[r.Id]
.ToAsyncEnumerable()
.SelectAwait(async s =>
{
var alertType = await referenceDataCache.GetAlertTypeByDqtSanctionCode(s.SanctionCode);
var alertCategory = await referenceDataCache.GetAlertCategoryById(alertType.AlertCategoryId);

return new Alert()
{
AlertId = s.Sanction.Id,
AlertType = new()
{
AlertTypeId = alertType.AlertTypeId,
AlertCategory = new()
{
AlertCategoryId = alertCategory.AlertCategoryId,
Name = alertCategory.Name
},
Name = alertType.Name,
DqtSanctionCode = alertType.DqtSanctionCode!
},
StartDate = s.Sanction.dfeta_StartDate?.ToDateOnlyWithDqtBstFix(isLocalTime: true),
EndDate = s.Sanction.dfeta_EndDate?.ToDateOnlyWithDqtBstFix(isLocalTime: true)
};
})
.AsReadOnlyAsync(),
PreviousNames = previousNameHelper.GetFullPreviousNames(previousNames[r.Id], contactsById[r.Id])
.Select(name => new NameInfo()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public record FindPersonsByTrnAndDateOfBirthResultItem
public required string MiddleName { get; init; }
public required string LastName { get; init; }
public required IReadOnlyCollection<SanctionInfo> Sanctions { get; init; }
public required IReadOnlyCollection<Alert> Alerts { get; init; }
public required IReadOnlyCollection<NameInfo> PreviousNames { get; init; }
public required InductionStatusInfo? InductionStatus { get; init; }
public required QtsInfo? Qts { get; init; }
Expand Down Expand Up @@ -60,7 +61,7 @@ public async Task<FindPersonsByTrnAndDateOfBirthResult> Handle(FindPersonsByTrnA
new GetSanctionsByContactIdsQuery(
contactsById.Keys,
ActiveOnly: true,
new()));
new(dfeta_sanction.Fields.dfeta_StartDate, dfeta_sanction.Fields.dfeta_EndDate)));

var getPreviousNamesTask = crmQueryDispatcher.ExecuteQuery(new GetPreviousNamesByContactIdsQuery(contactsById.Keys));

Expand Down Expand Up @@ -93,13 +94,39 @@ public async Task<FindPersonsByTrnAndDateOfBirthResult> Handle(FindPersonsByTrnA
MiddleName = r.ResolveMiddleName(),
LastName = r.ResolveLastName(),
Sanctions = sanctions[r.Id]
.Where(s => Constants.ExposableSanctionCodes.Contains(s.SanctionCode))
.Where(s => Constants.LegacyExposableSanctionCodes.Contains(s.SanctionCode))
.Select(s => new SanctionInfo()
{
Code = s.SanctionCode,
StartDate = s.Sanction.dfeta_StartDate?.ToDateOnlyWithDqtBstFix(isLocalTime: true)
})
.AsReadOnly(),
Alerts = await sanctions[r.Id]
.ToAsyncEnumerable()
.SelectAwait(async s =>
{
var alertType = await referenceDataCache.GetAlertTypeByDqtSanctionCode(s.SanctionCode);
var alertCategory = await referenceDataCache.GetAlertCategoryById(alertType.AlertCategoryId);

return new Alert()
{
AlertId = s.Sanction.Id,
AlertType = new()
{
AlertTypeId = alertType.AlertTypeId,
AlertCategory = new()
{
AlertCategoryId = alertCategory.AlertCategoryId,
Name = alertCategory.Name
},
Name = alertType.Name,
DqtSanctionCode = alertType.DqtSanctionCode!
},
StartDate = s.Sanction.dfeta_StartDate?.ToDateOnlyWithDqtBstFix(isLocalTime: true),
EndDate = s.Sanction.dfeta_EndDate?.ToDateOnlyWithDqtBstFix(isLocalTime: true)
};
})
.AsReadOnlyAsync(),
PreviousNames = previousNameHelper.GetFullPreviousNames(previousNames[r.Id], contactsById[r.Id])
.Select(name => new NameInfo()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
using Optional;
using TeachingRecordSystem.Api.V3.Core.SharedModels;
using TeachingRecordSystem.Core.DataStore.Postgres;
using TeachingRecordSystem.Core.DataStore.Postgres.Models;
using TeachingRecordSystem.Core.Dqt;
using TeachingRecordSystem.Core.Dqt.Models;
using TeachingRecordSystem.Core.Dqt.Queries;

namespace TeachingRecordSystem.Api.V3.Core.Operations;

public record GetPersonCommand(string Trn, GetPersonCommandIncludes Include, DateOnly? DateOfBirth);
public record GetPersonCommand(string Trn, GetPersonCommandIncludes Include, DateOnly? DateOfBirth, bool ApplyLegacyAlertsBehavior);

[Flags]
public enum GetPersonCommandIncludes
Expand Down Expand Up @@ -47,7 +46,7 @@ public record GetPersonResult
public required Option<IReadOnlyCollection<GetPersonResultMandatoryQualification>> MandatoryQualifications { get; init; }
public required Option<IReadOnlyCollection<GetPersonResultHigherEducationQualification>> HigherEducationQualifications { get; init; }
public required Option<IReadOnlyCollection<SanctionInfo>> Sanctions { get; init; }
public required Option<IReadOnlyCollection<AlertInfo>> Alerts { get; init; }
public required Option<IReadOnlyCollection<Alert>> Alerts { get; init; }
public required Option<IReadOnlyCollection<NameInfo>> PreviousNames { get; init; }
public required Option<bool> AllowIdSignInWithProhibitions { get; init; }
}
Expand Down Expand Up @@ -392,7 +391,7 @@ async Task<SanctionResult[]> GetSanctions()
default,
Sanctions = command.Include.HasFlag(GetPersonCommandIncludes.Sanctions) ?
Option.Some((await getSanctionsTask!)
.Where(s => Constants.ExposableSanctionCodes.Contains(s.SanctionCode))
.Where(s => Constants.LegacyExposableSanctionCodes.Contains(s.SanctionCode))
.Where(s => s.Sanction.dfeta_EndDate is null && s.Sanction.dfeta_Spent != true)
.Select(s => new SanctionInfo()
{
Expand All @@ -402,16 +401,34 @@ async Task<SanctionResult[]> GetSanctions()
.AsReadOnly()) :
default,
Alerts = command.Include.HasFlag(GetPersonCommandIncludes.Alerts) ?
Option.Some((await getSanctionsTask!)
.Where(s => Constants.ProhibitionSanctionCodes.Contains(s.SanctionCode))
.Select(s => new AlertInfo()
Option.Some(await (await getSanctionsTask!)
.ToAsyncEnumerable()
// The Legacy behavior is to only return prohibition-type alerts
.Where(s => !command.ApplyLegacyAlertsBehavior || Constants.LegacyProhibitionSanctionCodes.Contains(s.SanctionCode))
.SelectAwait(async s =>
{
AlertType = SharedModels.AlertType.Prohibition,
DqtSanctionCode = s.SanctionCode,
StartDate = s.Sanction.dfeta_StartDate?.ToDateOnlyWithDqtBstFix(isLocalTime: true),
EndDate = s.Sanction.dfeta_EndDate?.ToDateOnlyWithDqtBstFix(isLocalTime: true)
var alertType = await referenceDataCache.GetAlertTypeByDqtSanctionCode(s.SanctionCode);
var alertCategory = await referenceDataCache.GetAlertCategoryById(alertType.AlertCategoryId);

return new Alert()
{
AlertId = s.Sanction.Id,
AlertType = new()
{
AlertTypeId = alertType.AlertTypeId,
AlertCategory = new()
{
AlertCategoryId = alertCategory.AlertCategoryId,
Name = alertCategory.Name
},
Name = alertType.Name,
DqtSanctionCode = alertType.DqtSanctionCode!
},
StartDate = s.Sanction.dfeta_StartDate?.ToDateOnlyWithDqtBstFix(isLocalTime: true),
EndDate = s.Sanction.dfeta_EndDate?.ToDateOnlyWithDqtBstFix(isLocalTime: true)
};
})
.AsReadOnly()) :
.AsReadOnlyAsync()) :
default,
PreviousNames = command.Include.HasFlag(GetPersonCommandIncludes.PreviousNames) ?
Option.Some(previousNames.Select(n => n).AsReadOnly()) :
Expand Down Expand Up @@ -576,7 +593,7 @@ private static NpqQualificationType MapNpqQualificationType(dfeta_qualification_
_ => throw new ArgumentException($"Unrecognized qualification type: '{qualificationType}'.", nameof(qualificationType))
};

private static IReadOnlyCollection<GetPersonResultMandatoryQualification> MapMandatoryQualifications(MandatoryQualification[] qualifications) =>
private static IReadOnlyCollection<GetPersonResultMandatoryQualification> MapMandatoryQualifications(PostgresModels.MandatoryQualification[] qualifications) =>
qualifications
.Where(q => q.EndDate.HasValue && q.Specialism.HasValue)
.Select(mq => new GetPersonResultMandatoryQualification()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace TeachingRecordSystem.Api.V3.Core.SharedModels;

public record AlertInfo
public record Alert
{
public required Guid AlertId { get; init; }
public required AlertType AlertType { get; init; }
public required string DqtSanctionCode { get; init; }
public required DateOnly? StartDate { get; init; }
public required DateOnly? EndDate { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace TeachingRecordSystem.Api.V3.Core.SharedModels;

public record AlertCategory
{
public required Guid AlertCategoryId { get; init; }
public required string Name { get; init; }
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
namespace TeachingRecordSystem.Api.V3.Core.SharedModels;

public enum AlertType
public record AlertType
{
Prohibition,
// Only exposing Prohibitions for now
public required Guid AlertTypeId { get; init; }
public required AlertCategory AlertCategory { get; init; }
public required string Name { get; init; }
public required string DqtSanctionCode { get; init; }
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
using TeachingRecordSystem.Api.V3.Core.SharedModels;

namespace TeachingRecordSystem.Api.V3.V20240101.ApiModels;

[AutoMap(typeof(Core.SharedModels.AlertInfo))]
[AutoMap(typeof(Alert), TypeConverter = typeof(AlertInfoTypeConverter))]
public record AlertInfo
{
public required AlertType AlertType { get; init; }
public required string DqtSanctionCode { get; init; }
public required DateOnly? StartDate { get; init; }
public required DateOnly? EndDate { get; init; }
}

public class AlertInfoTypeConverter : ITypeConverter<Alert, AlertInfo>
{
public AlertInfo Convert(Alert source, AlertInfo destination, ResolutionContext context) =>
new()
{
AlertType = AlertType.Prohibition,
DqtSanctionCode = source.AlertType.DqtSanctionCode,
StartDate = source.StartDate,
EndDate = source.EndDate,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public async Task<IActionResult> Get(
var command = new GetPersonCommand(
trn,
include is not null ? (GetPersonCommandIncludes)include : GetPersonCommandIncludes.None,
DateOfBirth: null);
DateOfBirth: null,
ApplyLegacyAlertsBehavior: true);

var result = await handler.Handle(command);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public async Task<IActionResult> Get(
var command = new GetPersonCommand(
trn,
include is not null ? (GetPersonCommandIncludes)include : GetPersonCommandIncludes.None,
DateOfBirth: null);
DateOfBirth: null,
ApplyLegacyAlertsBehavior: true);

var result = await handler.Handle(command);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public async Task<IActionResult> Get(
var command = new GetPersonCommand(
trn,
include is not null ? (GetPersonCommandIncludes)include : GetPersonCommandIncludes.None,
DateOfBirth: null);
DateOfBirth: null,
ApplyLegacyAlertsBehavior: true);

var result = await handler.Handle(command);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ public async Task<IActionResult> Get(
var command = new GetPersonCommand(
trn,
include is not null ? (GetPersonCommandIncludes)include : GetPersonCommandIncludes.None,
dateOfBirth);
dateOfBirth,
ApplyLegacyAlertsBehavior: true);

var result = await handler.Handle(command);

Expand Down
Loading

0 comments on commit a4df696

Please sign in to comment.