Skip to content

Commit

Permalink
Merge pull request #188 from ministryofjustice/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
carlsixsmith-moj authored Aug 28, 2024
2 parents 6164760 + 45c4c5b commit 032cb77
Show file tree
Hide file tree
Showing 152 changed files with 28,058 additions and 589 deletions.
2 changes: 1 addition & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "8.0.7",
"version": "8.0.8",
"commands": [
"dotnet-ef"
],
Expand Down
2 changes: 1 addition & 1 deletion src/Application/Application.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="8.0.8" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="8.0.8" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.4" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Domain\Domain.csproj" />
Expand Down
6 changes: 5 additions & 1 deletion src/Application/Common/Interfaces/IApplicationDbContext.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Cfo.Cats.Domain.Entities.Administration;
using Cfo.Cats.Domain.Entities.Administration;
using Cfo.Cats.Domain.Entities.Assessments;
using Cfo.Cats.Domain.Entities.Documents;
using Cfo.Cats.Domain.Entities.Participants;
Expand All @@ -25,6 +25,8 @@ public interface IApplicationDbContext
DbSet<Document> Documents { get; }

DbSet<Participant> Participants { get; }

DbSet<PathwayPlan> PathwayPlans { get; }

DbSet<Risk> Risks { get; }

Expand All @@ -51,5 +53,7 @@ public interface IApplicationDbContext

DbSet<PasswordHistory> PasswordHistories { get; }

DbSet<IdentityAuditTrail> IdentityAuditTrails { get; }

}

1 change: 1 addition & 0 deletions src/Application/Common/Interfaces/Identity/IUserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ public interface IUserService
event Action? OnChange;
void Initialize();
void Refresh();
string? GetDisplayName(string userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public static class ValidationConstants
public const string LettersSpacesUnderscores = @"^[A-Za-z_ ]+$";
public const string LettersSpacesUnderscoresMessage = "{0} must contain only letters, spaces, and underscores.";

public const string LettersSpacesCommaApostorphe = @"^[A-Za-z ',’]+$";
public const string LettersSpacesCommaApostrophe = @"^[A-Za-z ',’]+$";
public const string LettersSpacesCommaApostropheMessage = "{0} must contain only letters, spaces, comma and an apostrophe.";

public const string AlphabetsDigitsSpaceSlashHyphenDot= @"^[a-zA-Z0-9\s\\\/\-.]+$";
Expand Down
20 changes: 20 additions & 0 deletions src/Application/Common/Versioning/Documents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Cfo.Cats.Infrastructure.Constants;

public static class Documents
{
public static class Consent
{
public const double MaximumSizeInMegabytes = 5;

public static IReadOnlyList<string> Versions { get; set; } =
[
"1.0"
];
}

public static class RightToWork
{
public const double MaximumSizeInMegabytes = 5;
}

}
24 changes: 11 additions & 13 deletions src/Application/Features/Assessments/Commands/SaveAssessment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,16 @@ public class Command : ICacheInvalidatorRequest<Result>

}

public class Handler : IRequestHandler<Command, Result>
public class Handler(IUnitOfWork unitOfWork, ICurrentUserService currentUserService) : IRequestHandler<Command, Result>
{
private readonly IUnitOfWork _unitOfWork;

public Handler(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;

}

public async Task<Result> Handle(Command request, CancellationToken cancellationToken)
{
ParticipantAssessment pa = _unitOfWork.DbContext.ParticipantAssessments.FirstOrDefault(r => r.Id == request.Assessment.Id && r.ParticipantId == request.Assessment.ParticipantId)
ParticipantAssessment pa = unitOfWork.DbContext.ParticipantAssessments.FirstOrDefault(r => r.Id == request.Assessment.Id && r.ParticipantId == request.Assessment.ParticipantId)
?? throw new NotFoundException(nameof(Assessment), new
{
request.Assessment.Id,
request.Assessment.ParticipantId
});


pa.UpdateJson(JsonConvert.SerializeObject(request.Assessment, new JsonSerializerSettings
{
Expand All @@ -50,7 +41,7 @@ public async Task<Result> Handle(Command request, CancellationToken cancellation

if (request.Submit)
{
var details = await _unitOfWork.DbContext.Participants
var details = await unitOfWork.DbContext.Participants
.Where(p => p.Id == request.Assessment.ParticipantId)
.Select(p =>
new
Expand All @@ -69,7 +60,8 @@ public async Task<Result> Handle(Command request, CancellationToken cancellation
{
pa.SetPathwayScore(pathway.Title, pathway.GetRagScore(age, location, sex));
}
pa.Submit();

pa.Submit(currentUserService.UserId!);
}

return Result.Success();
Expand All @@ -88,6 +80,10 @@ public Validator(IUnitOfWork unitOfWork)
.MustAsync(Exist)
.WithMessage("Assessment not found");

RuleFor(c => c.Assessment.Id)
.MustAsync(NotBeCompleted)
.WithMessage("Assessment already complete");

RuleFor(c => c.Assessment.ParticipantId)
.MustAsync(Exist)
.WithMessage("Participant not found")
Expand All @@ -104,5 +100,7 @@ private async Task<bool> Exist(string participantId, CancellationToken cancellat
private async Task<bool> HaveEnrolmentLocation(string participantId, CancellationToken cancellationToken)
=> await _unitOfWork.DbContext.Participants.AnyAsync(e => e.Id == participantId && e.EnrolmentLocation != null, cancellationToken);

private async Task<bool> NotBeCompleted(Guid assessmentId, CancellationToken cancellationToken)
=> await _unitOfWork.DbContext.ParticipantAssessments.AnyAsync(pa => pa.Id == assessmentId && pa.Completed == null, cancellationToken);
}
}
4 changes: 3 additions & 1 deletion src/Application/Features/Bios/Commands/BeginBio.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ public async Task<Result<Guid>> Handle(Command request, CancellationToken cancel
TypeNameHandling = TypeNameHandling.Auto
});

ParticipantBio bioSurvey = ParticipantBio.Create(bio.Id, request.ParticipantId, bioJson: json, BioStatus.NotStarted);
ParticipantBio bioSurvey = ParticipantBio.Create(bio.Id, request.ParticipantId, bioJson: json);

await _unitOfWork.DbContext.ParticipantBios.AddAsync(bioSurvey);

return Result<Guid>.Success(bio.Id);
}
}
Expand Down
33 changes: 17 additions & 16 deletions src/Application/Features/Bios/Commands/SaveBio.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,12 @@ public class Command : IRequest<Result>
public required Bio Bio { get; set; }
}

public class Handler : IRequestHandler<Command, Result>
public class Handler(IUnitOfWork unitOfWork, ICurrentUserService currentUserService) : IRequestHandler<Command, Result>
{
private readonly IUnitOfWork _unitOfWork;

public Handler(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;

}

public async Task<Result> Handle(Command request, CancellationToken cancellationToken)
{

ParticipantBio? bio = await _unitOfWork.DbContext.ParticipantBios
.FirstOrDefaultAsync(r => r.Id == request.Bio.Id && r.ParticipantId == request.Bio.ParticipantId, cancellationToken);
ParticipantBio? bio = await unitOfWork.DbContext.ParticipantBios
.FirstOrDefaultAsync(r => r.Id == request.Bio.Id, cancellationToken);

if(bio == null)
{
Expand All @@ -42,11 +33,13 @@ public async Task<Result> Handle(Command request, CancellationToken cancellation
{
TypeNameHandling = TypeNameHandling.Auto
}));

bio.UpdateStatus(BioStatus.InProgress);

if (request.Submit)
{
bio.UpdateStatus(BioStatus.Complete);
bio.Submit();
bio.Submit(currentUserService.UserId!);
}

return Result.Success();
Expand All @@ -55,8 +48,12 @@ public async Task<Result> Handle(Command request, CancellationToken cancellation

public class Validator : AbstractValidator<Command>
{
public Validator()
private readonly IUnitOfWork _unitOfWork;

public Validator(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;

RuleFor(x => x.Bio)
.NotNull();

Expand All @@ -67,9 +64,13 @@ public Validator()
.WithMessage(string.Format(ValidationConstants.AlphaNumericMessage, nameof(Command.Bio.ParticipantId)));

RuleFor(x => x.Bio.Id)
.NotEmpty();

.NotEmpty()
.MustAsync(NotBeCompleted)
.WithMessage("Bio already complete");
}

private async Task<bool> NotBeCompleted(Guid bioId, CancellationToken cancellationToken)
=> await _unitOfWork.DbContext.ParticipantBios.AnyAsync(b => b.Id == bioId && b.Completed == null, cancellationToken);
}

}
10 changes: 7 additions & 3 deletions src/Application/Features/Bios/Commands/SkipBioForNow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@ public async Task<Result> Handle(Command request, CancellationToken cancellation
{
ParticipantBio? bio = await unitOfWork.DbContext.ParticipantBios.FirstOrDefaultAsync(r => r.ParticipantId == request.ParticipantId);

if (bio == null)
if (bio is not null)
{
bio.UpdateStatus(BioStatus.SkippedForNow);
}
else
{
Bio newBio = new Bio()
{
Id = Guid.NewGuid(),
Expand All @@ -44,11 +48,11 @@ public async Task<Result> Handle(Command request, CancellationToken cancellation
TypeNameHandling = TypeNameHandling.Auto
});

bio = ParticipantBio.Create(newBio.Id, request.ParticipantId!, json, BioStatus.NotStarted);
bio = ParticipantBio.Skip(newBio.Id, request.ParticipantId!, json);

unitOfWork.DbContext.ParticipantBios.Add(bio);
}

bio.UpdateStatus(BioStatus.SkippedForNow);
return Result.Success();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public sealed partial class ChildhoodExperiencesPathway
{

[JsonIgnore]
public override string Title => "ChildhoodExperiences";
public override string Title => "Childhood Experiences";



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public sealed partial class RecentExperiencesPathway
{

[JsonIgnore]
public override string Title => "RecentExperiences";
public override string Title => "Recent Experiences";



Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public ApplicationUserDtoValidator(

RuleFor(x => x.MemorablePlace)
.MaximumLength(50).WithMessage(_localizer["Memorable place must be less than or equal to 50 characters"])
.Matches(ValidationConstants.LettersSpacesCommaApostorphe).WithMessage(_localizer[string.Format(ValidationConstants.LettersSpacesCommaApostropheMessage, "Memorable place")]);
.Matches(ValidationConstants.LettersSpacesCommaApostrophe).WithMessage(_localizer[string.Format(ValidationConstants.LettersSpacesCommaApostropheMessage, "Memorable place")]);

RuleFor(x => x.MemorableDate)
.NotEmpty().WithMessage(_localizer["Memorable date is required"])
Expand Down
32 changes: 32 additions & 0 deletions src/Application/Features/Identity/DTOs/IdentityAuditTrailDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Cfo.Cats.Application.Features.Identity.DTOs
{
public class IdentityAuditTrailDto
{

[Description("User Name")]
public string? UserName { get; set; }

[Description("Performed By")]
public string? PerformedBy { get; set; }

[Description("Date Time")]
public DateTime DateTime { get; set; }

[Description("Action Type")]
public IdentityActionType? ActionType { get;set; }

private class Mapping : Profile
{
public Mapping()
{
CreateMap<IdentityAuditTrail, IdentityAuditTrailDto>();
}
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
namespace Cfo.Cats.Application.Features.Identity.Notifications.IdentityEvents;

public class IdentityAuditNotification : INotification
{

private IdentityAuditNotification(IdentityActionType actionType, string? userName = null, string? performedBy = null)
{
ActionType = actionType;
UserName = userName;
PerformedBy = performedBy ?? userName;
}

public IdentityActionType ActionType { get; private set; }

public string? UserName { get; private set; }

public string? PerformedBy { get; private set; }

public static IdentityAuditNotification UnknownUserNameNotification(string userName)
=> new (IdentityActionType.UnknownUser, userName);

public static IdentityAuditNotification LoginFailedPassword(string userName)
=> new (IdentityActionType.IncorrectPasswordEntered, userName, null);
public static IdentityAuditNotification LoginFailedTwoFactor(string userName)
=> new (IdentityActionType.IncorrectTwoFactorCodeEntered, userName, null);

public static IdentityAuditNotification LoginSucceededNoTwoFactorRequired(string userName)
=> new(IdentityActionType.LoginPasswordOnly, userName);

public static IdentityAuditNotification LoginSucceededTwoFactorRequired(string userName)
=> new(IdentityActionType.LoginWithTwoFactorCode, userName);

public static IdentityAuditNotification UserLockedOut(string userName)
=> new(IdentityActionType.UserAccountLockedOut, userName);

public static IdentityAuditNotification PasswordReset(string userName, string? performedBy = null)
=> new(IdentityActionType.PasswordReset, userName, performedBy);

public static IdentityAuditNotification ActivateAccount(string userName, string performedBy)
=> new(IdentityActionType.AccountActivated, userName, performedBy);

public static IdentityAuditNotification DeactivateAccount(string userName, string performedBy)
=> new(IdentityActionType.AccountDeactivated, userName, performedBy);

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Cfo.Cats.Application.Features.Identity.Notifications.IdentityEvents;

public class IdentityAuditNotificationHandler(IUnitOfWork unitOfWork) : INotificationHandler<IdentityAuditNotification>
{
public async Task Handle(IdentityAuditNotification notification, CancellationToken cancellationToken)
{
IdentityAuditTrail audit = IdentityAuditTrail.Create(notification.UserName, notification.PerformedBy, notification.ActionType);
unitOfWork.DbContext.IdentityAuditTrails.Add(audit);
await unitOfWork.SaveChangesAsync(cancellationToken);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
namespace Cfo.Cats.Application.Features.Identity.Notifications.SendTwoFactorCode;

public class SendTwoFactorTextCodeNotificationHandler(ICommunicationsService communicationsService, ILogger<SendTwoFactorTextCodeNotificationHandler> logger)
public class SendTwoFactorTextCodeNotificationHandler(ICommunicationsService communicationsService)
: INotificationHandler<SendTwoFactorTextCodeNotification>
{
public async Task Handle(SendTwoFactorTextCodeNotification notification, CancellationToken cancellationToken)
{
await communicationsService.SendSmsCodeAsync(notification.MobileNumber, notification.AuthenticatorCode);
logger.LogDebug("Verification Code email sent to {UserName})", notification.UserName);
}
}
Loading

0 comments on commit 032cb77

Please sign in to comment.