Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/ilr submission #1969

Open
wants to merge 31 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e87ed4b
starter for 10 - no change of circumstance
rgparkins Feb 5, 2025
be5801e
Rate limiting implemented
rgparkins Feb 5, 2025
49b5354
API Key implemented
rgparkins Feb 5, 2025
72a0a35
remove the paged response as this is only for inner API
rgparkins Feb 5, 2025
a3fe5d2
description updates for put
rgparkins Feb 5, 2025
512e9a0
pages leaners per provider upport
rgparkins Feb 5, 2025
51c4072
rename directory to LearnerData
rgparkins Feb 5, 2025
bc77722
rename
rgparkins Feb 5, 2025
9883e7a
approved candidate support
rgparkins Feb 5, 2025
844ff00
Added a bad request and update descriptions for endpoints
rgparkins Feb 5, 2025
94d1e4a
rename candidae
rgparkins Feb 6, 2025
537ff84
rename candidate
rgparkins Feb 6, 2025
06fbfed
rename candidate
rgparkins Feb 6, 2025
5761ced
rename prior learning percent
rgparkins Feb 6, 2025
0bda689
add general endpoints plus agreementId on learner
rgparkins Feb 11, 2025
3ada273
add general endpoints plus agreementId on learner
rgparkins Feb 12, 2025
54a9702
add general endpoints plus agreementId on learner
rgparkins Feb 12, 2025
2762320
add general endpoints plus agreementId on learner
rgparkins Feb 12, 2025
8281ec5
remove POST and use PUT
rgparkins Feb 13, 2025
eaa3770
remove 404
rgparkins Feb 13, 2025
65a5689
Added the request bodygit add .git add .
rgparkins Feb 13, 2025
28c9f36
new paged link headers. Now accepting request in instead of creating
rgparkins Feb 13, 2025
55007a4
Endpoint for canned learner data for DC (SLD)
rgparkins Feb 20, 2025
8e944d9
Remove the resource Loader
rgparkins Feb 21, 2025
33754a0
make changes
cofaulco Feb 21, 2025
4810a6f
fix post merge
cofaulco Feb 21, 2025
7a42023
Merge pull request #1974 from SkillsFundingAgency/CF-ILR-updates
cofaulco Feb 21, 2025
6f13500
UKPRN and ULN cannot be primary key
rgparkins Feb 24, 2025
f5bbef0
UKPRN and ULN cannot be primary key
rgparkins Feb 24, 2025
e2b2181
UKPRN and ULN cannot be primary key
rgparkins Feb 24, 2025
0dae4af
Removed the GET call as not needed
rgparkins Feb 24, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using AutoFixture.NUnit3;
using FluentAssertions;
using MediatR;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Moq;
using SFA.DAS.Earnings.Api.Controllers;
using SFA.DAS.Earnings.Application.LearnerData.GetLearnerData;
using SFA.DAS.Testing.AutoFixture;

namespace SFA.DAS.Earnings.Api.UnitTests.Controllers.LearnerData;

public class WhenGettingLearnerData
{
[Test, MoqAutoData]
public async Task Then_Should_Return_Learner_Data_Response(
[Frozen] Mock<IMediator> mediator,
[Greedy]LearnerDataController controller)
{
// Arrange
const int ukprn = 1000001;
const int academicYear = 2425;
const int page = 1;
const int pageSize = 10;

const int totalRecords = 30;

var learnerDataResponse = new GetLearnerDataQueryResult
{
TotalRecords = totalRecords
};

SetUpHttpContext(controller, ukprn, academicYear);

mediator.Setup(m => m.Send(It.IsAny<GetLearnerDataQuery>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(learnerDataResponse);

// Act
var result = await controller.Search(ukprn, academicYear, page, pageSize) as OkObjectResult;

// Assert
result.Should().NotBeNull();
result.Should().BeAssignableTo<OkObjectResult>();
result!.Value.Should().BeEquivalentTo(learnerDataResponse);

// Verify the headers
controller.Response.Headers.Should().ContainKey("Link");
var linkHeader = controller.Response.Headers.Link.ToString();

var expectedBaseUrl = $"http://localhost/providers/{ukprn}/academicyears/{academicYear}/apprenticeships";
var expectedNextPage = $"<{expectedBaseUrl}?page=2&pageSize={pageSize}>; rel=\"next\"";

linkHeader.Should().Contain(expectedNextPage);
}

private void SetUpHttpContext(LearnerDataController controller, long ukprn, int academicYear)
{
var context = new DefaultHttpContext();
controller.ControllerContext = new ControllerContext
{
HttpContext = context
};
controller.ControllerContext.HttpContext.Request.Scheme = "http"; // Setting scheme
controller.ControllerContext.HttpContext.Request.Host = new Microsoft.AspNetCore.Http.HostString("localhost"); // Set Host
controller.ControllerContext.HttpContext.Request.PathBase = string.Empty; // Set PathBase (in case you use any path)
controller.ControllerContext.HttpContext.Request.Path = $"/providers/{ukprn}/academicyears/{academicYear}/apprenticeships"; // Set Path
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,4 @@
<Using Include="NUnit.Framework" />
</ItemGroup>

<ItemGroup>
<Folder Include="Controllers\" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using MediatR;
using Microsoft.AspNetCore.Mvc;
using SFA.DAS.Earnings.Api.LearnerData;
using SFA.DAS.Earnings.Application.LearnerData.GetLearnerData;

namespace SFA.DAS.Earnings.Api.Controllers;

[ApiController]
[Route("leanerdata")]
public class LearnerDataController(
IMediator mediator) : Controller
{
[HttpGet]
[Route("/providers/{ukprn}/academicyears/{academicyear}/apprenticeships")]
public async Task<IActionResult> Search(long ukprn, int academicyear, [FromQuery] int page, [FromQuery] int pageSize)
{
if (pageSize is < 1 or > 100)
{
return BadRequest("Page size must be between 1 and 100");
}

if (page < 1)
{
return BadRequest("Invalid page requested");
}

var result = await mediator.Send(new GetLearnerDataQuery(ukprn, academicyear, page, pageSize));
var response = new PagedResult
{
Apprenticeships = result.Apprenticeships.ConvertAll(app => new Apprenticeship { Uln = app.Uln }),
TotalRecords = result.TotalRecords,
Page = page,
PageSize = pageSize,
TotalPages = (int)Math.Ceiling((double)result.TotalRecords / pageSize)
};

// Add Link headers for pagination
var baseUrl = $"{Request.Scheme}://{Request.Host}{Request.PathBase}{Request.Path}";
var nextPage = page * pageSize < result.TotalRecords ? page + 1 : page;
var prevPage = page > 1 ? page - 1 : 1;

var header = "";

if (prevPage != page)
{
header += $"<{baseUrl}?page={prevPage}&pageSize={pageSize}>; rel=\"prev\", ";
}

if (nextPage != page)
{
if (header.Length > 0)
{
header += ", ";
}

header += $"<{baseUrl}?page={nextPage}&pageSize={pageSize}>; rel=\"next\"";
}

if (header.Length > 0)
{
Response.Headers.Link = header;
}

return Ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace SFA.DAS.Earnings.Api.LearnerData;

public class Apprenticeship {
public long Uln { get; set; }
}
9 changes: 9 additions & 0 deletions src/Earnings/SFA.DAS.Earnings.Api/LearnerData/PagedResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace SFA.DAS.Earnings.Api.LearnerData;

public class PagedResult {
public List<Apprenticeship> Apprenticeships { get; set; } = [];
public long TotalRecords { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
public int TotalPages { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace SFA.DAS.Earnings.Api.LearnerData;

public class Apprenticeship {
public long Uln { get; set; }
}
9 changes: 9 additions & 0 deletions src/Earnings/SFA.DAS.Earnings.Api/Learnerdata/PagedResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace SFA.DAS.Earnings.Api.LearnerData;

public class PagedResult {
public List<Apprenticeship> Apprenticeships { get; set; } = [];
public long TotalRecords { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
public int TotalPages { get; set; }
}
19 changes: 8 additions & 11 deletions src/Earnings/SFA.DAS.Earnings.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,27 @@
using SFA.DAS.Api.Common.AppStart;
using SFA.DAS.Api.Common.Configuration;
using SFA.DAS.Earnings.Api.AppStart;
using SFA.DAS.Earnings.Api.Controllers;
using SFA.DAS.Earnings.Api.Learnerdata;
using SFA.DAS.Earnings.Application.LearnerData;
using SFA.DAS.SharedOuterApi.AppStart;
using SFA.DAS.SharedOuterApi.Infrastructure.HealthCheck;

namespace SFA.DAS.Earnings.Api;

[ExcludeFromCodeCoverage]
public class Startup
public class Startup(IConfiguration configuration, IWebHostEnvironment env)
{
private readonly IWebHostEnvironment _env;
private readonly IConfiguration _configuration;

public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
_env = env;
_configuration = configuration.BuildSharedConfiguration();
}
private readonly IConfiguration _configuration = configuration.BuildSharedConfiguration();

public void ConfigureServices(IServiceCollection services)
{
services.AddNLog();
services.AddOptions();
services.AddSingleton(_env);
services.AddSingleton(env);

services.AddConfigurationOptions(_configuration);

if (!_configuration.IsLocalOrDev())
{
var azureAdConfiguration = _configuration
Expand All @@ -45,6 +41,7 @@ public void ConfigureServices(IServiceCollection services)
}

services.AddMediatR(configuration => configuration.RegisterServicesFromAssembly(typeof(Earning).Assembly));
services.AddSingleton<ILearnerDataStore, LearnerDataStore>();
services.AddServiceRegistration(_configuration);

services
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ public GetApprenticeshipsResponse BuildApprenticeshipsResponse(long ukprn)
Key = Guid.NewGuid(),
Episodes = new List<Episode>
{
new Episode
new()
{
Key = Guid.NewGuid(),
TrainingCode = $"{Fixture.Create<int>()} ",
Prices = new List<EpisodePrice>
{
new EpisodePrice
new()
{
StartDate = new DateTime(2020, 1, 1),
EndDate = new DateTime(2021, 1, 1),
Expand All @@ -95,13 +95,13 @@ public GetApprenticeshipsResponse BuildApprenticeshipsResponse(long ukprn)
Key = Guid.NewGuid(),
Episodes = new List<Episode>
{
new Episode
new()
{
Key = Guid.NewGuid(),
TrainingCode = $"{Fixture.Create<int>()} ",
Prices = new List<EpisodePrice>
{
new EpisodePrice
new()
{
StartDate = new DateTime(2020, 8, 1),
EndDate = new DateTime(2021, 5, 2),
Expand All @@ -110,7 +110,7 @@ public GetApprenticeshipsResponse BuildApprenticeshipsResponse(long ukprn)
TotalPrice = 22500,
FundingBandMaximum = 30000
},
new EpisodePrice
new()
{
StartDate = new DateTime(2021, 5, 3),
EndDate = new DateTime(2021, 7, 31),
Expand Down Expand Up @@ -154,26 +154,26 @@ public GetFm36DataResponse BuildEarningsResponse(GetApprenticeshipsResponse appr
FundingLineType = Fixture.Create<string>(),
Episodes = new List<SharedOuterApi.InnerApi.Responses.Earnings.Episode>
{
new SharedOuterApi.InnerApi.Responses.Earnings.Episode
new()
{
Key = apprenticeshipsResponse.Apprenticeships[0].Episodes[0].Key,
NumberOfInstalments = 12,
CompletionPayment = 3000,
OnProgramTotal = 12000,
Instalments = new List<Instalment>
{
new Instalment{ AcademicYear = 1920, DeliveryPeriod = 6, Amount = 1000 },
new Instalment{ AcademicYear = 1920, DeliveryPeriod = 7, Amount = 1000 },
new Instalment{ AcademicYear = 1920, DeliveryPeriod = 8, Amount = 1000 },
new Instalment{ AcademicYear = 1920, DeliveryPeriod = 9, Amount = 1000 },
new Instalment{ AcademicYear = 1920, DeliveryPeriod = 10, Amount = 1000 },
new Instalment{ AcademicYear = 1920, DeliveryPeriod = 11, Amount = 1000 },
new Instalment{ AcademicYear = 1920, DeliveryPeriod = 12, Amount = 1000 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 1, Amount = 1000 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 2, Amount = 1000 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 3, Amount = 1000 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 4, Amount = 1000 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 5, Amount = 1000 }
new() { AcademicYear = 1920, DeliveryPeriod = 6, Amount = 1000 },
new() { AcademicYear = 1920, DeliveryPeriod = 7, Amount = 1000 },
new() { AcademicYear = 1920, DeliveryPeriod = 8, Amount = 1000 },
new() { AcademicYear = 1920, DeliveryPeriod = 9, Amount = 1000 },
new() { AcademicYear = 1920, DeliveryPeriod = 10, Amount = 1000 },
new() { AcademicYear = 1920, DeliveryPeriod = 11, Amount = 1000 },
new() { AcademicYear = 1920, DeliveryPeriod = 12, Amount = 1000 },
new() { AcademicYear = 2021, DeliveryPeriod = 1, Amount = 1000 },
new() { AcademicYear = 2021, DeliveryPeriod = 2, Amount = 1000 },
new() { AcademicYear = 2021, DeliveryPeriod = 3, Amount = 1000 },
new() { AcademicYear = 2021, DeliveryPeriod = 4, Amount = 1000 },
new() { AcademicYear = 2021, DeliveryPeriod = 5, Amount = 1000 }
}
}
}
Expand All @@ -185,26 +185,26 @@ public GetFm36DataResponse BuildEarningsResponse(GetApprenticeshipsResponse appr
FundingLineType = Fixture.Create<string>(),
Episodes = new List<SharedOuterApi.InnerApi.Responses.Earnings.Episode>
{
new SharedOuterApi.InnerApi.Responses.Earnings.Episode
new()
{
Key = apprenticeshipsResponse.Apprenticeships[1].Episodes[0].Key,
NumberOfInstalments = 12,
CompletionPayment = 6000,
OnProgramTotal = 24000,
Instalments = new List<Instalment>
{
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 1, Amount = 1875 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 2, Amount = 1875 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 3, Amount = 1875 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 4, Amount = 1875 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 5, Amount = 1875 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 6, Amount = 1875 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 7, Amount = 1875 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 8, Amount = 1875 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 9, Amount = 1875 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 10, Amount = 4375 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 11, Amount = 4375 },
new Instalment{ AcademicYear = 2021, DeliveryPeriod = 12, Amount = 4375 }
new() { AcademicYear = 2021, DeliveryPeriod = 1, Amount = 1875 },
new() { AcademicYear = 2021, DeliveryPeriod = 2, Amount = 1875 },
new() { AcademicYear = 2021, DeliveryPeriod = 3, Amount = 1875 },
new() { AcademicYear = 2021, DeliveryPeriod = 4, Amount = 1875 },
new() { AcademicYear = 2021, DeliveryPeriod = 5, Amount = 1875 },
new() { AcademicYear = 2021, DeliveryPeriod = 6, Amount = 1875 },
new() { AcademicYear = 2021, DeliveryPeriod = 7, Amount = 1875 },
new() { AcademicYear = 2021, DeliveryPeriod = 8, Amount = 1875 },
new() { AcademicYear = 2021, DeliveryPeriod = 9, Amount = 1875 },
new() { AcademicYear = 2021, DeliveryPeriod = 10, Amount = 4375 },
new() { AcademicYear = 2021, DeliveryPeriod = 11, Amount = 4375 },
new() { AcademicYear = 2021, DeliveryPeriod = 12, Amount = 4375 }
}
}
}
Expand Down
Loading
Loading