Skip to content

Commit

Permalink
Merge pull request #438 from bcgov/tasks/ecer-2758
Browse files Browse the repository at this point in the history
ECER-2758: Renewal Validation Infra
  • Loading branch information
farzadnadiri authored Aug 19, 2024
2 parents 21d150b + a8d075a commit 9c877a6
Show file tree
Hide file tree
Showing 24 changed files with 815 additions and 475 deletions.
1 change: 1 addition & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<PackageVersion Include="Microsoft.PowerPlatform.Dataverse.Client" Version="1.1.22" />
<PackageVersion Include="Microsoft.PowerPlatform.Dataverse.Client.Dynamics" Version="1.1.27" />
<PackageVersion Include="MinimalApis.Extensions" Version="0.11.0" />
<PackageVersion Include="Moq" Version="4.20.70" />
<PackageVersion Include="nClam" Version="9.0.0" />
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.2" />
<PackageVersion Include="Shouldly" Version="4.2.1" />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using ECER.Managers.Registry.Contract.Applications;
using ECER.Resources.Documents.Certifications;
using System.Runtime.CompilerServices;

namespace ECER.Engines.Validation.Applications;

internal sealed partial class ApplicationRenewalValidationEngine : IApplicationValidationEngine
{
private ICertificationRepository _certificateRepository;

public ApplicationRenewalValidationEngine(ICertificationRepository certificationRepository)
{
_certificateRepository = certificationRepository;
}

public async Task<ValidationResults> Validate(Application application)
{
await Task.CompletedTask;
var validationErrors = new List<string>();

if (application.CertificationTypes.Contains(CertificationType.EceAssistant))
{
validationErrors = await EceAssistant(application);
}
else if (application.CertificationTypes.Contains(CertificationType.OneYear))
{
validationErrors = await OneYear(application);
}
else if (application.CertificationTypes.Contains(CertificationType.FiveYears))
{
validationErrors = await FiveYears(application);
}
return new ValidationResults(validationErrors);
}

private enum CertificateStatus
{
Active,
ExpiredLessThanFiveYearsAgo,
ExpiredMoreThanFiveYearsAgo,
NoCertificateFound
}

private async Task<CertificateStatus> GetCertificateStatus(string applicantId)
{
try
{
var expiryDate = await getLastCertificateExpiryDate(applicantId);
var now = DateTime.Now;

if (expiryDate > now)
{
return CertificateStatus.Active;
}
else if (expiryDate <= now && expiryDate > now.AddYears(-5))
{
return CertificateStatus.ExpiredLessThanFiveYearsAgo;
}
else
{
return CertificateStatus.ExpiredMoreThanFiveYearsAgo;
}
}
catch (InvalidOperationException)
{
return CertificateStatus.NoCertificateFound;
}
}

private async Task<DateTime> getLastCertificateExpiryDate(string applicantId)
{
var certificates = await _certificateRepository.Query(new UserCertificationQuery() { ByApplicantId = applicantId });
var lastCertificate = certificates.OrderByDescending(d => d.ExpiryDate).FirstOrDefault();
if (lastCertificate == null || lastCertificate.ExpiryDate == null)
{
throw new InvalidOperationException("Certificate or datetime is null");
}
return (DateTime)lastCertificate.ExpiryDate;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace ECER.Engines.Validation.Applications;

internal sealed class ApplicationSubmissionValidationEngine : IApplicationSubmissionValidationEngine
internal sealed class ApplicationSubmissionValidationEngine : IApplicationValidationEngine
{
private static readonly IEnumerable<CertificationType> fiveYearNestedCertificationTypes = new[] { CertificationType.Ite, CertificationType.Sne };

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using ECER.Managers.Registry.Contract.Applications;
using Microsoft.Extensions.DependencyInjection;

namespace ECER.Engines.Validation.Applications;

public interface IApplicationValidationEngineResolver
{
IApplicationValidationEngine Resolve(ApplicationTypes appType);
}

public class ApplicationValidationEngineResolver : IApplicationValidationEngineResolver
{
private readonly IServiceProvider _serviceProvider;

public ApplicationValidationEngineResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

public IApplicationValidationEngine Resolve(ApplicationTypes appType)
{
return appType switch
{
ApplicationTypes.New => _serviceProvider.GetRequiredService<ApplicationSubmissionValidationEngine>(),
ApplicationTypes.Renewal => _serviceProvider.GetRequiredService<ApplicationRenewalValidationEngine>(),
_ => throw new ArgumentOutOfRangeException(nameof(appType), appType, null)
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace ECER.Engines.Validation.Applications;
/// <summary>
/// Validates registry applications
/// </summary>
public interface IApplicationSubmissionValidationEngine
public interface IApplicationValidationEngine
{
/// <summary>
/// Validates if an application is completed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using ECER.Managers.Registry.Contract.Applications;

namespace ECER.Engines.Validation.Applications;

internal sealed partial class ApplicationRenewalValidationEngine
{
private async Task<List<string>> EceAssistant(Application application)
{
await Task.CompletedTask;
var validationErrors = new List<string>();
switch (await GetCertificateStatus(application.RegistrantId))
{
case CertificateStatus.Active:
break;

case CertificateStatus.ExpiredLessThanFiveYearsAgo:
break;

case CertificateStatus.ExpiredMoreThanFiveYearsAgo:
break;

case CertificateStatus.NoCertificateFound:
break;
}
return validationErrors;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using ECER.Managers.Registry.Contract.Applications;

namespace ECER.Engines.Validation.Applications;

internal sealed partial class ApplicationRenewalValidationEngine
{
private async Task<List<string>> FiveYears(Application application)
{
await Task.CompletedTask;
var validationErrors = new List<string>();
switch (await GetCertificateStatus(application.RegistrantId))
{
case CertificateStatus.Active:
break;

case CertificateStatus.ExpiredLessThanFiveYearsAgo:
break;

case CertificateStatus.ExpiredMoreThanFiveYearsAgo:
break;

case CertificateStatus.NoCertificateFound:
break;
}
return validationErrors;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using ECER.Managers.Registry.Contract.Applications;

namespace ECER.Engines.Validation.Applications;

internal sealed partial class ApplicationRenewalValidationEngine
{
private async Task<List<string>> OneYear(Application application)
{
await Task.CompletedTask;
var validationErrors = new List<string>();
switch (await GetCertificateStatus(application.RegistrantId))
{
case CertificateStatus.Active:

// each application should contain explanation letter
if (string.IsNullOrEmpty(application.ExplanationLetter))
{
validationErrors.Add("the application does not have explanation letter");
}
// each application should contain at least one character reference
if (!application.CharacterReferences.Any())
{
validationErrors.Add("the application does not have any character references");
}
break;

case CertificateStatus.ExpiredLessThanFiveYearsAgo:
break;

case CertificateStatus.ExpiredMoreThanFiveYearsAgo:
break;

case CertificateStatus.NoCertificateFound:
break;
}
return validationErrors;
}
}
13 changes: 10 additions & 3 deletions src/ECER.Engines.Validation/Configurer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,15 @@ namespace ECER.Engines.Validation;

public class Configurer : IConfigureComponents
{
public void Configure([NotNull] ConfigurationContext configurationContext)
public void Configure([NotNull] ConfigurationContext configurationContext)
{
configurationContext.Services.AddTransient<ApplicationSubmissionValidationEngine>();
configurationContext.Services.AddTransient<ApplicationRenewalValidationEngine>();
configurationContext.Services.AddTransient<IApplicationValidationEngineResolver, ApplicationValidationEngineResolver>();

configurationContext.Services.AddTransient<IApplicationValidationEngine>(provider =>
{
configurationContext.Services.AddTransient<IApplicationSubmissionValidationEngine, ApplicationSubmissionValidationEngine>();
}
return provider.GetRequiredService<ApplicationSubmissionValidationEngine>();
});
}
}
1 change: 1 addition & 0 deletions src/ECER.Engines.Validation/ECER.Engines.Validation.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
<ItemGroup>
<ProjectReference Include="..\ECER.Infrastructure.Common\ECER.Infrastructure.Common.csproj" />
<ProjectReference Include="..\ECER.Managers.Registry.Contract\ECER.Managers.Registry.Contract.csproj" />
<ProjectReference Include="..\ECER.Resources.Documents\ECER.Resources.Documents.csproj" />
</ItemGroup>
</Project>
3 changes: 2 additions & 1 deletion src/ECER.Managers.Registry/ApplicationHandlers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class ApplicationHandlers(
IPortalInvitationRepository portalInvitationRepository,
IApplicationRepository applicationRepository,
IMapper mapper,
IApplicationSubmissionValidationEngine validationEngine,
IApplicationValidationEngineResolver validationResolver,
EcerContext ecerContext)
: IRequestHandler<SaveDraftApplicationCommand, string>,
IRequestHandler<CancelDraftApplicationCommand, string>,
Expand Down Expand Up @@ -109,6 +109,7 @@ public async Task<ApplicationSubmissionResult> Handle(SubmitApplicationCommand r
}
var draftApplication = draftApplicationResults.Items.First();

var validationEngine = validationResolver?.Resolve(draftApplication.ApplicationType);
var validationErrors = await validationEngine?.Validate(draftApplication)!;
if (validationErrors.ValidationErrors.Any())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public record UserCertificationQuery
{
public string? ById { get; set; }
public string? ByApplicantId { get; set; }
public string? ByApplicationId { get; set; }
}

public record Certification(string Id)
Expand Down
2 changes: 2 additions & 0 deletions src/ECER.Tests/ECER.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<AssemblyName>ECER.Tests</AssemblyName>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<NoWarn>CA1001;1701;1702;CA1707</NoWarn>
Expand All @@ -11,6 +12,7 @@
<PackageReference Include="Bogus" />
<PackageReference Include="MartinCostello.Logging.XUnit" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
<PackageReference Include="Shouldly" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.categories" />
Expand Down
5 changes: 2 additions & 3 deletions src/ECER.Tests/Integration/Api/FileTests.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System.Net.Http.Headers;
using Alba;
using Alba;
using Bogus;
using ECER.Tests.Integration;
using Shouldly;
using System.Net.Http.Headers;
using Xunit.Abstractions;
using Xunit.Categories;

Expand Down
4 changes: 2 additions & 2 deletions src/ECER.Tests/Integration/AuthenticationHelper.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Security.Claims;
using Alba;
using Alba;
using ECER.Utilities.Security;
using System.Security.Claims;

namespace ECER.Tests.Integration;

Expand Down
6 changes: 2 additions & 4 deletions src/ECER.Tests/Integration/RegistryApi/ApplicationTests.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
using Alba;
using Bogus;
using ECER.Clients.RegistryPortal.Server.Applications;
using ECER.Clients.RegistryPortal.Server.Files;
using Shouldly;
using System.Net;
using Xunit.Abstractions;
using System.Net.Http.Headers;
using Xunit.Categories;
using ECER.Clients.RegistryPortal.Server.Files;
using System;
using Xunit.Abstractions;

namespace ECER.Tests.Integration.RegistryApi;

Expand Down
5 changes: 2 additions & 3 deletions src/ECER.Tests/Integration/RegistryApi/FileTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System.Net.Http.Headers;
using Alba;
using Alba;
using Bogus;
using System.Net.Http.Headers;
using Xunit.Abstractions;
using Xunit.Categories;
using Xunit.Sdk;

namespace ECER.Tests.Integration.RegistryApi;

Expand Down
3 changes: 1 addition & 2 deletions src/ECER.Tests/Integration/RegistryApi/ProfileTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ private PreviousName CreatePreviousName()
faker.Name.FirstName(), faker.Name.LastName()
);
}

private UserProfile CreateNewUser()
{
var address = new Faker<Address>("en_CA")
Expand All @@ -53,7 +53,6 @@ private UserProfile CreateNewUser()
f.Address.State(), f.Address.Country()
));


return new Faker<UserProfile>("en_CA")
.RuleFor(f => f.FirstName, f => f.Name.FirstName())
.RuleFor(f => f.LastName, f => f.Name.LastName())
Expand Down
4 changes: 2 additions & 2 deletions src/ECER.Tests/Integration/RegistryApi/UserInfoTests.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
using System.Net;
using Alba;
using Alba;
using Bogus;
using ECER.Clients.RegistryPortal.Server.Users;
using ECER.Utilities.Security;
using Shouldly;
using System.Net;
using Xunit.Abstractions;

namespace ECER.Tests.Integration.RegistryApi;
Expand Down
Loading

0 comments on commit 9c877a6

Please sign in to comment.