From bcc6b38559cb35cd355e957ad49c1b5c59feeae6 Mon Sep 17 00:00:00 2001 From: Sukhvinder Bhullar Date: Thu, 16 Jan 2025 13:39:01 +0000 Subject: [PATCH 01/47] CSV Generator root service added --- .../CsvExport/CSVFileContentGenerator.cs | 27 +++++++++ .../CsvExport/CSVFileContentGeneratorTests.cs | 59 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/CSVFileContentGenerator.cs create mode 100644 src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/CSVFileContentGeneratorTests.cs diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/CSVFileContentGenerator.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/CSVFileContentGenerator.cs new file mode 100644 index 00000000..22a0ab7d --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/CSVFileContentGenerator.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dfe.Complete.Application.Services.CsvExport +{ + public interface HeaderGenerator + { + string GenerateHeader(); + } + + public interface RowGenerator + { + string GenerateRow(TModel model); + } + + + public class CSVFileContentGenerator(HeaderGenerator header, RowGenerator rowGenerator) + { + public string Generate(IEnumerable models) + { + return $"{header.GenerateHeader()}\n\r{string.Join("\n\r", models.Select(rowGenerator.GenerateRow))}"; + } + } +} diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/CSVFileContentGeneratorTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/CSVFileContentGeneratorTests.cs new file mode 100644 index 00000000..7b5131ad --- /dev/null +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/CSVFileContentGeneratorTests.cs @@ -0,0 +1,59 @@ +using Dfe.Complete.Application.Services.CsvExport; + +namespace Dfe.Complete.Application.Tests.Services.CsvExport +{ + internal record TestModel(string SchoolName, int SchoolUrn, string ProjectType, string AcademyName, int AcademyUrn); + + internal class TestHeaderGenerator : HeaderGenerator + { + public string GenerateHeader() + { + return "School name,School URN,Project type,Academy name,Academy URN"; + } + } + + internal class TestRowGenerator : RowGenerator + { + public string GenerateRow(TestModel model) + { + return $"{model.SchoolName},{model.SchoolUrn},{model.ProjectType},{model.AcademyName},{model.AcademyUrn}"; + } + } + + public class CSVFileContentGeneratorTests + { + [Fact] + public void Generate_ShouldReturnCsvString_WhenModelIsProvided() + { + var model = new List + { + new TestModel("Round Hill Primary School", 122707, "Conversion", "Round Hill Primary School", 150250) + }; + + var generator = new CSVFileContentGenerator(new TestHeaderGenerator(), new TestRowGenerator()); + + var result = generator.Generate(model); + + Assert.Equal("School name,School URN,Project type,Academy name,Academy URN\n\rRound Hill Primary School,122707,Conversion,Round Hill Primary School,150250", result); + } + + [Fact] + public void Generate_ShouldReturnCsvString_WhenMultipleModelsAreProvided() + { + var models = new List + { + new TestModel("Round Hill Primary School", 122707, "Conversion", "Primary School", 150250), + new TestModel("Hill Primary School", 122708, "Transfer", "Hill Primary School", 150251), + new TestModel("Primary School", 122709, "Conversion", "Round Hill Primary School", 150252) + }; + + var generator = new CSVFileContentGenerator(new TestHeaderGenerator(), new TestRowGenerator()); + + var result = generator.Generate(models); + + var expected = string.Join("\n\r", models.Select(m => $"{m.SchoolName},{m.SchoolUrn},{m.ProjectType},{m.AcademyName},{m.AcademyUrn}")); + + Assert.Equal($"School name,School URN,Project type,Academy name,Academy URN\n\r{expected}", result); + } + } +} From a2169e9573b06760380c202594da6f16086b74fd Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 17 Jan 2025 11:46:07 +0000 Subject: [PATCH 02/47] Create list all project query --- .../IListAllProjectsQueryService.cs | 9 +++++ .../Model/ListAllProjectsQueryModel.cs | 5 +++ .../Model/ListAllProjectsResultModel.cs | 15 +++++++ .../ListAllProjects/ListAllProjects.cs | 40 +++++++++++++++++++ .../Dfe.Complete.Domain/Entities/Project.cs | 3 +- ...frastructureServiceCollectionExtensions.cs | 10 ++++- .../ListAllProjectsQueryService.cs | 24 +++++++++++ 7 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 src/Core/Dfe.Complete.Application/Projects/Interfaces/IListAllProjectsQueryService.cs create mode 100644 src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsQueryModel.cs create mode 100644 src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs create mode 100644 src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs create mode 100644 src/Core/Dfe.Complete.Infrastructure/QueryServices/ListAllProjectsQueryService.cs diff --git a/src/Core/Dfe.Complete.Application/Projects/Interfaces/IListAllProjectsQueryService.cs b/src/Core/Dfe.Complete.Application/Projects/Interfaces/IListAllProjectsQueryService.cs new file mode 100644 index 00000000..cd780da8 --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Projects/Interfaces/IListAllProjectsQueryService.cs @@ -0,0 +1,9 @@ +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Domain.Enums; + +namespace Dfe.Complete.Application.Projects.Interfaces; + +public interface IListAllProjectsQueryService +{ + IQueryable ListAllProjects(ProjectState? projectStatus, ProjectType? type, bool? includeFormAMat); +} \ No newline at end of file diff --git a/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsQueryModel.cs b/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsQueryModel.cs new file mode 100644 index 00000000..746342fd --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsQueryModel.cs @@ -0,0 +1,5 @@ +using Dfe.Complete.Domain.Entities; + +namespace Dfe.Complete.Application.Projects.Model; + +public record ListAllProjectsQueryModel(Project Project, GiasEstablishment Establishment); \ No newline at end of file diff --git a/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs b/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs new file mode 100644 index 00000000..6050a72b --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs @@ -0,0 +1,15 @@ +using Dfe.Complete.Domain.Entities; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Domain.ValueObjects; + +namespace Dfe.Complete.Application.Projects.Model; + +public record ListAllProjectsResultModel( + string? EstablishmentName, + ProjectId ProjectId, + Urn Urn, + DateOnly? ConversionOrTransferDate, + ProjectState State, + ProjectType? ProjectType, + bool IsFormAMAT, + User? AssignedTo); \ No newline at end of file diff --git a/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs b/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs new file mode 100644 index 00000000..a314c930 --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs @@ -0,0 +1,40 @@ +using Dfe.Complete.Application.Common.Models; +using Dfe.Complete.Application.Projects.Interfaces; +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Domain.Enums; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Dfe.Complete.Application.Projects.Queries.ListAllProjects +{ + public record ListAllProjectsQuery(ProjectState? ProjectStatus, ProjectType? Type, bool? IncludeFormAMat, int Page, int Count) : IRequest>>; + + public class ListAllProjectsQueryHandler(IListAllProjectsQueryService listAllProjectsQueryService) : IRequestHandler>> + { + public async Task>> Handle(ListAllProjectsQuery request, CancellationToken cancellationToken) + { + // var cacheKey = $"Project_{CacheKeyHelper.GenerateHashedCacheKey(request.Urn.Value.ToString())}"; + try + { + var result = await listAllProjectsQueryService.ListAllProjects(request.ProjectStatus, request.Type, request.IncludeFormAMat) + .Skip(request.Page * request.Count).Take(request.Count) + .Select(item => new ListAllProjectsResultModel( + item.Establishment.Name, + item.Project.Id, + item.Project.Urn, + item.Project.SignificantDate, + item.Project.State, + item.Project.Type, + item.Project.IncomingTrustUkprn == null, + item.Project.AssignedTo + )) + .ToListAsync(cancellationToken); + return Result>.Success(result); + } + catch (Exception ex) + { + return Result>.Failure(ex.Message); + } + } + } +} \ No newline at end of file diff --git a/src/Core/Dfe.Complete.Domain/Entities/Project.cs b/src/Core/Dfe.Complete.Domain/Entities/Project.cs index cd083174..15f8a07f 100644 --- a/src/Core/Dfe.Complete.Domain/Entities/Project.cs +++ b/src/Core/Dfe.Complete.Domain/Entities/Project.cs @@ -2,7 +2,6 @@ using Dfe.Complete.Domain.Enums; using Dfe.Complete.Domain.Events; using Dfe.Complete.Domain.ValueObjects; -using Dfe.Complete.Utils; namespace Dfe.Complete.Domain.Entities; @@ -74,7 +73,7 @@ public class Project : BaseAggregateRoot, IEntity public string? NewTrustName { get; set; } - public int State { get; set; } + public ProjectState State { get; set; } public int? PrepareId { get; set; } diff --git a/src/Core/Dfe.Complete.Infrastructure/InfrastructureServiceCollectionExtensions.cs b/src/Core/Dfe.Complete.Infrastructure/InfrastructureServiceCollectionExtensions.cs index d3dab851..22c1841f 100644 --- a/src/Core/Dfe.Complete.Infrastructure/InfrastructureServiceCollectionExtensions.cs +++ b/src/Core/Dfe.Complete.Infrastructure/InfrastructureServiceCollectionExtensions.cs @@ -1,11 +1,13 @@ +using Dfe.Complete.Application.Projects.Interfaces; using Dfe.Complete.Domain.Interfaces.Repositories; using Dfe.Complete.Infrastructure.Database; +using Dfe.Complete.Infrastructure.QueryServices; using Dfe.Complete.Infrastructure.Repositories; -using Dfe.Complete.Infrastructure.Security.Authorization; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; -namespace Microsoft.Extensions.DependencyInjection +namespace Dfe.Complete.Infrastructure { public static class InfrastructureServiceCollectionExtensions { @@ -22,6 +24,10 @@ public static IServiceCollection AddInfrastructureDependencyGroup( var connectionString = config.GetConnectionString("DefaultConnection"); services.AddDbContext(options => options.UseSqlServer(connectionString)); + + + //Queries + services.AddScoped(); // Authentication //services.AddCustomAuthorization(config); diff --git a/src/Core/Dfe.Complete.Infrastructure/QueryServices/ListAllProjectsQueryService.cs b/src/Core/Dfe.Complete.Infrastructure/QueryServices/ListAllProjectsQueryService.cs new file mode 100644 index 00000000..cc4ed78a --- /dev/null +++ b/src/Core/Dfe.Complete.Infrastructure/QueryServices/ListAllProjectsQueryService.cs @@ -0,0 +1,24 @@ +using Dfe.Complete.Application.Projects.Interfaces; +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Infrastructure.Database; +using Microsoft.EntityFrameworkCore; + +namespace Dfe.Complete.Infrastructure.QueryServices; + +internal class ListAllProjectsQueryService(CompleteContext context) : IListAllProjectsQueryService +{ + public IQueryable ListAllProjects(ProjectState? projectStatus, ProjectType? type, bool? includeFormAMat) + { + var query = context.Projects + .Where(project => projectStatus == null || project.State == projectStatus) + .Where(project => type == null || type == project.Type) + .Where(project => includeFormAMat == null || + includeFormAMat == (project.IncomingTrustUkprn == null)) + .Include(p => p.AssignedTo) + .Join(context.GiasEstablishments, project => project.Urn, establishment => establishment.Urn, + (project, establishment) => new ListAllProjectsQueryModel(project, establishment)); + + return query; + } +} \ No newline at end of file From 9dbe6db4e7e86743bb7cd68e109f02d93e960da7 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 17 Jan 2025 11:48:43 +0000 Subject: [PATCH 03/47] Uncomment pagination code --- .../Pages/Pagination/PaginationModel.cs | 32 +++++- .../Pages/Pagination/_Pagination.cshtml | 108 +++++++++--------- 2 files changed, 84 insertions(+), 56 deletions(-) diff --git a/src/Frontend/Dfe.Complete/Pages/Pagination/PaginationModel.cs b/src/Frontend/Dfe.Complete/Pages/Pagination/PaginationModel.cs index d72f2ce5..dca232a0 100644 --- a/src/Frontend/Dfe.Complete/Pages/Pagination/PaginationModel.cs +++ b/src/Frontend/Dfe.Complete/Pages/Pagination/PaginationModel.cs @@ -2,6 +2,34 @@ { public class PaginationModel { + public PaginationModel(string url, int pageNumber, int recordCount, int pageSize, string? elementIdPrefix = null) + { + Url = url; + PageNumber = pageNumber; + RecordCount = recordCount; + TotalPages = (int)Math.Ceiling((double)recordCount / pageSize); + if (pageNumber > 1) + { + HasPrevious = true; + Previous = pageNumber - 1; + } + else + { + HasPrevious = false; + } + + if (pageNumber < TotalPages) + { + HasNext = true; + Next = pageNumber + 1; + } + else + { + HasNext = false; + } + + ElementIdPrefix = elementIdPrefix; + } public string Url { get; set; } public bool HasNext { get; set; } @@ -23,12 +51,12 @@ public class PaginationModel /// This is for partial page reloads when pagination is invoked /// When we only want to refresh the content container, not the entire page /// - public string ContentContainerId { get; set; } + // public string ContentContainerId { get; set; } /// /// Prefix so that we can have multiple pagination elements on screen /// Ensures we can uniquely identify the pagination for separate content containers /// - public string ElementIdPrefix { get; set; } + public string? ElementIdPrefix { get; set; } } } diff --git a/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml b/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml index 6f1f87ef..0f88c5b0 100644 --- a/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml @@ -5,12 +5,12 @@ var ariaDisabled = "aria-disabled=true"; - var nextPageLink = $"{Model.Url}&pageNumber={Model.Next}"; + var nextPageLink = $"{Model.Url}?pageNumber={Model.Next}"; var nextDisabled = !Model.Next.HasValue; var nextDisabledStyle = nextDisabled ? "govuk-pagination__link-disabled aria-disabled=true" : ""; var nextDisabledAria = nextDisabled ? ariaDisabled : ""; - var previousPageLink = $"{Model.Url}&pageNumber={Model.Previous}"; + var previousPageLink = $"{Model.Url}?pageNumber={Model.Previous}"; var previousDisabled = !Model.Previous.HasValue; var previousDisabledStyle = previousDisabled ? "govuk-pagination__link-disabled aria-disabled=true" : ""; var previousDisabledAria = previousDisabled ? ariaDisabled : ""; @@ -67,7 +67,7 @@ }
  • - @pageNumber @@ -100,55 +100,55 @@ // When pagination is used on the screen without a full page refresh - if (!string.IsNullOrEmpty(Model.ContentContainerId)) - { - - } + @* if (!string.IsNullOrEmpty(Model.ContentContainerId)) *@ + @* { *@ + @* *@ + @* } *@ } \ No newline at end of file From 2fbb5d8e56a26d3eef01ea0c858950f4607ac368 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 17 Jan 2025 11:49:18 +0000 Subject: [PATCH 04/47] Create count project query --- .../Queries/CountProjects/CountProjects.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs diff --git a/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs b/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs new file mode 100644 index 00000000..3ac8b63e --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs @@ -0,0 +1,28 @@ +using Dfe.Complete.Application.Common.Models; +using Dfe.Complete.Application.Projects.Interfaces; +using Dfe.Complete.Domain.Enums; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace Dfe.Complete.Application.Projects.Queries.CountProjects +{ + public record CountProjectQuery(ProjectState? ProjectStatus, ProjectType? Type, bool? IncludeFormAMat) : IRequest>; + + public class CountProjectsQueryHandler(IListAllProjectsQueryService listAllProjectsQueryService) : IRequestHandler> + { + public async Task> Handle(CountProjectQuery request, CancellationToken cancellationToken) + { + // var cacheKey = $"Project_{CacheKeyHelper.GenerateHashedCacheKey(request.Urn.Value.ToString())}"; + try + { + var result = await listAllProjectsQueryService.ListAllProjects(request.ProjectStatus, request.Type, request.IncludeFormAMat) + .CountAsync(cancellationToken); + return Result.Success(result); + } + catch (Exception ex) + { + return Result.Failure(ex.Message); + } + } + } +} \ No newline at end of file From 1f0e906026c95b6a523259cd58f0c92147102389 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 17 Jan 2025 11:49:38 +0000 Subject: [PATCH 05/47] Create DateOnly Extensions --- .../Extensions/DateOnlyExtensions.cs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs diff --git a/src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs b/src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs new file mode 100644 index 00000000..4acfc12b --- /dev/null +++ b/src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs @@ -0,0 +1,45 @@ +using System; + +namespace Dfe.Complete.Extensions +{ + public static class DateOnlyExtensions + { + public static string ToUkDateString(this DateOnly dateTime) => dateTime.ToString("dd/MM/yyyy"); + + public static string ToDateString(this DateOnly? dateTime, bool includeDayOfWeek = false) + { + if (!dateTime.HasValue) + { + return string.Empty; + } + return ToDateString(dateTime.Value, includeDayOfWeek); + } + + public static string ToDateString(this DateOnly dateTime, bool includeDayOfWeek = false) + { + if (includeDayOfWeek) + { + return dateTime.ToString("dddd d MMMM yyyy"); + } + return dateTime.ToString("d MMMM yyyy"); + } + + public static DateOnly FirstOfMonth(this DateOnly thisMonth, int monthsToAdd) + { + var month = (thisMonth.Month + monthsToAdd) % 12; + if (month == 0) month = 12; + var yearsToAdd = (thisMonth.Month + monthsToAdd - 1) / 12; + return new DateOnly(thisMonth.Year + yearsToAdd, month, 1); + } + + public static string ToDateMonthYearString(this DateOnly? dateTime) + { + if (!dateTime.HasValue) + { + return string.Empty; + } + + return dateTime.Value.ToString("MMM yyyy"); + } + } +} \ No newline at end of file From ebbefbcefb6cfc14a5aff40a9d2e0cd016d47a7d Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 17 Jan 2025 11:51:55 +0000 Subject: [PATCH 06/47] Create All Projects layout and view model --- .../Projects/List/AllProjectsViewModel.cs | 14 ++++++++ .../Projects/List/_AllProjectsLayout.cshtml | 33 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/AllProjectsViewModel.cs create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/_AllProjectsLayout.cshtml diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/AllProjectsViewModel.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/AllProjectsViewModel.cs new file mode 100644 index 00000000..070b53f2 --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/AllProjectsViewModel.cs @@ -0,0 +1,14 @@ +using Dfe.Complete.Pages.Pagination; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Dfe.Complete.Pages.Projects.List; + +public class AllProjectsViewModel : PageModel +{ + [BindProperty(SupportsGet = true)] public int PageNumber { get; set; } = 1; + + public PaginationModel Pagination { get; set; } = default!; + + internal int PageSize = 20; +} \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/_AllProjectsLayout.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/_AllProjectsLayout.cshtml new file mode 100644 index 00000000..1350cfd0 --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/_AllProjectsLayout.cshtml @@ -0,0 +1,33 @@ +@model Dfe.Complete.Pages.Projects.List.AllProjectsViewModel +@{ + Layout = "Shared/_Layout"; +} + + +
    + @RenderBody() +
    + +@await Html.PartialAsync("/Pages/Pagination/_Pagination.cshtml", model: Model.Pagination) +@* *@ \ No newline at end of file From d7787bf1b3f061b6accbd76fd878501c2d261496 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 17 Jan 2025 11:52:16 +0000 Subject: [PATCH 07/47] Create pages for all projects list --- .../Projects/List/ProjectsInProgress.cshtml | 51 ------------------- .../List/ProjectsInProgress.cshtml.cs | 40 --------------- .../AllProjectsInProgress.cshtml | 50 ++++++++++++++++++ .../AllProjectsInProgress.cshtml.cs | 33 ++++++++++++ .../ConversionProjectsInProgress.cshtml | 50 ++++++++++++++++++ .../ConversionProjectsInProgress.cshtml.cs | 35 +++++++++++++ .../FormAMatProjectsInProgress.cshtml | 50 ++++++++++++++++++ .../FormAMatProjectsInProgress.cshtml.cs | 36 +++++++++++++ .../TransferProjectsInProgress.cshtml | 50 ++++++++++++++++++ .../TransferProjectsInProgress.cshtml.cs | 36 +++++++++++++ 10 files changed, 340 insertions(+), 91 deletions(-) delete mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress.cshtml delete mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress.cshtml.cs create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml.cs create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress.cshtml deleted file mode 100644 index 757033ea..00000000 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress.cshtml +++ /dev/null @@ -1,51 +0,0 @@ -@page "/projects/all/in-progress/all" -@using Dfe.Complete.Constants; -@model Dfe.Complete.Pages.Projects.List.ProjectsInProgressModel -@{ - ViewData["Title"] = "All projects in progress"; -} - -
    - - - - - - - - - - - - - - @* @foreach(var project in Model.Projects) *@ - @* { *@ - @* string projectSummaryUrl; *@ - @* *@ - @* if (project.ProjectType == ProjectType.Conversion) *@ - @* { *@ - @* projectSummaryUrl = string.Format(RouteConstants.ConversionProjectTaskList, project.Id); *@ - @* } *@ - @* else *@ - @* { *@ - @* projectSummaryUrl = string.Format(RouteConstants.TransferProjectTaskList, project.Id); *@ - @* } *@ - @* *@ - @* *@ - @* *@ - @* *@ - @* *@ - @* *@ - @* *@ - @* *@ - @* *@ - @* } *@ - -
    School or academyURNConversion or transfer dateProject typeForm a MAT project?Assigned to
    *@ - @* @project.SchoolName *@ - @* @project.Urn@project.ConversionOrTransferDate.ToDateMonthYearString()@project.ProjectType.ToString()@project.IsFormAMatProject.ToYesNoString()@project.AssignedTo
    - - - -
    \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress.cshtml.cs deleted file mode 100644 index 8f63ebf8..00000000 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress.cshtml.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Dfe.Complete.Pages.Pagination; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; -using System.Threading.Tasks; - -namespace Dfe.Complete.Pages.Projects.List -{ - public class ProjectsInProgressModel : PageModel - { - [BindProperty(SupportsGet = true)] - public int PageNumber { get; set; } = 1; - - public PaginationModel Pagination { get; set; } = new(); - - // public List Projects { get; set; } - - public async Task OnGet() - { - //TODO: Review pagination logic - // var parameters = new GetProjectListServiceParameters() - // { - // Status = ProjectStatusQueryParameter.InProgress, - // Page = PageNumber, - // Count = 20 - // }; - // - // var response = await _getProjectListService.Execute(parameters); - // Projects = response.Data.ToList(); - // - // var paginationModel = PaginationMapping.ToModel(response.Paging); - // paginationModel.Url = "?handler=movePage"; - // Pagination = paginationModel; - } - - public async Task OnGetMovePage() - { - await OnGet(); - } - } -} diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml new file mode 100644 index 00000000..c8f7d4cc --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml @@ -0,0 +1,50 @@ +@page "/projects/all/in-progress/all" +@using Dfe.Complete.Constants; +@using Dfe.Complete.Domain.Enums +@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.AllProjectsInProgressViewModel +@{ + Layout = "List/_AllProjectsLayout"; + ViewData["Title"] = "All projects in progress"; +} + +
    + + + + + + + + + + + + + + @foreach(var project in Model.Projects) + { + string projectSummaryUrl; + + if (project.ProjectType == ProjectType.Conversion) + { + projectSummaryUrl = string.Format(RouteConstants.ConversionProjectTaskList, project.ProjectId); + } + else + { + projectSummaryUrl = string.Format(RouteConstants.TransferProjectTaskList, project.ProjectId); + } + + + + + + + + + + } + +
    School or academyURNConversion or transfer dateProject typeForm a MAT project?Assigned to
    + @project.EstablishmentName + @project.Urn.Value@project.ConversionOrTransferDate.ToDateMonthYearString()@project.ProjectType.ToString()@project.IsFormAMAT.ToYesNoString()@project.AssignedTo?.FirstName @project.AssignedTo?.LastName
    +
    \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs new file mode 100644 index 00000000..56cd7cf3 --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs @@ -0,0 +1,33 @@ +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Application.Projects.Queries.CountProjects; +using Dfe.Complete.Application.Projects.Queries.ListAllProjects; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Pages.Pagination; +using MediatR; + +namespace Dfe.Complete.Pages.Projects.List.ProjectsInProgress +{ + public class AllProjectsInProgressViewModel(ISender sender) : AllProjectsViewModel + { + public List Projects { get; set; } = default!; + + public async Task OnGet() + { + + var listProjectQuery = new ListAllProjectsQuery(ProjectState.Active, null, null, PageNumber-1, PageSize); + + var listResponse = await sender.Send(listProjectQuery); + Projects = listResponse.Value ?? []; + + var countProjectQuery = new CountProjectQuery(ProjectState.Active, null, null); + var countResponse = await sender.Send(countProjectQuery); + + Pagination = new PaginationModel("/projects/all/in-progress/all" ,PageNumber, countResponse.Value, PageSize); + } + + public async Task OnGetMovePage() + { + await OnGet(); + } + } +} \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml new file mode 100644 index 00000000..fdd967f7 --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml @@ -0,0 +1,50 @@ +@page "/projects/all/in-progress/conversions" +@using Dfe.Complete.Constants; +@using Dfe.Complete.Domain.Enums +@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.ConversionProjectsInProgressModel +@{ + Layout = "List/_AllProjectsLayout"; + ViewData["Title"] = "All projects in progress"; +} + +
    + + + + + + + + + + + + + + @foreach(var project in Model.Projects) + { + string projectSummaryUrl; + + if (project.ProjectType == ProjectType.Conversion) + { + projectSummaryUrl = string.Format(RouteConstants.ConversionProjectTaskList, project.ProjectId); + } + else + { + projectSummaryUrl = string.Format(RouteConstants.TransferProjectTaskList, project.ProjectId); + } + + + + + + + + + + } + +
    School or academyURNConversion or transfer dateProject typeForm a MAT project?Assigned to
    + @project.EstablishmentName + @project.Urn.Value@project.ConversionOrTransferDate.ToDateMonthYearString()@project.ProjectType.ToString()@project.IsFormAMAT.ToYesNoString()@project.AssignedTo?.FirstName @project.AssignedTo?.LastName
    +
    \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs new file mode 100644 index 00000000..95ccacdd --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs @@ -0,0 +1,35 @@ +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Application.Projects.Queries.CountProjects; +using Dfe.Complete.Application.Projects.Queries.ListAllProjects; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Pages.Pagination; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Dfe.Complete.Pages.Projects.List.ProjectsInProgress +{ + public class ConversionProjectsInProgressModel(ISender sender) : AllProjectsViewModel + { + public List Projects { get; set; } = default!; + + public async Task OnGet() + { + //TODO: Review pagination logic + var listProjectQuery = new ListAllProjectsQuery(ProjectState.Active, ProjectType.Conversion, null, PageNumber-1, PageSize); + + var response = await sender.Send(listProjectQuery); + Projects = response.Value?.ToList() ?? []; + + var countProjectQuery = new CountProjectQuery(ProjectState.Active, ProjectType.Conversion, null); + var countResponse = await sender.Send(countProjectQuery); + + Pagination = new PaginationModel("/projects/all/in-progress/conversions" ,PageNumber, countResponse.Value, PageSize); + } + + public async Task OnGetMovePage() + { + await OnGet(); + } + } +} \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml new file mode 100644 index 00000000..97c5d279 --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml @@ -0,0 +1,50 @@ +@page "/projects/all/in-progress/form-a-multi-academy-trust" +@using Dfe.Complete.Constants; +@using Dfe.Complete.Domain.Enums +@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.FormAMatProjectsInProgressModel +@{ + Layout = "List/_AllProjectsLayout"; + ViewData["Title"] = "All projects in progress"; +} + +
    + + + + + + + + + + + + + + @foreach(var project in Model.Projects) + { + string projectSummaryUrl; + + if (project.ProjectType == ProjectType.Conversion) + { + projectSummaryUrl = string.Format(RouteConstants.ConversionProjectTaskList, project.ProjectId); + } + else + { + projectSummaryUrl = string.Format(RouteConstants.TransferProjectTaskList, project.ProjectId); + } + + + + + + + + + + } + +
    School or academyURNConversion or transfer dateProject typeForm a MAT project?Assigned to
    + @project.EstablishmentName + @project.Urn.Value@project.ConversionOrTransferDate.ToDateMonthYearString()@project.ProjectType.ToString()@project.IsFormAMAT.ToYesNoString()@project.AssignedTo?.FirstName @project.AssignedTo?.LastName
    +
    \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml.cs new file mode 100644 index 00000000..341bfa5e --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml.cs @@ -0,0 +1,36 @@ +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Application.Projects.Queries.CountProjects; +using Dfe.Complete.Application.Projects.Queries.ListAllProjects; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Pages.Pagination; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Dfe.Complete.Pages.Projects.List.ProjectsInProgress +{ + public class FormAMatProjectsInProgressModel(ISender sender) : AllProjectsViewModel + { + + public List Projects { get; set; } = default!; + + public async Task OnGet() + { + //TODO: Review pagination logic + var listProjectQuery = new ListAllProjectsQuery(ProjectState.Active, null, true, PageNumber-1, PageSize); + + var response = await sender.Send(listProjectQuery); + Projects = response.Value?.ToList() ?? []; + + var countProjectQuery = new CountProjectQuery(ProjectState.Active, null, true); + var countResponse = await sender.Send(countProjectQuery); + + Pagination = new PaginationModel("/projects/all/in-progress/form-a-multi-academy-trust" ,PageNumber, countResponse.Value, PageSize); + } + + public async Task OnGetMovePage() + { + await OnGet(); + } + } +} \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml new file mode 100644 index 00000000..93d8d749 --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml @@ -0,0 +1,50 @@ +@page "/projects/all/in-progress/transfers" +@using Dfe.Complete.Constants; +@using Dfe.Complete.Domain.Enums +@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.TransferProjectsInProgressModel +@{ + Layout = "List/_AllProjectsLayout"; + ViewData["Title"] = "All projects in progress"; +} + +
    + + + + + + + + + + + + + + @foreach(var project in Model.Projects) + { + string projectSummaryUrl; + + if (project.ProjectType == ProjectType.Conversion) + { + projectSummaryUrl = string.Format(RouteConstants.ConversionProjectTaskList, project.ProjectId); + } + else + { + projectSummaryUrl = string.Format(RouteConstants.TransferProjectTaskList, project.ProjectId); + } + + + + + + + + + + } + +
    School or academyURNConversion or transfer dateProject typeForm a MAT project?Assigned to
    + @project.EstablishmentName + @project.Urn.Value@project.ConversionOrTransferDate.ToDateMonthYearString()@project.ProjectType.ToString()@project.IsFormAMAT.ToYesNoString()@project.AssignedTo?.FirstName @project.AssignedTo?.LastName
    +
    \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs new file mode 100644 index 00000000..77c47b0c --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs @@ -0,0 +1,36 @@ +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Application.Projects.Queries.CountProjects; +using Dfe.Complete.Application.Projects.Queries.ListAllProjects; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Pages.Pagination; +using MediatR; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Dfe.Complete.Pages.Projects.List.ProjectsInProgress +{ + public class TransferProjectsInProgressModel(ISender sender) : AllProjectsViewModel + { + + public List Projects { get; set; } = default!; + + public async Task OnGet() + { + //TODO: Review pagination logic + var listProjectQuery = new ListAllProjectsQuery(ProjectState.Active, ProjectType.Transfer, null, PageNumber-1, PageSize); + + var response = await sender.Send(listProjectQuery); + Projects = response.Value?.ToList() ?? []; + + var countProjectQuery = new CountProjectQuery(ProjectState.Active, ProjectType.Transfer, null); + var countResponse = await sender.Send(countProjectQuery); + + Pagination = new PaginationModel("/projects/all/in-progress/transfers" ,PageNumber, countResponse.Value, PageSize); + } + + public async Task OnGetMovePage() + { + await OnGet(); + } + } +} \ No newline at end of file From f148f823626001925ecb77050a511a203a5154fa Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 17 Jan 2025 11:53:11 +0000 Subject: [PATCH 08/47] Update startup imports --- src/Api/Dfe.Complete.Api/Program.cs | 1 + src/Frontend/Dfe.Complete/Startup.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Api/Dfe.Complete.Api/Program.cs b/src/Api/Dfe.Complete.Api/Program.cs index 12e8cef9..4b843fcc 100644 --- a/src/Api/Dfe.Complete.Api/Program.cs +++ b/src/Api/Dfe.Complete.Api/Program.cs @@ -11,6 +11,7 @@ using System.Text.Json.Serialization; using Asp.Versioning; using Asp.Versioning.ApiExplorer; +using Dfe.Complete.Infrastructure; using DfE.CoreLibs.Http.Middlewares.CorrelationId; using DfE.CoreLibs.Http.Interfaces; diff --git a/src/Frontend/Dfe.Complete/Startup.cs b/src/Frontend/Dfe.Complete/Startup.cs index 3b9263ee..f7c338f8 100644 --- a/src/Frontend/Dfe.Complete/Startup.cs +++ b/src/Frontend/Dfe.Complete/Startup.cs @@ -2,7 +2,6 @@ using Dfe.Complete.Authorization; using Dfe.Complete.Configuration; using Dfe.Complete.Security; -using Dfe.Complete.Services; using Dfe.Complete.StartupConfiguration; using DfE.CoreLibs.Security.Authorization; using GovUk.Frontend.AspNetCore; @@ -14,6 +13,7 @@ using Microsoft.Identity.Web; using Microsoft.Identity.Web.UI; using System.Security.Claims; +using Dfe.Complete.Infrastructure; namespace Dfe.Complete; From f05a2b5d03166f04711487f07db2559134392aad Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 17 Jan 2025 11:53:45 +0000 Subject: [PATCH 09/47] Update GiasEstablishment model to inherit from the correct classes --- src/Core/Dfe.Complete.Domain/Entities/GiasEstablishment.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Core/Dfe.Complete.Domain/Entities/GiasEstablishment.cs b/src/Core/Dfe.Complete.Domain/Entities/GiasEstablishment.cs index c492915d..2fa1b669 100644 --- a/src/Core/Dfe.Complete.Domain/Entities/GiasEstablishment.cs +++ b/src/Core/Dfe.Complete.Domain/Entities/GiasEstablishment.cs @@ -1,8 +1,9 @@ -using Dfe.Complete.Domain.ValueObjects; +using Dfe.Complete.Domain.Common; +using Dfe.Complete.Domain.ValueObjects; -namespace Dfe.Complete.Infrastructure.Models; +namespace Dfe.Complete.Domain.Entities; -public class GiasEstablishment +public class GiasEstablishment : BaseAggregateRoot, IEntity { public GiasEstablishmentId Id { get; set; } From 05756bccda883aaa8dc3bf240f1c6331b0f50c04 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 17 Jan 2025 11:54:09 +0000 Subject: [PATCH 10/47] Update the base path so that migrations will function correctly --- .../Database/GenericDbContextFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Dfe.Complete.Infrastructure/Database/GenericDbContextFactory.cs b/src/Core/Dfe.Complete.Infrastructure/Database/GenericDbContextFactory.cs index 29923a68..b77f8667 100644 --- a/src/Core/Dfe.Complete.Infrastructure/Database/GenericDbContextFactory.cs +++ b/src/Core/Dfe.Complete.Infrastructure/Database/GenericDbContextFactory.cs @@ -10,7 +10,7 @@ public class GenericDbContextFactory : IDesignTimeDbContextFactory Date: Mon, 20 Jan 2025 11:17:08 +0000 Subject: [PATCH 11/47] Csv File generator implemented --- .../Common/Models/ConversionCsvModel.cs | 80 +++++ .../CsvExport/CSVFileContentGenerator.cs | 12 +- .../Conversion/ConversionHeaderGenerator.cs | 17 + .../Conversion/ConversionRowGenerator.cs | 162 +++++++++ .../Services/CsvExport/IHeaderGenerator.cs | 7 + .../Services/CsvExport/IRowGenerator.cs | 7 + .../CsvExport/CSVFileContentGeneratorTests.cs | 4 +- .../Conversion/ConversionRowGeneratorTests.cs | 319 ++++++++++++++++++ 8 files changed, 597 insertions(+), 11 deletions(-) create mode 100644 src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionHeaderGenerator.cs create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/IHeaderGenerator.cs create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/IRowGenerator.cs create mode 100644 src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs diff --git a/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs b/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs new file mode 100644 index 00000000..081c14be --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dfe.Complete.Application.Common.Models +{ + public class ConversionCsvModel + { + public string? SchoolName { get; set; } + public int SchoolUrn { get; set; } + public bool FormAMat { get; set; } + public string? AcademyName { get; set; } + public int? AcademyUrn { get; set; } + public string? AcademyDfENumber { get; set; } + public string IncomingTrustName { get; set; } + public string LocalAuthority { get; set; } + public string Region { get; set; } + public string Diocese { get; set; } + public DateOnly ProvisionalConversionDate { get; set; } + public DateOnly ConfirmedConversionDate { get; set; } + public string? AcademyOrderType { get; set; } + public bool TwoRequiresImprovement { get; set; } + public DateOnly AdvisoryBoardDate { get; set; } + public string? AdvisoryBoardConditions { get; set; } + public string RiskProtectionArrangement { get; set; } + public string ReasonForCommercialInsurance { get; set; } + public bool AllConditionsMet { get; set; } + public bool CompletedGrantPaymentCertificateReceived { get; set; } + public string SchoolType { get; set; } + public string SchoolAgeRange { get; set; } + public string SchoolPhase { get; set; } + public int ProposedCapacityForPupilsInReceptionToYear6 { get; set; } + public int ProposedCapacityForPupilsInYears7To11 { get; set; } + public int ProposedCapacityForStudentsInYear12OrAbove { get; set; } + public string SchoolAddress1 { get; set; } + public string SchoolAddress2 { get; set; } + public string SchoolAddress3 { get; set; } + public string SchoolTown { get; set; } + public string SchoolCounty { get; set; } + public string SchoolPostcode { get; set; } + public string SchoolSharepointFolder { get; set; } + public string ConversionType { get; set; } + public int IncomingTrustUKPRN { get; set; } + public string IncomingTrustGroupIdentifier { get; set; } + public string IncomingTrustCompaniesHouseNumber { get; set; } + public string IncomingTrustAddress1 { get; set; } + public string IncomingTrustAddress2 { get; set; } + public string IncomingTrustAddress3 { get; set; } + public string IncomingTrustAddressTown { get; set; } + public string IncomingTrustAddressCounty { get; set; } + public string IncomingTrustAddressPostcode { get; set; } + public string IncomingTrustSharepointLink { get; set; } + public string ProjectCreatedBy { get; set; } + public string ProjectCreatedByEmailAddress { get; set; } + public string AssignedToName { get; set; } + public string TeamManagingTheProject { get; set; } + public string ProjectMainContactName { get; set; } + public string HeadteacherName { get; set; } + public string HeadteacherRole { get; set; } + public string HeadteacherEmail { get; set; } + public string LocalAuthorityContactName { get; set; } + public string LocalAuthorityContactEmail { get; set; } + public string PrimaryContactForIncomingTrustName { get; set; } + public string PrimaryContactForIncomingTrustEmail { get; set; } + public string PrimaryContactForOutgoingTrustName { get; set; } + public string PrimaryContactForOutgoingTrustEmail { get; set; } + public string IncomingTrustCEOName { get; set; } + public string IncomingTrustCEORole { get; set; } + public string IncomingTrustCEOEmail { get; set; } + public string SolicitorContactName { get; set; } + public string SolicitorContactEmail { get; set; } + public string DioceseContactName { get; set; } + public string DioceseContactEmail { get; set; } + public string DirectorOfChildServicesName { get; set; } + public string DirectorOfChildServicesEmail { get; set; } + public string DirectorOfChildServicesRole { get; set; } + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/CSVFileContentGenerator.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/CSVFileContentGenerator.cs index 22a0ab7d..258b0b94 100644 --- a/src/Core/Dfe.Complete.Application/Services/CsvExport/CSVFileContentGenerator.cs +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/CSVFileContentGenerator.cs @@ -6,18 +6,12 @@ namespace Dfe.Complete.Application.Services.CsvExport { - public interface HeaderGenerator + public interface ICSVFileContentGenerator { - string GenerateHeader(); + string Generate(IEnumerable models); } - public interface RowGenerator - { - string GenerateRow(TModel model); - } - - - public class CSVFileContentGenerator(HeaderGenerator header, RowGenerator rowGenerator) + public class CSVFileContentGenerator(IHeaderGenerator header, IRowGenerator rowGenerator) : ICSVFileContentGenerator { public string Generate(IEnumerable models) { diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionHeaderGenerator.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionHeaderGenerator.cs new file mode 100644 index 00000000..b7cc3f47 --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionHeaderGenerator.cs @@ -0,0 +1,17 @@ +using Dfe.Complete.Application.Common.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dfe.Complete.Application.Services.CsvExport.Conversion +{ + public class ConversionHeaderGenerator : IHeaderGenerator + { + public string GenerateHeader() + { + return "School name,School URN,Project type,Academy name,Academy URN,Academy DfE number/LAESTAB,Incoming trust name,Local authority,Region,Diocese,Provisional conversion date,Confirmed conversion date,Academy order type,2RI (Two Requires Improvement),Advisory board date,Advisory board conditions,Risk protection arrangement,Reason for commercial insurance,All conditions met,Completed grant payment certificate received,School type,School age range,School phase,Proposed capacity for pupils in reception to year 6,Proposed capacity for pupils in years 7 to 11,Proposed capacity for students in year 12 or above,School address 1,School address 2,School address 3,School town,School county,School postcode,School sharepoint folder,Conversion type,Incoming trust UKPRN,Incoming trust group identifier,Incoming trust companies house number,Incoming trust address 1,Incoming trust address 2,Incoming trust address 3,Incoming trust address town,Incoming trust address county,Incoming trust address postcode,Incoming trust sharepoint link,Project created by name,Project created by email address,Assigned to name,Team managing the project,Project main contact name,Headteacher name,Headteacher role,Headteacher email,Local authority contact name,Local authority contact email,Primary contact for incoming trust name,Primary contact for incoming trust email,Primary contact for outgoing trust name,Primary contact for outgoing trust email,Incoming trust CEO name,Incoming trust CEO role,Incoming trust CEO email,Solicitor contact name,Solicitor contact email,Diocese contact name,Diocese contact email,Director of child services name,Director of child services email,Director of child services role"; + } + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs new file mode 100644 index 00000000..b9678f3b --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs @@ -0,0 +1,162 @@ +using Dfe.Complete.Application.Common.Models; +using System; +using System.Text; + +namespace Dfe.Complete.Application.Services.CsvExport.Conversion +{ + public class ConversionRowGenerator : IRowGenerator + { + public string GenerateRow(ConversionCsvModel model) + { + var row = new StringBuilder(); + + row.Append(model.SchoolName); + row.Append(","); + row.Append(model.SchoolUrn); + row.Append(","); + row.Append(model.FormAMat ? "form a MAT": "join a MAT"); + row.Append(","); + row.Append(BlankIfNull(model.AcademyName)); + row.Append(","); + row.Append(BlankIfNull(model.AcademyUrn)); + row.Append(","); + row.Append(model.AcademyDfENumber); + row.Append(","); + row.Append(model.IncomingTrustName); + row.Append(","); + row.Append(model.LocalAuthority); + row.Append(","); + row.Append(model.Region); + row.Append(","); + row.Append(model.Diocese); + row.Append(","); + row.Append(model.ProvisionalConversionDate); + row.Append(","); + row.Append(model.ConfirmedConversionDate); + row.Append(","); + row.Append(model.AcademyOrderType); + row.Append(","); + row.Append(model.TwoRequiresImprovement); + row.Append(","); + row.Append(model.AdvisoryBoardDate); + row.Append(","); + row.Append(model.AdvisoryBoardConditions); + row.Append(","); + row.Append(model.RiskProtectionArrangement); + row.Append(","); + row.Append(model.ReasonForCommercialInsurance); + row.Append(","); + row.Append(model.AllConditionsMet); + row.Append(","); + row.Append(model.CompletedGrantPaymentCertificateReceived); + row.Append(","); + row.Append(model.SchoolType); + row.Append(","); + row.Append(model.SchoolAgeRange); + row.Append(","); + row.Append(model.SchoolPhase); + row.Append(","); + row.Append(model.ProposedCapacityForPupilsInReceptionToYear6); + row.Append(","); + row.Append(model.ProposedCapacityForPupilsInYears7To11); + row.Append(","); + row.Append(model.ProposedCapacityForStudentsInYear12OrAbove); + row.Append(","); + row.Append(model.SchoolAddress1); + row.Append(","); + row.Append(model.SchoolAddress2); + row.Append(","); + row.Append(model.SchoolAddress3); + row.Append(","); + row.Append(model.SchoolTown); + row.Append(","); + row.Append(model.SchoolCounty); + row.Append(","); + row.Append(model.SchoolPostcode); + row.Append(","); + row.Append(model.SchoolSharepointFolder); + row.Append(","); + row.Append(model.ConversionType); + row.Append(","); + row.Append(model.IncomingTrustUKPRN); + row.Append(","); + row.Append(model.IncomingTrustGroupIdentifier); + row.Append(","); + row.Append(model.IncomingTrustCompaniesHouseNumber); + row.Append(","); + row.Append(model.IncomingTrustAddress1); + row.Append(","); + row.Append(model.IncomingTrustAddress2); + row.Append(","); + row.Append(model.IncomingTrustAddress3); + row.Append(","); + row.Append(model.IncomingTrustAddressTown); + row.Append(","); + row.Append(model.IncomingTrustAddressCounty); + row.Append(","); + row.Append(model.IncomingTrustAddressPostcode); + row.Append(","); + row.Append(model.IncomingTrustSharepointLink); + row.Append(","); + row.Append(model.ProjectCreatedBy); + row.Append(","); + row.Append(model.ProjectCreatedByEmailAddress); + row.Append(","); + row.Append(model.AssignedToName); + row.Append(","); + row.Append(model.TeamManagingTheProject); + row.Append(","); + row.Append(model.ProjectMainContactName); + row.Append(","); + row.Append(model.HeadteacherName); + row.Append(","); + row.Append(model.HeadteacherRole); + row.Append(","); + row.Append(model.HeadteacherEmail); + row.Append(","); + row.Append(model.LocalAuthorityContactName); + row.Append(","); + row.Append(model.LocalAuthorityContactEmail); + row.Append(","); + row.Append(model.PrimaryContactForIncomingTrustName); + row.Append(","); + row.Append(model.PrimaryContactForIncomingTrustEmail); + row.Append(","); + row.Append(model.PrimaryContactForOutgoingTrustName); + row.Append(","); + row.Append(model.PrimaryContactForOutgoingTrustEmail); + row.Append(","); + row.Append(model.IncomingTrustCEOName); + row.Append(","); + row.Append(model.IncomingTrustCEORole); + row.Append(","); + row.Append(model.IncomingTrustCEOEmail); + row.Append(","); + row.Append(model.SolicitorContactName); + row.Append(","); + row.Append(model.SolicitorContactEmail); + row.Append(","); + row.Append(model.DioceseContactName); + row.Append(","); + row.Append(model.DioceseContactEmail); + row.Append(","); + row.Append(model.DirectorOfChildServicesName); + row.Append(","); + row.Append(model.DirectorOfChildServicesEmail); + row.Append(","); + row.Append(model.DirectorOfChildServicesRole); + + return row.ToString(); + } + + private string BlankIfNull(string? value) + { + return value ?? string.Empty; + } + + private string BlankIfNull(int? value) + { + return value == null ? string.Empty: value.Value.ToString(); + } + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/IHeaderGenerator.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/IHeaderGenerator.cs new file mode 100644 index 00000000..678ceb0d --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/IHeaderGenerator.cs @@ -0,0 +1,7 @@ +namespace Dfe.Complete.Application.Services.CsvExport +{ + public interface IHeaderGenerator + { + string GenerateHeader(); + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/IRowGenerator.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/IRowGenerator.cs new file mode 100644 index 00000000..d50b888f --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/IRowGenerator.cs @@ -0,0 +1,7 @@ +namespace Dfe.Complete.Application.Services.CsvExport +{ + public interface IRowGenerator + { + public string GenerateRow(TModel model); + } +} diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/CSVFileContentGeneratorTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/CSVFileContentGeneratorTests.cs index 7b5131ad..3590bf6c 100644 --- a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/CSVFileContentGeneratorTests.cs +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/CSVFileContentGeneratorTests.cs @@ -4,7 +4,7 @@ namespace Dfe.Complete.Application.Tests.Services.CsvExport { internal record TestModel(string SchoolName, int SchoolUrn, string ProjectType, string AcademyName, int AcademyUrn); - internal class TestHeaderGenerator : HeaderGenerator + internal class TestHeaderGenerator : IHeaderGenerator { public string GenerateHeader() { @@ -12,7 +12,7 @@ public string GenerateHeader() } } - internal class TestRowGenerator : RowGenerator + internal class TestRowGenerator : IRowGenerator { public string GenerateRow(TestModel model) { diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs new file mode 100644 index 00000000..c86e1ed6 --- /dev/null +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs @@ -0,0 +1,319 @@ +using Dfe.Complete.Application.Common.Models; +using Dfe.Complete.Application.Services.CsvExport.Conversion; +namespace Dfe.Complete.Application.Tests.Services.CsvExport.Conversion +{ + public class ConversionRowGeneratorTests + { + [Fact] + public void RowGeneratesAccountsForBlankData() + { + var model = new ConversionCsvModel() + { + AllConditionsMet = true, + AcademyDfENumber = null, + AcademyName = null, + AcademyOrderType = null, + AcademyUrn = null, + AdvisoryBoardConditions = null, + AdvisoryBoardDate = new DateOnly(2024, 3, 2), + CompletedGrantPaymentCertificateReceived = true, + ConfirmedConversionDate = new DateOnly(2024, 4, 3), + ConversionType = "ConversionType", + Diocese = "Diocese", + HeadteacherName = "HeadteacherName", + IncomingTrustAddress1 = "IncomingTrustAddress1", + IncomingTrustAddress2 = "IncomingTrustAddress2", + IncomingTrustAddress3 = "IncomingTrustAddress3", + IncomingTrustAddressCounty = "IncomingTrustAddressCounty", + IncomingTrustAddressPostcode = "IncomingTrustAddressPostcode", + IncomingTrustAddressTown = "IncomingTrustAddressTown", + IncomingTrustCompaniesHouseNumber = "IncomingTrustCompaniesHouseNumber", + IncomingTrustGroupIdentifier = "IncomingTrustGroupIdentifier", + IncomingTrustName = "IncomingTrustName", + IncomingTrustSharepointLink = "IncomingTrustSharepointLink", + IncomingTrustUKPRN = 12345647, + AssignedToName = "AssignedToName", + DioceseContactEmail = "DioceseContactEmail", + DioceseContactName = "DioceseContactName", + DirectorOfChildServicesEmail = "DirectorOfChildServicesEmail", + DirectorOfChildServicesName = "DirectorOfChildServicesName", + DirectorOfChildServicesRole = "DirectorOfChildServicesRole", + HeadteacherEmail = "HeadteacherEmail", + HeadteacherRole = "HeadteacherRole", + IncomingTrustCEOEmail = "IncomingTrustCEOEmail", + IncomingTrustCEOName = "IncomingTrustCEOName", + IncomingTrustCEORole = "IncomingTrustCEORole", + LocalAuthority = "LocalAuthority", + LocalAuthorityContactEmail = "LocalAuthorityContactEmail", + LocalAuthorityContactName = "LocalAuthorityContactName", + PrimaryContactForIncomingTrustEmail = "PrimaryContactForIncomingTrustEmail", + PrimaryContactForIncomingTrustName = "PrimaryContactForIncomingTrustName", + PrimaryContactForOutgoingTrustEmail = "PrimaryContactForOutgoingTrustEmail", + PrimaryContactForOutgoingTrustName = "PrimaryContactForOutgoingTrustName", + ProjectCreatedBy = "ProjectCreatedBy", + ProjectCreatedByEmailAddress = "ProjectCreatedByEmailAddress", + ProjectMainContactName = "ProjectMainContactName", + FormAMat = false, + ProposedCapacityForPupilsInReceptionToYear6 = 123, + ProposedCapacityForPupilsInYears7To11 = 456, + ProposedCapacityForStudentsInYear12OrAbove = 789, + ProvisionalConversionDate = new DateOnly(2024, 5, 6), + ReasonForCommercialInsurance = "ReasonForCommercialInsurance", + Region = "Region", + RiskProtectionArrangement = "RiskProtectionArrangement", + SchoolAddress1 = "SchoolAddress1", + SchoolAddress2 = "SchoolAddress2", + SchoolAddress3 = "SchoolAddress3", + SchoolAgeRange = "SchoolAgeRange", + SchoolCounty = "SchoolCounty", + SchoolName = "SchoolName", + SchoolPhase = "SchoolPhase", + SchoolPostcode = "SchoolPostcode", + SchoolSharepointFolder = "SchoolSharepointFolder", + SchoolTown = "SchoolTown", + SchoolType = "SchoolType", + SolicitorContactEmail = "SolicitorContactEmail", + SchoolUrn = 12345, + SolicitorContactName = "SolicitorContactName", + TeamManagingTheProject = "TeamManagingTheProject", + TwoRequiresImprovement = true + }; + + var generator = new ConversionRowGenerator(); + + generator.GenerateRow(model); + + var result = generator.GenerateRow(model).Split(","); + + Assert.Equal("SchoolName", result[0]); + Assert.Equal("12345", result[1]); + Assert.Equal("ProjectType", result[2]); + Assert.Equal("AcademyName", result[3]); + Assert.Equal("54321", result[4]); + Assert.Equal("AcademyDfENumber", result[5]); + Assert.Equal("IncomingTrustName", result[6]); + Assert.Equal("LocalAuthority", result[7]); + Assert.Equal("Region", result[8]); + Assert.Equal("Diocese", result[9]); + Assert.Equal("06/05/2024", result[10]); + Assert.Equal("03/04/2024", result[11]); + Assert.Equal("AcademyOrderType", result[12]); + Assert.Equal("True", result[13]); + Assert.Equal("02/03/2024", result[14]); + Assert.Equal("AdvisoryBoardConditions", result[15]); + Assert.Equal("RiskProtectionArrangement", result[16]); + Assert.Equal("ReasonForCommercialInsurance", result[17]); + Assert.Equal("True", result[18]); + Assert.Equal("True", result[19]); + Assert.Equal("SchoolType", result[20]); + Assert.Equal("SchoolAgeRange", result[21]); + Assert.Equal("SchoolPhase", result[22]); + Assert.Equal("123", result[23]); + Assert.Equal("456", result[24]); + Assert.Equal("789", result[25]); + Assert.Equal("SchoolAddress1", result[26]); + Assert.Equal("SchoolAddress2", result[27]); + Assert.Equal("SchoolAddress3", result[28]); + Assert.Equal("SchoolTown", result[29]); + Assert.Equal("SchoolCounty", result[30]); + Assert.Equal("SchoolPostcode", result[31]); + Assert.Equal("SchoolSharepointFolder", result[32]); + Assert.Equal("ConversionType", result[33]); + Assert.Equal("12345647", result[34]); + Assert.Equal("IncomingTrustGroupIdentifier", result[35]); + Assert.Equal("IncomingTrustCompaniesHouseNumber", result[36]); + Assert.Equal("IncomingTrustAddress1", result[37]); + Assert.Equal("IncomingTrustAddress2", result[38]); + Assert.Equal("IncomingTrustAddress3", result[39]); + Assert.Equal("IncomingTrustAddressTown", result[40]); + Assert.Equal("IncomingTrustAddressCounty", result[41]); + Assert.Equal("IncomingTrustAddressPostcode", result[42]); + Assert.Equal("IncomingTrustSharepointLink", result[43]); + Assert.Equal("ProjectCreatedBy", result[44]); + Assert.Equal("ProjectCreatedByEmailAddress", result[45]); + Assert.Equal("AssignedToName", result[46]); + Assert.Equal("TeamManagingTheProject", result[47]); + Assert.Equal("ProjectMainContactName", result[48]); + Assert.Equal("HeadteacherName", result[49]); + Assert.Equal("HeadteacherRole", result[50]); + Assert.Equal("HeadteacherEmail", result[51]); + Assert.Equal("LocalAuthorityContactName", result[52]); + Assert.Equal("LocalAuthorityContactEmail", result[53]); + Assert.Equal("PrimaryContactForIncomingTrustName", result[54]); + Assert.Equal("PrimaryContactForIncomingTrustEmail", result[55]); + Assert.Equal("PrimaryContactForOutgoingTrustName", result[56]); + Assert.Equal("PrimaryContactForOutgoingTrustEmail", result[57]); + Assert.Equal("IncomingTrustCEOName", result[58]); + Assert.Equal("IncomingTrustCEORole", result[59]); + Assert.Equal("IncomingTrustCEOEmail", result[60]); + Assert.Equal("SolicitorContactName", result[61]); + Assert.Equal("SolicitorContactEmail", result[62]); + Assert.Equal("DioceseContactName", result[63]); + Assert.Equal("DioceseContactEmail", result[64]); + Assert.Equal("DirectorOfChildServicesName", result[65]); + Assert.Equal("DirectorOfChildServicesEmail", result[66]); + Assert.Equal("DirectorOfChildServicesRole", result[67]); + + + + + + } + + [Fact] + public void RowGeneratesBasedOnModel() + { + var model = new ConversionCsvModel() + { + AllConditionsMet = true, + AcademyDfENumber = "AcademyDfENumber", + AcademyName = "AcademyName", + AcademyOrderType = "AcademyOrderType", + AcademyUrn = 54321, + AdvisoryBoardConditions = "AdvisoryBoardConditions", + AdvisoryBoardDate = new DateOnly(2024, 3, 2), + CompletedGrantPaymentCertificateReceived = true, + ConfirmedConversionDate = new DateOnly(2024, 4, 3), + ConversionType = "ConversionType", + Diocese = "Diocese", + HeadteacherName = "HeadteacherName", + IncomingTrustAddress1 = "IncomingTrustAddress1", + IncomingTrustAddress2 = "IncomingTrustAddress2", + IncomingTrustAddress3 = "IncomingTrustAddress3", + IncomingTrustAddressCounty = "IncomingTrustAddressCounty", + IncomingTrustAddressPostcode = "IncomingTrustAddressPostcode", + IncomingTrustAddressTown = "IncomingTrustAddressTown", + IncomingTrustCompaniesHouseNumber = "IncomingTrustCompaniesHouseNumber", + IncomingTrustGroupIdentifier = "IncomingTrustGroupIdentifier", + IncomingTrustName = "IncomingTrustName", + IncomingTrustSharepointLink = "IncomingTrustSharepointLink", + IncomingTrustUKPRN = 12345647, + AssignedToName = "AssignedToName", + DioceseContactEmail = "DioceseContactEmail", + DioceseContactName = "DioceseContactName", + DirectorOfChildServicesEmail = "DirectorOfChildServicesEmail", + DirectorOfChildServicesName = "DirectorOfChildServicesName", + DirectorOfChildServicesRole = "DirectorOfChildServicesRole", + HeadteacherEmail = "HeadteacherEmail", + HeadteacherRole = "HeadteacherRole", + IncomingTrustCEOEmail = "IncomingTrustCEOEmail", + IncomingTrustCEOName = "IncomingTrustCEOName", + IncomingTrustCEORole = "IncomingTrustCEORole", + LocalAuthority = "LocalAuthority", + LocalAuthorityContactEmail = "LocalAuthorityContactEmail", + LocalAuthorityContactName = "LocalAuthorityContactName", + PrimaryContactForIncomingTrustEmail = "PrimaryContactForIncomingTrustEmail", + PrimaryContactForIncomingTrustName = "PrimaryContactForIncomingTrustName", + PrimaryContactForOutgoingTrustEmail = "PrimaryContactForOutgoingTrustEmail", + PrimaryContactForOutgoingTrustName = "PrimaryContactForOutgoingTrustName", + ProjectCreatedBy = "ProjectCreatedBy", + ProjectCreatedByEmailAddress = "ProjectCreatedByEmailAddress", + ProjectMainContactName = "ProjectMainContactName", + FormAMat = true, + ProposedCapacityForPupilsInReceptionToYear6 = 123, + ProposedCapacityForPupilsInYears7To11 = 456, + ProposedCapacityForStudentsInYear12OrAbove = 789, + ProvisionalConversionDate = new DateOnly(2024, 5, 6), + ReasonForCommercialInsurance = "ReasonForCommercialInsurance", + Region = "Region", + RiskProtectionArrangement = "RiskProtectionArrangement", + SchoolAddress1 = "SchoolAddress1", + SchoolAddress2 = "SchoolAddress2", + SchoolAddress3 = "SchoolAddress3", + SchoolAgeRange = "SchoolAgeRange", + SchoolCounty = "SchoolCounty", + SchoolName = "SchoolName", + SchoolPhase = "SchoolPhase", + SchoolPostcode = "SchoolPostcode", + SchoolSharepointFolder = "SchoolSharepointFolder", + SchoolTown = "SchoolTown", + SchoolType = "SchoolType", + SolicitorContactEmail = "SolicitorContactEmail", + SchoolUrn = 12345, + SolicitorContactName = "SolicitorContactName", + TeamManagingTheProject = "TeamManagingTheProject", + TwoRequiresImprovement = true + }; + + var generator = new ConversionRowGenerator(); + + generator.GenerateRow(model); + + var result = generator.GenerateRow(model).Split(","); + + Assert.Equal("SchoolName", result[0]); + Assert.Equal("12345", result[1]); + Assert.Equal("ProjectType", result[2]); + Assert.Equal("AcademyName", result[3]); + Assert.Equal("54321", result[4]); + Assert.Equal("AcademyDfENumber", result[5]); + Assert.Equal("IncomingTrustName", result[6]); + Assert.Equal("LocalAuthority", result[7]); + Assert.Equal("Region", result[8]); + Assert.Equal("Diocese", result[9]); + Assert.Equal("06/05/2024", result[10]); + Assert.Equal("03/04/2024", result[11]); + Assert.Equal("AcademyOrderType", result[12]); + Assert.Equal("True", result[13]); + Assert.Equal("02/03/2024", result[14]); + Assert.Equal("AdvisoryBoardConditions", result[15]); + Assert.Equal("RiskProtectionArrangement", result[16]); + Assert.Equal("ReasonForCommercialInsurance", result[17]); + Assert.Equal("True", result[18]); + Assert.Equal("True", result[19]); + Assert.Equal("SchoolType", result[20]); + Assert.Equal("SchoolAgeRange", result[21]); + Assert.Equal("SchoolPhase", result[22]); + Assert.Equal("123", result[23]); + Assert.Equal("456", result[24]); + Assert.Equal("789", result[25]); + Assert.Equal("SchoolAddress1", result[26]); + Assert.Equal("SchoolAddress2", result[27]); + Assert.Equal("SchoolAddress3", result[28]); + Assert.Equal("SchoolTown", result[29]); + Assert.Equal("SchoolCounty", result[30]); + Assert.Equal("SchoolPostcode", result[31]); + Assert.Equal("SchoolSharepointFolder", result[32]); + Assert.Equal("ConversionType", result[33]); + Assert.Equal("12345647", result[34]); + Assert.Equal("IncomingTrustGroupIdentifier", result[35]); + Assert.Equal("IncomingTrustCompaniesHouseNumber", result[36]); + Assert.Equal("IncomingTrustAddress1", result[37]); + Assert.Equal("IncomingTrustAddress2", result[38]); + Assert.Equal("IncomingTrustAddress3", result[39]); + Assert.Equal("IncomingTrustAddressTown", result[40]); + Assert.Equal("IncomingTrustAddressCounty", result[41]); + Assert.Equal("IncomingTrustAddressPostcode", result[42]); + Assert.Equal("IncomingTrustSharepointLink", result[43]); + Assert.Equal("ProjectCreatedBy", result[44]); + Assert.Equal("ProjectCreatedByEmailAddress", result[45]); + Assert.Equal("AssignedToName", result[46]); + Assert.Equal("TeamManagingTheProject", result[47]); + Assert.Equal("ProjectMainContactName", result[48]); + Assert.Equal("HeadteacherName", result[49]); + Assert.Equal("HeadteacherRole", result[50]); + Assert.Equal("HeadteacherEmail", result[51]); + Assert.Equal("LocalAuthorityContactName", result[52]); + Assert.Equal("LocalAuthorityContactEmail", result[53]); + Assert.Equal("PrimaryContactForIncomingTrustName", result[54]); + Assert.Equal("PrimaryContactForIncomingTrustEmail", result[55]); + Assert.Equal("PrimaryContactForOutgoingTrustName", result[56]); + Assert.Equal("PrimaryContactForOutgoingTrustEmail", result[57]); + Assert.Equal("IncomingTrustCEOName", result[58]); + Assert.Equal("IncomingTrustCEORole", result[59]); + Assert.Equal("IncomingTrustCEOEmail", result[60]); + Assert.Equal("SolicitorContactName", result[61]); + Assert.Equal("SolicitorContactEmail", result[62]); + Assert.Equal("DioceseContactName", result[63]); + Assert.Equal("DioceseContactEmail", result[64]); + Assert.Equal("DirectorOfChildServicesName", result[65]); + Assert.Equal("DirectorOfChildServicesEmail", result[66]); + Assert.Equal("DirectorOfChildServicesRole", result[67]); + + + + + + } + } +} From 1c0dd945f8e1f4728191750d4938b0901ba297ee Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 20 Jan 2025 14:46:10 +0000 Subject: [PATCH 12/47] Change layouts to add titles and main navigation for all projects page --- .../Projects/List/AllProjectsViewModel.cs | 14 ---- .../AllProjectsInProgress.cshtml | 26 +++---- .../AllProjectsInProgress.cshtml.cs | 2 +- .../ConversionProjectsInProgress.cshtml | 14 ++-- .../ConversionProjectsInProgress.cshtml.cs | 2 +- .../FormAMatProjectsInProgress.cshtml | 50 ------------- .../FormAMatProjectsInProgress.cshtml.cs | 36 ---------- .../ProjectsInProgressViewModel.cs | 22 ++++++ .../TransferProjectsInProgress.cshtml | 15 ++-- .../TransferProjectsInProgress.cshtml.cs | 2 +- .../_ProjectsInProgressLayout.cshtml | 34 +++++++++ .../Projects/List/_AllProjectsLayout.cshtml | 72 +++++++++++-------- 12 files changed, 130 insertions(+), 159 deletions(-) delete mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/AllProjectsViewModel.cs delete mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml delete mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml.cs create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModel.cs create mode 100644 src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/_ProjectsInProgressLayout.cshtml diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/AllProjectsViewModel.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/AllProjectsViewModel.cs deleted file mode 100644 index 070b53f2..00000000 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/AllProjectsViewModel.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Dfe.Complete.Pages.Pagination; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace Dfe.Complete.Pages.Projects.List; - -public class AllProjectsViewModel : PageModel -{ - [BindProperty(SupportsGet = true)] public int PageNumber { get; set; } = 1; - - public PaginationModel Pagination { get; set; } = default!; - - internal int PageSize = 20; -} \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml index c8f7d4cc..81b46184 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml @@ -1,11 +1,16 @@ @page "/projects/all/in-progress/all" -@using Dfe.Complete.Constants; -@using Dfe.Complete.Domain.Enums -@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.AllProjectsInProgressViewModel +@using Dfe.Complete.Pages.Projects.List +@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.ProjectsInProgressInProgressViewModel @{ - Layout = "List/_AllProjectsLayout"; + Layout = "List/ProjectsInProgress/_ProjectsInProgressLayout"; ViewData["Title"] = "All projects in progress"; } +@section PageHeading +{ +

    + All projects in progress +

    +}
    @@ -23,20 +28,9 @@ @foreach(var project in Model.Projects) { - string projectSummaryUrl; - - if (project.ProjectType == ProjectType.Conversion) - { - projectSummaryUrl = string.Format(RouteConstants.ConversionProjectTaskList, project.ProjectId); - } - else - { - projectSummaryUrl = string.Format(RouteConstants.TransferProjectTaskList, project.ProjectId); - } - diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs index 56cd7cf3..a9983ee1 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs @@ -7,7 +7,7 @@ namespace Dfe.Complete.Pages.Projects.List.ProjectsInProgress { - public class AllProjectsInProgressViewModel(ISender sender) : AllProjectsViewModel + public class ProjectsInProgressInProgressViewModel(ISender sender) : ProjectsInProgressViewModel { public List Projects { get; set; } = default!; diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml index fdd967f7..81c7f62e 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml @@ -1,11 +1,17 @@ @page "/projects/all/in-progress/conversions" @using Dfe.Complete.Constants; @using Dfe.Complete.Domain.Enums -@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.ConversionProjectsInProgressModel +@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.ConversionProjectsInProgressInProgressModel @{ - Layout = "List/_AllProjectsLayout"; + Layout = "List/ProjectsInProgress/_ProjectsInProgressLayout"; ViewData["Title"] = "All projects in progress"; } +@section PageHeading +{ +

    + Conversion projects in progress +

    +}
    - @project.EstablishmentName + @project.EstablishmentName @project.Urn.Value @project.ConversionOrTransferDate.ToDateMonthYearString()
    @@ -13,8 +19,7 @@ - - + @@ -40,7 +45,6 @@ - diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs index 95ccacdd..63caf3a7 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs @@ -9,7 +9,7 @@ namespace Dfe.Complete.Pages.Projects.List.ProjectsInProgress { - public class ConversionProjectsInProgressModel(ISender sender) : AllProjectsViewModel + public class ConversionProjectsInProgressInProgressModel(ISender sender) : ProjectsInProgressViewModel { public List Projects { get; set; } = default!; diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml deleted file mode 100644 index 97c5d279..00000000 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml +++ /dev/null @@ -1,50 +0,0 @@ -@page "/projects/all/in-progress/form-a-multi-academy-trust" -@using Dfe.Complete.Constants; -@using Dfe.Complete.Domain.Enums -@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.FormAMatProjectsInProgressModel -@{ - Layout = "List/_AllProjectsLayout"; - ViewData["Title"] = "All projects in progress"; -} - -
    -
    School or academy URNConversion or transfer dateProject typeConversion date Form a MAT project? Assigned to
    @project.Urn.Value @project.ConversionOrTransferDate.ToDateMonthYearString()@project.ProjectType.ToString() @project.IsFormAMAT.ToYesNoString() @project.AssignedTo?.FirstName @project.AssignedTo?.LastName
    - - - - - - - - - - - - - @foreach(var project in Model.Projects) - { - string projectSummaryUrl; - - if (project.ProjectType == ProjectType.Conversion) - { - projectSummaryUrl = string.Format(RouteConstants.ConversionProjectTaskList, project.ProjectId); - } - else - { - projectSummaryUrl = string.Format(RouteConstants.TransferProjectTaskList, project.ProjectId); - } - - - - - - - - - - } - -
    School or academyURNConversion or transfer dateProject typeForm a MAT project?Assigned to
    - @project.EstablishmentName - @project.Urn.Value@project.ConversionOrTransferDate.ToDateMonthYearString()@project.ProjectType.ToString()@project.IsFormAMAT.ToYesNoString()@project.AssignedTo?.FirstName @project.AssignedTo?.LastName
    -
    \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml.cs deleted file mode 100644 index 341bfa5e..00000000 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/FormAMatProjectsInProgress.cshtml.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Dfe.Complete.Application.Projects.Model; -using Dfe.Complete.Application.Projects.Queries.CountProjects; -using Dfe.Complete.Application.Projects.Queries.ListAllProjects; -using Dfe.Complete.Domain.Enums; -using Dfe.Complete.Pages.Pagination; -using MediatR; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.RazorPages; - -namespace Dfe.Complete.Pages.Projects.List.ProjectsInProgress -{ - public class FormAMatProjectsInProgressModel(ISender sender) : AllProjectsViewModel - { - - public List Projects { get; set; } = default!; - - public async Task OnGet() - { - //TODO: Review pagination logic - var listProjectQuery = new ListAllProjectsQuery(ProjectState.Active, null, true, PageNumber-1, PageSize); - - var response = await sender.Send(listProjectQuery); - Projects = response.Value?.ToList() ?? []; - - var countProjectQuery = new CountProjectQuery(ProjectState.Active, null, true); - var countResponse = await sender.Send(countProjectQuery); - - Pagination = new PaginationModel("/projects/all/in-progress/form-a-multi-academy-trust" ,PageNumber, countResponse.Value, PageSize); - } - - public async Task OnGetMovePage() - { - await OnGet(); - } - } -} \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModel.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModel.cs new file mode 100644 index 00000000..a6de5476 --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModel.cs @@ -0,0 +1,22 @@ +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Constants; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Pages.Pagination; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace Dfe.Complete.Pages.Projects.List; + +public class ProjectsInProgressViewModel : PageModel +{ + [BindProperty(SupportsGet = true)] public int PageNumber { get; set; } = 1; + + public PaginationModel Pagination { get; set; } = default!; + + internal int PageSize = 20; + + public static string GetProjectSummaryUrl(ListAllProjectsResultModel project) + { + return string.Format(project.ProjectType == ProjectType.Conversion ? RouteConstants.ConversionProjectTaskList : RouteConstants.TransferProjectTaskList, project.ProjectId); + } +} \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml index 93d8d749..0a6a038c 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml @@ -1,20 +1,26 @@ @page "/projects/all/in-progress/transfers" @using Dfe.Complete.Constants; @using Dfe.Complete.Domain.Enums -@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.TransferProjectsInProgressModel +@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.TransferProjectsInProgressInProgressModel @{ - Layout = "List/_AllProjectsLayout"; + Layout = "List/ProjectsInProgress/_ProjectsInProgressLayout"; ViewData["Title"] = "All projects in progress"; } +@section PageHeading +{ +

    + Transfer projects in progress +

    +} +
    - - + @@ -40,7 +46,6 @@ - diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs index 77c47b0c..6e25965b 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs @@ -9,7 +9,7 @@ namespace Dfe.Complete.Pages.Projects.List.ProjectsInProgress { - public class TransferProjectsInProgressModel(ISender sender) : AllProjectsViewModel + public class TransferProjectsInProgressInProgressModel(ISender sender) : ProjectsInProgressViewModel { public List Projects { get; set; } = default!; diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/_ProjectsInProgressLayout.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/_ProjectsInProgressLayout.cshtml new file mode 100644 index 00000000..f4d60b78 --- /dev/null +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/_ProjectsInProgressLayout.cshtml @@ -0,0 +1,34 @@ +@model Dfe.Complete.Pages.Projects.List.ProjectsInProgressViewModel +@{ + Layout = "List/_AllProjectsLayout"; +} + +@await RenderSectionAsync("PageHeading", true) + + +
    + @RenderBody() +
    + +@await Html.PartialAsync("/Pages/Pagination/_Pagination.cshtml", model: Model.Pagination) \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/_AllProjectsLayout.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/_AllProjectsLayout.cshtml index 1350cfd0..69d9e068 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/_AllProjectsLayout.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/_AllProjectsLayout.cshtml @@ -1,33 +1,45 @@ -@model Dfe.Complete.Pages.Projects.List.AllProjectsViewModel -@{ +@{ Layout = "Shared/_Layout"; } - -
    - @RenderBody() -
    - -@await Html.PartialAsync("/Pages/Pagination/_Pagination.cshtml", model: Model.Pagination) -@* *@ \ No newline at end of file +@section BeforeMainFullWidth +{ +
    +
    +
    + +
    +
    +
    +} +@RenderBody() From 1f3f599e42ba08aa40042e6ef5fd9c10643877ec Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 20 Jan 2025 14:49:20 +0000 Subject: [PATCH 13/47] Update frontend packages so that the stylings and components are up to date with other DfE services Change the default page width to be in line the DfE standards Update the service name in the header to use the new design/stylings for the name --- .../Dfe.Complete/Pages/Shared/_Layout.cshtml | 354 +++++++++--------- .../Dfe.Complete/wwwroot/package.json | 6 +- .../wwwroot/src/css/dfe-frontend.scss | 2 +- .../Dfe.Complete/wwwroot/src/index.js | 4 +- .../Dfe.Complete/wwwroot/src/index.scss | 17 +- .../wwwroot/webpack.assets.config.js | 10 +- 6 files changed, 204 insertions(+), 189 deletions(-) diff --git a/src/Frontend/Dfe.Complete/Pages/Shared/_Layout.cshtml b/src/Frontend/Dfe.Complete/Pages/Shared/_Layout.cshtml index 6df3f90e..296ed480 100644 --- a/src/Frontend/Dfe.Complete/Pages/Shared/_Layout.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Shared/_Layout.cshtml @@ -3,18 +3,18 @@ @inject Dfe.Complete.Services.IAnalyticsConsentService _analytisConsentService @{ - var showAnalytics = _configuration["GoogleAnalytics:Enable"] == "Yes" && _analytisConsentService.HasConsent(); + var showAnalytics = _configuration["GoogleAnalytics:Enable"] == "Yes" && _analytisConsentService.HasConsent(); - var titleDescription = " - Complete conversions, transfers and changes"; + var titleDescription = " - Complete conversions, transfers and changes"; } - @if (showAnalytics) - { - - - - } - - - - - - - - - - - - - - - - @ViewData["Title"] @titleDescription - - + + } + + + + + + + + + + + + + + + + @ViewData["Title"] @titleDescription + + - @if (showAnalytics) - { - - - - } +@if (showAnalytics) +{ + + + +} - - - - - Skip to main content - - - - - + + + +Skip to main content + + + + + +@await RenderSectionAsync("BeforeMainFullWidth", required: false) + +
    + @await RenderSectionAsync("BeforeMain", required: false) +
    + +
    +
    +
    +
    + + @RenderBody() +
    - -
    -
    -
    -
    - - @RenderBody() -
    -
    -
    +
    +
    + + + \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/wwwroot/package.json b/src/Frontend/Dfe.Complete/wwwroot/package.json index a69d0d25..9ecc60f5 100644 --- a/src/Frontend/Dfe.Complete/wwwroot/package.json +++ b/src/Frontend/Dfe.Complete/wwwroot/package.json @@ -17,10 +17,10 @@ "webpack-cli": "^5.0.0" }, "dependencies": { - "@ministryofjustice/frontend": "^1.0.0", + "@ministryofjustice/frontend": "^3.3.1", "accessible-autocomplete": "^2.0.4", - "dfe-frontend-alpha": "1.0.1", - "govuk-frontend": "^4.4.1", + "dfe-frontend": "^2.0.1", + "govuk-frontend": "^5.8.0", "jquery": "^3.6.1" } } diff --git a/src/Frontend/Dfe.Complete/wwwroot/src/css/dfe-frontend.scss b/src/Frontend/Dfe.Complete/wwwroot/src/css/dfe-frontend.scss index dc704351..b24b7861 100644 --- a/src/Frontend/Dfe.Complete/wwwroot/src/css/dfe-frontend.scss +++ b/src/Frontend/Dfe.Complete/wwwroot/src/css/dfe-frontend.scss @@ -5,7 +5,7 @@ // Add extra styles here -@import '../../node_modules/dfe-frontend-alpha/packages/dfefrontend'; +@import '../../node_modules/dfe-frontend/packages/dfefrontend'; .dfe-width-container { max-width: 1200px; diff --git a/src/Frontend/Dfe.Complete/wwwroot/src/index.js b/src/Frontend/Dfe.Complete/wwwroot/src/index.js index b79a2568..d95a9bbe 100644 --- a/src/Frontend/Dfe.Complete/wwwroot/src/index.js +++ b/src/Frontend/Dfe.Complete/wwwroot/src/index.js @@ -3,7 +3,7 @@ // Write your JavaScript code. -import GOVUKFrontend from "govuk-frontend/govuk/all"; -GOVUKFrontend.initAll(); +import { initAll } from "govuk-frontend/dist/govuk/govuk-frontend.min"; +initAll(); import MOJFrontend from "@ministryofjustice/frontend/moj/all"; MOJFrontend.initAll(); \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/wwwroot/src/index.scss b/src/Frontend/Dfe.Complete/wwwroot/src/index.scss index 4f9bfef6..f87c4567 100644 --- a/src/Frontend/Dfe.Complete/wwwroot/src/index.scss +++ b/src/Frontend/Dfe.Complete/wwwroot/src/index.scss @@ -1,6 +1,19 @@ /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification for details on configuring this project to bundle and minify static web assets. */ -@import "node_modules/govuk-frontend/govuk/all.scss"; + +// override the default page width +$govuk-page-width: 1200px; +$moj-page-width: $govuk-page-width; + + +// get the Inter font +@import url("https://rsms.me/inter/inter.css"); + +// Use the DfE font 'Inter', do not user GDS Transport +$govuk-font-family: "Inter", sans-serif; + + +@import "node_modules/govuk-frontend/dist/govuk/all.scss"; @import "node_modules/@ministryofjustice/frontend/moj/all.scss"; @import "./css/attachment.scss"; @import "./css/related-items.scss"; @@ -11,7 +24,7 @@ for details on configuring this project to bundle and minify static web assets. @import "./css/risk.scss"; @import "./css/project-overview.scss"; @import "./css/pupil-numbers.scss"; -@import "./css//pagination.scss"; +@import "./css/pagination.scss"; .moj-sub-navigation__link { font-weight: bold; diff --git a/src/Frontend/Dfe.Complete/wwwroot/webpack.assets.config.js b/src/Frontend/Dfe.Complete/wwwroot/webpack.assets.config.js index 06df6be1..c6985122 100644 --- a/src/Frontend/Dfe.Complete/wwwroot/webpack.assets.config.js +++ b/src/Frontend/Dfe.Complete/wwwroot/webpack.assets.config.js @@ -6,15 +6,15 @@ module.exports = { plugins: [ new CopyPlugin({ patterns: [ - { from: path.join(__dirname, 'node_modules/govuk-frontend/govuk/assets'), to: path.join(__dirname, 'assets') }, + { from: path.join(__dirname, 'node_modules/govuk-frontend/dist/govuk/assets'), to: path.join(__dirname, 'assets') }, { from: path.join(__dirname, 'node_modules/accessible-autocomplete/dist'), to: path.join(__dirname, 'dist') }, { from: path.resolve(__dirname, 'node_modules/@ministryofjustice/frontend/moj/assets'), to: path.resolve(__dirname, 'assets') }, - { from: path.resolve(__dirname, 'node_modules/dfe-frontend-alpha/packages/assets'), to: path.resolve(__dirname, 'assets') }, - { from: path.resolve(__dirname, 'node_modules/dfe-frontend-alpha/dist'), to: path.resolve(__dirname, 'dist') }, + { from: path.resolve(__dirname, 'node_modules/dfe-frontend/packages/assets'), to: path.resolve(__dirname, 'assets') }, + { from: path.resolve(__dirname, 'node_modules/dfe-frontend/dist'), to: path.resolve(__dirname, 'dist') }, { from: path.resolve(__dirname, 'node_modules/jquery/dist'), to: path.resolve(__dirname, 'dist') }, { from: path.resolve(__dirname, 'node_modules/@ministryofjustice/frontend/moj/assets'), to: path.resolve(__dirname, 'dist/assets') }, - { from: path.resolve(__dirname, 'node_modules/dfe-frontend-alpha/packages/assets'), to: path.resolve(__dirname, 'dist/assets') }, - { from: path.join(__dirname, 'node_modules/govuk-frontend/govuk/assets'), to: path.join(__dirname, 'dist/assets') }, + { from: path.resolve(__dirname, 'node_modules/dfe-frontend/packages/assets'), to: path.resolve(__dirname, 'dist/assets') }, + { from: path.join(__dirname, 'node_modules/govuk-frontend/dist/govuk/assets'), to: path.join(__dirname, 'dist/assets') }, ], }) ], From d0eb52b95b00fcd3fbce4fb8696493d0aa8cc04e Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 20 Jan 2025 14:49:59 +0000 Subject: [PATCH 14/47] Add caching for the CountProjects and ListAllProjects Command --- .../Queries/CountProjects/CountProjects.cs | 41 +++++++---- .../ListAllProjects/ListAllProjects.cs | 70 ++++++++++++------- 2 files changed, 75 insertions(+), 36 deletions(-) diff --git a/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs b/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs index 3ac8b63e..b2da8e88 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs @@ -1,28 +1,45 @@ using Dfe.Complete.Application.Common.Models; using Dfe.Complete.Application.Projects.Interfaces; using Dfe.Complete.Domain.Enums; +using DfE.CoreLibs.Caching.Helpers; +using DfE.CoreLibs.Caching.Interfaces; using MediatR; using Microsoft.EntityFrameworkCore; namespace Dfe.Complete.Application.Projects.Queries.CountProjects { - public record CountProjectQuery(ProjectState? ProjectStatus, ProjectType? Type, bool? IncludeFormAMat) : IRequest>; + public record CountProjectQuery(ProjectState? ProjectStatus, ProjectType? Type, bool? IncludeFormAMat) + : IRequest> + { + public override string ToString() + { + return $"{ProjectStatus.ToString()}{Type.ToString()}{IncludeFormAMat.ToString()}"; + } + } - public class CountProjectsQueryHandler(IListAllProjectsQueryService listAllProjectsQueryService) : IRequestHandler> + public class CountProjectsQueryHandler( + IListAllProjectsQueryService listAllProjectsQueryService, + ICacheService cacheService) + : IRequestHandler> { public async Task> Handle(CountProjectQuery request, CancellationToken cancellationToken) { - // var cacheKey = $"Project_{CacheKeyHelper.GenerateHashedCacheKey(request.Urn.Value.ToString())}"; - try - { - var result = await listAllProjectsQueryService.ListAllProjects(request.ProjectStatus, request.Type, request.IncludeFormAMat) - .CountAsync(cancellationToken); - return Result.Success(result); - } - catch (Exception ex) + var cacheKey = $"Project_{CacheKeyHelper.GenerateHashedCacheKey(request.ToString())}"; + var methodName = nameof(CountProjectsQueryHandler); + return await cacheService.GetOrAddAsync(cacheKey, async () => { - return Result.Failure(ex.Message); - } + try + { + var result = await listAllProjectsQueryService + .ListAllProjects(request.ProjectStatus, request.Type, request.IncludeFormAMat) + .CountAsync(cancellationToken); + return Result.Success(result); + } + catch (Exception ex) + { + return Result.Failure(ex.Message); + } + }, methodName); } } } \ No newline at end of file diff --git a/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs b/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs index a314c930..3846b0ce 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs @@ -2,39 +2,61 @@ using Dfe.Complete.Application.Projects.Interfaces; using Dfe.Complete.Application.Projects.Model; using Dfe.Complete.Domain.Enums; +using DfE.CoreLibs.Caching.Helpers; +using DfE.CoreLibs.Caching.Interfaces; using MediatR; using Microsoft.EntityFrameworkCore; namespace Dfe.Complete.Application.Projects.Queries.ListAllProjects { - public record ListAllProjectsQuery(ProjectState? ProjectStatus, ProjectType? Type, bool? IncludeFormAMat, int Page, int Count) : IRequest>>; + public record ListAllProjectsQuery( + ProjectState? ProjectStatus, + ProjectType? Type, + bool? IncludeFormAMat, + int Page, + int Count) : IRequest>> + { + public override string ToString() + { + return $"{ProjectStatus.ToString()}{Type.ToString()}{IncludeFormAMat.ToString()}{Page}{Count}"; + } + } - public class ListAllProjectsQueryHandler(IListAllProjectsQueryService listAllProjectsQueryService) : IRequestHandler>> + public class ListAllProjectsQueryHandler( + IListAllProjectsQueryService listAllProjectsQueryService, + ICacheService cacheService) + : IRequestHandler>> { - public async Task>> Handle(ListAllProjectsQuery request, CancellationToken cancellationToken) + public async Task>> Handle(ListAllProjectsQuery request, + CancellationToken cancellationToken) { - // var cacheKey = $"Project_{CacheKeyHelper.GenerateHashedCacheKey(request.Urn.Value.ToString())}"; - try - { - var result = await listAllProjectsQueryService.ListAllProjects(request.ProjectStatus, request.Type, request.IncludeFormAMat) - .Skip(request.Page * request.Count).Take(request.Count) - .Select(item => new ListAllProjectsResultModel( - item.Establishment.Name, - item.Project.Id, - item.Project.Urn, - item.Project.SignificantDate, - item.Project.State, - item.Project.Type, - item.Project.IncomingTrustUkprn == null, - item.Project.AssignedTo - )) - .ToListAsync(cancellationToken); - return Result>.Success(result); - } - catch (Exception ex) + var cacheKey = $"ListAllProjects_{CacheKeyHelper.GenerateHashedCacheKey(request.ToString())}"; + var methodName = nameof(ListAllProjectsQueryHandler); + return await cacheService.GetOrAddAsync(cacheKey, async () => { - return Result>.Failure(ex.Message); - } + try + { + var result = await listAllProjectsQueryService + .ListAllProjects(request.ProjectStatus, request.Type, request.IncludeFormAMat) + .Skip(request.Page * request.Count).Take(request.Count) + .Select(item => new ListAllProjectsResultModel( + item.Establishment.Name, + item.Project.Id, + item.Project.Urn, + item.Project.SignificantDate, + item.Project.State, + item.Project.Type, + item.Project.IncomingTrustUkprn == null, + item.Project.AssignedTo + )) + .ToListAsync(cancellationToken); + return Result>.Success(result); + } + catch (Exception ex) + { + return Result>.Failure(ex.Message); + } + }, methodName); } } } \ No newline at end of file From d6addec3c91e3f0f330c03d74b55d97ad916001d Mon Sep 17 00:00:00 2001 From: Sukhvinder Bhullar Date: Tue, 21 Jan 2025 09:20:10 +0000 Subject: [PATCH 15/47] refactored to work with architecture --- .../Generated/Contracts.g.cs | 18 +- .../Generated/swagger.json | 17 +- .../Common/Models/ConversionCsvModel.cs | 153 ++--- .../CsvExport/IConversionCsvQueryService.cs | 9 + .../Conversion/ConversionRowGenerator.cs | 322 ++++++---- .../CsvExport/ConversionCsvQueryService.cs | 24 + .../Conversion/ConversionRowGeneratorTests.cs | 595 +++++++++--------- .../IgnoreVirtualMembersCustomization .cs | 42 ++ .../Models/EstablishmentsCustomization.cs | 16 + 9 files changed, 692 insertions(+), 504 deletions(-) create mode 100644 src/Core/Dfe.Complete.Application/Projects/Interfaces/CsvExport/IConversionCsvQueryService.cs create mode 100644 src/Core/Dfe.Complete.Infrastructure/QueryServices/CsvExport/ConversionCsvQueryService.cs create mode 100644 src/Tests/Dfe.Complete.Tests.Common/Customizations/Behaviours/IgnoreVirtualMembersCustomization .cs create mode 100644 src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/EstablishmentsCustomization.cs diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs index a8711fc0..527a80e7 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs +++ b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs @@ -302,7 +302,8 @@ public partial class Project : BaseAggregateRoot public string? NewTrustName { get; set; } = default!; [Newtonsoft.Json.JsonProperty("state", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public int? State { get; set; } = default!; + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public ProjectState? State { get; set; } = default!; [Newtonsoft.Json.JsonProperty("prepareId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int? PrepareId { get; set; } = default!; @@ -481,6 +482,21 @@ public static ContactId FromJson(string data) } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum ProjectState + { + + [System.Runtime.Serialization.EnumMember(Value = @"Active")] + Active = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"Completed")] + Completed = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"Cancelled")] + Cancelled = 2, + + } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class User { diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json index bab08629..12227f74 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json +++ b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json @@ -363,8 +363,7 @@ "nullable": true }, "state": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/ProjectState" }, "prepareId": { "type": "integer", @@ -524,6 +523,20 @@ } } }, + "ProjectState": { + "type": "string", + "description": "", + "x-enumNames": [ + "Active", + "Completed", + "Cancelled" + ], + "enum": [ + "Active", + "Completed", + "Cancelled" + ] + }, "User": { "type": "object", "additionalProperties": false, diff --git a/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs b/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs index 081c14be..f5bcca31 100644 --- a/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs +++ b/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs @@ -1,80 +1,81 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Dfe.Complete.Domain.Entities; namespace Dfe.Complete.Application.Common.Models { - public class ConversionCsvModel - { - public string? SchoolName { get; set; } - public int SchoolUrn { get; set; } - public bool FormAMat { get; set; } - public string? AcademyName { get; set; } - public int? AcademyUrn { get; set; } - public string? AcademyDfENumber { get; set; } - public string IncomingTrustName { get; set; } - public string LocalAuthority { get; set; } - public string Region { get; set; } - public string Diocese { get; set; } - public DateOnly ProvisionalConversionDate { get; set; } - public DateOnly ConfirmedConversionDate { get; set; } - public string? AcademyOrderType { get; set; } - public bool TwoRequiresImprovement { get; set; } - public DateOnly AdvisoryBoardDate { get; set; } - public string? AdvisoryBoardConditions { get; set; } - public string RiskProtectionArrangement { get; set; } - public string ReasonForCommercialInsurance { get; set; } - public bool AllConditionsMet { get; set; } - public bool CompletedGrantPaymentCertificateReceived { get; set; } - public string SchoolType { get; set; } - public string SchoolAgeRange { get; set; } - public string SchoolPhase { get; set; } - public int ProposedCapacityForPupilsInReceptionToYear6 { get; set; } - public int ProposedCapacityForPupilsInYears7To11 { get; set; } - public int ProposedCapacityForStudentsInYear12OrAbove { get; set; } - public string SchoolAddress1 { get; set; } - public string SchoolAddress2 { get; set; } - public string SchoolAddress3 { get; set; } - public string SchoolTown { get; set; } - public string SchoolCounty { get; set; } - public string SchoolPostcode { get; set; } - public string SchoolSharepointFolder { get; set; } - public string ConversionType { get; set; } - public int IncomingTrustUKPRN { get; set; } - public string IncomingTrustGroupIdentifier { get; set; } - public string IncomingTrustCompaniesHouseNumber { get; set; } - public string IncomingTrustAddress1 { get; set; } - public string IncomingTrustAddress2 { get; set; } - public string IncomingTrustAddress3 { get; set; } - public string IncomingTrustAddressTown { get; set; } - public string IncomingTrustAddressCounty { get; set; } - public string IncomingTrustAddressPostcode { get; set; } - public string IncomingTrustSharepointLink { get; set; } - public string ProjectCreatedBy { get; set; } - public string ProjectCreatedByEmailAddress { get; set; } - public string AssignedToName { get; set; } - public string TeamManagingTheProject { get; set; } - public string ProjectMainContactName { get; set; } - public string HeadteacherName { get; set; } - public string HeadteacherRole { get; set; } - public string HeadteacherEmail { get; set; } - public string LocalAuthorityContactName { get; set; } - public string LocalAuthorityContactEmail { get; set; } - public string PrimaryContactForIncomingTrustName { get; set; } - public string PrimaryContactForIncomingTrustEmail { get; set; } - public string PrimaryContactForOutgoingTrustName { get; set; } - public string PrimaryContactForOutgoingTrustEmail { get; set; } - public string IncomingTrustCEOName { get; set; } - public string IncomingTrustCEORole { get; set; } - public string IncomingTrustCEOEmail { get; set; } - public string SolicitorContactName { get; set; } - public string SolicitorContactEmail { get; set; } - public string DioceseContactName { get; set; } - public string DioceseContactEmail { get; set; } - public string DirectorOfChildServicesName { get; set; } - public string DirectorOfChildServicesEmail { get; set; } - public string DirectorOfChildServicesRole { get; set; } - } + public record ConversionCsvModel(Project Project, GiasEstablishment Establishment); + + //public class ConversionCsvModel + //{ + // public string? SchoolName { get; set; } + // public int SchoolUrn { get; set; } + // public bool FormAMat { get; set; } + // public string? AcademyName { get; set; } + // public int? AcademyUrn { get; set; } + // public string? AcademyDfENumber { get; set; } + // public string IncomingTrustName { get; set; } + // public string LocalAuthority { get; set; } + // public string Region { get; set; } + // public string Diocese { get; set; } + // public DateOnly ProvisionalConversionDate { get; set; } + // public DateOnly ConfirmedConversionDate { get; set; } + // public string? AcademyOrderType { get; set; } + // public bool TwoRequiresImprovement { get; set; } + // public DateOnly AdvisoryBoardDate { get; set; } + // public string? AdvisoryBoardConditions { get; set; } + // public string RiskProtectionArrangement { get; set; } + // public string ReasonForCommercialInsurance { get; set; } + // public bool AllConditionsMet { get; set; } + // public bool CompletedGrantPaymentCertificateReceived { get; set; } + // public string SchoolType { get; set; } + // public string SchoolAgeRange { get; set; } + // public string SchoolPhase { get; set; } + // public int ProposedCapacityForPupilsInReceptionToYear6 { get; set; } + // public int ProposedCapacityForPupilsInYears7To11 { get; set; } + // public int ProposedCapacityForStudentsInYear12OrAbove { get; set; } + // public string SchoolAddress1 { get; set; } + // public string SchoolAddress2 { get; set; } + // public string SchoolAddress3 { get; set; } + // public string SchoolTown { get; set; } + // public string SchoolCounty { get; set; } + // public string SchoolPostcode { get; set; } + // public string SchoolSharepointFolder { get; set; } + // public string ConversionType { get; set; } + // public int IncomingTrustUKPRN { get; set; } + // public string IncomingTrustGroupIdentifier { get; set; } + // public string IncomingTrustCompaniesHouseNumber { get; set; } + // public string IncomingTrustAddress1 { get; set; } + // public string IncomingTrustAddress2 { get; set; } + // public string IncomingTrustAddress3 { get; set; } + // public string IncomingTrustAddressTown { get; set; } + // public string IncomingTrustAddressCounty { get; set; } + // public string IncomingTrustAddressPostcode { get; set; } + // public string IncomingTrustSharepointLink { get; set; } + // public string ProjectCreatedBy { get; set; } + // public string ProjectCreatedByEmailAddress { get; set; } + // public string AssignedToName { get; set; } + // public string TeamManagingTheProject { get; set; } + // public string ProjectMainContactName { get; set; } + // public string HeadteacherName { get; set; } + // public string HeadteacherRole { get; set; } + // public string HeadteacherEmail { get; set; } + // public string LocalAuthorityContactName { get; set; } + // public string LocalAuthorityContactEmail { get; set; } + // public string PrimaryContactForIncomingTrustName { get; set; } + // public string PrimaryContactForIncomingTrustEmail { get; set; } + // public string PrimaryContactForOutgoingTrustName { get; set; } + // public string PrimaryContactForOutgoingTrustEmail { get; set; } + // public string IncomingTrustCEOName { get; set; } + // public string IncomingTrustCEORole { get; set; } + // public string IncomingTrustCEOEmail { get; set; } + // public string SolicitorContactName { get; set; } + // public string SolicitorContactEmail { get; set; } + // public string DioceseContactName { get; set; } + // public string DioceseContactEmail { get; set; } + // public string DirectorOfChildServicesName { get; set; } + // public string DirectorOfChildServicesEmail { get; set; } + // public string DirectorOfChildServicesRole { get; set; } + + + + //} } diff --git a/src/Core/Dfe.Complete.Application/Projects/Interfaces/CsvExport/IConversionCsvQueryService.cs b/src/Core/Dfe.Complete.Application/Projects/Interfaces/CsvExport/IConversionCsvQueryService.cs new file mode 100644 index 00000000..4b7fea56 --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Projects/Interfaces/CsvExport/IConversionCsvQueryService.cs @@ -0,0 +1,9 @@ +using Dfe.Complete.Application.Common.Models; + +namespace Dfe.Complete.Application.Projects.Interfaces.CsvExport +{ + public interface IConversionCsvQueryService + { + IQueryable GetByMonth(int month, int year); + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs index b9678f3b..d63e83b3 100644 --- a/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs @@ -1,150 +1,200 @@ using Dfe.Complete.Application.Common.Models; -using System; using System.Text; namespace Dfe.Complete.Application.Services.CsvExport.Conversion { + + public interface IColumnBuilder + { + string Build(T value); + } + + public class BlankIfEmpty(Func func): IColumnBuilder + { + public string Build(T input) + { + object? value = func(input); + + if(value == null) + { + return string.Empty; + } + else + { + return value.ToString(); + } + } + } + + public class FormAMat() : IColumnBuilder + { + public string Build(ConversionCsvModel input) + { + var projectType = input.Project.Type; + + if (projectType == Domain.Enums.ProjectType.Conversion) + { + return "join a MAT"; + } + else + { + return "form a MAT"; + } + } + } + public class ConversionRowGenerator : IRowGenerator { + private IColumnBuilder[] _columnBuilders = + [ + new BlankIfEmpty(x => x.Establishment.Name), + new BlankIfEmpty(x => x.Project.Urn), + + ]; + + + public string GenerateRow(ConversionCsvModel model) { var row = new StringBuilder(); - row.Append(model.SchoolName); - row.Append(","); - row.Append(model.SchoolUrn); - row.Append(","); - row.Append(model.FormAMat ? "form a MAT": "join a MAT"); - row.Append(","); - row.Append(BlankIfNull(model.AcademyName)); - row.Append(","); - row.Append(BlankIfNull(model.AcademyUrn)); - row.Append(","); - row.Append(model.AcademyDfENumber); - row.Append(","); - row.Append(model.IncomingTrustName); - row.Append(","); - row.Append(model.LocalAuthority); - row.Append(","); - row.Append(model.Region); - row.Append(","); - row.Append(model.Diocese); - row.Append(","); - row.Append(model.ProvisionalConversionDate); - row.Append(","); - row.Append(model.ConfirmedConversionDate); - row.Append(","); - row.Append(model.AcademyOrderType); - row.Append(","); - row.Append(model.TwoRequiresImprovement); - row.Append(","); - row.Append(model.AdvisoryBoardDate); - row.Append(","); - row.Append(model.AdvisoryBoardConditions); - row.Append(","); - row.Append(model.RiskProtectionArrangement); - row.Append(","); - row.Append(model.ReasonForCommercialInsurance); - row.Append(","); - row.Append(model.AllConditionsMet); - row.Append(","); - row.Append(model.CompletedGrantPaymentCertificateReceived); - row.Append(","); - row.Append(model.SchoolType); - row.Append(","); - row.Append(model.SchoolAgeRange); - row.Append(","); - row.Append(model.SchoolPhase); - row.Append(","); - row.Append(model.ProposedCapacityForPupilsInReceptionToYear6); - row.Append(","); - row.Append(model.ProposedCapacityForPupilsInYears7To11); - row.Append(","); - row.Append(model.ProposedCapacityForStudentsInYear12OrAbove); - row.Append(","); - row.Append(model.SchoolAddress1); - row.Append(","); - row.Append(model.SchoolAddress2); - row.Append(","); - row.Append(model.SchoolAddress3); - row.Append(","); - row.Append(model.SchoolTown); - row.Append(","); - row.Append(model.SchoolCounty); - row.Append(","); - row.Append(model.SchoolPostcode); - row.Append(","); - row.Append(model.SchoolSharepointFolder); - row.Append(","); - row.Append(model.ConversionType); - row.Append(","); - row.Append(model.IncomingTrustUKPRN); - row.Append(","); - row.Append(model.IncomingTrustGroupIdentifier); - row.Append(","); - row.Append(model.IncomingTrustCompaniesHouseNumber); - row.Append(","); - row.Append(model.IncomingTrustAddress1); - row.Append(","); - row.Append(model.IncomingTrustAddress2); - row.Append(","); - row.Append(model.IncomingTrustAddress3); - row.Append(","); - row.Append(model.IncomingTrustAddressTown); - row.Append(","); - row.Append(model.IncomingTrustAddressCounty); - row.Append(","); - row.Append(model.IncomingTrustAddressPostcode); - row.Append(","); - row.Append(model.IncomingTrustSharepointLink); - row.Append(","); - row.Append(model.ProjectCreatedBy); - row.Append(","); - row.Append(model.ProjectCreatedByEmailAddress); - row.Append(","); - row.Append(model.AssignedToName); - row.Append(","); - row.Append(model.TeamManagingTheProject); - row.Append(","); - row.Append(model.ProjectMainContactName); - row.Append(","); - row.Append(model.HeadteacherName); - row.Append(","); - row.Append(model.HeadteacherRole); - row.Append(","); - row.Append(model.HeadteacherEmail); - row.Append(","); - row.Append(model.LocalAuthorityContactName); - row.Append(","); - row.Append(model.LocalAuthorityContactEmail); - row.Append(","); - row.Append(model.PrimaryContactForIncomingTrustName); - row.Append(","); - row.Append(model.PrimaryContactForIncomingTrustEmail); - row.Append(","); - row.Append(model.PrimaryContactForOutgoingTrustName); - row.Append(","); - row.Append(model.PrimaryContactForOutgoingTrustEmail); - row.Append(","); - row.Append(model.IncomingTrustCEOName); - row.Append(","); - row.Append(model.IncomingTrustCEORole); - row.Append(","); - row.Append(model.IncomingTrustCEOEmail); - row.Append(","); - row.Append(model.SolicitorContactName); - row.Append(","); - row.Append(model.SolicitorContactEmail); - row.Append(","); - row.Append(model.DioceseContactName); - row.Append(","); - row.Append(model.DioceseContactEmail); - row.Append(","); - row.Append(model.DirectorOfChildServicesName); - row.Append(","); - row.Append(model.DirectorOfChildServicesEmail); - row.Append(","); - row.Append(model.DirectorOfChildServicesRole); + _columnBuilders.Select(x => x.Build(model)).Aggregate(row, (acc, x) => acc.Append(x).Append(",")); + + //row.Append(model.SchoolName); + //row.Append(","); + //row.Append(model.SchoolUrn); + //row.Append(","); + //row.Append(model.FormAMat ? "form a MAT": "join a MAT"); + //row.Append(","); + //row.Append(BlankIfNull(model.AcademyName)); + //row.Append(","); + //row.Append(BlankIfNull(model.AcademyUrn)); + //row.Append(","); + //row.Append(model.AcademyDfENumber); + //row.Append(","); + //row.Append(model.IncomingTrustName); + //row.Append(","); + //row.Append(model.LocalAuthority); + //row.Append(","); + //row.Append(model.Region); + //row.Append(","); + //row.Append(model.Diocese); + //row.Append(","); + //row.Append(model.ProvisionalConversionDate); + //row.Append(","); + //row.Append(model.ConfirmedConversionDate); + //row.Append(","); + //row.Append(model.AcademyOrderType); + //row.Append(","); + //row.Append(model.TwoRequiresImprovement); + //row.Append(","); + //row.Append(model.AdvisoryBoardDate); + //row.Append(","); + //row.Append(model.AdvisoryBoardConditions); + //row.Append(","); + //row.Append(model.RiskProtectionArrangement); + //row.Append(","); + //row.Append(model.ReasonForCommercialInsurance); + //row.Append(","); + //row.Append(model.AllConditionsMet); + //row.Append(","); + //row.Append(model.CompletedGrantPaymentCertificateReceived); + //row.Append(","); + //row.Append(model.SchoolType); + //row.Append(","); + //row.Append(model.SchoolAgeRange); + //row.Append(","); + //row.Append(model.SchoolPhase); + //row.Append(","); + //row.Append(model.ProposedCapacityForPupilsInReceptionToYear6); + //row.Append(","); + //row.Append(model.ProposedCapacityForPupilsInYears7To11); + //row.Append(","); + //row.Append(model.ProposedCapacityForStudentsInYear12OrAbove); + //row.Append(","); + //row.Append(model.SchoolAddress1); + //row.Append(","); + //row.Append(model.SchoolAddress2); + //row.Append(","); + //row.Append(model.SchoolAddress3); + //row.Append(","); + //row.Append(model.SchoolTown); + //row.Append(","); + //row.Append(model.SchoolCounty); + //row.Append(","); + //row.Append(model.SchoolPostcode); + //row.Append(","); + //row.Append(model.SchoolSharepointFolder); + //row.Append(","); + //row.Append(model.ConversionType); + //row.Append(","); + //row.Append(model.IncomingTrustUKPRN); + //row.Append(","); + //row.Append(model.IncomingTrustGroupIdentifier); + //row.Append(","); + //row.Append(model.IncomingTrustCompaniesHouseNumber); + //row.Append(","); + //row.Append(model.IncomingTrustAddress1); + //row.Append(","); + //row.Append(model.IncomingTrustAddress2); + //row.Append(","); + //row.Append(model.IncomingTrustAddress3); + //row.Append(","); + //row.Append(model.IncomingTrustAddressTown); + //row.Append(","); + //row.Append(model.IncomingTrustAddressCounty); + //row.Append(","); + //row.Append(model.IncomingTrustAddressPostcode); + //row.Append(","); + //row.Append(model.IncomingTrustSharepointLink); + //row.Append(","); + //row.Append(model.ProjectCreatedBy); + //row.Append(","); + //row.Append(model.ProjectCreatedByEmailAddress); + //row.Append(","); + //row.Append(model.AssignedToName); + //row.Append(","); + //row.Append(model.TeamManagingTheProject); + //row.Append(","); + //row.Append(model.ProjectMainContactName); + //row.Append(","); + //row.Append(model.HeadteacherName); + //row.Append(","); + //row.Append(model.HeadteacherRole); + //row.Append(","); + //row.Append(model.HeadteacherEmail); + //row.Append(","); + //row.Append(model.LocalAuthorityContactName); + //row.Append(","); + //row.Append(model.LocalAuthorityContactEmail); + //row.Append(","); + //row.Append(model.PrimaryContactForIncomingTrustName); + //row.Append(","); + //row.Append(model.PrimaryContactForIncomingTrustEmail); + //row.Append(","); + //row.Append(model.PrimaryContactForOutgoingTrustName); + //row.Append(","); + //row.Append(model.PrimaryContactForOutgoingTrustEmail); + //row.Append(","); + //row.Append(model.IncomingTrustCEOName); + //row.Append(","); + //row.Append(model.IncomingTrustCEORole); + //row.Append(","); + //row.Append(model.IncomingTrustCEOEmail); + //row.Append(","); + //row.Append(model.SolicitorContactName); + //row.Append(","); + //row.Append(model.SolicitorContactEmail); + //row.Append(","); + //row.Append(model.DioceseContactName); + //row.Append(","); + //row.Append(model.DioceseContactEmail); + //row.Append(","); + //row.Append(model.DirectorOfChildServicesName); + //row.Append(","); + //row.Append(model.DirectorOfChildServicesEmail); + //row.Append(","); + //row.Append(model.DirectorOfChildServicesRole); return row.ToString(); } diff --git a/src/Core/Dfe.Complete.Infrastructure/QueryServices/CsvExport/ConversionCsvQueryService.cs b/src/Core/Dfe.Complete.Infrastructure/QueryServices/CsvExport/ConversionCsvQueryService.cs new file mode 100644 index 00000000..3d7a66ab --- /dev/null +++ b/src/Core/Dfe.Complete.Infrastructure/QueryServices/CsvExport/ConversionCsvQueryService.cs @@ -0,0 +1,24 @@ +using Dfe.Complete.Application.Common.Models; +using Dfe.Complete.Application.Projects.Interfaces.CsvExport; +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Infrastructure.Database; + +namespace Dfe.Complete.Infrastructure.QueryServices.CsvExport +{ + public class ConversionCsvQueryService(CompleteContext context) : IConversionCsvQueryService + { + public IQueryable GetByMonth(int month, int year) + { + var query = context.Projects + .Where(project => project.Type == ProjectType.Conversion) + .Where(project => project.SignificantDate.Value.Month == month && project.SignificantDate.Value.Month == year) + .Join(context.GiasEstablishments, project => project.Urn, establishment => establishment.Urn, + (project, establishment) => new ConversionCsvModel(project, establishment)); + + return query; + + throw new NotImplementedException(); + } + } +} diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs index c86e1ed6..124bb19f 100644 --- a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs @@ -1,83 +1,97 @@ -using Dfe.Complete.Application.Common.Models; +using AutoFixture; +using AutoFixture.Xunit2; +using Dfe.Complete.Application.Common.Models; using Dfe.Complete.Application.Services.CsvExport.Conversion; +using Dfe.Complete.Domain.Entities; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Tests.Common.Customizations.Behaviours; +using Dfe.Complete.Tests.Common.Customizations.Models; +using DfE.CoreLibs.Testing.AutoFixture.Attributes; +using DfE.CoreLibs.Testing.AutoFixture.Customizations; namespace Dfe.Complete.Application.Tests.Services.CsvExport.Conversion { public class ConversionRowGeneratorTests { - [Fact] - public void RowGeneratesAccountsForBlankData() - { - var model = new ConversionCsvModel() - { - AllConditionsMet = true, - AcademyDfENumber = null, - AcademyName = null, - AcademyOrderType = null, - AcademyUrn = null, - AdvisoryBoardConditions = null, - AdvisoryBoardDate = new DateOnly(2024, 3, 2), - CompletedGrantPaymentCertificateReceived = true, - ConfirmedConversionDate = new DateOnly(2024, 4, 3), - ConversionType = "ConversionType", - Diocese = "Diocese", - HeadteacherName = "HeadteacherName", - IncomingTrustAddress1 = "IncomingTrustAddress1", - IncomingTrustAddress2 = "IncomingTrustAddress2", - IncomingTrustAddress3 = "IncomingTrustAddress3", - IncomingTrustAddressCounty = "IncomingTrustAddressCounty", - IncomingTrustAddressPostcode = "IncomingTrustAddressPostcode", - IncomingTrustAddressTown = "IncomingTrustAddressTown", - IncomingTrustCompaniesHouseNumber = "IncomingTrustCompaniesHouseNumber", - IncomingTrustGroupIdentifier = "IncomingTrustGroupIdentifier", - IncomingTrustName = "IncomingTrustName", - IncomingTrustSharepointLink = "IncomingTrustSharepointLink", - IncomingTrustUKPRN = 12345647, - AssignedToName = "AssignedToName", - DioceseContactEmail = "DioceseContactEmail", - DioceseContactName = "DioceseContactName", - DirectorOfChildServicesEmail = "DirectorOfChildServicesEmail", - DirectorOfChildServicesName = "DirectorOfChildServicesName", - DirectorOfChildServicesRole = "DirectorOfChildServicesRole", - HeadteacherEmail = "HeadteacherEmail", - HeadteacherRole = "HeadteacherRole", - IncomingTrustCEOEmail = "IncomingTrustCEOEmail", - IncomingTrustCEOName = "IncomingTrustCEOName", - IncomingTrustCEORole = "IncomingTrustCEORole", - LocalAuthority = "LocalAuthority", - LocalAuthorityContactEmail = "LocalAuthorityContactEmail", - LocalAuthorityContactName = "LocalAuthorityContactName", - PrimaryContactForIncomingTrustEmail = "PrimaryContactForIncomingTrustEmail", - PrimaryContactForIncomingTrustName = "PrimaryContactForIncomingTrustName", - PrimaryContactForOutgoingTrustEmail = "PrimaryContactForOutgoingTrustEmail", - PrimaryContactForOutgoingTrustName = "PrimaryContactForOutgoingTrustName", - ProjectCreatedBy = "ProjectCreatedBy", - ProjectCreatedByEmailAddress = "ProjectCreatedByEmailAddress", - ProjectMainContactName = "ProjectMainContactName", - FormAMat = false, - ProposedCapacityForPupilsInReceptionToYear6 = 123, - ProposedCapacityForPupilsInYears7To11 = 456, - ProposedCapacityForStudentsInYear12OrAbove = 789, - ProvisionalConversionDate = new DateOnly(2024, 5, 6), - ReasonForCommercialInsurance = "ReasonForCommercialInsurance", - Region = "Region", - RiskProtectionArrangement = "RiskProtectionArrangement", - SchoolAddress1 = "SchoolAddress1", - SchoolAddress2 = "SchoolAddress2", - SchoolAddress3 = "SchoolAddress3", - SchoolAgeRange = "SchoolAgeRange", - SchoolCounty = "SchoolCounty", - SchoolName = "SchoolName", - SchoolPhase = "SchoolPhase", - SchoolPostcode = "SchoolPostcode", - SchoolSharepointFolder = "SchoolSharepointFolder", - SchoolTown = "SchoolTown", - SchoolType = "SchoolType", - SolicitorContactEmail = "SolicitorContactEmail", - SchoolUrn = 12345, - SolicitorContactName = "SolicitorContactName", - TeamManagingTheProject = "TeamManagingTheProject", - TwoRequiresImprovement = true - }; + + + [Theory] + [CustomAutoData(typeof(ProjectCustomization), typeof(EstablishmentsCustomization), typeof(DateOnlyCustomization), typeof(IgnoreVirtualMembersCustomisation))] + public void RowGeneratesAccountsForBlankData(Project project, GiasEstablishment establishment) + { + project.Type = ProjectType.Conversion; + var model = new ConversionCsvModel(project, establishment); + + //var model = new ConversionCsvModel() + //{ + // AllConditionsMet = true, + // AcademyDfENumber = null, + // AcademyName = null, + // AcademyOrderType = null, + // AcademyUrn = null, + // AdvisoryBoardConditions = null, + // AdvisoryBoardDate = new DateOnly(2024, 3, 2), + // CompletedGrantPaymentCertificateReceived = true, + // ConfirmedConversionDate = new DateOnly(2024, 4, 3), + // ConversionType = "ConversionType", + // Diocese = "Diocese", + // HeadteacherName = "HeadteacherName", + // IncomingTrustAddress1 = "IncomingTrustAddress1", + // IncomingTrustAddress2 = "IncomingTrustAddress2", + // IncomingTrustAddress3 = "IncomingTrustAddress3", + // IncomingTrustAddressCounty = "IncomingTrustAddressCounty", + // IncomingTrustAddressPostcode = "IncomingTrustAddressPostcode", + // IncomingTrustAddressTown = "IncomingTrustAddressTown", + // IncomingTrustCompaniesHouseNumber = "IncomingTrustCompaniesHouseNumber", + // IncomingTrustGroupIdentifier = "IncomingTrustGroupIdentifier", + // IncomingTrustName = "IncomingTrustName", + // IncomingTrustSharepointLink = "IncomingTrustSharepointLink", + // IncomingTrustUKPRN = 12345647, + // AssignedToName = "AssignedToName", + // DioceseContactEmail = "DioceseContactEmail", + // DioceseContactName = "DioceseContactName", + // DirectorOfChildServicesEmail = "DirectorOfChildServicesEmail", + // DirectorOfChildServicesName = "DirectorOfChildServicesName", + // DirectorOfChildServicesRole = "DirectorOfChildServicesRole", + // HeadteacherEmail = "HeadteacherEmail", + // HeadteacherRole = "HeadteacherRole", + // IncomingTrustCEOEmail = "IncomingTrustCEOEmail", + // IncomingTrustCEOName = "IncomingTrustCEOName", + // IncomingTrustCEORole = "IncomingTrustCEORole", + // LocalAuthority = "LocalAuthority", + // LocalAuthorityContactEmail = "LocalAuthorityContactEmail", + // LocalAuthorityContactName = "LocalAuthorityContactName", + // PrimaryContactForIncomingTrustEmail = "PrimaryContactForIncomingTrustEmail", + // PrimaryContactForIncomingTrustName = "PrimaryContactForIncomingTrustName", + // PrimaryContactForOutgoingTrustEmail = "PrimaryContactForOutgoingTrustEmail", + // PrimaryContactForOutgoingTrustName = "PrimaryContactForOutgoingTrustName", + // ProjectCreatedBy = "ProjectCreatedBy", + // ProjectCreatedByEmailAddress = "ProjectCreatedByEmailAddress", + // ProjectMainContactName = "ProjectMainContactName", + // FormAMat = false, + // ProposedCapacityForPupilsInReceptionToYear6 = 123, + // ProposedCapacityForPupilsInYears7To11 = 456, + // ProposedCapacityForStudentsInYear12OrAbove = 789, + // ProvisionalConversionDate = new DateOnly(2024, 5, 6), + // ReasonForCommercialInsurance = "ReasonForCommercialInsurance", + // Region = "Region", + // RiskProtectionArrangement = "RiskProtectionArrangement", + // SchoolAddress1 = "SchoolAddress1", + // SchoolAddress2 = "SchoolAddress2", + // SchoolAddress3 = "SchoolAddress3", + // SchoolAgeRange = "SchoolAgeRange", + // SchoolCounty = "SchoolCounty", + // SchoolName = "SchoolName", + // SchoolPhase = "SchoolPhase", + // SchoolPostcode = "SchoolPostcode", + // SchoolSharepointFolder = "SchoolSharepointFolder", + // SchoolTown = "SchoolTown", + // SchoolType = "SchoolType", + // SolicitorContactEmail = "SolicitorContactEmail", + // SchoolUrn = 12345, + // SolicitorContactName = "SolicitorContactName", + // TeamManagingTheProject = "TeamManagingTheProject", + // TwoRequiresImprovement = true + //}; var generator = new ConversionRowGenerator(); @@ -85,74 +99,74 @@ public void RowGeneratesAccountsForBlankData() var result = generator.GenerateRow(model).Split(","); - Assert.Equal("SchoolName", result[0]); - Assert.Equal("12345", result[1]); - Assert.Equal("ProjectType", result[2]); - Assert.Equal("AcademyName", result[3]); - Assert.Equal("54321", result[4]); - Assert.Equal("AcademyDfENumber", result[5]); - Assert.Equal("IncomingTrustName", result[6]); - Assert.Equal("LocalAuthority", result[7]); - Assert.Equal("Region", result[8]); - Assert.Equal("Diocese", result[9]); - Assert.Equal("06/05/2024", result[10]); - Assert.Equal("03/04/2024", result[11]); - Assert.Equal("AcademyOrderType", result[12]); - Assert.Equal("True", result[13]); - Assert.Equal("02/03/2024", result[14]); - Assert.Equal("AdvisoryBoardConditions", result[15]); - Assert.Equal("RiskProtectionArrangement", result[16]); - Assert.Equal("ReasonForCommercialInsurance", result[17]); - Assert.Equal("True", result[18]); - Assert.Equal("True", result[19]); - Assert.Equal("SchoolType", result[20]); - Assert.Equal("SchoolAgeRange", result[21]); - Assert.Equal("SchoolPhase", result[22]); - Assert.Equal("123", result[23]); - Assert.Equal("456", result[24]); - Assert.Equal("789", result[25]); - Assert.Equal("SchoolAddress1", result[26]); - Assert.Equal("SchoolAddress2", result[27]); - Assert.Equal("SchoolAddress3", result[28]); - Assert.Equal("SchoolTown", result[29]); - Assert.Equal("SchoolCounty", result[30]); - Assert.Equal("SchoolPostcode", result[31]); - Assert.Equal("SchoolSharepointFolder", result[32]); - Assert.Equal("ConversionType", result[33]); - Assert.Equal("12345647", result[34]); - Assert.Equal("IncomingTrustGroupIdentifier", result[35]); - Assert.Equal("IncomingTrustCompaniesHouseNumber", result[36]); - Assert.Equal("IncomingTrustAddress1", result[37]); - Assert.Equal("IncomingTrustAddress2", result[38]); - Assert.Equal("IncomingTrustAddress3", result[39]); - Assert.Equal("IncomingTrustAddressTown", result[40]); - Assert.Equal("IncomingTrustAddressCounty", result[41]); - Assert.Equal("IncomingTrustAddressPostcode", result[42]); - Assert.Equal("IncomingTrustSharepointLink", result[43]); - Assert.Equal("ProjectCreatedBy", result[44]); - Assert.Equal("ProjectCreatedByEmailAddress", result[45]); - Assert.Equal("AssignedToName", result[46]); - Assert.Equal("TeamManagingTheProject", result[47]); - Assert.Equal("ProjectMainContactName", result[48]); - Assert.Equal("HeadteacherName", result[49]); - Assert.Equal("HeadteacherRole", result[50]); - Assert.Equal("HeadteacherEmail", result[51]); - Assert.Equal("LocalAuthorityContactName", result[52]); - Assert.Equal("LocalAuthorityContactEmail", result[53]); - Assert.Equal("PrimaryContactForIncomingTrustName", result[54]); - Assert.Equal("PrimaryContactForIncomingTrustEmail", result[55]); - Assert.Equal("PrimaryContactForOutgoingTrustName", result[56]); - Assert.Equal("PrimaryContactForOutgoingTrustEmail", result[57]); - Assert.Equal("IncomingTrustCEOName", result[58]); - Assert.Equal("IncomingTrustCEORole", result[59]); - Assert.Equal("IncomingTrustCEOEmail", result[60]); - Assert.Equal("SolicitorContactName", result[61]); - Assert.Equal("SolicitorContactEmail", result[62]); - Assert.Equal("DioceseContactName", result[63]); - Assert.Equal("DioceseContactEmail", result[64]); - Assert.Equal("DirectorOfChildServicesName", result[65]); - Assert.Equal("DirectorOfChildServicesEmail", result[66]); - Assert.Equal("DirectorOfChildServicesRole", result[67]); + Assert.Equal(establishment.Name, result[0]); + Assert.Equal(project.Urn.ToString(), result[1]); + Assert.Equal("join a MAT", result[2]); + //Assert.Equal("AcademyName", result[3]); + //Assert.Equal("54321", result[4]); + //Assert.Equal("AcademyDfENumber", result[5]); + //Assert.Equal("IncomingTrustName", result[6]); + //Assert.Equal("LocalAuthority", result[7]); + //Assert.Equal("Region", result[8]); + //Assert.Equal("Diocese", result[9]); + //Assert.Equal("06/05/2024", result[10]); + //Assert.Equal("03/04/2024", result[11]); + //Assert.Equal("AcademyOrderType", result[12]); + //Assert.Equal("True", result[13]); + //Assert.Equal("02/03/2024", result[14]); + //Assert.Equal("AdvisoryBoardConditions", result[15]); + //Assert.Equal("RiskProtectionArrangement", result[16]); + //Assert.Equal("ReasonForCommercialInsurance", result[17]); + //Assert.Equal("True", result[18]); + //Assert.Equal("True", result[19]); + //Assert.Equal("SchoolType", result[20]); + //Assert.Equal("SchoolAgeRange", result[21]); + //Assert.Equal("SchoolPhase", result[22]); + //Assert.Equal("123", result[23]); + //Assert.Equal("456", result[24]); + //Assert.Equal("789", result[25]); + //Assert.Equal("SchoolAddress1", result[26]); + //Assert.Equal("SchoolAddress2", result[27]); + //Assert.Equal("SchoolAddress3", result[28]); + //Assert.Equal("SchoolTown", result[29]); + //Assert.Equal("SchoolCounty", result[30]); + //Assert.Equal("SchoolPostcode", result[31]); + //Assert.Equal("SchoolSharepointFolder", result[32]); + //Assert.Equal("ConversionType", result[33]); + //Assert.Equal("12345647", result[34]); + //Assert.Equal("IncomingTrustGroupIdentifier", result[35]); + //Assert.Equal("IncomingTrustCompaniesHouseNumber", result[36]); + //Assert.Equal("IncomingTrustAddress1", result[37]); + //Assert.Equal("IncomingTrustAddress2", result[38]); + //Assert.Equal("IncomingTrustAddress3", result[39]); + //Assert.Equal("IncomingTrustAddressTown", result[40]); + //Assert.Equal("IncomingTrustAddressCounty", result[41]); + //Assert.Equal("IncomingTrustAddressPostcode", result[42]); + //Assert.Equal("IncomingTrustSharepointLink", result[43]); + //Assert.Equal("ProjectCreatedBy", result[44]); + //Assert.Equal("ProjectCreatedByEmailAddress", result[45]); + //Assert.Equal("AssignedToName", result[46]); + //Assert.Equal("TeamManagingTheProject", result[47]); + //Assert.Equal("ProjectMainContactName", result[48]); + //Assert.Equal("HeadteacherName", result[49]); + //Assert.Equal("HeadteacherRole", result[50]); + //Assert.Equal("HeadteacherEmail", result[51]); + //Assert.Equal("LocalAuthorityContactName", result[52]); + //Assert.Equal("LocalAuthorityContactEmail", result[53]); + //Assert.Equal("PrimaryContactForIncomingTrustName", result[54]); + //Assert.Equal("PrimaryContactForIncomingTrustEmail", result[55]); + //Assert.Equal("PrimaryContactForOutgoingTrustName", result[56]); + //Assert.Equal("PrimaryContactForOutgoingTrustEmail", result[57]); + //Assert.Equal("IncomingTrustCEOName", result[58]); + //Assert.Equal("IncomingTrustCEORole", result[59]); + //Assert.Equal("IncomingTrustCEOEmail", result[60]); + //Assert.Equal("SolicitorContactName", result[61]); + //Assert.Equal("SolicitorContactEmail", result[62]); + //Assert.Equal("DioceseContactName", result[63]); + //Assert.Equal("DioceseContactEmail", result[64]); + //Assert.Equal("DirectorOfChildServicesName", result[65]); + //Assert.Equal("DirectorOfChildServicesEmail", result[66]); + //Assert.Equal("DirectorOfChildServicesRole", result[67]); @@ -160,160 +174,163 @@ public void RowGeneratesAccountsForBlankData() } - [Fact] - public void RowGeneratesBasedOnModel() - { - var model = new ConversionCsvModel() - { - AllConditionsMet = true, - AcademyDfENumber = "AcademyDfENumber", - AcademyName = "AcademyName", - AcademyOrderType = "AcademyOrderType", - AcademyUrn = 54321, - AdvisoryBoardConditions = "AdvisoryBoardConditions", - AdvisoryBoardDate = new DateOnly(2024, 3, 2), - CompletedGrantPaymentCertificateReceived = true, - ConfirmedConversionDate = new DateOnly(2024, 4, 3), - ConversionType = "ConversionType", - Diocese = "Diocese", - HeadteacherName = "HeadteacherName", - IncomingTrustAddress1 = "IncomingTrustAddress1", - IncomingTrustAddress2 = "IncomingTrustAddress2", - IncomingTrustAddress3 = "IncomingTrustAddress3", - IncomingTrustAddressCounty = "IncomingTrustAddressCounty", - IncomingTrustAddressPostcode = "IncomingTrustAddressPostcode", - IncomingTrustAddressTown = "IncomingTrustAddressTown", - IncomingTrustCompaniesHouseNumber = "IncomingTrustCompaniesHouseNumber", - IncomingTrustGroupIdentifier = "IncomingTrustGroupIdentifier", - IncomingTrustName = "IncomingTrustName", - IncomingTrustSharepointLink = "IncomingTrustSharepointLink", - IncomingTrustUKPRN = 12345647, - AssignedToName = "AssignedToName", - DioceseContactEmail = "DioceseContactEmail", - DioceseContactName = "DioceseContactName", - DirectorOfChildServicesEmail = "DirectorOfChildServicesEmail", - DirectorOfChildServicesName = "DirectorOfChildServicesName", - DirectorOfChildServicesRole = "DirectorOfChildServicesRole", - HeadteacherEmail = "HeadteacherEmail", - HeadteacherRole = "HeadteacherRole", - IncomingTrustCEOEmail = "IncomingTrustCEOEmail", - IncomingTrustCEOName = "IncomingTrustCEOName", - IncomingTrustCEORole = "IncomingTrustCEORole", - LocalAuthority = "LocalAuthority", - LocalAuthorityContactEmail = "LocalAuthorityContactEmail", - LocalAuthorityContactName = "LocalAuthorityContactName", - PrimaryContactForIncomingTrustEmail = "PrimaryContactForIncomingTrustEmail", - PrimaryContactForIncomingTrustName = "PrimaryContactForIncomingTrustName", - PrimaryContactForOutgoingTrustEmail = "PrimaryContactForOutgoingTrustEmail", - PrimaryContactForOutgoingTrustName = "PrimaryContactForOutgoingTrustName", - ProjectCreatedBy = "ProjectCreatedBy", - ProjectCreatedByEmailAddress = "ProjectCreatedByEmailAddress", - ProjectMainContactName = "ProjectMainContactName", - FormAMat = true, - ProposedCapacityForPupilsInReceptionToYear6 = 123, - ProposedCapacityForPupilsInYears7To11 = 456, - ProposedCapacityForStudentsInYear12OrAbove = 789, - ProvisionalConversionDate = new DateOnly(2024, 5, 6), - ReasonForCommercialInsurance = "ReasonForCommercialInsurance", - Region = "Region", - RiskProtectionArrangement = "RiskProtectionArrangement", - SchoolAddress1 = "SchoolAddress1", - SchoolAddress2 = "SchoolAddress2", - SchoolAddress3 = "SchoolAddress3", - SchoolAgeRange = "SchoolAgeRange", - SchoolCounty = "SchoolCounty", - SchoolName = "SchoolName", - SchoolPhase = "SchoolPhase", - SchoolPostcode = "SchoolPostcode", - SchoolSharepointFolder = "SchoolSharepointFolder", - SchoolTown = "SchoolTown", - SchoolType = "SchoolType", - SolicitorContactEmail = "SolicitorContactEmail", - SchoolUrn = 12345, - SolicitorContactName = "SolicitorContactName", - TeamManagingTheProject = "TeamManagingTheProject", - TwoRequiresImprovement = true - }; + //[Fact] + //public void RowGeneratesBasedOnModel() + //{ + // //var model = new ConversionCsvModel() + // //{ + // // AllConditionsMet = true, + // // AcademyDfENumber = "AcademyDfENumber", + // // AcademyName = "AcademyName", + // // AcademyOrderType = "AcademyOrderType", + // // AcademyUrn = 54321, + // // AdvisoryBoardConditions = "AdvisoryBoardConditions", + // // AdvisoryBoardDate = new DateOnly(2024, 3, 2), + // // CompletedGrantPaymentCertificateReceived = true, + // // ConfirmedConversionDate = new DateOnly(2024, 4, 3), + // // ConversionType = "ConversionType", + // // Diocese = "Diocese", + // // HeadteacherName = "HeadteacherName", + // // IncomingTrustAddress1 = "IncomingTrustAddress1", + // // IncomingTrustAddress2 = "IncomingTrustAddress2", + // // IncomingTrustAddress3 = "IncomingTrustAddress3", + // // IncomingTrustAddressCounty = "IncomingTrustAddressCounty", + // // IncomingTrustAddressPostcode = "IncomingTrustAddressPostcode", + // // IncomingTrustAddressTown = "IncomingTrustAddressTown", + // // IncomingTrustCompaniesHouseNumber = "IncomingTrustCompaniesHouseNumber", + // // IncomingTrustGroupIdentifier = "IncomingTrustGroupIdentifier", + // // IncomingTrustName = "IncomingTrustName", + // // IncomingTrustSharepointLink = "IncomingTrustSharepointLink", + // // IncomingTrustUKPRN = 12345647, + // // AssignedToName = "AssignedToName", + // // DioceseContactEmail = "DioceseContactEmail", + // // DioceseContactName = "DioceseContactName", + // // DirectorOfChildServicesEmail = "DirectorOfChildServicesEmail", + // // DirectorOfChildServicesName = "DirectorOfChildServicesName", + // // DirectorOfChildServicesRole = "DirectorOfChildServicesRole", + // // HeadteacherEmail = "HeadteacherEmail", + // // HeadteacherRole = "HeadteacherRole", + // // IncomingTrustCEOEmail = "IncomingTrustCEOEmail", + // // IncomingTrustCEOName = "IncomingTrustCEOName", + // // IncomingTrustCEORole = "IncomingTrustCEORole", + // // LocalAuthority = "LocalAuthority", + // // LocalAuthorityContactEmail = "LocalAuthorityContactEmail", + // // LocalAuthorityContactName = "LocalAuthorityContactName", + // // PrimaryContactForIncomingTrustEmail = "PrimaryContactForIncomingTrustEmail", + // // PrimaryContactForIncomingTrustName = "PrimaryContactForIncomingTrustName", + // // PrimaryContactForOutgoingTrustEmail = "PrimaryContactForOutgoingTrustEmail", + // // PrimaryContactForOutgoingTrustName = "PrimaryContactForOutgoingTrustName", + // // ProjectCreatedBy = "ProjectCreatedBy", + // // ProjectCreatedByEmailAddress = "ProjectCreatedByEmailAddress", + // // ProjectMainContactName = "ProjectMainContactName", + // // FormAMat = true, + // // ProposedCapacityForPupilsInReceptionToYear6 = 123, + // // ProposedCapacityForPupilsInYears7To11 = 456, + // // ProposedCapacityForStudentsInYear12OrAbove = 789, + // // ProvisionalConversionDate = new DateOnly(2024, 5, 6), + // // ReasonForCommercialInsurance = "ReasonForCommercialInsurance", + // // Region = "Region", + // // RiskProtectionArrangement = "RiskProtectionArrangement", + // // SchoolAddress1 = "SchoolAddress1", + // // SchoolAddress2 = "SchoolAddress2", + // // SchoolAddress3 = "SchoolAddress3", + // // SchoolAgeRange = "SchoolAgeRange", + // // SchoolCounty = "SchoolCounty", + // // SchoolName = "SchoolName", + // // SchoolPhase = "SchoolPhase", + // // SchoolPostcode = "SchoolPostcode", + // // SchoolSharepointFolder = "SchoolSharepointFolder", + // // SchoolTown = "SchoolTown", + // // SchoolType = "SchoolType", + // // SolicitorContactEmail = "SolicitorContactEmail", + // // SchoolUrn = 12345, + // // SolicitorContactName = "SolicitorContactName", + // // TeamManagingTheProject = "TeamManagingTheProject", + // // TwoRequiresImprovement = true + // //}; - var generator = new ConversionRowGenerator(); - generator.GenerateRow(model); + // var model = new ConversionCsvModel(null, null); + + // var generator = new ConversionRowGenerator(); + + // generator.GenerateRow(model); - var result = generator.GenerateRow(model).Split(","); + // var result = generator.GenerateRow(model).Split(","); - Assert.Equal("SchoolName", result[0]); - Assert.Equal("12345", result[1]); - Assert.Equal("ProjectType", result[2]); - Assert.Equal("AcademyName", result[3]); - Assert.Equal("54321", result[4]); - Assert.Equal("AcademyDfENumber", result[5]); - Assert.Equal("IncomingTrustName", result[6]); - Assert.Equal("LocalAuthority", result[7]); - Assert.Equal("Region", result[8]); - Assert.Equal("Diocese", result[9]); - Assert.Equal("06/05/2024", result[10]); - Assert.Equal("03/04/2024", result[11]); - Assert.Equal("AcademyOrderType", result[12]); - Assert.Equal("True", result[13]); - Assert.Equal("02/03/2024", result[14]); - Assert.Equal("AdvisoryBoardConditions", result[15]); - Assert.Equal("RiskProtectionArrangement", result[16]); - Assert.Equal("ReasonForCommercialInsurance", result[17]); - Assert.Equal("True", result[18]); - Assert.Equal("True", result[19]); - Assert.Equal("SchoolType", result[20]); - Assert.Equal("SchoolAgeRange", result[21]); - Assert.Equal("SchoolPhase", result[22]); - Assert.Equal("123", result[23]); - Assert.Equal("456", result[24]); - Assert.Equal("789", result[25]); - Assert.Equal("SchoolAddress1", result[26]); - Assert.Equal("SchoolAddress2", result[27]); - Assert.Equal("SchoolAddress3", result[28]); - Assert.Equal("SchoolTown", result[29]); - Assert.Equal("SchoolCounty", result[30]); - Assert.Equal("SchoolPostcode", result[31]); - Assert.Equal("SchoolSharepointFolder", result[32]); - Assert.Equal("ConversionType", result[33]); - Assert.Equal("12345647", result[34]); - Assert.Equal("IncomingTrustGroupIdentifier", result[35]); - Assert.Equal("IncomingTrustCompaniesHouseNumber", result[36]); - Assert.Equal("IncomingTrustAddress1", result[37]); - Assert.Equal("IncomingTrustAddress2", result[38]); - Assert.Equal("IncomingTrustAddress3", result[39]); - Assert.Equal("IncomingTrustAddressTown", result[40]); - Assert.Equal("IncomingTrustAddressCounty", result[41]); - Assert.Equal("IncomingTrustAddressPostcode", result[42]); - Assert.Equal("IncomingTrustSharepointLink", result[43]); - Assert.Equal("ProjectCreatedBy", result[44]); - Assert.Equal("ProjectCreatedByEmailAddress", result[45]); - Assert.Equal("AssignedToName", result[46]); - Assert.Equal("TeamManagingTheProject", result[47]); - Assert.Equal("ProjectMainContactName", result[48]); - Assert.Equal("HeadteacherName", result[49]); - Assert.Equal("HeadteacherRole", result[50]); - Assert.Equal("HeadteacherEmail", result[51]); - Assert.Equal("LocalAuthorityContactName", result[52]); - Assert.Equal("LocalAuthorityContactEmail", result[53]); - Assert.Equal("PrimaryContactForIncomingTrustName", result[54]); - Assert.Equal("PrimaryContactForIncomingTrustEmail", result[55]); - Assert.Equal("PrimaryContactForOutgoingTrustName", result[56]); - Assert.Equal("PrimaryContactForOutgoingTrustEmail", result[57]); - Assert.Equal("IncomingTrustCEOName", result[58]); - Assert.Equal("IncomingTrustCEORole", result[59]); - Assert.Equal("IncomingTrustCEOEmail", result[60]); - Assert.Equal("SolicitorContactName", result[61]); - Assert.Equal("SolicitorContactEmail", result[62]); - Assert.Equal("DioceseContactName", result[63]); - Assert.Equal("DioceseContactEmail", result[64]); - Assert.Equal("DirectorOfChildServicesName", result[65]); - Assert.Equal("DirectorOfChildServicesEmail", result[66]); - Assert.Equal("DirectorOfChildServicesRole", result[67]); + // Assert.Equal("SchoolName", result[0]); + // Assert.Equal("12345", result[1]); + // Assert.Equal("ProjectType", result[2]); + // Assert.Equal("AcademyName", result[3]); + // Assert.Equal("54321", result[4]); + // Assert.Equal("AcademyDfENumber", result[5]); + // Assert.Equal("IncomingTrustName", result[6]); + // Assert.Equal("LocalAuthority", result[7]); + // Assert.Equal("Region", result[8]); + // Assert.Equal("Diocese", result[9]); + // Assert.Equal("06/05/2024", result[10]); + // Assert.Equal("03/04/2024", result[11]); + // Assert.Equal("AcademyOrderType", result[12]); + // Assert.Equal("True", result[13]); + // Assert.Equal("02/03/2024", result[14]); + // Assert.Equal("AdvisoryBoardConditions", result[15]); + // Assert.Equal("RiskProtectionArrangement", result[16]); + // Assert.Equal("ReasonForCommercialInsurance", result[17]); + // Assert.Equal("True", result[18]); + // Assert.Equal("True", result[19]); + // Assert.Equal("SchoolType", result[20]); + // Assert.Equal("SchoolAgeRange", result[21]); + // Assert.Equal("SchoolPhase", result[22]); + // Assert.Equal("123", result[23]); + // Assert.Equal("456", result[24]); + // Assert.Equal("789", result[25]); + // Assert.Equal("SchoolAddress1", result[26]); + // Assert.Equal("SchoolAddress2", result[27]); + // Assert.Equal("SchoolAddress3", result[28]); + // Assert.Equal("SchoolTown", result[29]); + // Assert.Equal("SchoolCounty", result[30]); + // Assert.Equal("SchoolPostcode", result[31]); + // Assert.Equal("SchoolSharepointFolder", result[32]); + // Assert.Equal("ConversionType", result[33]); + // Assert.Equal("12345647", result[34]); + // Assert.Equal("IncomingTrustGroupIdentifier", result[35]); + // Assert.Equal("IncomingTrustCompaniesHouseNumber", result[36]); + // Assert.Equal("IncomingTrustAddress1", result[37]); + // Assert.Equal("IncomingTrustAddress2", result[38]); + // Assert.Equal("IncomingTrustAddress3", result[39]); + // Assert.Equal("IncomingTrustAddressTown", result[40]); + // Assert.Equal("IncomingTrustAddressCounty", result[41]); + // Assert.Equal("IncomingTrustAddressPostcode", result[42]); + // Assert.Equal("IncomingTrustSharepointLink", result[43]); + // Assert.Equal("ProjectCreatedBy", result[44]); + // Assert.Equal("ProjectCreatedByEmailAddress", result[45]); + // Assert.Equal("AssignedToName", result[46]); + // Assert.Equal("TeamManagingTheProject", result[47]); + // Assert.Equal("ProjectMainContactName", result[48]); + // Assert.Equal("HeadteacherName", result[49]); + // Assert.Equal("HeadteacherRole", result[50]); + // Assert.Equal("HeadteacherEmail", result[51]); + // Assert.Equal("LocalAuthorityContactName", result[52]); + // Assert.Equal("LocalAuthorityContactEmail", result[53]); + // Assert.Equal("PrimaryContactForIncomingTrustName", result[54]); + // Assert.Equal("PrimaryContactForIncomingTrustEmail", result[55]); + // Assert.Equal("PrimaryContactForOutgoingTrustName", result[56]); + // Assert.Equal("PrimaryContactForOutgoingTrustEmail", result[57]); + // Assert.Equal("IncomingTrustCEOName", result[58]); + // Assert.Equal("IncomingTrustCEORole", result[59]); + // Assert.Equal("IncomingTrustCEOEmail", result[60]); + // Assert.Equal("SolicitorContactName", result[61]); + // Assert.Equal("SolicitorContactEmail", result[62]); + // Assert.Equal("DioceseContactName", result[63]); + // Assert.Equal("DioceseContactEmail", result[64]); + // Assert.Equal("DirectorOfChildServicesName", result[65]); + // Assert.Equal("DirectorOfChildServicesEmail", result[66]); + // Assert.Equal("DirectorOfChildServicesRole", result[67]); - } + //} } } diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Behaviours/IgnoreVirtualMembersCustomization .cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Behaviours/IgnoreVirtualMembersCustomization .cs new file mode 100644 index 00000000..f48ce5a1 --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Behaviours/IgnoreVirtualMembersCustomization .cs @@ -0,0 +1,42 @@ +using AutoFixture.Kernel; +using AutoFixture; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Dfe.Complete.Tests.Common.Customizations.Behaviours +{ + public class IgnoreVirtualMembers : ISpecimenBuilder + { + public Type ReflectedType { get; } + + public object Create(object request, ISpecimenContext context) + { + var pi = request as PropertyInfo; + if (pi != null) + { + if (ReflectedType == null || + ReflectedType == pi.ReflectedType) + { + if (pi.GetGetMethod().IsVirtual) + { + return new OmitSpecimen(); + } + } + } + + return new NoSpecimen(); + } + } + + public class IgnoreVirtualMembersCustomisation : ICustomization + { + public void Customize(IFixture fixture) + { + fixture.Customizations.Add(new IgnoreVirtualMembers()); + } + } +} diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/EstablishmentsCustomization.cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/EstablishmentsCustomization.cs new file mode 100644 index 00000000..c3f41ba1 --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/EstablishmentsCustomization.cs @@ -0,0 +1,16 @@ +using AutoFixture; +using Dfe.Complete.Domain.Entities; + +namespace Dfe.Complete.Tests.Common.Customizations.Models +{ + public class EstablishmentsCustomization : ICustomization + { + public string? EstablishmentName { get; set; } + + public void Customize(IFixture fixture) + { + fixture.Customize(composer => composer + .With(x => x.Name, EstablishmentName ?? fixture.Create())); + } + } +} From 3eaafe7dc80bfe74c9de2cc7407c4101ae7839b7 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Tue, 21 Jan 2025 09:45:47 +0000 Subject: [PATCH 16/47] Update pagination to use the tag helpers --- .../Pages/Pagination/_Pagination.cshtml | 235 ++++++++---------- 1 file changed, 97 insertions(+), 138 deletions(-) diff --git a/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml b/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml index 0f88c5b0..2915d143 100644 --- a/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml @@ -1,154 +1,113 @@ @using NetEscapades.AspNetCore.SecurityHeaders; @model Dfe.Complete.Pages.Pagination.PaginationModel @{ - var prefix = !string.IsNullOrEmpty(Model.ElementIdPrefix) ? Model.ElementIdPrefix : ""; + var prefix = !string.IsNullOrEmpty(Model.ElementIdPrefix) ? Model.ElementIdPrefix : ""; - var ariaDisabled = "aria-disabled=true"; + var nextPageLink = $"{Model.Url}?pageNumber={Model.Next}"; - var nextPageLink = $"{Model.Url}?pageNumber={Model.Next}"; - var nextDisabled = !Model.Next.HasValue; - var nextDisabledStyle = nextDisabled ? "govuk-pagination__link-disabled aria-disabled=true" : ""; - var nextDisabledAria = nextDisabled ? ariaDisabled : ""; + var previousPageLink = $"{Model.Url}?pageNumber={Model.Previous}"; - var previousPageLink = $"{Model.Url}?pageNumber={Model.Previous}"; - var previousDisabled = !Model.Previous.HasValue; - var previousDisabledStyle = previousDisabled ? "govuk-pagination__link-disabled aria-disabled=true" : ""; - var previousDisabledAria = previousDisabled ? ariaDisabled : ""; + var nonce = Context.GetNonce(); - var nonce = Context.GetNonce(); - - var paginationContainerId = $"{prefix}pagination-container"; - var nextButtonId = $"{prefix}next-page"; - var nextPageValueId = $"{prefix}next-page-value"; - var previousButtonId = $"{prefix}previous-page"; - var previousPageValueId = $"{prefix}previous-page-value"; - var pagesToDisplay = new List() { 1, Model.PageNumber, Model.TotalPages }; - if (Model.Previous.HasValue) - pagesToDisplay.Add((int)Model.Previous); - if (Model.Next.HasValue) - pagesToDisplay.Add((int)Model.Next); + var paginationContainerId = $"{prefix}pagination-container"; + var nextButtonId = $"{prefix}next-page"; + var previousButtonId = $"{prefix}previous-page"; + var pagesToDisplay = new List() { 1, Model.PageNumber, Model.TotalPages }; + if (Model.Previous.HasValue) + pagesToDisplay.Add((int)Model.Previous); + if (Model.Next.HasValue) + pagesToDisplay.Add((int)Model.Next); } @if (Model.TotalPages > 1) { - - // When pagination is used on the screen without a full page refresh - @* if (!string.IsNullOrEmpty(Model.ContentContainerId)) *@ - @* { *@ - @* *@ - @* } *@ + if (Model.Next.HasValue && Model.Next == pageNumber && Model.Next + 1 < Model.TotalPages) + { + + } + } + @if (Model.HasNext) + { + + } + + // When pagination is used on the screen without a full page refresh + @* if (!string.IsNullOrEmpty(Model.ContentContainerId)) *@ + @* { *@ + @* *@ + @* } *@ } \ No newline at end of file From 892c542e60702f421fc7dc82d29817ff0f786592 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Tue, 21 Jan 2025 14:18:15 +0000 Subject: [PATCH 17/47] Fix circular dependency issues by switching to returning the users name instead of the full user object Remove the FormAMat filter --- .../Interfaces/IListAllProjectsQueryService.cs | 2 +- .../Projects/Model/ListAllProjectsResultModel.cs | 2 +- .../Projects/Queries/CountProjects/CountProjects.cs | 6 +++--- .../Queries/ListAllProjects/ListAllProjects.cs | 11 +++++------ .../QueryServices/ListAllProjectsQueryService.cs | 4 +--- .../ProjectsInProgress/AllProjectsInProgress.cshtml | 2 +- .../AllProjectsInProgress.cshtml.cs | 4 ++-- .../ConversionProjectsInProgress.cshtml | 2 +- .../ConversionProjectsInProgress.cshtml.cs | 4 ++-- .../TransferProjectsInProgress.cshtml | 2 +- .../TransferProjectsInProgress.cshtml.cs | 4 ++-- 11 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/Core/Dfe.Complete.Application/Projects/Interfaces/IListAllProjectsQueryService.cs b/src/Core/Dfe.Complete.Application/Projects/Interfaces/IListAllProjectsQueryService.cs index cd780da8..661b8a42 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Interfaces/IListAllProjectsQueryService.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Interfaces/IListAllProjectsQueryService.cs @@ -5,5 +5,5 @@ namespace Dfe.Complete.Application.Projects.Interfaces; public interface IListAllProjectsQueryService { - IQueryable ListAllProjects(ProjectState? projectStatus, ProjectType? type, bool? includeFormAMat); + IQueryable ListAllProjects(ProjectState? projectStatus, ProjectType? type); } \ No newline at end of file diff --git a/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs b/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs index 6050a72b..e4b6e748 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs @@ -12,4 +12,4 @@ public record ListAllProjectsResultModel( ProjectState State, ProjectType? ProjectType, bool IsFormAMAT, - User? AssignedTo); \ No newline at end of file + string? AssignedToFullName); \ No newline at end of file diff --git a/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs b/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs index b2da8e88..acd88806 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs @@ -8,12 +8,12 @@ namespace Dfe.Complete.Application.Projects.Queries.CountProjects { - public record CountProjectQuery(ProjectState? ProjectStatus, ProjectType? Type, bool? IncludeFormAMat) + public record CountProjectQuery(ProjectState? ProjectStatus, ProjectType? Type) : IRequest> { public override string ToString() { - return $"{ProjectStatus.ToString()}{Type.ToString()}{IncludeFormAMat.ToString()}"; + return $"{ProjectStatus.ToString()}{Type.ToString()}"; } } @@ -31,7 +31,7 @@ public async Task> Handle(CountProjectQuery request, CancellationTok try { var result = await listAllProjectsQueryService - .ListAllProjects(request.ProjectStatus, request.Type, request.IncludeFormAMat) + .ListAllProjects(request.ProjectStatus, request.Type) .CountAsync(cancellationToken); return Result.Success(result); } diff --git a/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs b/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs index 3846b0ce..c4dab3fb 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs @@ -12,13 +12,12 @@ namespace Dfe.Complete.Application.Projects.Queries.ListAllProjects public record ListAllProjectsQuery( ProjectState? ProjectStatus, ProjectType? Type, - bool? IncludeFormAMat, - int Page, - int Count) : IRequest>> + int Page = 0, + int Count = 20) : IRequest>> { public override string ToString() { - return $"{ProjectStatus.ToString()}{Type.ToString()}{IncludeFormAMat.ToString()}{Page}{Count}"; + return $"{ProjectStatus.ToString()}{Type.ToString()}{Page}{Count}"; } } @@ -37,7 +36,7 @@ public async Task>> Handle(ListAllProjec try { var result = await listAllProjectsQueryService - .ListAllProjects(request.ProjectStatus, request.Type, request.IncludeFormAMat) + .ListAllProjects(request.ProjectStatus, request.Type) .Skip(request.Page * request.Count).Take(request.Count) .Select(item => new ListAllProjectsResultModel( item.Establishment.Name, @@ -47,7 +46,7 @@ public async Task>> Handle(ListAllProjec item.Project.State, item.Project.Type, item.Project.IncomingTrustUkprn == null, - item.Project.AssignedTo + item.Project.AssignedTo != null ? $"{item.Project.AssignedTo.FirstName} {item.Project.AssignedTo.LastName}" : null )) .ToListAsync(cancellationToken); return Result>.Success(result); diff --git a/src/Core/Dfe.Complete.Infrastructure/QueryServices/ListAllProjectsQueryService.cs b/src/Core/Dfe.Complete.Infrastructure/QueryServices/ListAllProjectsQueryService.cs index cc4ed78a..ffd6988d 100644 --- a/src/Core/Dfe.Complete.Infrastructure/QueryServices/ListAllProjectsQueryService.cs +++ b/src/Core/Dfe.Complete.Infrastructure/QueryServices/ListAllProjectsQueryService.cs @@ -8,13 +8,11 @@ namespace Dfe.Complete.Infrastructure.QueryServices; internal class ListAllProjectsQueryService(CompleteContext context) : IListAllProjectsQueryService { - public IQueryable ListAllProjects(ProjectState? projectStatus, ProjectType? type, bool? includeFormAMat) + public IQueryable ListAllProjects(ProjectState? projectStatus, ProjectType? type) { var query = context.Projects .Where(project => projectStatus == null || project.State == projectStatus) .Where(project => type == null || type == project.Type) - .Where(project => includeFormAMat == null || - includeFormAMat == (project.IncomingTrustUkprn == null)) .Include(p => p.AssignedTo) .Join(context.GiasEstablishments, project => project.Urn, establishment => establishment.Urn, (project, establishment) => new ListAllProjectsQueryModel(project, establishment)); diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml index 81b46184..38974d0b 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml @@ -36,7 +36,7 @@
    - + } diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs index a9983ee1..ac85dd8f 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs @@ -14,12 +14,12 @@ public class ProjectsInProgressInProgressViewModel(ISender sender) : ProjectsInP public async Task OnGet() { - var listProjectQuery = new ListAllProjectsQuery(ProjectState.Active, null, null, PageNumber-1, PageSize); + var listProjectQuery = new ListAllProjectsQuery(ProjectState.Active, null, PageNumber-1, PageSize); var listResponse = await sender.Send(listProjectQuery); Projects = listResponse.Value ?? []; - var countProjectQuery = new CountProjectQuery(ProjectState.Active, null, null); + var countProjectQuery = new CountProjectQuery(ProjectState.Active, null); var countResponse = await sender.Send(countProjectQuery); Pagination = new PaginationModel("/projects/all/in-progress/all" ,PageNumber, countResponse.Value, PageSize); diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml index 81c7f62e..570208c4 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml @@ -46,7 +46,7 @@ - + } diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs index 63caf3a7..d3a58cc8 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs @@ -16,12 +16,12 @@ public class ConversionProjectsInProgressInProgressModel(ISender sender) : Proje public async Task OnGet() { //TODO: Review pagination logic - var listProjectQuery = new ListAllProjectsQuery(ProjectState.Active, ProjectType.Conversion, null, PageNumber-1, PageSize); + var listProjectQuery = new ListAllProjectsQuery(ProjectState.Active, ProjectType.Conversion, PageNumber-1, PageSize); var response = await sender.Send(listProjectQuery); Projects = response.Value?.ToList() ?? []; - var countProjectQuery = new CountProjectQuery(ProjectState.Active, ProjectType.Conversion, null); + var countProjectQuery = new CountProjectQuery(ProjectState.Active, ProjectType.Conversion); var countResponse = await sender.Send(countProjectQuery); Pagination = new PaginationModel("/projects/all/in-progress/conversions" ,PageNumber, countResponse.Value, PageSize); diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml index 0a6a038c..988f5610 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml @@ -47,7 +47,7 @@ - + } diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs index 6e25965b..a5870a1d 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs @@ -17,12 +17,12 @@ public class TransferProjectsInProgressInProgressModel(ISender sender) : Project public async Task OnGet() { //TODO: Review pagination logic - var listProjectQuery = new ListAllProjectsQuery(ProjectState.Active, ProjectType.Transfer, null, PageNumber-1, PageSize); + var listProjectQuery = new ListAllProjectsQuery(ProjectState.Active, ProjectType.Transfer, PageNumber-1, PageSize); var response = await sender.Send(listProjectQuery); Projects = response.Value?.ToList() ?? []; - var countProjectQuery = new CountProjectQuery(ProjectState.Active, ProjectType.Transfer, null); + var countProjectQuery = new CountProjectQuery(ProjectState.Active, ProjectType.Transfer); var countResponse = await sender.Send(countProjectQuery); Pagination = new PaginationModel("/projects/all/in-progress/transfers" ,PageNumber, countResponse.Value, PageSize); From 963ba8912feb830ef856454136f0c7d88c503492 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Tue, 21 Jan 2025 14:19:51 +0000 Subject: [PATCH 18/47] Add api endpoints for listing all projects --- .../Generated/Client.g.cs | 549 +++++++++++++++++- .../Generated/Contracts.g.cs | 94 ++- .../Generated/swagger.json | 195 ++++++- .../Controllers/ProjectsController.cs | 37 +- .../Queries/CountProjects/CountProjects.cs | 10 +- .../AllProjectsInProgress.cshtml.cs | 2 +- .../ConversionProjectsInProgress.cshtml.cs | 2 +- .../TransferProjectsInProgress.cshtml.cs | 2 +- 8 files changed, 848 insertions(+), 43 deletions(-) diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs b/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs index 29975837..aafe152f 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs +++ b/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs @@ -336,36 +336,27 @@ public string BaseUrl /// /// Gets a Project /// - /// The request. /// Project /// A server side error occurred. - public virtual System.Threading.Tasks.Task Projects_GetProject_Async(GetProjectByUrnQuery request) + public virtual System.Threading.Tasks.Task Projects_GetProject_Async(int? urn_Value) { - return Projects_GetProject_Async(request, System.Threading.CancellationToken.None); + return Projects_GetProject_Async(urn_Value, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// /// Gets a Project /// - /// The request. /// Project /// A server side error occurred. - public virtual async System.Threading.Tasks.Task Projects_GetProject_Async(GetProjectByUrnQuery request, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task Projects_GetProject_Async(int? urn_Value, System.Threading.CancellationToken cancellationToken) { - if (request == null) - throw new System.ArgumentNullException("request"); - var client_ = _httpClient; var disposeClient_ = false; try { using (var request_ = new System.Net.Http.HttpRequestMessage()) { - var json_ = Newtonsoft.Json.JsonConvert.SerializeObject(request, JsonSerializerSettings); - var content_ = new System.Net.Http.StringContent(json_); - content_.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"); - request_.Content = content_; request_.Method = new System.Net.Http.HttpMethod("GET"); request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); @@ -373,6 +364,12 @@ public virtual async System.Threading.Tasks.Task Projects_GetProject_As if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); // Operation Path: "v1/Projects" urlBuilder_.Append("v1/Projects"); + urlBuilder_.Append('?'); + if (urn_Value != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("Urn.Value")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(urn_Value, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + urlBuilder_.Length--; PrepareRequest(client_, request_, urlBuilder_); @@ -544,6 +541,534 @@ private string ConvertToString(object? value, System.Globalization.CultureInfo c } } + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class ListProjectsClient : IListProjectsClient + { + #pragma warning disable 8618 + private string _baseUrl; + #pragma warning restore 8618 + + private System.Net.Http.HttpClient _httpClient; + private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); + private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; + + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public ListProjectsClient(string baseUrl, System.Net.Http.HttpClient httpClient) + #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + { + BaseUrl = baseUrl; + _httpClient = httpClient; + Initialize(); + } + + private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() + { + var settings = new Newtonsoft.Json.JsonSerializerSettings(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set + { + _baseUrl = value; + if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) + _baseUrl += '/'; + } + } + + protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } + + static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); + + partial void Initialize(); + + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); + partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); + + /// + /// Returns a list of Projects + /// + /// Project + /// A server side error occurred. + public virtual System.Threading.Tasks.Task> Projects_ListProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count) + { + return Projects_ListProjects_Async(projectStatus, type, page, count, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Returns a list of Projects + /// + /// Project + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task> Projects_ListProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count, System.Threading.CancellationToken cancellationToken) + { + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "v1/Projects/List" + urlBuilder_.Append("v1/Projects/List"); + urlBuilder_.Append('?'); + if (projectStatus != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("ProjectStatus")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(projectStatus, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + if (type != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("Type")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(type, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + if (page != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("Page")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(page, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + if (count != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("Count")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(count, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + urlBuilder_.Length--; + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new PersonsApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new PersonsApiException("Invalid request data.", status_, responseText_, headers_, null); + } + else + { + var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new PersonsApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + protected struct ObjectResponseResult + { + public ObjectResponseResult(T responseObject, string responseText) + { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T)!, string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody!, responseText); + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new PersonsApiException(message, (int)response.StatusCode, responseText, headers, exception); + } + } + else + { + try + { + using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + using (var streamReader = new System.IO.StreamReader(responseStream)) + using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) + { + var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); + var typedBody = serializer.Deserialize(jsonTextReader); + return new ObjectResponseResult(typedBody!, string.Empty); + } + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new PersonsApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + } + } + } + + private string ConvertToString(object? value, System.Globalization.CultureInfo cultureInfo) + { + if (value == null) + { + return ""; + } + + if (value is System.Enum) + { + var name = System.Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field != null) + { + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + + var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); + return converted == null ? string.Empty : converted; + } + } + else if (value is bool) + { + return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[]) value); + } + else if (value is string[]) + { + return string.Join(",", (string[])value); + } + else if (value.GetType().IsArray) + { + var valueArray = (System.Array)value; + var valueTextArray = new string[valueArray.Length]; + for (var i = 0; i < valueArray.Length; i++) + { + valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); + } + return string.Join(",", valueTextArray); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial class CountProjectsClient : ICountProjectsClient + { + #pragma warning disable 8618 + private string _baseUrl; + #pragma warning restore 8618 + + private System.Net.Http.HttpClient _httpClient; + private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); + private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; + + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + public CountProjectsClient(string baseUrl, System.Net.Http.HttpClient httpClient) + #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. + { + BaseUrl = baseUrl; + _httpClient = httpClient; + Initialize(); + } + + private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() + { + var settings = new Newtonsoft.Json.JsonSerializerSettings(); + UpdateJsonSerializerSettings(settings); + return settings; + } + + public string BaseUrl + { + get { return _baseUrl; } + set + { + _baseUrl = value; + if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) + _baseUrl += '/'; + } + } + + protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } + + static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); + + partial void Initialize(); + + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); + partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); + partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); + + /// + /// Returns the number of Projects + /// + /// Project + /// A server side error occurred. + public virtual System.Threading.Tasks.Task Projects_CountProjects_Async(ProjectState? projectStatus, ProjectType? type) + { + return Projects_CountProjects_Async(projectStatus, type, System.Threading.CancellationToken.None); + } + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Returns the number of Projects + /// + /// Project + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task Projects_CountProjects_Async(ProjectState? projectStatus, ProjectType? type, System.Threading.CancellationToken cancellationToken) + { + var client_ = _httpClient; + var disposeClient_ = false; + try + { + using (var request_ = new System.Net.Http.HttpRequestMessage()) + { + request_.Method = new System.Net.Http.HttpMethod("GET"); + request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); + + var urlBuilder_ = new System.Text.StringBuilder(); + if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); + // Operation Path: "v1/Projects/Count" + urlBuilder_.Append("v1/Projects/Count"); + urlBuilder_.Append('?'); + if (projectStatus != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("ProjectStatus")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(projectStatus, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + if (type != null) + { + urlBuilder_.Append(System.Uri.EscapeDataString("Type")).Append('=').Append(System.Uri.EscapeDataString(ConvertToString(type, System.Globalization.CultureInfo.InvariantCulture))).Append('&'); + } + urlBuilder_.Length--; + + PrepareRequest(client_, request_, urlBuilder_); + + var url_ = urlBuilder_.ToString(); + request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); + + PrepareRequest(client_, request_, url_); + + var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); + var disposeResponse_ = true; + try + { + var headers_ = new System.Collections.Generic.Dictionary>(); + foreach (var item_ in response_.Headers) + headers_[item_.Key] = item_.Value; + if (response_.Content != null && response_.Content.Headers != null) + { + foreach (var item_ in response_.Content.Headers) + headers_[item_.Key] = item_.Value; + } + + ProcessResponse(client_, response_); + + var status_ = (int)response_.StatusCode; + if (status_ == 200) + { + var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); + if (objectResponse_.Object == null) + { + throw new PersonsApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + } + return objectResponse_.Object; + } + else + if (status_ == 400) + { + string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new PersonsApiException("Invalid request data.", status_, responseText_, headers_, null); + } + else + { + var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new PersonsApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + } + } + finally + { + if (disposeResponse_) + response_.Dispose(); + } + } + } + finally + { + if (disposeClient_) + client_.Dispose(); + } + } + + protected struct ObjectResponseResult + { + public ObjectResponseResult(T responseObject, string responseText) + { + this.Object = responseObject; + this.Text = responseText; + } + + public T Object { get; } + + public string Text { get; } + } + + public bool ReadResponseAsString { get; set; } + + protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) + { + if (response == null || response.Content == null) + { + return new ObjectResponseResult(default(T)!, string.Empty); + } + + if (ReadResponseAsString) + { + var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + try + { + var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); + return new ObjectResponseResult(typedBody!, responseText); + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; + throw new PersonsApiException(message, (int)response.StatusCode, responseText, headers, exception); + } + } + else + { + try + { + using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) + using (var streamReader = new System.IO.StreamReader(responseStream)) + using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) + { + var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); + var typedBody = serializer.Deserialize(jsonTextReader); + return new ObjectResponseResult(typedBody!, string.Empty); + } + } + catch (Newtonsoft.Json.JsonException exception) + { + var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; + throw new PersonsApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + } + } + } + + private string ConvertToString(object? value, System.Globalization.CultureInfo cultureInfo) + { + if (value == null) + { + return ""; + } + + if (value is System.Enum) + { + var name = System.Enum.GetName(value.GetType(), value); + if (name != null) + { + var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); + if (field != null) + { + var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) + as System.Runtime.Serialization.EnumMemberAttribute; + if (attribute != null) + { + return attribute.Value != null ? attribute.Value : name; + } + } + + var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); + return converted == null ? string.Empty : converted; + } + } + else if (value is bool) + { + return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); + } + else if (value is byte[]) + { + return System.Convert.ToBase64String((byte[]) value); + } + else if (value is string[]) + { + return string.Join(",", (string[])value); + } + else if (value.GetType().IsArray) + { + var valueArray = (System.Array)value; + var valueTextArray = new string[valueArray.Length]; + for (var i = 0; i < valueArray.Length; i++) + { + valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); + } + return string.Join(",", valueTextArray); + } + + var result = System.Convert.ToString(value, cultureInfo); + return result == null ? "" : result; + } + } + } #pragma warning restore 108 diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs index a8711fc0..c9eeb913 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs +++ b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs @@ -52,19 +52,57 @@ public partial interface IGetProjectClient /// /// Gets a Project /// - /// The request. /// Project /// A server side error occurred. - System.Threading.Tasks.Task Projects_GetProject_Async(GetProjectByUrnQuery request); + System.Threading.Tasks.Task Projects_GetProject_Async(int? urn_Value); /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// /// Gets a Project /// - /// The request. /// Project /// A server side error occurred. - System.Threading.Tasks.Task Projects_GetProject_Async(GetProjectByUrnQuery request, System.Threading.CancellationToken cancellationToken); + System.Threading.Tasks.Task Projects_GetProject_Async(int? urn_Value, System.Threading.CancellationToken cancellationToken); + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial interface IListProjectsClient + { + /// + /// Returns a list of Projects + /// + /// Project + /// A server side error occurred. + System.Threading.Tasks.Task> Projects_ListProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Returns a list of Projects + /// + /// Project + /// A server side error occurred. + System.Threading.Tasks.Task> Projects_ListProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count, System.Threading.CancellationToken cancellationToken); + + } + + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public partial interface ICountProjectsClient + { + /// + /// Returns the number of Projects + /// + /// Project + /// A server side error occurred. + System.Threading.Tasks.Task Projects_CountProjects_Async(ProjectState? projectStatus, ProjectType? type); + + /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. + /// + /// Returns the number of Projects + /// + /// Project + /// A server side error occurred. + System.Threading.Tasks.Task Projects_CountProjects_Async(ProjectState? projectStatus, ProjectType? type, System.Threading.CancellationToken cancellationToken); } @@ -302,7 +340,8 @@ public partial class Project : BaseAggregateRoot public string? NewTrustName { get; set; } = default!; [Newtonsoft.Json.JsonProperty("state", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public int? State { get; set; } = default!; + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public ProjectState? State { get; set; } = default!; [Newtonsoft.Json.JsonProperty("prepareId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int? PrepareId { get; set; } = default!; @@ -481,6 +520,21 @@ public static ContactId FromJson(string data) } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + public enum ProjectState + { + + [System.Runtime.Serialization.EnumMember(Value = @"Active")] + Active = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"Completed")] + Completed = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"Cancelled")] + Cancelled = 2, + + } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] public partial class User { @@ -758,21 +812,45 @@ public static IDomainEvent FromJson(string data) } [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class GetProjectByUrnQuery + public partial class ListAllProjectsResultModel { + [Newtonsoft.Json.JsonProperty("establishmentName", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string? EstablishmentName { get; set; } = default!; + + [Newtonsoft.Json.JsonProperty("projectId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public ProjectId? ProjectId { get; set; } = default!; + [Newtonsoft.Json.JsonProperty("urn", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public Urn? Urn { get; set; } = default!; + [Newtonsoft.Json.JsonProperty("conversionOrTransferDate", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(DateFormatConverter))] + public System.DateTime? ConversionOrTransferDate { get; set; } = default!; + + [Newtonsoft.Json.JsonProperty("state", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public ProjectState? State { get; set; } = default!; + + [Newtonsoft.Json.JsonProperty("projectType", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public ProjectType? ProjectType { get; set; } = default!; + + [Newtonsoft.Json.JsonProperty("isFormAMAT", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public bool? IsFormAMAT { get; set; } = default!; + + [Newtonsoft.Json.JsonProperty("assignedToFullName", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] + public string? AssignedToFullName { get; set; } = default!; + public string ToJson() { return Newtonsoft.Json.JsonConvert.SerializeObject(this, new Newtonsoft.Json.JsonSerializerSettings()); } - public static GetProjectByUrnQuery FromJson(string data) + public static ListAllProjectsResultModel FromJson(string data) { - return Newtonsoft.Json.JsonConvert.DeserializeObject(data, new Newtonsoft.Json.JsonSerializerSettings()); + return Newtonsoft.Json.JsonConvert.DeserializeObject(data, new Newtonsoft.Json.JsonSerializerSettings()); } diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json index bab08629..58ddabed 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json +++ b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json @@ -48,26 +48,150 @@ ], "summary": "Gets a Project", "operationId": "Projects_GetProject_", - "requestBody": { - "x-name": "request", - "description": "The request.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GetProjectByUrnQuery" + "parameters": [ + { + "name": "Urn.Value", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + }, + "x-position": 1 + } + ], + "responses": { + "200": { + "description": "Project", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Project" + } } } }, - "required": true, - "x-position": 1 - }, + "400": { + "description": "Invalid request data." + } + } + } + }, + "/v1/Projects/List": { + "get": { + "tags": [ + "Projects" + ], + "summary": "Returns a list of Projects", + "operationId": "Projects_ListProjects_", + "parameters": [ + { + "name": "ProjectStatus", + "in": "query", + "schema": { + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/ProjectState" + } + ] + }, + "x-position": 1 + }, + { + "name": "Type", + "in": "query", + "schema": { + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/ProjectType" + } + ] + }, + "x-position": 2 + }, + { + "name": "Page", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + }, + "x-position": 3 + }, + { + "name": "Count", + "in": "query", + "schema": { + "type": "integer", + "format": "int32" + }, + "x-position": 4 + } + ], "responses": { "200": { "description": "Project", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/Project" + "type": "array", + "items": { + "$ref": "#/components/schemas/ListAllProjectsResultModel" + } + } + } + } + }, + "400": { + "description": "Invalid request data." + } + } + } + }, + "/v1/Projects/Count": { + "get": { + "tags": [ + "Projects" + ], + "summary": "Returns the number of Projects", + "operationId": "Projects_CountProjects_", + "parameters": [ + { + "name": "ProjectStatus", + "in": "query", + "schema": { + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/ProjectState" + } + ] + }, + "x-position": 1 + }, + { + "name": "Type", + "in": "query", + "schema": { + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/ProjectType" + } + ] + }, + "x-position": 2 + } + ], + "responses": { + "200": { + "description": "Project", + "content": { + "application/json": { + "schema": { + "type": "integer", + "format": "int32" } } } @@ -363,8 +487,7 @@ "nullable": true }, "state": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/ProjectState" }, "prepareId": { "type": "integer", @@ -524,6 +647,20 @@ } } }, + "ProjectState": { + "type": "string", + "description": "", + "x-enumNames": [ + "Active", + "Completed", + "Cancelled" + ], + "enum": [ + "Active", + "Completed", + "Cancelled" + ] + }, "User": { "type": "object", "additionalProperties": false, @@ -804,12 +941,42 @@ } } }, - "GetProjectByUrnQuery": { + "ListAllProjectsResultModel": { "type": "object", "additionalProperties": false, "properties": { + "establishmentName": { + "type": "string", + "nullable": true + }, + "projectId": { + "$ref": "#/components/schemas/ProjectId" + }, "urn": { "$ref": "#/components/schemas/Urn" + }, + "conversionOrTransferDate": { + "type": "string", + "format": "date", + "nullable": true + }, + "state": { + "$ref": "#/components/schemas/ProjectState" + }, + "projectType": { + "nullable": true, + "oneOf": [ + { + "$ref": "#/components/schemas/ProjectType" + } + ] + }, + "isFormAMAT": { + "type": "boolean" + }, + "assignedToFullName": { + "type": "string", + "nullable": true } } } diff --git a/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs b/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs index df1fb7de..513af2ec 100644 --- a/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs +++ b/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs @@ -5,8 +5,11 @@ using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; using Dfe.Complete.Application.Projects.Commands.CreateProject; +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Application.Projects.Queries.CountProjects; using Dfe.Complete.Domain.Entities; using Dfe.Complete.Application.Projects.Queries.GetProject; +using Dfe.Complete.Application.Projects.Queries.ListAllProjects; namespace Dfe.Complete.Api.Controllers { @@ -39,7 +42,39 @@ public async Task CreateProject_Async([FromBody] CreateConversion [HttpGet] [SwaggerResponse(200, "Project", typeof(Project))] [SwaggerResponse(400, "Invalid request data.")] - public async Task GetProject_Async([FromBody] GetProjectByUrnQuery request, CancellationToken cancellationToken) + public async Task GetProject_Async([FromQuery] GetProjectByUrnQuery request, CancellationToken cancellationToken) + { + var project = await sender.Send(request, cancellationToken); + return Ok(project); + } + + /// + /// Returns a list of Projects + /// + /// The request. + /// The cancellation token. + //[Authorize(Policy = "API.Read")] + [HttpGet] + [Route("List/All")] + [SwaggerResponse(200, "Project", typeof(List))] + [SwaggerResponse(400, "Invalid request data.")] + public async Task ListAllProjects_Async([FromQuery] ListAllProjectsQuery request, CancellationToken cancellationToken) + { + var project = await sender.Send(request, cancellationToken); + return Ok(project); + } + + /// + /// Returns the number of Projects + /// + /// The request. + /// The cancellation token. + //[Authorize(Policy = "API.Read")] + [HttpGet] + [Route("Count/All")] + [SwaggerResponse(200, "Project", typeof(int))] + [SwaggerResponse(400, "Invalid request data.")] + public async Task CountAllProjects_Async([FromQuery] CountAllProjectsQuery request, CancellationToken cancellationToken) { var project = await sender.Send(request, cancellationToken); return Ok(project); diff --git a/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs b/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs index acd88806..dc89c1bc 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs @@ -8,7 +8,7 @@ namespace Dfe.Complete.Application.Projects.Queries.CountProjects { - public record CountProjectQuery(ProjectState? ProjectStatus, ProjectType? Type) + public record CountAllProjectsQuery(ProjectState? ProjectStatus, ProjectType? Type) : IRequest> { public override string ToString() @@ -17,15 +17,15 @@ public override string ToString() } } - public class CountProjectsQueryHandler( + public class CountAllProjectsQueryHandler( IListAllProjectsQueryService listAllProjectsQueryService, ICacheService cacheService) - : IRequestHandler> + : IRequestHandler> { - public async Task> Handle(CountProjectQuery request, CancellationToken cancellationToken) + public async Task> Handle(CountAllProjectsQuery request, CancellationToken cancellationToken) { var cacheKey = $"Project_{CacheKeyHelper.GenerateHashedCacheKey(request.ToString())}"; - var methodName = nameof(CountProjectsQueryHandler); + var methodName = nameof(CountAllProjectsQueryHandler); return await cacheService.GetOrAddAsync(cacheKey, async () => { try diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs index ac85dd8f..8bebf22c 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs @@ -19,7 +19,7 @@ public async Task OnGet() var listResponse = await sender.Send(listProjectQuery); Projects = listResponse.Value ?? []; - var countProjectQuery = new CountProjectQuery(ProjectState.Active, null); + var countProjectQuery = new CountAllProjectsQuery(ProjectState.Active, null); var countResponse = await sender.Send(countProjectQuery); Pagination = new PaginationModel("/projects/all/in-progress/all" ,PageNumber, countResponse.Value, PageSize); diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs index d3a58cc8..9bfe65b5 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs @@ -21,7 +21,7 @@ public async Task OnGet() var response = await sender.Send(listProjectQuery); Projects = response.Value?.ToList() ?? []; - var countProjectQuery = new CountProjectQuery(ProjectState.Active, ProjectType.Conversion); + var countProjectQuery = new CountAllProjectsQuery(ProjectState.Active, ProjectType.Conversion); var countResponse = await sender.Send(countProjectQuery); Pagination = new PaginationModel("/projects/all/in-progress/conversions" ,PageNumber, countResponse.Value, PageSize); diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs index a5870a1d..853f8329 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs @@ -22,7 +22,7 @@ public async Task OnGet() var response = await sender.Send(listProjectQuery); Projects = response.Value?.ToList() ?? []; - var countProjectQuery = new CountProjectQuery(ProjectState.Active, ProjectType.Transfer); + var countProjectQuery = new CountAllProjectsQuery(ProjectState.Active, ProjectType.Transfer); var countResponse = await sender.Send(countProjectQuery); Pagination = new PaginationModel("/projects/all/in-progress/transfers" ,PageNumber, countResponse.Value, PageSize); From 88b8bd99809af4d4b6c1c8b2d0c57b0ed4992cdd Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Tue, 21 Jan 2025 16:11:46 +0000 Subject: [PATCH 19/47] Rename to CountAllProjects and ListAllProjects refactor --- .../Generated/Client.g.cs | 28 +++++++++---------- .../Generated/Contracts.g.cs | 12 ++++---- .../Generated/swagger.json | 8 +++--- .../Controllers/ProjectsController.cs | 2 +- .../Model/ListAllProjectsResultModel.cs | 3 +- .../CountAllProjects.cs} | 2 +- .../AllProjectsInProgress.cshtml.cs | 2 +- .../ConversionProjectsInProgress.cshtml.cs | 2 +- .../TransferProjectsInProgress.cshtml.cs | 2 +- 9 files changed, 30 insertions(+), 31 deletions(-) rename src/Core/Dfe.Complete.Application/Projects/Queries/{CountProjects/CountProjects.cs => CountAllProjects/CountAllProjects.cs} (95%) diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs b/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs index aafe152f..cd54c7dd 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs +++ b/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs @@ -542,7 +542,7 @@ private string ConvertToString(object? value, System.Globalization.CultureInfo c } [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ListProjectsClient : IListProjectsClient + public partial class ListAllProjectsClient : IListAllProjectsClient { #pragma warning disable 8618 private string _baseUrl; @@ -553,7 +553,7 @@ public partial class ListProjectsClient : IListProjectsClient private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public ListProjectsClient(string baseUrl, System.Net.Http.HttpClient httpClient) + public ListAllProjectsClient(string baseUrl, System.Net.Http.HttpClient httpClient) #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. { BaseUrl = baseUrl; @@ -594,9 +594,9 @@ public string BaseUrl /// /// Project /// A server side error occurred. - public virtual System.Threading.Tasks.Task> Projects_ListProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count) + public virtual System.Threading.Tasks.Task> Projects_ListAllProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count) { - return Projects_ListProjects_Async(projectStatus, type, page, count, System.Threading.CancellationToken.None); + return Projects_ListAllProjects_Async(projectStatus, type, page, count, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. @@ -605,7 +605,7 @@ public string BaseUrl /// /// Project /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> Projects_ListProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task> Projects_ListAllProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -618,8 +618,8 @@ public string BaseUrl var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "v1/Projects/List" - urlBuilder_.Append("v1/Projects/List"); + // Operation Path: "v1/Projects/List/All" + urlBuilder_.Append("v1/Projects/List/All"); urlBuilder_.Append('?'); if (projectStatus != null) { @@ -810,7 +810,7 @@ private string ConvertToString(object? value, System.Globalization.CultureInfo c } [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CountProjectsClient : ICountProjectsClient + public partial class CountAllProjectsClient : ICountAllProjectsClient { #pragma warning disable 8618 private string _baseUrl; @@ -821,7 +821,7 @@ public partial class CountProjectsClient : ICountProjectsClient private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public CountProjectsClient(string baseUrl, System.Net.Http.HttpClient httpClient) + public CountAllProjectsClient(string baseUrl, System.Net.Http.HttpClient httpClient) #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. { BaseUrl = baseUrl; @@ -862,9 +862,9 @@ public string BaseUrl /// /// Project /// A server side error occurred. - public virtual System.Threading.Tasks.Task Projects_CountProjects_Async(ProjectState? projectStatus, ProjectType? type) + public virtual System.Threading.Tasks.Task Projects_CountAllProjects_Async(ProjectState? projectStatus, ProjectType? type) { - return Projects_CountProjects_Async(projectStatus, type, System.Threading.CancellationToken.None); + return Projects_CountAllProjects_Async(projectStatus, type, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. @@ -873,7 +873,7 @@ public virtual System.Threading.Tasks.Task Projects_CountProjects_Async(Pro /// /// Project /// A server side error occurred. - public virtual async System.Threading.Tasks.Task Projects_CountProjects_Async(ProjectState? projectStatus, ProjectType? type, System.Threading.CancellationToken cancellationToken) + public virtual async System.Threading.Tasks.Task Projects_CountAllProjects_Async(ProjectState? projectStatus, ProjectType? type, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -886,8 +886,8 @@ public virtual async System.Threading.Tasks.Task Projects_CountProjects_Asy var urlBuilder_ = new System.Text.StringBuilder(); if (!string.IsNullOrEmpty(_baseUrl)) urlBuilder_.Append(_baseUrl); - // Operation Path: "v1/Projects/Count" - urlBuilder_.Append("v1/Projects/Count"); + // Operation Path: "v1/Projects/Count/All" + urlBuilder_.Append("v1/Projects/Count/All"); urlBuilder_.Append('?'); if (projectStatus != null) { diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs index c9eeb913..5c80e825 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs +++ b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs @@ -67,14 +67,14 @@ public partial interface IGetProjectClient } [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial interface IListProjectsClient + public partial interface IListAllProjectsClient { /// /// Returns a list of Projects /// /// Project /// A server side error occurred. - System.Threading.Tasks.Task> Projects_ListProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count); + System.Threading.Tasks.Task> Projects_ListAllProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count); /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// @@ -82,19 +82,19 @@ public partial interface IListProjectsClient /// /// Project /// A server side error occurred. - System.Threading.Tasks.Task> Projects_ListProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count, System.Threading.CancellationToken cancellationToken); + System.Threading.Tasks.Task> Projects_ListAllProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count, System.Threading.CancellationToken cancellationToken); } [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] - public partial interface ICountProjectsClient + public partial interface ICountAllProjectsClient { /// /// Returns the number of Projects /// /// Project /// A server side error occurred. - System.Threading.Tasks.Task Projects_CountProjects_Async(ProjectState? projectStatus, ProjectType? type); + System.Threading.Tasks.Task Projects_CountAllProjects_Async(ProjectState? projectStatus, ProjectType? type); /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// @@ -102,7 +102,7 @@ public partial interface ICountProjectsClient /// /// Project /// A server side error occurred. - System.Threading.Tasks.Task Projects_CountProjects_Async(ProjectState? projectStatus, ProjectType? type, System.Threading.CancellationToken cancellationToken); + System.Threading.Tasks.Task Projects_CountAllProjects_Async(ProjectState? projectStatus, ProjectType? type, System.Threading.CancellationToken cancellationToken); } diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json index 58ddabed..5a968002 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json +++ b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json @@ -76,13 +76,13 @@ } } }, - "/v1/Projects/List": { + "/v1/Projects/List/All": { "get": { "tags": [ "Projects" ], "summary": "Returns a list of Projects", - "operationId": "Projects_ListProjects_", + "operationId": "Projects_ListAllProjects_", "parameters": [ { "name": "ProjectStatus", @@ -149,13 +149,13 @@ } } }, - "/v1/Projects/Count": { + "/v1/Projects/Count/All": { "get": { "tags": [ "Projects" ], "summary": "Returns the number of Projects", - "operationId": "Projects_CountProjects_", + "operationId": "Projects_CountAllProjects_", "parameters": [ { "name": "ProjectStatus", diff --git a/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs b/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs index 513af2ec..7d7e1aad 100644 --- a/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs +++ b/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs @@ -6,7 +6,7 @@ using Swashbuckle.AspNetCore.Annotations; using Dfe.Complete.Application.Projects.Commands.CreateProject; using Dfe.Complete.Application.Projects.Model; -using Dfe.Complete.Application.Projects.Queries.CountProjects; +using Dfe.Complete.Application.Projects.Queries.CountAllProjects; using Dfe.Complete.Domain.Entities; using Dfe.Complete.Application.Projects.Queries.GetProject; using Dfe.Complete.Application.Projects.Queries.ListAllProjects; diff --git a/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs b/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs index e4b6e748..40d41b3b 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs @@ -1,5 +1,4 @@ -using Dfe.Complete.Domain.Entities; -using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Domain.Enums; using Dfe.Complete.Domain.ValueObjects; namespace Dfe.Complete.Application.Projects.Model; diff --git a/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs b/src/Core/Dfe.Complete.Application/Projects/Queries/CountAllProjects/CountAllProjects.cs similarity index 95% rename from src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs rename to src/Core/Dfe.Complete.Application/Projects/Queries/CountAllProjects/CountAllProjects.cs index dc89c1bc..4feabfec 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Queries/CountProjects/CountProjects.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Queries/CountAllProjects/CountAllProjects.cs @@ -6,7 +6,7 @@ using MediatR; using Microsoft.EntityFrameworkCore; -namespace Dfe.Complete.Application.Projects.Queries.CountProjects +namespace Dfe.Complete.Application.Projects.Queries.CountAllProjects { public record CountAllProjectsQuery(ProjectState? ProjectStatus, ProjectType? Type) : IRequest> diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs index 8bebf22c..d0daef4c 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml.cs @@ -1,5 +1,5 @@ using Dfe.Complete.Application.Projects.Model; -using Dfe.Complete.Application.Projects.Queries.CountProjects; +using Dfe.Complete.Application.Projects.Queries.CountAllProjects; using Dfe.Complete.Application.Projects.Queries.ListAllProjects; using Dfe.Complete.Domain.Enums; using Dfe.Complete.Pages.Pagination; diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs index 9bfe65b5..d4184e49 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ConversionProjectsInProgress.cshtml.cs @@ -1,5 +1,5 @@ using Dfe.Complete.Application.Projects.Model; -using Dfe.Complete.Application.Projects.Queries.CountProjects; +using Dfe.Complete.Application.Projects.Queries.CountAllProjects; using Dfe.Complete.Application.Projects.Queries.ListAllProjects; using Dfe.Complete.Domain.Enums; using Dfe.Complete.Pages.Pagination; diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs index 853f8329..6cdac1e9 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/TransferProjectsInProgress.cshtml.cs @@ -1,5 +1,5 @@ using Dfe.Complete.Application.Projects.Model; -using Dfe.Complete.Application.Projects.Queries.CountProjects; +using Dfe.Complete.Application.Projects.Queries.CountAllProjects; using Dfe.Complete.Application.Projects.Queries.ListAllProjects; using Dfe.Complete.Domain.Enums; using Dfe.Complete.Pages.Pagination; From 6a6ecded577529924bf3b35b2d35e09d365c54d5 Mon Sep 17 00:00:00 2001 From: Sukhvinder Bhullar Date: Tue, 21 Jan 2025 20:13:31 +0000 Subject: [PATCH 20/47] Rationalised Autofixture customisation, added more columns, refactored to build columns --- .../Generated/Client.g.cs | 6 +- .../Generated/Contracts.g.cs | 52 +-- .../Generated/swagger.json | 2 +- .../Common/Models/ConversionCsvModel.cs | 2 +- .../CsvExport/Builders/BlankIfEmpty.cs | 19 + .../Services/CsvExport/Builders/DefaultIf.cs | 23 ++ .../CsvExport/Builders/DefaultIfEmpty.cs | 19 + .../Builders/DfeNumberLAESTABBuilder.cs | 17 + .../Services/CsvExport/Builders/FormAMat.cs | 21 ++ .../CsvExport/Builders/IColumnBuilder.cs | 7 + .../CsvExport/Builders/ProjectTypeBuilder.cs | 26 ++ .../Conversion/ConversionRowGenerator.cs | 50 +-- .../CsvExport/ConversionCsvQueryService.cs | 8 +- .../CsvExport/Builders/BlankIfEmptyTests.cs | 38 ++ .../CsvExport/Builders/BuilderTestModel.cs | 5 + .../CsvExport/Builders/DefaultIfEmptyTests.cs | 39 +++ .../CsvExport/Builders/DefaultIfTests.cs | 29 ++ .../Builders/DfeNumberLAESTABBuilderTests.cs | 27 ++ .../CsvExport/Builders/FormAMatTests.cs | 28 ++ .../CsvExport/Builders/ProjectTypeTests.cs | 43 +++ .../Conversion/ConversionRowGeneratorTests.cs | 326 +++++------------- .../IgnoreVirtualMembersCustomization .cs | 42 --- .../Models/EstablishmentsCustomization.cs | 17 +- .../Models/ProjectCustomization.cs | 8 +- .../Customizations/Models/UrnCustomization.cs | 14 + 25 files changed, 511 insertions(+), 357 deletions(-) create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/BlankIfEmpty.cs create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIf.cs create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIfEmpty.cs create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DfeNumberLAESTABBuilder.cs create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/FormAMat.cs create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/IColumnBuilder.cs create mode 100644 src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/ProjectTypeBuilder.cs create mode 100644 src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/BlankIfEmptyTests.cs create mode 100644 src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/BuilderTestModel.cs create mode 100644 src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DefaultIfEmptyTests.cs create mode 100644 src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DefaultIfTests.cs create mode 100644 src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DfeNumberLAESTABBuilderTests.cs create mode 100644 src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/FormAMatTests.cs create mode 100644 src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/ProjectTypeTests.cs delete mode 100644 src/Tests/Dfe.Complete.Tests.Common/Customizations/Behaviours/IgnoreVirtualMembersCustomization .cs create mode 100644 src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/UrnCustomization.cs diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs b/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs index 29975837..df7d926a 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs +++ b/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs @@ -1,6 +1,6 @@ //---------------------- // -// Generated using the NSwag toolchain v14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) +// Generated using the NSwag toolchain v14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) // //---------------------- @@ -26,7 +26,7 @@ namespace Dfe.Complete.Client { using System = global::System; - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class CreateProjectClient : ICreateProjectClient { #pragma warning disable 8618 @@ -285,7 +285,7 @@ private string ConvertToString(object? value, System.Globalization.CultureInfo c } } - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class GetProjectClient : IGetProjectClient { #pragma warning disable 8618 diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs index 527a80e7..d02baf4e 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs +++ b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs @@ -1,6 +1,6 @@ //---------------------- // -// Generated using the NSwag toolchain v14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) +// Generated using the NSwag toolchain v14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org) // //---------------------- @@ -24,7 +24,7 @@ namespace Dfe.Complete.Client.Contracts { using System = global::System; - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial interface ICreateProjectClient { /// @@ -46,7 +46,7 @@ public partial interface ICreateProjectClient } - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial interface IGetProjectClient { /// @@ -68,7 +68,7 @@ public partial interface IGetProjectClient } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ProjectId { [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -89,7 +89,7 @@ public static ProjectId FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class CreateConversionProjectCommand { [Newtonsoft.Json.JsonProperty("urn", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -151,7 +151,7 @@ public static CreateConversionProjectCommand FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Urn { [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -172,7 +172,7 @@ public static Urn FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Ukprn { [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -193,7 +193,7 @@ public static Ukprn FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Project : BaseAggregateRoot { [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -344,7 +344,7 @@ public static Project FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class UserId { [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -365,7 +365,7 @@ public static UserId FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public enum ProjectType { @@ -377,7 +377,7 @@ public enum ProjectType } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public enum Region { @@ -410,7 +410,7 @@ public enum Region } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public enum TaskType { @@ -422,7 +422,7 @@ public enum TaskType } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public enum ProjectTeam { @@ -461,7 +461,7 @@ public enum ProjectTeam } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ContactId { [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -482,7 +482,7 @@ public static ContactId FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public enum ProjectState { @@ -497,7 +497,7 @@ public enum ProjectState } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class User { [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -578,7 +578,7 @@ public static User FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Note { [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -629,7 +629,7 @@ public static Note FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class NoteId { [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -650,7 +650,7 @@ public static NoteId FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Contact { [Newtonsoft.Json.JsonProperty("id", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -710,7 +710,7 @@ public static Contact FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class LocalAuthorityId { [Newtonsoft.Json.JsonProperty("value", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -731,7 +731,7 @@ public static LocalAuthorityId FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public abstract partial class BaseAggregateRoot { [Newtonsoft.Json.JsonProperty("domainEvents", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -752,7 +752,7 @@ public static BaseAggregateRoot FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public abstract partial class IDomainEvent { [Newtonsoft.Json.JsonProperty("occurredOn", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -773,7 +773,7 @@ public static IDomainEvent FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class GetProjectByUrnQuery { [Newtonsoft.Json.JsonProperty("urn", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] @@ -794,7 +794,7 @@ public static GetProjectByUrnQuery FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] internal class DateFormatConverter : Newtonsoft.Json.Converters.IsoDateTimeConverter { public DateFormatConverter() @@ -805,7 +805,7 @@ public DateFormatConverter() - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class PersonsApiException : System.Exception { public int StatusCode { get; private set; } @@ -828,7 +828,7 @@ public override string ToString() } } - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))")] + [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class PersonsApiException : PersonsApiException { public TResult Result { get; private set; } diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json index 12227f74..32dcab50 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json +++ b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json @@ -1,5 +1,5 @@ { - "x-generator": "NSwag v14.1.0.0 (NJsonSchema v11.0.2.0 (Newtonsoft.Json v13.0.0.0))", + "x-generator": "NSwag v14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))", "openapi": "3.0.0", "info": { "title": "Api", diff --git a/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs b/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs index f5bcca31..8abf74dd 100644 --- a/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs +++ b/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs @@ -2,7 +2,7 @@ namespace Dfe.Complete.Application.Common.Models { - public record ConversionCsvModel(Project Project, GiasEstablishment Establishment); + public record ConversionCsvModel(Project Project, GiasEstablishment CurrentSchool, GiasEstablishment Academy); //public class ConversionCsvModel //{ diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/BlankIfEmpty.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/BlankIfEmpty.cs new file mode 100644 index 00000000..542f2bae --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/BlankIfEmpty.cs @@ -0,0 +1,19 @@ +namespace Dfe.Complete.Application.Services.CsvExport.Builders +{ + public class BlankIfEmpty(Func func) : IColumnBuilder + { + public string Build(T input) + { + object? value = func(input); + + if (value == null) + { + return string.Empty; + } + else + { + return value.ToString(); + } + } + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIf.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIf.cs new file mode 100644 index 00000000..5b2e747c --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIf.cs @@ -0,0 +1,23 @@ +namespace Dfe.Complete.Application.Services.CsvExport.Builders +{ + public class DefaultIfEmpty(Func func, string defaultValue) : IColumnBuilder + { + public string Build(T input) + { + object? value = func(input); + + if (value == null) + { + return defaultValue; + } + if (value.ToString() == string.Empty) + { + return defaultValue; + } + else + { + return value.ToString(); + } + } + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIfEmpty.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIfEmpty.cs new file mode 100644 index 00000000..5e47869f --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIfEmpty.cs @@ -0,0 +1,19 @@ +namespace Dfe.Complete.Application.Services.CsvExport.Builders +{ + public class DefaultIf(Func condition, Func valueFunc, string defaultValue) : IColumnBuilder + { + public string Build(T input) + { + object? value = valueFunc(input); + + if (condition(input)) + { + return defaultValue; + } + else + { + return value.ToString(); + } + } + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DfeNumberLAESTABBuilder.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DfeNumberLAESTABBuilder.cs new file mode 100644 index 00000000..a777090f --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DfeNumberLAESTABBuilder.cs @@ -0,0 +1,17 @@ +using Dfe.Complete.Application.Common.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dfe.Complete.Application.Services.CsvExport.Builders +{ + public class DfeNumberLAESTABBuilder: IColumnBuilder + { + public string Build(ConversionCsvModel input) + { + return input.Academy.LocalAuthorityCode + "/" + input.Academy.EstablishmentNumber; + } + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/FormAMat.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/FormAMat.cs new file mode 100644 index 00000000..d69612b7 --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/FormAMat.cs @@ -0,0 +1,21 @@ +using Dfe.Complete.Application.Common.Models; + +namespace Dfe.Complete.Application.Services.CsvExport.Builders +{ + public class FormAMat() : IColumnBuilder + { + public string Build(ConversionCsvModel input) + { + var projectType = input.Project.Type; + + if (projectType == Domain.Enums.ProjectType.Conversion) + { + return "join a MAT"; + } + else + { + return "form a MAT"; + } + } + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/IColumnBuilder.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/IColumnBuilder.cs new file mode 100644 index 00000000..eb938ffe --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/IColumnBuilder.cs @@ -0,0 +1,7 @@ +namespace Dfe.Complete.Application.Services.CsvExport.Builders +{ + public interface IColumnBuilder + { + string Build(T value); + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/ProjectTypeBuilder.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/ProjectTypeBuilder.cs new file mode 100644 index 00000000..58bf8427 --- /dev/null +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/ProjectTypeBuilder.cs @@ -0,0 +1,26 @@ +using Dfe.Complete.Application.Common.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dfe.Complete.Application.Services.CsvExport.Builders +{ + public class ProjectTypeBuilder: IColumnBuilder + { + public string Build(ConversionCsvModel input) + { + var projectType = input.Project.Type; + + if (projectType == Domain.Enums.ProjectType.Conversion) + { + return "Conversion"; + } + else + { + return "Transfer"; + } + } + } +} diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs index d63e83b3..d2fd516c 100644 --- a/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs @@ -1,55 +1,21 @@ using Dfe.Complete.Application.Common.Models; +using Dfe.Complete.Application.Services.CsvExport.Builders; using System.Text; namespace Dfe.Complete.Application.Services.CsvExport.Conversion { - - public interface IColumnBuilder - { - string Build(T value); - } - - public class BlankIfEmpty(Func func): IColumnBuilder - { - public string Build(T input) - { - object? value = func(input); - - if(value == null) - { - return string.Empty; - } - else - { - return value.ToString(); - } - } - } - - public class FormAMat() : IColumnBuilder - { - public string Build(ConversionCsvModel input) - { - var projectType = input.Project.Type; - - if (projectType == Domain.Enums.ProjectType.Conversion) - { - return "join a MAT"; - } - else - { - return "form a MAT"; - } - } - } - public class ConversionRowGenerator : IRowGenerator { + private const string Unconfirmed = "unconfirmed"; + private IColumnBuilder[] _columnBuilders = [ - new BlankIfEmpty(x => x.Establishment.Name), + new BlankIfEmpty(x => x.CurrentSchool.Name), new BlankIfEmpty(x => x.Project.Urn), - + new ProjectTypeBuilder(), + new DefaultIf(x => x.Project.AcademyUrn == null, x => x.Academy.Name, Unconfirmed), + new DefaultIf(x => x.Project.AcademyUrn == null, x => x.Academy.Urn, Unconfirmed), + new DfeNumberLAESTABBuilder(), ]; diff --git a/src/Core/Dfe.Complete.Infrastructure/QueryServices/CsvExport/ConversionCsvQueryService.cs b/src/Core/Dfe.Complete.Infrastructure/QueryServices/CsvExport/ConversionCsvQueryService.cs index 3d7a66ab..38665135 100644 --- a/src/Core/Dfe.Complete.Infrastructure/QueryServices/CsvExport/ConversionCsvQueryService.cs +++ b/src/Core/Dfe.Complete.Infrastructure/QueryServices/CsvExport/ConversionCsvQueryService.cs @@ -13,8 +13,12 @@ public IQueryable GetByMonth(int month, int year) var query = context.Projects .Where(project => project.Type == ProjectType.Conversion) .Where(project => project.SignificantDate.Value.Month == month && project.SignificantDate.Value.Month == year) - .Join(context.GiasEstablishments, project => project.Urn, establishment => establishment.Urn, - (project, establishment) => new ConversionCsvModel(project, establishment)); + .Join(context.GiasEstablishments, project => project.Urn, establishment => establishment.Urn, + (project, establishment) => new { project, establishment }) + .Join(context.GiasEstablishments, composite => composite.project.AcademyUrn, establishment => establishment.Urn, + (composite, academy) => new ConversionCsvModel( composite.project, composite.establishment, academy)) + + ; return query; diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/BlankIfEmptyTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/BlankIfEmptyTests.cs new file mode 100644 index 00000000..43dc3c04 --- /dev/null +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/BlankIfEmptyTests.cs @@ -0,0 +1,38 @@ +using Dfe.Complete.Application.Services.CsvExport.Builders; + +namespace Dfe.Complete.Application.Tests.Services.CsvExport.Builders +{ + + public class BlankIfEmptyTests + { + [Fact] + public void Build_WhenValueIsNull_ReturnsEmptyString() + { + var builder = new BlankIfEmpty(x => x.Value); + + var result = builder.Build(new BuilderTestModel(null)); + + Assert.Equal(string.Empty, result); + } + + [Fact] + public void Build_WhenValueIsEmpty_ReturnsEmptyString() + { + var builder = new BlankIfEmpty(x => x.Value); + + var result = builder.Build(new BuilderTestModel(string.Empty)); + + Assert.Equal(string.Empty, result); + } + + [Fact] + public void Build_WhenValueIsNotNullOrEmpty_ReturnsValueToString() + { + var builder = new BlankIfEmpty(x => x.Value); + + var result = builder.Build(new BuilderTestModel("Hello")); + + Assert.Equal("Hello", result); + } + } +} diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/BuilderTestModel.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/BuilderTestModel.cs new file mode 100644 index 00000000..6bf12e7e --- /dev/null +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/BuilderTestModel.cs @@ -0,0 +1,5 @@ +namespace Dfe.Complete.Application.Tests.Services.CsvExport.Builders +{ + public record BuilderTestModel(string? Value); + public record ConditionalTestModel(string? Value, bool Condition); +} diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DefaultIfEmptyTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DefaultIfEmptyTests.cs new file mode 100644 index 00000000..9ab346a4 --- /dev/null +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DefaultIfEmptyTests.cs @@ -0,0 +1,39 @@ +using Dfe.Complete.Application.Services.CsvExport.Builders; + +namespace Dfe.Complete.Application.Tests.Services.CsvExport.Builders +{ + public class DefaultIfEmptyTests + { + private const string DefaultValue = "Default"; + + [Fact] + public void Build_WhenValueIsNull_ReturnsDefault() + { + var builder = new DefaultIfEmpty(x => x.Value, DefaultValue); + + var result = builder.Build(new BuilderTestModel(null)); + + Assert.Equal(DefaultValue, result); + } + + [Fact] + public void Build_WhenValueIsEmpty_ReturnsDefault() + { + var builder = new DefaultIfEmpty(x => x.Value, DefaultValue); + + var result = builder.Build(new BuilderTestModel(string.Empty)); + + Assert.Equal(DefaultValue, result); + } + + [Fact] + public void Build_WhenValueIsNotNullOrEmpty_ReturnsValueToString() + { + var builder = new DefaultIfEmpty(x => x.Value, DefaultValue); + + var result = builder.Build(new BuilderTestModel("Hello")); + + Assert.Equal("Hello", result); + } + } +} diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DefaultIfTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DefaultIfTests.cs new file mode 100644 index 00000000..6e9b89e8 --- /dev/null +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DefaultIfTests.cs @@ -0,0 +1,29 @@ +using Dfe.Complete.Application.Services.CsvExport.Builders; + +namespace Dfe.Complete.Application.Tests.Services.CsvExport.Builders +{ + public class DefaultIfTests + { + private const string DefaultValue = "Default"; + + [Fact] + public void Build_WhenValueIsNull_ReturnsDefault() + { + var builder = new DefaultIf(x => x.Condition, x => x.Value, DefaultValue); + + var result = builder.Build(new ConditionalTestModel("Hello", true)); + + Assert.Equal(DefaultValue, result); + } + + [Fact] + public void Build_WhenValueIsNotNullOrEmpty_ReturnsValueToString() + { + var builder = new DefaultIf(x => x.Condition, x => x.Value, DefaultValue); + + var result = builder.Build(new ConditionalTestModel("Hello", false)); + + Assert.Equal("Hello", result); + } + } +} diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DfeNumberLAESTABBuilderTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DfeNumberLAESTABBuilderTests.cs new file mode 100644 index 00000000..54e569e6 --- /dev/null +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DfeNumberLAESTABBuilderTests.cs @@ -0,0 +1,27 @@ +using Dfe.Complete.Application.Common.Models; +using Dfe.Complete.Application.Services.CsvExport.Builders; +using Dfe.Complete.Domain.Entities; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Tests.Common.Customizations.Behaviours; +using Dfe.Complete.Tests.Common.Customizations.Models; +using DfE.CoreLibs.Testing.AutoFixture.Attributes; +using DfE.CoreLibs.Testing.AutoFixture.Customizations; + +namespace Dfe.Complete.Application.Tests.Services.CsvExport.Builders +{ + + public class DfeNumberLAESTABBuilderTests + { + [Theory] + [CustomAutoData(typeof(EstablishmentsCustomization))] + + public void BuildsCorrectDfeNumber(GiasEstablishment academy) + { + var builder = new DfeNumberLAESTABBuilder(); + + var result = builder.Build(new ConversionCsvModel(null, null, academy)); + + Assert.Equal(academy.LocalAuthorityCode + "/" + academy.EstablishmentNumber, result); + } + } +} diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/FormAMatTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/FormAMatTests.cs new file mode 100644 index 00000000..186dbbb9 --- /dev/null +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/FormAMatTests.cs @@ -0,0 +1,28 @@ +using Dfe.Complete.Application.Common.Models; +using Dfe.Complete.Application.Services.CsvExport.Builders; +using Dfe.Complete.Domain.Entities; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Tests.Common.Customizations.Behaviours; +using Dfe.Complete.Tests.Common.Customizations.Models; +using DfE.CoreLibs.Testing.AutoFixture.Attributes; +using DfE.CoreLibs.Testing.AutoFixture.Customizations; + +namespace Dfe.Complete.Application.Tests.Services.CsvExport.Builders +{ + + public class FormAMatTests + { + [Theory] + [CustomAutoData(typeof(ProjectCustomization), typeof(DateOnlyCustomization))] + + public void Build_When_Conversion(Project project) + { + project.Type = ProjectType.Conversion; + var builder = new FormAMat(); + + var result = builder.Build(new ConversionCsvModel(project, null, null)); + + Assert.Equal("", result); + } + } +} diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/ProjectTypeTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/ProjectTypeTests.cs new file mode 100644 index 00000000..69273e51 --- /dev/null +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/ProjectTypeTests.cs @@ -0,0 +1,43 @@ +using Dfe.Complete.Application.Common.Models; +using Dfe.Complete.Application.Services.CsvExport.Builders; +using Dfe.Complete.Domain.Entities; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Tests.Common.Customizations.Behaviours; +using Dfe.Complete.Tests.Common.Customizations.Models; +using DfE.CoreLibs.Testing.AutoFixture.Attributes; +using DfE.CoreLibs.Testing.AutoFixture.Customizations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dfe.Complete.Application.Tests.Services.CsvExport.Builders +{ + public class ProjectTypeTests + { + [Theory] + [CustomAutoData(typeof(ProjectCustomization), typeof(DateOnlyCustomization), typeof(IgnoreVirtualMembersCustomisation))] + public void Build_When_Conversion(Project project) + { + project.Type = ProjectType.Conversion; + var builder = new ProjectTypeBuilder(); + + var result = builder.Build(new ConversionCsvModel(project, null, null)); + + Assert.Equal("Conversion", result); + } + + [Theory] + [CustomAutoData(typeof(ProjectCustomization), typeof(DateOnlyCustomization), typeof(IgnoreVirtualMembersCustomisation))] + public void Build_When_Transfer(Project project) + { + project.Type = ProjectType.Transfer; + var builder = new ProjectTypeBuilder(); + + var result = builder.Build(new ConversionCsvModel(project, null, null)); + + Assert.Equal("Transfer", result); + } + } +} diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs index 124bb19f..9f57d14e 100644 --- a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs @@ -4,6 +4,7 @@ using Dfe.Complete.Application.Services.CsvExport.Conversion; using Dfe.Complete.Domain.Entities; using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Domain.ValueObjects; using Dfe.Complete.Tests.Common.Customizations.Behaviours; using Dfe.Complete.Tests.Common.Customizations.Models; using DfE.CoreLibs.Testing.AutoFixture.Attributes; @@ -15,95 +16,25 @@ public class ConversionRowGeneratorTests [Theory] - [CustomAutoData(typeof(ProjectCustomization), typeof(EstablishmentsCustomization), typeof(DateOnlyCustomization), typeof(IgnoreVirtualMembersCustomisation))] - public void RowGeneratesAccountsForBlankData(Project project, GiasEstablishment establishment) + [CustomAutoData(typeof(ProjectCustomization), typeof(EstablishmentsCustomization))] + public void RowGeneratesAccountsForBlankData(Project project, GiasEstablishment currentSchool, GiasEstablishment academy) { project.Type = ProjectType.Conversion; - var model = new ConversionCsvModel(project, establishment); - - //var model = new ConversionCsvModel() - //{ - // AllConditionsMet = true, - // AcademyDfENumber = null, - // AcademyName = null, - // AcademyOrderType = null, - // AcademyUrn = null, - // AdvisoryBoardConditions = null, - // AdvisoryBoardDate = new DateOnly(2024, 3, 2), - // CompletedGrantPaymentCertificateReceived = true, - // ConfirmedConversionDate = new DateOnly(2024, 4, 3), - // ConversionType = "ConversionType", - // Diocese = "Diocese", - // HeadteacherName = "HeadteacherName", - // IncomingTrustAddress1 = "IncomingTrustAddress1", - // IncomingTrustAddress2 = "IncomingTrustAddress2", - // IncomingTrustAddress3 = "IncomingTrustAddress3", - // IncomingTrustAddressCounty = "IncomingTrustAddressCounty", - // IncomingTrustAddressPostcode = "IncomingTrustAddressPostcode", - // IncomingTrustAddressTown = "IncomingTrustAddressTown", - // IncomingTrustCompaniesHouseNumber = "IncomingTrustCompaniesHouseNumber", - // IncomingTrustGroupIdentifier = "IncomingTrustGroupIdentifier", - // IncomingTrustName = "IncomingTrustName", - // IncomingTrustSharepointLink = "IncomingTrustSharepointLink", - // IncomingTrustUKPRN = 12345647, - // AssignedToName = "AssignedToName", - // DioceseContactEmail = "DioceseContactEmail", - // DioceseContactName = "DioceseContactName", - // DirectorOfChildServicesEmail = "DirectorOfChildServicesEmail", - // DirectorOfChildServicesName = "DirectorOfChildServicesName", - // DirectorOfChildServicesRole = "DirectorOfChildServicesRole", - // HeadteacherEmail = "HeadteacherEmail", - // HeadteacherRole = "HeadteacherRole", - // IncomingTrustCEOEmail = "IncomingTrustCEOEmail", - // IncomingTrustCEOName = "IncomingTrustCEOName", - // IncomingTrustCEORole = "IncomingTrustCEORole", - // LocalAuthority = "LocalAuthority", - // LocalAuthorityContactEmail = "LocalAuthorityContactEmail", - // LocalAuthorityContactName = "LocalAuthorityContactName", - // PrimaryContactForIncomingTrustEmail = "PrimaryContactForIncomingTrustEmail", - // PrimaryContactForIncomingTrustName = "PrimaryContactForIncomingTrustName", - // PrimaryContactForOutgoingTrustEmail = "PrimaryContactForOutgoingTrustEmail", - // PrimaryContactForOutgoingTrustName = "PrimaryContactForOutgoingTrustName", - // ProjectCreatedBy = "ProjectCreatedBy", - // ProjectCreatedByEmailAddress = "ProjectCreatedByEmailAddress", - // ProjectMainContactName = "ProjectMainContactName", - // FormAMat = false, - // ProposedCapacityForPupilsInReceptionToYear6 = 123, - // ProposedCapacityForPupilsInYears7To11 = 456, - // ProposedCapacityForStudentsInYear12OrAbove = 789, - // ProvisionalConversionDate = new DateOnly(2024, 5, 6), - // ReasonForCommercialInsurance = "ReasonForCommercialInsurance", - // Region = "Region", - // RiskProtectionArrangement = "RiskProtectionArrangement", - // SchoolAddress1 = "SchoolAddress1", - // SchoolAddress2 = "SchoolAddress2", - // SchoolAddress3 = "SchoolAddress3", - // SchoolAgeRange = "SchoolAgeRange", - // SchoolCounty = "SchoolCounty", - // SchoolName = "SchoolName", - // SchoolPhase = "SchoolPhase", - // SchoolPostcode = "SchoolPostcode", - // SchoolSharepointFolder = "SchoolSharepointFolder", - // SchoolTown = "SchoolTown", - // SchoolType = "SchoolType", - // SolicitorContactEmail = "SolicitorContactEmail", - // SchoolUrn = 12345, - // SolicitorContactName = "SolicitorContactName", - // TeamManagingTheProject = "TeamManagingTheProject", - // TwoRequiresImprovement = true - //}; + project.AcademyUrn = null; + var model = new ConversionCsvModel(project, currentSchool, academy); + var generator = new ConversionRowGenerator(); generator.GenerateRow(model); var result = generator.GenerateRow(model).Split(","); - Assert.Equal(establishment.Name, result[0]); + Assert.Equal(currentSchool.Name, result[0]); Assert.Equal(project.Urn.ToString(), result[1]); - Assert.Equal("join a MAT", result[2]); - //Assert.Equal("AcademyName", result[3]); - //Assert.Equal("54321", result[4]); + Assert.Equal("Conversion", result[2]); + Assert.Equal("unconfirmed", result[3]); + Assert.Equal("unconfirmed", result[4]); //Assert.Equal("AcademyDfENumber", result[5]); //Assert.Equal("IncomingTrustName", result[6]); //Assert.Equal("LocalAuthority", result[7]); @@ -167,170 +98,89 @@ public void RowGeneratesAccountsForBlankData(Project project, GiasEstablishment //Assert.Equal("DirectorOfChildServicesName", result[65]); //Assert.Equal("DirectorOfChildServicesEmail", result[66]); //Assert.Equal("DirectorOfChildServicesRole", result[67]); - - - - - } - //[Fact] - //public void RowGeneratesBasedOnModel() - //{ - // //var model = new ConversionCsvModel() - // //{ - // // AllConditionsMet = true, - // // AcademyDfENumber = "AcademyDfENumber", - // // AcademyName = "AcademyName", - // // AcademyOrderType = "AcademyOrderType", - // // AcademyUrn = 54321, - // // AdvisoryBoardConditions = "AdvisoryBoardConditions", - // // AdvisoryBoardDate = new DateOnly(2024, 3, 2), - // // CompletedGrantPaymentCertificateReceived = true, - // // ConfirmedConversionDate = new DateOnly(2024, 4, 3), - // // ConversionType = "ConversionType", - // // Diocese = "Diocese", - // // HeadteacherName = "HeadteacherName", - // // IncomingTrustAddress1 = "IncomingTrustAddress1", - // // IncomingTrustAddress2 = "IncomingTrustAddress2", - // // IncomingTrustAddress3 = "IncomingTrustAddress3", - // // IncomingTrustAddressCounty = "IncomingTrustAddressCounty", - // // IncomingTrustAddressPostcode = "IncomingTrustAddressPostcode", - // // IncomingTrustAddressTown = "IncomingTrustAddressTown", - // // IncomingTrustCompaniesHouseNumber = "IncomingTrustCompaniesHouseNumber", - // // IncomingTrustGroupIdentifier = "IncomingTrustGroupIdentifier", - // // IncomingTrustName = "IncomingTrustName", - // // IncomingTrustSharepointLink = "IncomingTrustSharepointLink", - // // IncomingTrustUKPRN = 12345647, - // // AssignedToName = "AssignedToName", - // // DioceseContactEmail = "DioceseContactEmail", - // // DioceseContactName = "DioceseContactName", - // // DirectorOfChildServicesEmail = "DirectorOfChildServicesEmail", - // // DirectorOfChildServicesName = "DirectorOfChildServicesName", - // // DirectorOfChildServicesRole = "DirectorOfChildServicesRole", - // // HeadteacherEmail = "HeadteacherEmail", - // // HeadteacherRole = "HeadteacherRole", - // // IncomingTrustCEOEmail = "IncomingTrustCEOEmail", - // // IncomingTrustCEOName = "IncomingTrustCEOName", - // // IncomingTrustCEORole = "IncomingTrustCEORole", - // // LocalAuthority = "LocalAuthority", - // // LocalAuthorityContactEmail = "LocalAuthorityContactEmail", - // // LocalAuthorityContactName = "LocalAuthorityContactName", - // // PrimaryContactForIncomingTrustEmail = "PrimaryContactForIncomingTrustEmail", - // // PrimaryContactForIncomingTrustName = "PrimaryContactForIncomingTrustName", - // // PrimaryContactForOutgoingTrustEmail = "PrimaryContactForOutgoingTrustEmail", - // // PrimaryContactForOutgoingTrustName = "PrimaryContactForOutgoingTrustName", - // // ProjectCreatedBy = "ProjectCreatedBy", - // // ProjectCreatedByEmailAddress = "ProjectCreatedByEmailAddress", - // // ProjectMainContactName = "ProjectMainContactName", - // // FormAMat = true, - // // ProposedCapacityForPupilsInReceptionToYear6 = 123, - // // ProposedCapacityForPupilsInYears7To11 = 456, - // // ProposedCapacityForStudentsInYear12OrAbove = 789, - // // ProvisionalConversionDate = new DateOnly(2024, 5, 6), - // // ReasonForCommercialInsurance = "ReasonForCommercialInsurance", - // // Region = "Region", - // // RiskProtectionArrangement = "RiskProtectionArrangement", - // // SchoolAddress1 = "SchoolAddress1", - // // SchoolAddress2 = "SchoolAddress2", - // // SchoolAddress3 = "SchoolAddress3", - // // SchoolAgeRange = "SchoolAgeRange", - // // SchoolCounty = "SchoolCounty", - // // SchoolName = "SchoolName", - // // SchoolPhase = "SchoolPhase", - // // SchoolPostcode = "SchoolPostcode", - // // SchoolSharepointFolder = "SchoolSharepointFolder", - // // SchoolTown = "SchoolTown", - // // SchoolType = "SchoolType", - // // SolicitorContactEmail = "SolicitorContactEmail", - // // SchoolUrn = 12345, - // // SolicitorContactName = "SolicitorContactName", - // // TeamManagingTheProject = "TeamManagingTheProject", - // // TwoRequiresImprovement = true - // //}; - - - // var model = new ConversionCsvModel(null, null); - - // var generator = new ConversionRowGenerator(); - - // generator.GenerateRow(model); - - // var result = generator.GenerateRow(model).Split(","); + [Theory] + [CustomAutoData(typeof(ProjectCustomization), typeof(EstablishmentsCustomization))] + public void RowGeneratesBasedOnModel(Project project, GiasEstablishment currentSchool, GiasEstablishment academy) + { + project.Type = ProjectType.Transfer; + var model = new ConversionCsvModel(project, currentSchool, academy); - // Assert.Equal("SchoolName", result[0]); - // Assert.Equal("12345", result[1]); - // Assert.Equal("ProjectType", result[2]); - // Assert.Equal("AcademyName", result[3]); - // Assert.Equal("54321", result[4]); - // Assert.Equal("AcademyDfENumber", result[5]); - // Assert.Equal("IncomingTrustName", result[6]); - // Assert.Equal("LocalAuthority", result[7]); - // Assert.Equal("Region", result[8]); - // Assert.Equal("Diocese", result[9]); - // Assert.Equal("06/05/2024", result[10]); - // Assert.Equal("03/04/2024", result[11]); - // Assert.Equal("AcademyOrderType", result[12]); - // Assert.Equal("True", result[13]); - // Assert.Equal("02/03/2024", result[14]); - // Assert.Equal("AdvisoryBoardConditions", result[15]); - // Assert.Equal("RiskProtectionArrangement", result[16]); - // Assert.Equal("ReasonForCommercialInsurance", result[17]); - // Assert.Equal("True", result[18]); - // Assert.Equal("True", result[19]); - // Assert.Equal("SchoolType", result[20]); - // Assert.Equal("SchoolAgeRange", result[21]); - // Assert.Equal("SchoolPhase", result[22]); - // Assert.Equal("123", result[23]); - // Assert.Equal("456", result[24]); - // Assert.Equal("789", result[25]); - // Assert.Equal("SchoolAddress1", result[26]); - // Assert.Equal("SchoolAddress2", result[27]); - // Assert.Equal("SchoolAddress3", result[28]); - // Assert.Equal("SchoolTown", result[29]); - // Assert.Equal("SchoolCounty", result[30]); - // Assert.Equal("SchoolPostcode", result[31]); - // Assert.Equal("SchoolSharepointFolder", result[32]); - // Assert.Equal("ConversionType", result[33]); - // Assert.Equal("12345647", result[34]); - // Assert.Equal("IncomingTrustGroupIdentifier", result[35]); - // Assert.Equal("IncomingTrustCompaniesHouseNumber", result[36]); - // Assert.Equal("IncomingTrustAddress1", result[37]); - // Assert.Equal("IncomingTrustAddress2", result[38]); - // Assert.Equal("IncomingTrustAddress3", result[39]); - // Assert.Equal("IncomingTrustAddressTown", result[40]); - // Assert.Equal("IncomingTrustAddressCounty", result[41]); - // Assert.Equal("IncomingTrustAddressPostcode", result[42]); - // Assert.Equal("IncomingTrustSharepointLink", result[43]); - // Assert.Equal("ProjectCreatedBy", result[44]); - // Assert.Equal("ProjectCreatedByEmailAddress", result[45]); - // Assert.Equal("AssignedToName", result[46]); - // Assert.Equal("TeamManagingTheProject", result[47]); - // Assert.Equal("ProjectMainContactName", result[48]); - // Assert.Equal("HeadteacherName", result[49]); - // Assert.Equal("HeadteacherRole", result[50]); - // Assert.Equal("HeadteacherEmail", result[51]); - // Assert.Equal("LocalAuthorityContactName", result[52]); - // Assert.Equal("LocalAuthorityContactEmail", result[53]); - // Assert.Equal("PrimaryContactForIncomingTrustName", result[54]); - // Assert.Equal("PrimaryContactForIncomingTrustEmail", result[55]); - // Assert.Equal("PrimaryContactForOutgoingTrustName", result[56]); - // Assert.Equal("PrimaryContactForOutgoingTrustEmail", result[57]); - // Assert.Equal("IncomingTrustCEOName", result[58]); - // Assert.Equal("IncomingTrustCEORole", result[59]); - // Assert.Equal("IncomingTrustCEOEmail", result[60]); - // Assert.Equal("SolicitorContactName", result[61]); - // Assert.Equal("SolicitorContactEmail", result[62]); - // Assert.Equal("DioceseContactName", result[63]); - // Assert.Equal("DioceseContactEmail", result[64]); - // Assert.Equal("DirectorOfChildServicesName", result[65]); - // Assert.Equal("DirectorOfChildServicesEmail", result[66]); - // Assert.Equal("DirectorOfChildServicesRole", result[67]); + var generator = new ConversionRowGenerator(); - + generator.GenerateRow(model); + var result = generator.GenerateRow(model).Split(","); - - //} + Assert.Equal(currentSchool.Name, result[0]); + Assert.Equal(project.Urn.ToString(), result[1]); + Assert.Equal("Transfer", result[2]); + Assert.Equal(academy.Name, result[3]); + Assert.Equal(academy.Urn.ToString(), result[4]); + Assert.Equal(academy.LocalAuthorityCode + "/" + academy.EstablishmentNumber, result[5]); + //Assert.Equal("IncomingTrustName", result[6]); + //Assert.Equal("LocalAuthority", result[7]); + //Assert.Equal("Region", result[8]); + //Assert.Equal("Diocese", result[9]); + //Assert.Equal("06/05/2024", result[10]); + //Assert.Equal("03/04/2024", result[11]); + //Assert.Equal("AcademyOrderType", result[12]); + //Assert.Equal("True", result[13]); + //Assert.Equal("02/03/2024", result[14]); + //Assert.Equal("AdvisoryBoardConditions", result[15]); + //Assert.Equal("RiskProtectionArrangement", result[16]); + //Assert.Equal("ReasonForCommercialInsurance", result[17]); + //Assert.Equal("True", result[18]); + //Assert.Equal("True", result[19]); + //Assert.Equal("SchoolType", result[20]); + //Assert.Equal("SchoolAgeRange", result[21]); + //Assert.Equal("SchoolPhase", result[22]); + //Assert.Equal("123", result[23]); + //Assert.Equal("456", result[24]); + //Assert.Equal("789", result[25]); + //Assert.Equal("SchoolAddress1", result[26]); + //Assert.Equal("SchoolAddress2", result[27]); + //Assert.Equal("SchoolAddress3", result[28]); + //Assert.Equal("SchoolTown", result[29]); + //Assert.Equal("SchoolCounty", result[30]); + //Assert.Equal("SchoolPostcode", result[31]); + //Assert.Equal("SchoolSharepointFolder", result[32]); + //Assert.Equal("ConversionType", result[33]); + //Assert.Equal("12345647", result[34]); + //Assert.Equal("IncomingTrustGroupIdentifier", result[35]); + //Assert.Equal("IncomingTrustCompaniesHouseNumber", result[36]); + //Assert.Equal("IncomingTrustAddress1", result[37]); + //Assert.Equal("IncomingTrustAddress2", result[38]); + //Assert.Equal("IncomingTrustAddress3", result[39]); + //Assert.Equal("IncomingTrustAddressTown", result[40]); + //Assert.Equal("IncomingTrustAddressCounty", result[41]); + //Assert.Equal("IncomingTrustAddressPostcode", result[42]); + //Assert.Equal("IncomingTrustSharepointLink", result[43]); + //Assert.Equal("ProjectCreatedBy", result[44]); + //Assert.Equal("ProjectCreatedByEmailAddress", result[45]); + //Assert.Equal("AssignedToName", result[46]); + //Assert.Equal("TeamManagingTheProject", result[47]); + //Assert.Equal("ProjectMainContactName", result[48]); + //Assert.Equal("HeadteacherName", result[49]); + //Assert.Equal("HeadteacherRole", result[50]); + //Assert.Equal("HeadteacherEmail", result[51]); + //Assert.Equal("LocalAuthorityContactName", result[52]); + //Assert.Equal("LocalAuthorityContactEmail", result[53]); + //Assert.Equal("PrimaryContactForIncomingTrustName", result[54]); + //Assert.Equal("PrimaryContactForIncomingTrustEmail", result[55]); + //Assert.Equal("PrimaryContactForOutgoingTrustName", result[56]); + //Assert.Equal("PrimaryContactForOutgoingTrustEmail", result[57]); + //Assert.Equal("IncomingTrustCEOName", result[58]); + //Assert.Equal("IncomingTrustCEORole", result[59]); + //Assert.Equal("IncomingTrustCEOEmail", result[60]); + //Assert.Equal("SolicitorContactName", result[61]); + //Assert.Equal("SolicitorContactEmail", result[62]); + //Assert.Equal("DioceseContactName", result[63]); + //Assert.Equal("DioceseContactEmail", result[64]); + //Assert.Equal("DirectorOfChildServicesName", result[65]); + //Assert.Equal("DirectorOfChildServicesEmail", result[66]); + //Assert.Equal("DirectorOfChildServicesRole", result[67]); + } } } diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Behaviours/IgnoreVirtualMembersCustomization .cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Behaviours/IgnoreVirtualMembersCustomization .cs deleted file mode 100644 index f48ce5a1..00000000 --- a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Behaviours/IgnoreVirtualMembersCustomization .cs +++ /dev/null @@ -1,42 +0,0 @@ -using AutoFixture.Kernel; -using AutoFixture; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace Dfe.Complete.Tests.Common.Customizations.Behaviours -{ - public class IgnoreVirtualMembers : ISpecimenBuilder - { - public Type ReflectedType { get; } - - public object Create(object request, ISpecimenContext context) - { - var pi = request as PropertyInfo; - if (pi != null) - { - if (ReflectedType == null || - ReflectedType == pi.ReflectedType) - { - if (pi.GetGetMethod().IsVirtual) - { - return new OmitSpecimen(); - } - } - } - - return new NoSpecimen(); - } - } - - public class IgnoreVirtualMembersCustomisation : ICustomization - { - public void Customize(IFixture fixture) - { - fixture.Customizations.Add(new IgnoreVirtualMembers()); - } - } -} diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/EstablishmentsCustomization.cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/EstablishmentsCustomization.cs index c3f41ba1..ca68f066 100644 --- a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/EstablishmentsCustomization.cs +++ b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/EstablishmentsCustomization.cs @@ -1,5 +1,7 @@ using AutoFixture; using Dfe.Complete.Domain.Entities; +using Dfe.Complete.Domain.ValueObjects; +using DfE.CoreLibs.Testing.AutoFixture.Customizations; namespace Dfe.Complete.Tests.Common.Customizations.Models { @@ -9,8 +11,21 @@ public class EstablishmentsCustomization : ICustomization public void Customize(IFixture fixture) { - fixture.Customize(composer => composer + fixture + .Customize(new CompositeCustomization( + new UrnCustomization(), + new DateOnlyCustomization())) + .Customize(composer => composer .With(x => x.Name, EstablishmentName ?? fixture.Create())); } } + + public static class FixtureExtensions + { + public static Urn CreateUrn(this IFixture fixture) + { + var urn = new Urn(fixture.Create() % (99999 - 10000 + 1) + 10000); + return urn; + } + } } diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ProjectCustomization.cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ProjectCustomization.cs index 8db6f538..9aa2022c 100644 --- a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ProjectCustomization.cs +++ b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ProjectCustomization.cs @@ -3,6 +3,8 @@ using Dfe.Complete.Domain.Enums; using Dfe.Complete.Domain.ValueObjects; using Dfe.Complete.Infrastructure.Models; +using Dfe.Complete.Tests.Common.Customizations.Behaviours; +using DfE.CoreLibs.Testing.AutoFixture.Customizations; namespace Dfe.Complete.Tests.Common.Customizations.Models { @@ -84,7 +86,11 @@ public class ProjectCustomization : ICustomization public void Customize(IFixture fixture) { - fixture.Customize(composer => composer + fixture.Customize(new CompositeCustomization( + new UrnCustomization(), + new DateOnlyCustomization(), + new IgnoreVirtualMembersCustomisation())) + .Customize(composer => composer .With(x => x.IncomingTrustUkprn, IncomingTrustUkprn ?? fixture.Create()) .With(x => x.RegionalDeliveryOfficerId, RegionalDeliveryOfficerId ?? fixture.Create()) .With(x => x.CaseworkerId, CaseworkerId ?? fixture.Create()) diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/UrnCustomization.cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/UrnCustomization.cs new file mode 100644 index 00000000..ad692d5d --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/UrnCustomization.cs @@ -0,0 +1,14 @@ +using AutoFixture; +using Dfe.Complete.Domain.ValueObjects; + +namespace Dfe.Complete.Tests.Common.Customizations.Models +{ + public class UrnCustomization : ICustomization + { + public void Customize(IFixture fixture) + { + fixture.Register(() => fixture.CreateUrn()); + //fixture.Customize(composer => composer.FromFactory(() => fixture.CreateUrn())); + } + } +} From 1575b74947661822f0ebc79417d9e740d01077e9 Mon Sep 17 00:00:00 2001 From: Sukhvinder Bhullar Date: Wed, 22 Jan 2025 09:54:47 +0000 Subject: [PATCH 21/47] Refactored dependencies and added columns --- .../Common/Models/ConversionCsvModel.cs | 2 +- .../CsvExport/Builders/DefaultIfEmpty.cs | 3 +- .../Builders/DfeNumberLAESTABBuilder.cs | 10 ++++++ .../Conversion/ConversionRowGenerator.cs | 7 ++-- .../Entities/LocalAuthority.cs | 5 +-- .../Controllers/ProjectsControllerTests.cs | 1 + ...pplicationDbContextFactoryCustomization.cs | 0 ...ustomWebApplicationFactoryCustomization.cs | 0 .../Dfe.Complete.Api.Tests.Integration.csproj | 2 ++ .../OpenApiTests/OpenApiDocumentTests.cs | 1 + .../Builders/DfeNumberLAESTABBuilderTests.cs | 35 +++++++++++++++---- .../Conversion/ConversionRowGeneratorTests.cs | 11 +++--- .../Dfe.Complete.Tests.Common.csproj | 2 -- 13 files changed, 58 insertions(+), 21 deletions(-) rename src/Tests/{Dfe.Complete.Tests.Common => Dfe.Complete.Api.Tests.Integration}/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs (100%) rename src/Tests/{Dfe.Complete.Tests.Common => Dfe.Complete.Api.Tests.Integration}/Customizations/CustomWebApplicationFactoryCustomization.cs (100%) diff --git a/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs b/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs index 8abf74dd..df69397b 100644 --- a/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs +++ b/src/Core/Dfe.Complete.Application/Common/Models/ConversionCsvModel.cs @@ -2,7 +2,7 @@ namespace Dfe.Complete.Application.Common.Models { - public record ConversionCsvModel(Project Project, GiasEstablishment CurrentSchool, GiasEstablishment Academy); + public record ConversionCsvModel(Project Project, GiasEstablishment CurrentSchool, GiasEstablishment? Academy); //public class ConversionCsvModel //{ diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIfEmpty.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIfEmpty.cs index 5e47869f..94fd0e7f 100644 --- a/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIfEmpty.cs +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DefaultIfEmpty.cs @@ -4,14 +4,13 @@ public class DefaultIf(Func condition, Func valueFunc, s { public string Build(T input) { - object? value = valueFunc(input); - if (condition(input)) { return defaultValue; } else { + object? value = valueFunc(input); return value.ToString(); } } diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DfeNumberLAESTABBuilder.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DfeNumberLAESTABBuilder.cs index a777090f..df9e977b 100644 --- a/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DfeNumberLAESTABBuilder.cs +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Builders/DfeNumberLAESTABBuilder.cs @@ -11,6 +11,16 @@ public class DfeNumberLAESTABBuilder: IColumnBuilder { public string Build(ConversionCsvModel input) { + if(input.Project.AcademyUrn == null) + { + return string.Empty; + } + + if (input.Academy == null) + { + return string.Empty; + } + return input.Academy.LocalAuthorityCode + "/" + input.Academy.EstablishmentNumber; } } diff --git a/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs b/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs index d2fd516c..374c6e91 100644 --- a/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs +++ b/src/Core/Dfe.Complete.Application/Services/CsvExport/Conversion/ConversionRowGenerator.cs @@ -13,9 +13,12 @@ public class ConversionRowGenerator : IRowGenerator new BlankIfEmpty(x => x.CurrentSchool.Name), new BlankIfEmpty(x => x.Project.Urn), new ProjectTypeBuilder(), - new DefaultIf(x => x.Project.AcademyUrn == null, x => x.Academy.Name, Unconfirmed), - new DefaultIf(x => x.Project.AcademyUrn == null, x => x.Academy.Urn, Unconfirmed), + new DefaultIf(x => x.Project.AcademyUrn == null, x => x.Academy?.Name, Unconfirmed), + new DefaultIf(x => x.Project.AcademyUrn == null, x => x.Academy?.Urn, Unconfirmed), new DfeNumberLAESTABBuilder(), + new BlankIfEmpty(x => "IncomingTrustName"), //Incoming trust name API call + + ]; diff --git a/src/Core/Dfe.Complete.Domain/Entities/LocalAuthority.cs b/src/Core/Dfe.Complete.Domain/Entities/LocalAuthority.cs index 70baf833..6b0bf33e 100644 --- a/src/Core/Dfe.Complete.Domain/Entities/LocalAuthority.cs +++ b/src/Core/Dfe.Complete.Domain/Entities/LocalAuthority.cs @@ -1,10 +1,11 @@ -using Dfe.Complete.Domain.ValueObjects; +using Dfe.Complete.Domain.Common; +using Dfe.Complete.Domain.ValueObjects; using System; using System.Collections.Generic; namespace Dfe.Complete.Infrastructure.Models; -public class LocalAuthority +public class LocalAuthority: BaseAggregateRoot, IEntity { public LocalAuthorityId Id { get; set; } diff --git a/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs b/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs index 3f600c6c..8da93e1c 100644 --- a/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs +++ b/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs @@ -6,6 +6,7 @@ using DfE.CoreLibs.Testing.AutoFixture.Attributes; using DfE.CoreLibs.Testing.Mocks.WebApplicationFactory; using Microsoft.EntityFrameworkCore; +using Microsoft.VisualStudio.TestPlatform.TestHost; namespace Dfe.Complete.Api.Tests.Integration.Controllers; diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs b/src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs similarity index 100% rename from src/Tests/Dfe.Complete.Tests.Common/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs rename to src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/CustomWebApplicationFactoryCustomization.cs b/src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationFactoryCustomization.cs similarity index 100% rename from src/Tests/Dfe.Complete.Tests.Common/Customizations/CustomWebApplicationFactoryCustomization.cs rename to src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationFactoryCustomization.cs diff --git a/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj b/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj index bbcffbba..17d28dae 100644 --- a/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj +++ b/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj @@ -22,6 +22,8 @@ + + diff --git a/src/Tests/Dfe.Complete.Api.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs b/src/Tests/Dfe.Complete.Api.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs index 4bcdcc59..71d71baf 100644 --- a/src/Tests/Dfe.Complete.Api.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs +++ b/src/Tests/Dfe.Complete.Api.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs @@ -2,6 +2,7 @@ using DfE.CoreLibs.Testing.Mocks.WebApplicationFactory; using Dfe.Complete.Tests.Common.Customizations; using System.Net; +using Microsoft.VisualStudio.TestPlatform.TestHost; namespace Dfe.Complete.Api.Tests.Integration.OpenApiTests; diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DfeNumberLAESTABBuilderTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DfeNumberLAESTABBuilderTests.cs index 54e569e6..a3b7c99f 100644 --- a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DfeNumberLAESTABBuilderTests.cs +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/DfeNumberLAESTABBuilderTests.cs @@ -1,27 +1,48 @@ using Dfe.Complete.Application.Common.Models; using Dfe.Complete.Application.Services.CsvExport.Builders; using Dfe.Complete.Domain.Entities; -using Dfe.Complete.Domain.Enums; -using Dfe.Complete.Tests.Common.Customizations.Behaviours; using Dfe.Complete.Tests.Common.Customizations.Models; using DfE.CoreLibs.Testing.AutoFixture.Attributes; -using DfE.CoreLibs.Testing.AutoFixture.Customizations; namespace Dfe.Complete.Application.Tests.Services.CsvExport.Builders { - + public class DfeNumberLAESTABBuilderTests { [Theory] - [CustomAutoData(typeof(EstablishmentsCustomization))] + [CustomAutoData(typeof(ProjectCustomization), typeof(EstablishmentsCustomization))] - public void BuildsCorrectDfeNumber(GiasEstablishment academy) + public void BuildsCorrectDfeNumber(Project project, GiasEstablishment academy) { var builder = new DfeNumberLAESTABBuilder(); - var result = builder.Build(new ConversionCsvModel(null, null, academy)); + var result = builder.Build(new ConversionCsvModel(project, null, academy)); Assert.Equal(academy.LocalAuthorityCode + "/" + academy.EstablishmentNumber, result); } + + [Theory] + [CustomAutoData(typeof(ProjectCustomization), typeof(EstablishmentsCustomization))] + public void BuildBlankIfEmpty(Project project, GiasEstablishment academy) + { + project.AcademyUrn = null; + var builder = new DfeNumberLAESTABBuilder(); + + var result = builder.Build(new ConversionCsvModel(project, null, academy)); + + Assert.Equal(string.Empty, result); + } + + [Theory] + [CustomAutoData(typeof(ProjectCustomization), typeof(EstablishmentsCustomization))] + public void BuildBlankIfAcademyNotFound(Project project) + { + project.AcademyUrn = null; + var builder = new DfeNumberLAESTABBuilder(); + + var result = builder.Build(new ConversionCsvModel(project, null, null)); + + Assert.Equal(string.Empty, result); + } } } diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs index 9f57d14e..b7948fef 100644 --- a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Conversion/ConversionRowGeneratorTests.cs @@ -1,5 +1,6 @@ using AutoFixture; using AutoFixture.Xunit2; +using Castle.Core.Logging; using Dfe.Complete.Application.Common.Models; using Dfe.Complete.Application.Services.CsvExport.Conversion; using Dfe.Complete.Domain.Entities; @@ -17,12 +18,12 @@ public class ConversionRowGeneratorTests [Theory] [CustomAutoData(typeof(ProjectCustomization), typeof(EstablishmentsCustomization))] - public void RowGeneratesAccountsForBlankData(Project project, GiasEstablishment currentSchool, GiasEstablishment academy) + public void RowGeneratesAccountsForBlankData(Project project, GiasEstablishment currentSchool) { project.Type = ProjectType.Conversion; project.AcademyUrn = null; - var model = new ConversionCsvModel(project, currentSchool, academy); + var model = new ConversionCsvModel(project, currentSchool, null); var generator = new ConversionRowGenerator(); @@ -35,8 +36,8 @@ public void RowGeneratesAccountsForBlankData(Project project, GiasEstablishment Assert.Equal("Conversion", result[2]); Assert.Equal("unconfirmed", result[3]); Assert.Equal("unconfirmed", result[4]); - //Assert.Equal("AcademyDfENumber", result[5]); - //Assert.Equal("IncomingTrustName", result[6]); + Assert.Equal("", result[5]); + Assert.Equal("IncomingTrustName", result[6]); //Assert.Equal("LocalAuthority", result[7]); //Assert.Equal("Region", result[8]); //Assert.Equal("Diocese", result[9]); @@ -119,7 +120,7 @@ public void RowGeneratesBasedOnModel(Project project, GiasEstablishment currentS Assert.Equal(academy.Name, result[3]); Assert.Equal(academy.Urn.ToString(), result[4]); Assert.Equal(academy.LocalAuthorityCode + "/" + academy.EstablishmentNumber, result[5]); - //Assert.Equal("IncomingTrustName", result[6]); + Assert.Equal("IncomingTrustName", result[6]); //Assert.Equal("LocalAuthority", result[7]); //Assert.Equal("Region", result[8]); //Assert.Equal("Diocese", result[9]); diff --git a/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj b/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj index 52dac54b..19627d10 100644 --- a/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj +++ b/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj @@ -38,8 +38,6 @@ - - From 12fe0340c128f1ea01f517ee34ca8051117b10bb Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Wed, 22 Jan 2025 11:03:00 +0000 Subject: [PATCH 22/47] Remove caching from the all projects queries --- .../CountAllProjects/CountAllProjects.cs | 39 ++++-------- .../ListAllProjects/ListAllProjects.cs | 62 ++++++++----------- 2 files changed, 37 insertions(+), 64 deletions(-) diff --git a/src/Core/Dfe.Complete.Application/Projects/Queries/CountAllProjects/CountAllProjects.cs b/src/Core/Dfe.Complete.Application/Projects/Queries/CountAllProjects/CountAllProjects.cs index 4feabfec..10409c66 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Queries/CountAllProjects/CountAllProjects.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Queries/CountAllProjects/CountAllProjects.cs @@ -1,45 +1,30 @@ using Dfe.Complete.Application.Common.Models; using Dfe.Complete.Application.Projects.Interfaces; using Dfe.Complete.Domain.Enums; -using DfE.CoreLibs.Caching.Helpers; -using DfE.CoreLibs.Caching.Interfaces; using MediatR; using Microsoft.EntityFrameworkCore; namespace Dfe.Complete.Application.Projects.Queries.CountAllProjects { - public record CountAllProjectsQuery(ProjectState? ProjectStatus, ProjectType? Type) - : IRequest> - { - public override string ToString() - { - return $"{ProjectStatus.ToString()}{Type.ToString()}"; - } - } + public record CountAllProjectsQuery(ProjectState? ProjectStatus, ProjectType? Type) : IRequest>; public class CountAllProjectsQueryHandler( - IListAllProjectsQueryService listAllProjectsQueryService, - ICacheService cacheService) + IListAllProjectsQueryService listAllProjectsQueryService) : IRequestHandler> { public async Task> Handle(CountAllProjectsQuery request, CancellationToken cancellationToken) { - var cacheKey = $"Project_{CacheKeyHelper.GenerateHashedCacheKey(request.ToString())}"; - var methodName = nameof(CountAllProjectsQueryHandler); - return await cacheService.GetOrAddAsync(cacheKey, async () => + try + { + var result = await listAllProjectsQueryService + .ListAllProjects(request.ProjectStatus, request.Type) + .CountAsync(cancellationToken); + return Result.Success(result); + } + catch (Exception ex) { - try - { - var result = await listAllProjectsQueryService - .ListAllProjects(request.ProjectStatus, request.Type) - .CountAsync(cancellationToken); - return Result.Success(result); - } - catch (Exception ex) - { - return Result.Failure(ex.Message); - } - }, methodName); + return Result.Failure(ex.Message); + } } } } \ No newline at end of file diff --git a/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs b/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs index c4dab3fb..0130198f 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Queries/ListAllProjects/ListAllProjects.cs @@ -2,8 +2,6 @@ using Dfe.Complete.Application.Projects.Interfaces; using Dfe.Complete.Application.Projects.Model; using Dfe.Complete.Domain.Enums; -using DfE.CoreLibs.Caching.Helpers; -using DfE.CoreLibs.Caching.Interfaces; using MediatR; using Microsoft.EntityFrameworkCore; @@ -13,49 +11,39 @@ public record ListAllProjectsQuery( ProjectState? ProjectStatus, ProjectType? Type, int Page = 0, - int Count = 20) : IRequest>> - { - public override string ToString() - { - return $"{ProjectStatus.ToString()}{Type.ToString()}{Page}{Count}"; - } - } + int Count = 20) : IRequest>>; public class ListAllProjectsQueryHandler( - IListAllProjectsQueryService listAllProjectsQueryService, - ICacheService cacheService) + IListAllProjectsQueryService listAllProjectsQueryService) : IRequestHandler>> { public async Task>> Handle(ListAllProjectsQuery request, CancellationToken cancellationToken) { - var cacheKey = $"ListAllProjects_{CacheKeyHelper.GenerateHashedCacheKey(request.ToString())}"; - var methodName = nameof(ListAllProjectsQueryHandler); - return await cacheService.GetOrAddAsync(cacheKey, async () => + try + { + var result = await listAllProjectsQueryService + .ListAllProjects(request.ProjectStatus, request.Type) + .Skip(request.Page * request.Count).Take(request.Count) + .Select(item => new ListAllProjectsResultModel( + item.Establishment.Name, + item.Project.Id, + item.Project.Urn, + item.Project.SignificantDate, + item.Project.State, + item.Project.Type, + item.Project.IncomingTrustUkprn == null, + item.Project.AssignedTo != null + ? $"{item.Project.AssignedTo.FirstName} {item.Project.AssignedTo.LastName}" + : null + )) + .ToListAsync(cancellationToken); + return Result>.Success(result); + } + catch (Exception ex) { - try - { - var result = await listAllProjectsQueryService - .ListAllProjects(request.ProjectStatus, request.Type) - .Skip(request.Page * request.Count).Take(request.Count) - .Select(item => new ListAllProjectsResultModel( - item.Establishment.Name, - item.Project.Id, - item.Project.Urn, - item.Project.SignificantDate, - item.Project.State, - item.Project.Type, - item.Project.IncomingTrustUkprn == null, - item.Project.AssignedTo != null ? $"{item.Project.AssignedTo.FirstName} {item.Project.AssignedTo.LastName}" : null - )) - .ToListAsync(cancellationToken); - return Result>.Success(result); - } - catch (Exception ex) - { - return Result>.Failure(ex.Message); - } - }, methodName); + return Result>.Failure(ex.Message); + } } } } \ No newline at end of file From 2055544e52306d7d30601f854860fdf5db5a88a0 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Wed, 22 Jan 2025 11:03:39 +0000 Subject: [PATCH 23/47] Create tests for the Query handlers --- .../CountAllProjectsQueryHandlerTests.cs | 57 +++++++ .../ListAllProjectsQueryHandlerTests.cs | 140 ++++++++++++++++++ .../ListAllProjectResultModelCustomization.cs | 23 +++ .../ListAllProjectsQueryModelCustomization.cs | 22 +++ .../CountAllProjectsQueryCustomization.cs | 18 +++ .../ListAllProjectsQueryCustomization.cs | 22 +++ 6 files changed, 282 insertions(+) create mode 100644 src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs create mode 100644 src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/ListAllProjectsQueryHandlerTests.cs create mode 100644 src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectResultModelCustomization.cs create mode 100644 src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectsQueryModelCustomization.cs create mode 100644 src/Tests/Dfe.Complete.Tests.Common/Customizations/Queries/CountAllProjectsQueryCustomization.cs create mode 100644 src/Tests/Dfe.Complete.Tests.Common/Customizations/Queries/ListAllProjectsQueryCustomization.cs diff --git a/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs b/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs new file mode 100644 index 00000000..041a4675 --- /dev/null +++ b/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs @@ -0,0 +1,57 @@ +using AutoFixture.Xunit2; +using DfE.CoreLibs.Testing.AutoFixture.Attributes; +using NSubstitute; +using DfE.CoreLibs.Testing.AutoFixture.Customizations; +using DfE.CoreLibs.Caching.Interfaces; +using Dfe.Complete.Application.Common.Models; +using DfE.CoreLibs.Caching.Helpers; +using Dfe.Complete.Application.Projects.Interfaces; +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Application.Projects.Queries.CountAllProjects; +using Dfe.Complete.Tests.Common.Customizations.Models; +using MockQueryable; + +namespace Dfe.Complete.Application.Tests.QueryHandlers.Project +{ + public class CountAllProjectsQueryHandlerTests + { + + [Theory] + [CustomAutoData( + typeof(OmitCircularReferenceCustomization), + typeof(ListAllProjectsQueryModelCustomization), + typeof(DateOnlyCustomization))] + public async Task Handle_ShouldReturnCorrectCount( + [Frozen] IListAllProjectsQueryService mockEstablishmentQueryService, + [Frozen] ICacheService mockCacheService, + CountAllProjectsQueryHandler handler, + CountAllProjectsQuery query, + List listAllProjectsQueryModels) + { + // Arrange + var expected = listAllProjectsQueryModels.Count; + + var cacheKey = $"Project_{CacheKeyHelper.GenerateHashedCacheKey(query.ToString())}"; + + var mock = listAllProjectsQueryModels.BuildMock(); + + mockEstablishmentQueryService.ListAllProjects(query.ProjectStatus, query.Type) + .Returns(mock); + + mockCacheService.GetOrAddAsync(cacheKey, Arg.Any>>>(), Arg.Any()) + .Returns(callInfo => + { + var callback = callInfo.ArgAt>>>(1); + return callback(); + }); + + // Act + var result = await handler.Handle(query, default); + + // Assert + Assert.Equal(expected, result.Value); + + await mockCacheService.Received(1).GetOrAddAsync(cacheKey, Arg.Any>>>(), nameof(CountAllProjectsQueryHandler)); + } + } +} diff --git a/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/ListAllProjectsQueryHandlerTests.cs b/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/ListAllProjectsQueryHandlerTests.cs new file mode 100644 index 00000000..3317572e --- /dev/null +++ b/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/ListAllProjectsQueryHandlerTests.cs @@ -0,0 +1,140 @@ +using AutoFixture; +using AutoFixture.Xunit2; +using DfE.CoreLibs.Testing.AutoFixture.Attributes; +using NSubstitute; +using DfE.CoreLibs.Testing.AutoFixture.Customizations; +using DfE.CoreLibs.Caching.Interfaces; +using Dfe.Complete.Application.Common.Models; +using DfE.CoreLibs.Caching.Helpers; +using Dfe.Complete.Application.Projects.Interfaces; +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Application.Projects.Queries.CountAllProjects; +using Dfe.Complete.Application.Projects.Queries.ListAllProjects; +using Dfe.Complete.Tests.Common.Customizations.Models; +using Dfe.Complete.Tests.Common.Customizations.Queries; +using MockQueryable; + +namespace Dfe.Complete.Application.Tests.QueryHandlers.Project +{ + public class ListAllProjectsQueryHandlerTests + { + [Theory] + [CustomAutoData( + typeof(OmitCircularReferenceCustomization), + typeof(ListAllProjectsQueryModelCustomization), + typeof(DateOnlyCustomization))] + public async Task Handle_ShouldReturnCorrectList_WhenPaginationIsCorrect( + [Frozen] IListAllProjectsQueryService mockEstablishmentQueryService, + ListAllProjectsQueryHandler handler, + IFixture fixture) + { + // Arrange + var listAllProjectsQueryModels = fixture.CreateMany(50).ToList(); + + var expected = listAllProjectsQueryModels.Select(item => new ListAllProjectsResultModel( + item.Establishment.Name, + item.Project.Id, + item.Project.Urn, + item.Project.SignificantDate, + item.Project.State, + item.Project.Type, + item.Project.IncomingTrustUkprn == null, + item.Project.AssignedTo != null + ? $"{item.Project.AssignedTo.FirstName} {item.Project.AssignedTo.LastName}" + : null)).Take(20).ToList(); + + var query = new ListAllProjectsQuery(null, null); + + var mock = listAllProjectsQueryModels.BuildMock(); + + mockEstablishmentQueryService.ListAllProjects(query.ProjectStatus, query.Type) + .Returns(mock); + + // Act + var result = await handler.Handle(query, default); + + // Assert + Assert.NotNull(result); + Assert.Equal(expected.Count, result.Value?.Count); + + for (int i = 0; i < result.Value!.Count; i++) + { + Assert.Equivalent(expected[i], result.Value![i]); + } + } + + [Theory] + [CustomAutoData( + typeof(OmitCircularReferenceCustomization), + typeof(ListAllProjectsQueryModelCustomization), + typeof(DateOnlyCustomization))] + public async Task Handle_ShouldReturnCorrectList_ForOtherPages( + [Frozen] IListAllProjectsQueryService mockEstablishmentQueryService, + ListAllProjectsQueryHandler handler, + IFixture fixture) + { + // Arrange + var listAllProjectsQueryModels = fixture.CreateMany(50).ToList(); + + var expected = listAllProjectsQueryModels.Select(item => new ListAllProjectsResultModel( + item.Establishment.Name, + item.Project.Id, + item.Project.Urn, + item.Project.SignificantDate, + item.Project.State, + item.Project.Type, + item.Project.IncomingTrustUkprn == null, + item.Project.AssignedTo != null + ? $"{item.Project.AssignedTo.FirstName} {item.Project.AssignedTo.LastName}" + : null)).Skip(20).Take(20).ToList(); + + var query = new ListAllProjectsQuery(null, null, 1); + + + var mock = listAllProjectsQueryModels.BuildMock(); + + mockEstablishmentQueryService.ListAllProjects(query.ProjectStatus, query.Type) + .Returns(mock); + + // Act + var result = await handler.Handle(query, default); + + // Assert + Assert.NotNull(result); + Assert.Equal(expected.Count, result.Value?.Count); + + for (int i = 0; i < result.Value!.Count; i++) + { + Assert.Equivalent(expected[i], result.Value![i]); + } + } + + [Theory] + [CustomAutoData( + typeof(OmitCircularReferenceCustomization), + typeof(ListAllProjectsQueryModelCustomization), + typeof(DateOnlyCustomization))] + public async Task Handle_ShouldReturnCorrectList_WhenAllPagesAreSkipped( + [Frozen] IListAllProjectsQueryService mockEstablishmentQueryService, + ListAllProjectsQueryHandler handler, + IFixture fixture) + { + // Arrange + var listAllProjectsQueryModels = fixture.CreateMany(50).ToList(); + + var query = new ListAllProjectsQuery(null, null, 10); + + var mock = listAllProjectsQueryModels.BuildMock(); + + mockEstablishmentQueryService.ListAllProjects(query.ProjectStatus, query.Type) + .Returns(mock); + + // Act + var result = await handler.Handle(query, default); + + // Assert + Assert.NotNull(result); + Assert.Equal(0, result.Value?.Count); + } + } +} \ No newline at end of file diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectResultModelCustomization.cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectResultModelCustomization.cs new file mode 100644 index 00000000..66500ffc --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectResultModelCustomization.cs @@ -0,0 +1,23 @@ +// using AutoFixture; +// using Dfe.Complete.Application.Projects.Model; +// using Dfe.Complete.Domain.Enums; +// using Dfe.Complete.Domain.ValueObjects; +// +// namespace Dfe.Complete.Tests.Common.Customizations.Models; +// +// public class ListAllProjectResultModelCustomization : ICustomization +// { +// public string? EstablishmentName { get; set; } +// public ProjectId ProjectId { get; set; } +// public Urn Urn { get; set; } +// public DateOnly? ConversionOrTransferDate { get; set; } +// public ProjectState State { get; set; } +// public ProjectType? ProjectType { get; set; } +// public bool IsFormAMAT { get; set; } +// public string? AssignedToFullName { get; set; } +// +// public void Customize(IFixture fixture) +// { +// fixture.Customize(composer => composer.WithAutoProperties()); +// } +// } \ No newline at end of file diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectsQueryModelCustomization.cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectsQueryModelCustomization.cs new file mode 100644 index 00000000..5aff3039 --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectsQueryModelCustomization.cs @@ -0,0 +1,22 @@ +using AutoFixture; +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Domain.Entities; + +namespace Dfe.Complete.Tests.Common.Customizations.Models; + +public class ListAllProjectsQueryModelCustomization : ICustomization +{ + public Project Project { get; set; } + public GiasEstablishment Establishment{ get; set; } + + public void Customize(IFixture fixture) + { + fixture.Customize(composer => composer + .FromFactory(() => + { + var project = fixture.Create(); + var giasEstablishment = fixture.Create(); + return new ListAllProjectsQueryModel(project, giasEstablishment); + })); + } +} \ No newline at end of file diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Queries/CountAllProjectsQueryCustomization.cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Queries/CountAllProjectsQueryCustomization.cs new file mode 100644 index 00000000..26256c83 --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Queries/CountAllProjectsQueryCustomization.cs @@ -0,0 +1,18 @@ +using AutoFixture; +using Dfe.Complete.Application.Projects.Queries.ListAllProjects; +using Dfe.Complete.Domain.Enums; + +namespace Dfe.Complete.Tests.Common.Customizations.Queries; + +public class CountAllProjectsQueryCustomization : ICustomization +{ + public ProjectState? ProjectStatus { get; set; } + public ProjectType? Type{ get; set; } + + public void Customize(IFixture fixture) + { + fixture.Customize(composer => composer + .With(x => x.ProjectStatus, ProjectStatus) + .With(x => x.Type, Type)); + } +} \ No newline at end of file diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Queries/ListAllProjectsQueryCustomization.cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Queries/ListAllProjectsQueryCustomization.cs new file mode 100644 index 00000000..d68a0cd5 --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Queries/ListAllProjectsQueryCustomization.cs @@ -0,0 +1,22 @@ +using AutoFixture; +using Dfe.Complete.Application.Projects.Queries.ListAllProjects; +using Dfe.Complete.Domain.Enums; + +namespace Dfe.Complete.Tests.Common.Customizations.Queries; + +public class ListAllProjectsQueryCustomization : ICustomization +{ + public ProjectState? ProjectStatus { get; set; } + public ProjectType? Type{ get; set; } + public int Page { get; set; } + public int Count { get; set; } + + public void Customize(IFixture fixture) + { + fixture.Customize(composer => composer + .With(x => x.ProjectStatus, ProjectStatus) + .With(x => x.Type, Type) + .With(x => x.Page, 0) + .With(x => x.Count, 20)); + } +} \ No newline at end of file From e8b4b85dd9b4dfe9ec391811d7723238bd209e00 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Wed, 22 Jan 2025 11:04:01 +0000 Subject: [PATCH 24/47] Add a bit more info to the README --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index d9ca70de..1276a1f4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,11 @@ # complete-api API service to the Complete application to help the process of schools converting to academies, transferring between academy trusts or changing their academy status. + +## Setup + +### Frontend setup + +- wwwroot npm i then npm run build +- Setup db +- Run migration +- User secrets From 4d4064288b5de340a8d975347c37eb39a2ec5ca7 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Wed, 22 Jan 2025 11:05:23 +0000 Subject: [PATCH 25/47] Add initial api tests --- .../Controllers/ProjectsControllerTests.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs b/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs index 3f600c6c..104e1f2a 100644 --- a/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs +++ b/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs @@ -1,9 +1,14 @@ using System.Net; +using Dfe.Complete.Application.Projects.Queries.CountAllProjects; +using Dfe.Complete.Application.Projects.Queries.ListAllProjects; using Dfe.Complete.Client.Contracts; using Dfe.Complete.Infrastructure.Database; using Dfe.Complete.Tests.Common.Customizations; using Dfe.Complete.Tests.Common.Customizations.Commands; +using Dfe.Complete.Tests.Common.Customizations.Models; +using Dfe.Complete.Tests.Common.Customizations.Queries; using DfE.CoreLibs.Testing.AutoFixture.Attributes; +using DfE.CoreLibs.Testing.AutoFixture.Customizations; using DfE.CoreLibs.Testing.Mocks.WebApplicationFactory; using Microsoft.EntityFrameworkCore; @@ -56,4 +61,58 @@ public async Task CreateProject_WithNullRequest_ThrowsException( Assert.Equal(HttpStatusCode.BadRequest, (HttpStatusCode)exception.StatusCode); } + + [Theory] + [CustomAutoData(typeof(CountAllProjectsQueryCustomization), typeof(ProjectCustomization), typeof(DateOnlyCustomization))] + public async Task CountAllProjects_Async_ShouldReturnCorrectNumber( + CustomWebApplicationDbContextFactory factory, + CountAllProjectsQuery countAllProjectsQuery, + ICountAllProjectsClient countAllProjectsClient) + { + //todo: when auth is done, add this back in + + var dbContext = factory.GetDbContext(); + + dbContext.Projects.AddRangeAsync() + + // dbContext.Users.Update(testUser); + // await dbContext.SaveChangesAsync(); + + var result = await countAllProjectsClient.Projects_CountAllProjects_Async( + (ProjectState?)countAllProjectsQuery.ProjectStatus, (ProjectType?)countAllProjectsQuery.Type); + + // Assert.NotNull(result); + Assert.IsType(result); + } + + [Theory] + [CustomAutoData(typeof(ListAllProjectsQueryCustomization))] + public async Task ListAllProjects_Async_ShouldReturnList( + ListAllProjectsQuery listAllProjectsQuery, + IListAllProjectsClient listAllProjectsClient) + { + //todo: when auth is done, add this back in + // factory.TestClaims = [new Claim(ClaimTypes.Role, "API.Write")]; + + // var testUserAdId = createConversionProjectCommand.UserAdId; + + // var dbContext = factory.GetDbContext(); + + // var testUser = await dbContext.Users.FirstOrDefaultAsync(); + // testUser.ActiveDirectoryUserId = testUserAdId; + + // dbContext.Users.Update(testUser); + // await dbContext.SaveChangesAsync(); + + var result = await listAllProjectsClient.Projects_ListAllProjects_Async( + (ProjectState?)listAllProjectsQuery.ProjectStatus, (ProjectType?)listAllProjectsQuery.Type, + listAllProjectsQuery.Page, listAllProjectsQuery.Count); + + Assert.NotNull(result); + Assert.IsAssignableFrom>(result); + foreach (var item in result) + { + //Do something to match all the results + } + } } \ No newline at end of file From 5942fb29b8abbdfd8958b47340fb4cb9558658c0 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 24 Jan 2025 16:16:10 +0000 Subject: [PATCH 26/47] Update xunit and NSubstitue library Added DfE CoreLibs Testing to Complete Test project --- .../Dfe.Complete.Api.Tests.Integration.csproj | 2 +- .../Dfe.Complete.Domain.Tests.csproj | 2 +- .../Dfe.Complete.Tests.Common.csproj | 2 +- src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj | 6 ++++-- .../Dfe.Complete.UserContext.Tests.csproj | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj b/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj index 17d28dae..1332dda9 100644 --- a/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj +++ b/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj @@ -12,7 +12,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Tests/Dfe.Complete.Domain.Tests/Dfe.Complete.Domain.Tests.csproj b/src/Tests/Dfe.Complete.Domain.Tests/Dfe.Complete.Domain.Tests.csproj index 710e0af3..3c3a9b7c 100644 --- a/src/Tests/Dfe.Complete.Domain.Tests/Dfe.Complete.Domain.Tests.csproj +++ b/src/Tests/Dfe.Complete.Domain.Tests/Dfe.Complete.Domain.Tests.csproj @@ -10,7 +10,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj b/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj index 19627d10..c96a6d17 100644 --- a/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj +++ b/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj @@ -30,7 +30,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj b/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj index 6777952b..40e19839 100644 --- a/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj +++ b/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj @@ -11,12 +11,13 @@ + - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all @@ -29,6 +30,7 @@ + diff --git a/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj b/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj index 94238e87..e2323474 100644 --- a/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj +++ b/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj @@ -15,7 +15,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 2b7796df61da06d27ee31314ac3d7aff0ddde7a1 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 24 Jan 2025 16:55:37 +0000 Subject: [PATCH 27/47] Create Date Format constants --- .../Dfe.Complete/Constants/DateFormatConstants.cs | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/Frontend/Dfe.Complete/Constants/DateFormatConstants.cs diff --git a/src/Frontend/Dfe.Complete/Constants/DateFormatConstants.cs b/src/Frontend/Dfe.Complete/Constants/DateFormatConstants.cs new file mode 100644 index 00000000..f0606c86 --- /dev/null +++ b/src/Frontend/Dfe.Complete/Constants/DateFormatConstants.cs @@ -0,0 +1,9 @@ +namespace Dfe.Complete.Constants; + +public static class DateFormatConstants +{ + public const string DateWithDayOfTheWeek = "dddd d MMMM yyyy"; + public const string DateWithoutDayOfTheWeek = "d MMMM yyyy"; + public const string MonthAndYearFormat = "MMM yyyy"; + public const string DateUkFormat = "dd/MM/yyyy"; +} \ No newline at end of file From a87302c80d2efa08e582a53f3e84ff4ce007bc9e Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Fri, 24 Jan 2025 16:55:57 +0000 Subject: [PATCH 28/47] Use DateFormatConstants in Date Extensions --- .../Dfe.Complete/Extensions/DateOnlyExtensions.cs | 10 +++++----- .../Dfe.Complete/Extensions/DateTimeExtensions.cs | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs b/src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs index 4acfc12b..7113396c 100644 --- a/src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs +++ b/src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs @@ -1,10 +1,10 @@ -using System; +using Dfe.Complete.Constants; namespace Dfe.Complete.Extensions { public static class DateOnlyExtensions { - public static string ToUkDateString(this DateOnly dateTime) => dateTime.ToString("dd/MM/yyyy"); + public static string ToUkDateString(this DateOnly dateTime) => dateTime.ToString(DateFormatConstants.DateUkFormat); public static string ToDateString(this DateOnly? dateTime, bool includeDayOfWeek = false) { @@ -19,9 +19,9 @@ public static string ToDateString(this DateOnly dateTime, bool includeDayOfWeek { if (includeDayOfWeek) { - return dateTime.ToString("dddd d MMMM yyyy"); + return dateTime.ToString(DateFormatConstants.DateWithDayOfTheWeek); } - return dateTime.ToString("d MMMM yyyy"); + return dateTime.ToString(DateFormatConstants.DateWithoutDayOfTheWeek); } public static DateOnly FirstOfMonth(this DateOnly thisMonth, int monthsToAdd) @@ -39,7 +39,7 @@ public static string ToDateMonthYearString(this DateOnly? dateTime) return string.Empty; } - return dateTime.Value.ToString("MMM yyyy"); + return dateTime.Value.ToString(DateFormatConstants.MonthAndYearFormat); } } } \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Extensions/DateTimeExtensions.cs b/src/Frontend/Dfe.Complete/Extensions/DateTimeExtensions.cs index e3da0bb0..71877a99 100644 --- a/src/Frontend/Dfe.Complete/Extensions/DateTimeExtensions.cs +++ b/src/Frontend/Dfe.Complete/Extensions/DateTimeExtensions.cs @@ -1,10 +1,10 @@ -using System; +using Dfe.Complete.Constants; namespace Dfe.Complete.Extensions { public static class DateTimeExtensions { - public static string ToUkDateString(this DateTime dateTime) => dateTime.ToString("dd/MM/yyyy"); + public static string ToUkDateString(this DateTime dateTime) => dateTime.ToString(DateFormatConstants.DateUkFormat); public static string ToDateString(this DateTime? dateTime, bool includeDayOfWeek = false) { @@ -19,9 +19,9 @@ public static string ToDateString(this DateTime dateTime, bool includeDayOfWeek { if (includeDayOfWeek) { - return dateTime.ToString("dddd d MMMM yyyy"); + return dateTime.ToString(DateFormatConstants.DateWithDayOfTheWeek); } - return dateTime.ToString("d MMMM yyyy"); + return dateTime.ToString(DateFormatConstants.DateWithoutDayOfTheWeek); } public static DateTime FirstOfMonth(this DateTime thisMonth, int monthsToAdd) @@ -39,7 +39,7 @@ public static string ToDateMonthYearString(this DateTime? dateTime) return string.Empty; } - return dateTime.Value.ToString("MMM yyyy"); + return dateTime.Value.ToString(DateFormatConstants.MonthAndYearFormat); } } } \ No newline at end of file From 134626b3c25b7fc8405ae8f4aff7b7ed57bf1187 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 27 Jan 2025 10:26:16 +0000 Subject: [PATCH 29/47] Rename Model directories to Models --- .../Projects/{Model => Models}/ListAllProjectsQueryModel.cs | 2 +- .../Projects/{Model => Models}/ListAllProjectsResultModel.cs | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/Core/Dfe.Complete.Application/Projects/{Model => Models}/ListAllProjectsQueryModel.cs (50%) rename src/Core/Dfe.Complete.Application/Projects/{Model => Models}/ListAllProjectsResultModel.cs (100%) diff --git a/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsQueryModel.cs b/src/Core/Dfe.Complete.Application/Projects/Models/ListAllProjectsQueryModel.cs similarity index 50% rename from src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsQueryModel.cs rename to src/Core/Dfe.Complete.Application/Projects/Models/ListAllProjectsQueryModel.cs index 746342fd..c262d9e1 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsQueryModel.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Models/ListAllProjectsQueryModel.cs @@ -2,4 +2,4 @@ namespace Dfe.Complete.Application.Projects.Model; -public record ListAllProjectsQueryModel(Project Project, GiasEstablishment Establishment); \ No newline at end of file +public record ListAllProjectsQueryModel(Project? Project, GiasEstablishment? Establishment); \ No newline at end of file diff --git a/src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs b/src/Core/Dfe.Complete.Application/Projects/Models/ListAllProjectsResultModel.cs similarity index 100% rename from src/Core/Dfe.Complete.Application/Projects/Model/ListAllProjectsResultModel.cs rename to src/Core/Dfe.Complete.Application/Projects/Models/ListAllProjectsResultModel.cs From e1d64014687c3a64fce0e8e3d00c71756279d931 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 27 Jan 2025 10:31:20 +0000 Subject: [PATCH 30/47] Update customizations to work with new tests and API endpoints --- ...pplicationDbContextFactoryCustomization.cs | 23 ++++----- .../ListAllProjectResultModelCustomization.cs | 48 ++++++++++--------- .../Models/ProjectCustomization.cs | 4 +- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs b/src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs index 20fcdabb..5a08851d 100644 --- a/src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs +++ b/src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationDbContextFactoryCustomization.cs @@ -1,20 +1,19 @@ +using System.Net.Http.Headers; +using System.Security.Claims; using AutoFixture; -using DfE.CoreLibs.Testing.Mocks.Authentication; -using DfE.CoreLibs.Testing.Mocks.WebApplicationFactory; -using Dfe.Complete.Api; using Dfe.Complete.Api.Client.Extensions; using Dfe.Complete.Client; using Dfe.Complete.Client.Contracts; -using Microsoft.AspNetCore.Authentication; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using System.Net.Http.Headers; -using System.Security.Claims; using Dfe.Complete.Infrastructure.Database; using Dfe.Complete.Tests.Common.Seeders; +using DfE.CoreLibs.Testing.Mocks.Authentication; +using DfE.CoreLibs.Testing.Mocks.WebApplicationFactory; +using Microsoft.AspNetCore.Authentication; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; -namespace Dfe.Complete.Tests.Common.Customizations +namespace Dfe.Complete.Api.Tests.Integration.Customizations { public class CustomWebApplicationDbContextFactoryCustomization : ICustomization { @@ -60,15 +59,13 @@ public void Customize(IFixture fixture) var services = new ServiceCollection(); services.AddSingleton(config); - services.AddCompleteApiClient(config, client); - + services.AddCompleteApiClient(config, client); var serviceProvider = services.BuildServiceProvider(); fixture.Inject(factory); fixture.Inject(serviceProvider); fixture.Inject(client); - fixture.Inject(serviceProvider.GetRequiredService()); - + fixture.Inject(serviceProvider.GetRequiredService()); fixture.Inject(new List()); return factory; diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectResultModelCustomization.cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectResultModelCustomization.cs index 66500ffc..fb3e97a4 100644 --- a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectResultModelCustomization.cs +++ b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ListAllProjectResultModelCustomization.cs @@ -1,23 +1,25 @@ -// using AutoFixture; -// using Dfe.Complete.Application.Projects.Model; -// using Dfe.Complete.Domain.Enums; -// using Dfe.Complete.Domain.ValueObjects; -// -// namespace Dfe.Complete.Tests.Common.Customizations.Models; -// -// public class ListAllProjectResultModelCustomization : ICustomization -// { -// public string? EstablishmentName { get; set; } -// public ProjectId ProjectId { get; set; } -// public Urn Urn { get; set; } -// public DateOnly? ConversionOrTransferDate { get; set; } -// public ProjectState State { get; set; } -// public ProjectType? ProjectType { get; set; } -// public bool IsFormAMAT { get; set; } -// public string? AssignedToFullName { get; set; } -// -// public void Customize(IFixture fixture) -// { -// fixture.Customize(composer => composer.WithAutoProperties()); -// } -// } \ No newline at end of file +using AutoFixture; +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Domain.ValueObjects; +using DfE.CoreLibs.Testing.AutoFixture.Customizations; + +namespace Dfe.Complete.Tests.Common.Customizations.Models; + +public class ListAllProjectResultModelCustomization : ICustomization +{ + public string? EstablishmentName { get; set; } + public ProjectId ProjectId { get; set; } + public Urn Urn { get; set; } + public DateOnly? ConversionOrTransferDate { get; set; } + public ProjectState State { get; set; } + public ProjectType? ProjectType { get; set; } + public bool IsFormAMAT { get; set; } + public string? AssignedToFullName { get; set; } + + public void Customize(IFixture fixture) + { + fixture.Customize(new DateOnlyCustomization()).Customize(composer => + composer.With(x => x.ProjectType, ProjectType ?? fixture.Create())); + } +} \ No newline at end of file diff --git a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ProjectCustomization.cs b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ProjectCustomization.cs index 9aa2022c..f6e79dcc 100644 --- a/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ProjectCustomization.cs +++ b/src/Tests/Dfe.Complete.Tests.Common/Customizations/Models/ProjectCustomization.cs @@ -36,7 +36,7 @@ public class ProjectCustomization : ICustomization public string? IncomingTrustSharepointLink { get; set; } - public string? Type { get; set; } + public ProjectType? Type { get; set; } public UserId? AssignedToId { get; set; } @@ -97,7 +97,7 @@ public void Customize(IFixture fixture) .With(x => x.AdvisoryBoardConditions, AdvisoryBoardConditions ?? fixture.Create()) .With(x => x.EstablishmentSharepointLink, EstablishmentSharepointLink ?? fixture.Create()) .With(x => x.IncomingTrustSharepointLink, IncomingTrustSharepointLink ?? fixture.Create()) - .With(x => x.Type, fixture.Create()) + .With(x => x.Type, Type ?? fixture.Create()) .With(x => x.AssignedToId, AssignedToId ?? fixture.Create()) .With(x => x.SignificantDateProvisional, SignificantDateProvisional ?? fixture.Create()) .With(x => x.DirectiveAcademyOrder, DirectiveAcademyOrder ?? fixture.Create()) From ace7f41ef2e1031cb380b7d04933b2ec5aa4ff7e Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 27 Jan 2025 10:31:45 +0000 Subject: [PATCH 31/47] Refactor pagination model so it can be unit tested --- .../Pages/Pagination/PaginationModel.cs | 116 ++++++++++-------- .../Pages/Pagination/_Pagination.cshtml | 36 ++---- 2 files changed, 78 insertions(+), 74 deletions(-) diff --git a/src/Frontend/Dfe.Complete/Pages/Pagination/PaginationModel.cs b/src/Frontend/Dfe.Complete/Pages/Pagination/PaginationModel.cs index dca232a0..76dd5348 100644 --- a/src/Frontend/Dfe.Complete/Pages/Pagination/PaginationModel.cs +++ b/src/Frontend/Dfe.Complete/Pages/Pagination/PaginationModel.cs @@ -1,62 +1,82 @@ -namespace Dfe.Complete.Pages.Pagination +namespace Dfe.Complete.Pages.Pagination; + +public class PaginationModel { - public class PaginationModel + public PaginationModel(string url, int currentPageNumber, int recordCount, int pageSize, string? elementIdPrefix = null) { - public PaginationModel(string url, int pageNumber, int recordCount, int pageSize, string? elementIdPrefix = null) + Url = url; + CurrentPageNumber = currentPageNumber; + RecordCount = recordCount; + TotalPages = (int)Math.Ceiling((double)recordCount / pageSize); + if (currentPageNumber > 1) + { + HasPrevious = true; + Previous = currentPageNumber - 1; + } + else { - Url = url; - PageNumber = pageNumber; - RecordCount = recordCount; - TotalPages = (int)Math.Ceiling((double)recordCount / pageSize); - if (pageNumber > 1) - { - HasPrevious = true; - Previous = pageNumber - 1; - } - else - { - HasPrevious = false; - } + HasPrevious = false; + } - if (pageNumber < TotalPages) - { - HasNext = true; - Next = pageNumber + 1; - } - else - { - HasNext = false; - } - - ElementIdPrefix = elementIdPrefix; + if (currentPageNumber < TotalPages) + { + HasNext = true; + Next = currentPageNumber + 1; + } + else + { + HasNext = false; } - public string Url { get; set; } - public bool HasNext { get; set; } + ElementIdPrefix = elementIdPrefix; + + PagesToDisplay = new List(); + + if(currentPageNumber != 1) + PagesToDisplay.Add(1); + if (Previous is > 1) + PagesToDisplay.Add(Previous.Value); + PagesToDisplay.Add(currentPageNumber); + if (Next < TotalPages) + PagesToDisplay.Add(Next.Value); + if(currentPageNumber != TotalPages) + PagesToDisplay.Add(TotalPages); + } + + public List PagesToDisplay { get; init; } + public string Url { get; init; } - public bool HasPrevious { get; set; } + public bool HasNext { get; init; } - public int TotalPages { get; set; } + public bool HasPrevious { get; init; } - public int PageNumber { get; set; } + public int TotalPages { get; init; } - public int? Next { get; set; } + public int CurrentPageNumber { get; init; } - public int? Previous { get; set; } + public int? Next { get; init; } - public int RecordCount { get; set; } + public int? Previous { get; init; } - /// - /// The ID of the container that has the content to be changed - /// This is for partial page reloads when pagination is invoked - /// When we only want to refresh the content container, not the entire page - /// - // public string ContentContainerId { get; set; } + public int RecordCount { get; init; } - /// - /// Prefix so that we can have multiple pagination elements on screen - /// Ensures we can uniquely identify the pagination for separate content containers - /// - public string? ElementIdPrefix { get; set; } - } -} + /// + /// The ID of the container that has the content to be changed + /// This is for partial page reloads when pagination is invoked + /// When we only want to refresh the content container, not the entire page + /// + // public string ContentContainerId { get; set; } + + /// + /// Prefix so that we can have multiple pagination elements on screen + /// Ensures we can uniquely identify the pagination for separate content containers + /// + public string? ElementIdPrefix { get; init; } + + public string Prefix => !string.IsNullOrEmpty(ElementIdPrefix) ? ElementIdPrefix : ""; + public string? NextPageLink => Next.HasValue ? $"{Url}?pageNumber={Next}" : null; + public string? PreviousPageLink => Previous.HasValue ? $"{Url}?pageNumber={Previous}" : null; + public string PaginationContainerId => $"{Prefix}pagination-container"; + public string NextButtonId => $"{Prefix}next-page"; + public string PreviousButtonId => $"{Prefix}previous-page"; +} \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml b/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml index 2915d143..8402b303 100644 --- a/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Pagination/_Pagination.cshtml @@ -1,40 +1,24 @@ -@using NetEscapades.AspNetCore.SecurityHeaders; -@model Dfe.Complete.Pages.Pagination.PaginationModel -@{ - var prefix = !string.IsNullOrEmpty(Model.ElementIdPrefix) ? Model.ElementIdPrefix : ""; - - var nextPageLink = $"{Model.Url}?pageNumber={Model.Next}"; - - var previousPageLink = $"{Model.Url}?pageNumber={Model.Previous}"; - - var nonce = Context.GetNonce(); - - var paginationContainerId = $"{prefix}pagination-container"; - var nextButtonId = $"{prefix}next-page"; - var previousButtonId = $"{prefix}previous-page"; - var pagesToDisplay = new List() { 1, Model.PageNumber, Model.TotalPages }; - if (Model.Previous.HasValue) - pagesToDisplay.Add((int)Model.Previous); - if (Model.Next.HasValue) - pagesToDisplay.Add((int)Model.Next); -} +@model Dfe.Complete.Pages.Pagination.PaginationModel +@* @{ *@ + @* var nonce = Context.GetNonce(); *@ +@* } *@ @if (Model.TotalPages > 1) { - + @if (Model.HasPrevious) { - + } @for (var pageIdx = 0; pageIdx < Model.TotalPages; pageIdx++) { var pageNumber = pageIdx + 1; - var isCurrentPage = Model.PageNumber == pageNumber; - var pageNumberLinkId = $"{prefix}page-{pageNumber}"; + var isCurrentPage = Model.CurrentPageNumber == pageNumber; + var pageNumberLinkId = $"{Model.Prefix}page-{pageNumber}"; var ariaCurrentPage = isCurrentPage ? "true" : "false"; var ariaLabel = isCurrentPage ? $"Current page, page {pageNumber}" : $"Go to page {pageNumber}"; - if (!pagesToDisplay.Contains(pageNumber)) + if (!Model.PagesToDisplay.Contains(pageNumber)) { continue; } @@ -55,7 +39,7 @@ } @if (Model.HasNext) { - + } // When pagination is used on the screen without a full page refresh From d7fdcccc50fb06858864d36028c364f5c7588082 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 27 Jan 2025 10:32:11 +0000 Subject: [PATCH 32/47] Update API to return the correct results --- .../Generated/Client.g.cs | 552 ++---------------- .../Generated/Contracts.g.cs | 57 +- .../Generated/swagger.json | 8 +- src/Api/Dfe.Complete.Api.Client/nswag.json | 2 +- .../Controllers/ProjectsController.cs | 14 +- 5 files changed, 69 insertions(+), 564 deletions(-) diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs b/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs index 214d03bc..990ffc7a 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs +++ b/src/Api/Dfe.Complete.Api.Client/Generated/Client.g.cs @@ -27,7 +27,7 @@ namespace Dfe.Complete.Client using System = global::System; [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CreateProjectClient : ICreateProjectClient + public partial class ProjectsClient : IProjectsClient { #pragma warning disable 8618 private string _baseUrl; @@ -38,7 +38,7 @@ public partial class CreateProjectClient : ICreateProjectClient private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public CreateProjectClient(string baseUrl, System.Net.Http.HttpClient httpClient) + public ProjectsClient(string baseUrl, System.Net.Http.HttpClient httpClient) #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. { BaseUrl = baseUrl; @@ -79,10 +79,10 @@ public string BaseUrl /// /// The request. /// Project created successfully. - /// A server side error occurred. - public virtual System.Threading.Tasks.Task Projects_CreateProject_Async(CreateConversionProjectCommand request) + /// A server side error occurred. + public virtual System.Threading.Tasks.Task CreateProjectAsync(CreateConversionProjectCommand request) { - return Projects_CreateProject_Async(request, System.Threading.CancellationToken.None); + return CreateProjectAsync(request, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. @@ -91,8 +91,8 @@ public virtual System.Threading.Tasks.Task Projects_CreateProject_Asy /// /// The request. /// Project created successfully. - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task Projects_CreateProject_Async(CreateConversionProjectCommand request, System.Threading.CancellationToken cancellationToken) + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task CreateProjectAsync(CreateConversionProjectCommand request, System.Threading.CancellationToken cancellationToken) { if (request == null) throw new System.ArgumentNullException("request"); @@ -143,7 +143,7 @@ public virtual async System.Threading.Tasks.Task Projects_CreateProje var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { - throw new PersonsApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + throw new CompleteApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } return objectResponse_.Object; } @@ -151,12 +151,12 @@ public virtual async System.Threading.Tasks.Task Projects_CreateProje if (status_ == 400) { string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new PersonsApiException("Invalid request data.", status_, responseText_, headers_, null); + throw new CompleteApiException("Invalid request data.", status_, responseText_, headers_, null); } else { var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new PersonsApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + throw new CompleteApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); } } finally @@ -173,174 +173,14 @@ public virtual async System.Threading.Tasks.Task Projects_CreateProje } } - protected struct ObjectResponseResult - { - public ObjectResponseResult(T responseObject, string responseText) - { - this.Object = responseObject; - this.Text = responseText; - } - - public T Object { get; } - - public string Text { get; } - } - - public bool ReadResponseAsString { get; set; } - - protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) - { - if (response == null || response.Content == null) - { - return new ObjectResponseResult(default(T)!, string.Empty); - } - - if (ReadResponseAsString) - { - var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); - return new ObjectResponseResult(typedBody!, responseText); - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; - throw new PersonsApiException(message, (int)response.StatusCode, responseText, headers, exception); - } - } - else - { - try - { - using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) - using (var streamReader = new System.IO.StreamReader(responseStream)) - using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) - { - var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); - var typedBody = serializer.Deserialize(jsonTextReader); - return new ObjectResponseResult(typedBody!, string.Empty); - } - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; - throw new PersonsApiException(message, (int)response.StatusCode, string.Empty, headers, exception); - } - } - } - - private string ConvertToString(object? value, System.Globalization.CultureInfo cultureInfo) - { - if (value == null) - { - return ""; - } - - if (value is System.Enum) - { - var name = System.Enum.GetName(value.GetType(), value); - if (name != null) - { - var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); - if (field != null) - { - var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) - as System.Runtime.Serialization.EnumMemberAttribute; - if (attribute != null) - { - return attribute.Value != null ? attribute.Value : name; - } - } - - var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); - return converted == null ? string.Empty : converted; - } - } - else if (value is bool) - { - return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); - } - else if (value is byte[]) - { - return System.Convert.ToBase64String((byte[]) value); - } - else if (value is string[]) - { - return string.Join(",", (string[])value); - } - else if (value.GetType().IsArray) - { - var valueArray = (System.Array)value; - var valueTextArray = new string[valueArray.Length]; - for (var i = 0; i < valueArray.Length; i++) - { - valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); - } - return string.Join(",", valueTextArray); - } - - var result = System.Convert.ToString(value, cultureInfo); - return result == null ? "" : result; - } - } - - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class GetProjectClient : IGetProjectClient - { - #pragma warning disable 8618 - private string _baseUrl; - #pragma warning restore 8618 - - private System.Net.Http.HttpClient _httpClient; - private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); - private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; - - #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public GetProjectClient(string baseUrl, System.Net.Http.HttpClient httpClient) - #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - { - BaseUrl = baseUrl; - _httpClient = httpClient; - Initialize(); - } - - private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() - { - var settings = new Newtonsoft.Json.JsonSerializerSettings(); - UpdateJsonSerializerSettings(settings); - return settings; - } - - public string BaseUrl - { - get { return _baseUrl; } - set - { - _baseUrl = value; - if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) - _baseUrl += '/'; - } - } - - protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } - - static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); - - partial void Initialize(); - - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); - partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); - /// /// Gets a Project /// /// Project - /// A server side error occurred. - public virtual System.Threading.Tasks.Task Projects_GetProject_Async(int? urn_Value) + /// A server side error occurred. + public virtual System.Threading.Tasks.Task GetProjectAsync(int? urn_Value) { - return Projects_GetProject_Async(urn_Value, System.Threading.CancellationToken.None); + return GetProjectAsync(urn_Value, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. @@ -348,8 +188,8 @@ public virtual System.Threading.Tasks.Task Projects_GetProject_Async(in /// Gets a Project /// /// Project - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task Projects_GetProject_Async(int? urn_Value, System.Threading.CancellationToken cancellationToken) + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task GetProjectAsync(int? urn_Value, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -399,7 +239,7 @@ public virtual async System.Threading.Tasks.Task Projects_GetProject_As var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { - throw new PersonsApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + throw new CompleteApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } return objectResponse_.Object; } @@ -407,12 +247,12 @@ public virtual async System.Threading.Tasks.Task Projects_GetProject_As if (status_ == 400) { string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new PersonsApiException("Invalid request data.", status_, responseText_, headers_, null); + throw new CompleteApiException("Invalid request data.", status_, responseText_, headers_, null); } else { var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new PersonsApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + throw new CompleteApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); } } finally @@ -429,174 +269,14 @@ public virtual async System.Threading.Tasks.Task Projects_GetProject_As } } - protected struct ObjectResponseResult - { - public ObjectResponseResult(T responseObject, string responseText) - { - this.Object = responseObject; - this.Text = responseText; - } - - public T Object { get; } - - public string Text { get; } - } - - public bool ReadResponseAsString { get; set; } - - protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) - { - if (response == null || response.Content == null) - { - return new ObjectResponseResult(default(T)!, string.Empty); - } - - if (ReadResponseAsString) - { - var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); - return new ObjectResponseResult(typedBody!, responseText); - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; - throw new PersonsApiException(message, (int)response.StatusCode, responseText, headers, exception); - } - } - else - { - try - { - using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) - using (var streamReader = new System.IO.StreamReader(responseStream)) - using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) - { - var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); - var typedBody = serializer.Deserialize(jsonTextReader); - return new ObjectResponseResult(typedBody!, string.Empty); - } - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; - throw new PersonsApiException(message, (int)response.StatusCode, string.Empty, headers, exception); - } - } - } - - private string ConvertToString(object? value, System.Globalization.CultureInfo cultureInfo) - { - if (value == null) - { - return ""; - } - - if (value is System.Enum) - { - var name = System.Enum.GetName(value.GetType(), value); - if (name != null) - { - var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); - if (field != null) - { - var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) - as System.Runtime.Serialization.EnumMemberAttribute; - if (attribute != null) - { - return attribute.Value != null ? attribute.Value : name; - } - } - - var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); - return converted == null ? string.Empty : converted; - } - } - else if (value is bool) - { - return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); - } - else if (value is byte[]) - { - return System.Convert.ToBase64String((byte[]) value); - } - else if (value is string[]) - { - return string.Join(",", (string[])value); - } - else if (value.GetType().IsArray) - { - var valueArray = (System.Array)value; - var valueTextArray = new string[valueArray.Length]; - for (var i = 0; i < valueArray.Length; i++) - { - valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); - } - return string.Join(",", valueTextArray); - } - - var result = System.Convert.ToString(value, cultureInfo); - return result == null ? "" : result; - } - } - - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class ListAllProjectsClient : IListAllProjectsClient - { - #pragma warning disable 8618 - private string _baseUrl; - #pragma warning restore 8618 - - private System.Net.Http.HttpClient _httpClient; - private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); - private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; - - #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public ListAllProjectsClient(string baseUrl, System.Net.Http.HttpClient httpClient) - #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - { - BaseUrl = baseUrl; - _httpClient = httpClient; - Initialize(); - } - - private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() - { - var settings = new Newtonsoft.Json.JsonSerializerSettings(); - UpdateJsonSerializerSettings(settings); - return settings; - } - - public string BaseUrl - { - get { return _baseUrl; } - set - { - _baseUrl = value; - if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) - _baseUrl += '/'; - } - } - - protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } - - static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); - - partial void Initialize(); - - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); - partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); - /// /// Returns a list of Projects /// /// Project - /// A server side error occurred. - public virtual System.Threading.Tasks.Task> Projects_ListAllProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count) + /// A server side error occurred. + public virtual System.Threading.Tasks.Task> ListAllProjectsAsync(ProjectState? projectStatus, ProjectType? type, int? page, int? count) { - return Projects_ListAllProjects_Async(projectStatus, type, page, count, System.Threading.CancellationToken.None); + return ListAllProjectsAsync(projectStatus, type, page, count, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. @@ -604,8 +284,8 @@ public string BaseUrl /// Returns a list of Projects /// /// Project - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> Projects_ListAllProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count, System.Threading.CancellationToken cancellationToken) + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task> ListAllProjectsAsync(ProjectState? projectStatus, ProjectType? type, int? page, int? count, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -667,7 +347,7 @@ public string BaseUrl var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { - throw new PersonsApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + throw new CompleteApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } return objectResponse_.Object; } @@ -675,12 +355,12 @@ public string BaseUrl if (status_ == 400) { string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new PersonsApiException("Invalid request data.", status_, responseText_, headers_, null); + throw new CompleteApiException("Invalid request data.", status_, responseText_, headers_, null); } else { var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new PersonsApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + throw new CompleteApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); } } finally @@ -697,174 +377,14 @@ public string BaseUrl } } - protected struct ObjectResponseResult - { - public ObjectResponseResult(T responseObject, string responseText) - { - this.Object = responseObject; - this.Text = responseText; - } - - public T Object { get; } - - public string Text { get; } - } - - public bool ReadResponseAsString { get; set; } - - protected virtual async System.Threading.Tasks.Task> ReadObjectResponseAsync(System.Net.Http.HttpResponseMessage response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Threading.CancellationToken cancellationToken) - { - if (response == null || response.Content == null) - { - return new ObjectResponseResult(default(T)!, string.Empty); - } - - if (ReadResponseAsString) - { - var responseText = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - var typedBody = Newtonsoft.Json.JsonConvert.DeserializeObject(responseText, JsonSerializerSettings); - return new ObjectResponseResult(typedBody!, responseText); - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; - throw new PersonsApiException(message, (int)response.StatusCode, responseText, headers, exception); - } - } - else - { - try - { - using (var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) - using (var streamReader = new System.IO.StreamReader(responseStream)) - using (var jsonTextReader = new Newtonsoft.Json.JsonTextReader(streamReader)) - { - var serializer = Newtonsoft.Json.JsonSerializer.Create(JsonSerializerSettings); - var typedBody = serializer.Deserialize(jsonTextReader); - return new ObjectResponseResult(typedBody!, string.Empty); - } - } - catch (Newtonsoft.Json.JsonException exception) - { - var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; - throw new PersonsApiException(message, (int)response.StatusCode, string.Empty, headers, exception); - } - } - } - - private string ConvertToString(object? value, System.Globalization.CultureInfo cultureInfo) - { - if (value == null) - { - return ""; - } - - if (value is System.Enum) - { - var name = System.Enum.GetName(value.GetType(), value); - if (name != null) - { - var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name); - if (field != null) - { - var attribute = System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) - as System.Runtime.Serialization.EnumMemberAttribute; - if (attribute != null) - { - return attribute.Value != null ? attribute.Value : name; - } - } - - var converted = System.Convert.ToString(System.Convert.ChangeType(value, System.Enum.GetUnderlyingType(value.GetType()), cultureInfo)); - return converted == null ? string.Empty : converted; - } - } - else if (value is bool) - { - return System.Convert.ToString((bool)value, cultureInfo).ToLowerInvariant(); - } - else if (value is byte[]) - { - return System.Convert.ToBase64String((byte[]) value); - } - else if (value is string[]) - { - return string.Join(",", (string[])value); - } - else if (value.GetType().IsArray) - { - var valueArray = (System.Array)value; - var valueTextArray = new string[valueArray.Length]; - for (var i = 0; i < valueArray.Length; i++) - { - valueTextArray[i] = ConvertToString(valueArray.GetValue(i), cultureInfo); - } - return string.Join(",", valueTextArray); - } - - var result = System.Convert.ToString(value, cultureInfo); - return result == null ? "" : result; - } - } - - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class CountAllProjectsClient : ICountAllProjectsClient - { - #pragma warning disable 8618 - private string _baseUrl; - #pragma warning restore 8618 - - private System.Net.Http.HttpClient _httpClient; - private static System.Lazy _settings = new System.Lazy(CreateSerializerSettings, true); - private Newtonsoft.Json.JsonSerializerSettings _instanceSettings; - - #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - public CountAllProjectsClient(string baseUrl, System.Net.Http.HttpClient httpClient) - #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. - { - BaseUrl = baseUrl; - _httpClient = httpClient; - Initialize(); - } - - private static Newtonsoft.Json.JsonSerializerSettings CreateSerializerSettings() - { - var settings = new Newtonsoft.Json.JsonSerializerSettings(); - UpdateJsonSerializerSettings(settings); - return settings; - } - - public string BaseUrl - { - get { return _baseUrl; } - set - { - _baseUrl = value; - if (!string.IsNullOrEmpty(_baseUrl) && !_baseUrl.EndsWith("/")) - _baseUrl += '/'; - } - } - - protected Newtonsoft.Json.JsonSerializerSettings JsonSerializerSettings { get { return _instanceSettings ?? _settings.Value; } } - - static partial void UpdateJsonSerializerSettings(Newtonsoft.Json.JsonSerializerSettings settings); - - partial void Initialize(); - - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, string url); - partial void PrepareRequest(System.Net.Http.HttpClient client, System.Net.Http.HttpRequestMessage request, System.Text.StringBuilder urlBuilder); - partial void ProcessResponse(System.Net.Http.HttpClient client, System.Net.Http.HttpResponseMessage response); - /// /// Returns the number of Projects /// /// Project - /// A server side error occurred. - public virtual System.Threading.Tasks.Task Projects_CountAllProjects_Async(ProjectState? projectStatus, ProjectType? type) + /// A server side error occurred. + public virtual System.Threading.Tasks.Task CountAllProjectsAsync(ProjectState? projectStatus, ProjectType? type) { - return Projects_CountAllProjects_Async(projectStatus, type, System.Threading.CancellationToken.None); + return CountAllProjectsAsync(projectStatus, type, System.Threading.CancellationToken.None); } /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. @@ -872,8 +392,8 @@ public virtual System.Threading.Tasks.Task Projects_CountAllProjects_Async( /// Returns the number of Projects /// /// Project - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task Projects_CountAllProjects_Async(ProjectState? projectStatus, ProjectType? type, System.Threading.CancellationToken cancellationToken) + /// A server side error occurred. + public virtual async System.Threading.Tasks.Task CountAllProjectsAsync(ProjectState? projectStatus, ProjectType? type, System.Threading.CancellationToken cancellationToken) { var client_ = _httpClient; var disposeClient_ = false; @@ -927,7 +447,7 @@ public virtual async System.Threading.Tasks.Task Projects_CountAllProjects_ var objectResponse_ = await ReadObjectResponseAsync(response_, headers_, cancellationToken).ConfigureAwait(false); if (objectResponse_.Object == null) { - throw new PersonsApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); + throw new CompleteApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); } return objectResponse_.Object; } @@ -935,12 +455,12 @@ public virtual async System.Threading.Tasks.Task Projects_CountAllProjects_ if (status_ == 400) { string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new PersonsApiException("Invalid request data.", status_, responseText_, headers_, null); + throw new CompleteApiException("Invalid request data.", status_, responseText_, headers_, null); } else { var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new PersonsApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); + throw new CompleteApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); } } finally @@ -990,7 +510,7 @@ protected virtual async System.Threading.Tasks.Task> Rea catch (Newtonsoft.Json.JsonException exception) { var message = "Could not deserialize the response body string as " + typeof(T).FullName + "."; - throw new PersonsApiException(message, (int)response.StatusCode, responseText, headers, exception); + throw new CompleteApiException(message, (int)response.StatusCode, responseText, headers, exception); } } else @@ -1009,7 +529,7 @@ protected virtual async System.Threading.Tasks.Task> Rea catch (Newtonsoft.Json.JsonException exception) { var message = "Could not deserialize the response body stream as " + typeof(T).FullName + "."; - throw new PersonsApiException(message, (int)response.StatusCode, string.Empty, headers, exception); + throw new CompleteApiException(message, (int)response.StatusCode, string.Empty, headers, exception); } } } diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs index d52167db..30806fae 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs +++ b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs @@ -25,15 +25,15 @@ namespace Dfe.Complete.Client.Contracts using System = global::System; [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial interface ICreateProjectClient + public partial interface IProjectsClient { /// /// Creates a new Project /// /// The request. /// Project created successfully. - /// A server side error occurred. - System.Threading.Tasks.Task Projects_CreateProject_Async(CreateConversionProjectCommand request); + /// A server side error occurred. + System.Threading.Tasks.Task CreateProjectAsync(CreateConversionProjectCommand request); /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// @@ -41,68 +41,53 @@ public partial interface ICreateProjectClient /// /// The request. /// Project created successfully. - /// A server side error occurred. - System.Threading.Tasks.Task Projects_CreateProject_Async(CreateConversionProjectCommand request, System.Threading.CancellationToken cancellationToken); + /// A server side error occurred. + System.Threading.Tasks.Task CreateProjectAsync(CreateConversionProjectCommand request, System.Threading.CancellationToken cancellationToken); - } - - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial interface IGetProjectClient - { /// /// Gets a Project /// /// Project - /// A server side error occurred. - System.Threading.Tasks.Task Projects_GetProject_Async(int? urn_Value); + /// A server side error occurred. + System.Threading.Tasks.Task GetProjectAsync(int? urn_Value); /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// /// Gets a Project /// /// Project - /// A server side error occurred. - System.Threading.Tasks.Task Projects_GetProject_Async(int? urn_Value, System.Threading.CancellationToken cancellationToken); - - } + /// A server side error occurred. + System.Threading.Tasks.Task GetProjectAsync(int? urn_Value, System.Threading.CancellationToken cancellationToken); - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial interface IListAllProjectsClient - { /// /// Returns a list of Projects /// /// Project - /// A server side error occurred. - System.Threading.Tasks.Task> Projects_ListAllProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count); + /// A server side error occurred. + System.Threading.Tasks.Task> ListAllProjectsAsync(ProjectState? projectStatus, ProjectType? type, int? page, int? count); /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// /// Returns a list of Projects /// /// Project - /// A server side error occurred. - System.Threading.Tasks.Task> Projects_ListAllProjects_Async(ProjectState? projectStatus, ProjectType? type, int? page, int? count, System.Threading.CancellationToken cancellationToken); - - } + /// A server side error occurred. + System.Threading.Tasks.Task> ListAllProjectsAsync(ProjectState? projectStatus, ProjectType? type, int? page, int? count, System.Threading.CancellationToken cancellationToken); - [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial interface ICountAllProjectsClient - { /// /// Returns the number of Projects /// /// Project - /// A server side error occurred. - System.Threading.Tasks.Task Projects_CountAllProjects_Async(ProjectState? projectStatus, ProjectType? type); + /// A server side error occurred. + System.Threading.Tasks.Task CountAllProjectsAsync(ProjectState? projectStatus, ProjectType? type); /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// /// Returns the number of Projects /// /// Project - /// A server side error occurred. - System.Threading.Tasks.Task Projects_CountAllProjects_Async(ProjectState? projectStatus, ProjectType? type, System.Threading.CancellationToken cancellationToken); + /// A server side error occurred. + System.Threading.Tasks.Task CountAllProjectsAsync(ProjectState? projectStatus, ProjectType? type, System.Threading.CancellationToken cancellationToken); } @@ -868,7 +853,7 @@ public DateFormatConverter() [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class PersonsApiException : System.Exception + public partial class CompleteApiException : System.Exception { public int StatusCode { get; private set; } @@ -876,7 +861,7 @@ public partial class PersonsApiException : System.Exception public System.Collections.Generic.IReadOnlyDictionary> Headers { get; private set; } - public PersonsApiException(string message, int statusCode, string? response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception? innerException) + public CompleteApiException(string message, int statusCode, string? response, System.Collections.Generic.IReadOnlyDictionary> headers, System.Exception? innerException) : base(message + "\n\nStatus: " + statusCode + "\nResponse: \n" + ((response == null) ? "(null)" : response.Substring(0, response.Length >= 512 ? 512 : response.Length)), innerException) { StatusCode = statusCode; @@ -891,11 +876,11 @@ public override string ToString() } [System.CodeDom.Compiler.GeneratedCode("NSwag", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] - public partial class PersonsApiException : PersonsApiException + public partial class CompleteApiException : CompleteApiException { public TResult Result { get; private set; } - public PersonsApiException(string message, int statusCode, string? response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception? innerException) + public CompleteApiException(string message, int statusCode, string? response, System.Collections.Generic.IReadOnlyDictionary> headers, TResult result, System.Exception? innerException) : base(message, statusCode, response, headers, innerException) { Result = result; diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json index 982c83b7..343ca87e 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json +++ b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json @@ -12,7 +12,7 @@ "Projects" ], "summary": "Creates a new Project", - "operationId": "Projects_CreateProject_", + "operationId": "Projects_CreateProject", "requestBody": { "x-name": "request", "description": "The request.", @@ -47,7 +47,7 @@ "Projects" ], "summary": "Gets a Project", - "operationId": "Projects_GetProject_", + "operationId": "Projects_GetProject", "parameters": [ { "name": "Urn.Value", @@ -82,7 +82,7 @@ "Projects" ], "summary": "Returns a list of Projects", - "operationId": "Projects_ListAllProjects_", + "operationId": "Projects_ListAllProjects", "parameters": [ { "name": "ProjectStatus", @@ -155,7 +155,7 @@ "Projects" ], "summary": "Returns the number of Projects", - "operationId": "Projects_CountAllProjects_", + "operationId": "Projects_CountAllProjects", "parameters": [ { "name": "ProjectStatus", diff --git a/src/Api/Dfe.Complete.Api.Client/nswag.json b/src/Api/Dfe.Complete.Api.Client/nswag.json index 6a90f3e3..b90c3fa8 100644 --- a/src/Api/Dfe.Complete.Api.Client/nswag.json +++ b/src/Api/Dfe.Complete.Api.Client/nswag.json @@ -66,7 +66,7 @@ "disposeHttpClient": false, "protectedMethods": [], "generateExceptionClasses": true, - "exceptionClass": "PersonsApiException", + "exceptionClass": "CompleteApiException", "wrapDtoExceptions": true, "useHttpClientCreationMethod": false, "httpClientType": "System.Net.Http.HttpClient", diff --git a/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs b/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs index 7d7e1aad..2f16487c 100644 --- a/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs +++ b/src/Api/Dfe.Complete.Api/Controllers/ProjectsController.cs @@ -27,7 +27,7 @@ public class ProjectsController(ISender sender) : ControllerBase [HttpPost] [SwaggerResponse(201, "Project created successfully.", typeof(ProjectId))] [SwaggerResponse(400, "Invalid request data.")] - public async Task CreateProject_Async([FromBody] CreateConversionProjectCommand request, CancellationToken cancellationToken) + public async Task CreateProjectAsync([FromBody] CreateConversionProjectCommand request, CancellationToken cancellationToken) { var projectId = await sender.Send(request, cancellationToken); return Created("", projectId); @@ -42,10 +42,10 @@ public async Task CreateProject_Async([FromBody] CreateConversion [HttpGet] [SwaggerResponse(200, "Project", typeof(Project))] [SwaggerResponse(400, "Invalid request data.")] - public async Task GetProject_Async([FromQuery] GetProjectByUrnQuery request, CancellationToken cancellationToken) + public async Task GetProjectAsync([FromQuery] GetProjectByUrnQuery request, CancellationToken cancellationToken) { var project = await sender.Send(request, cancellationToken); - return Ok(project); + return Ok(project.Value); } /// @@ -58,10 +58,10 @@ public async Task GetProject_Async([FromQuery] GetProjectByUrnQue [Route("List/All")] [SwaggerResponse(200, "Project", typeof(List))] [SwaggerResponse(400, "Invalid request data.")] - public async Task ListAllProjects_Async([FromQuery] ListAllProjectsQuery request, CancellationToken cancellationToken) + public async Task ListAllProjectsAsync([FromQuery] ListAllProjectsQuery request, CancellationToken cancellationToken) { var project = await sender.Send(request, cancellationToken); - return Ok(project); + return Ok(project.Value); } /// @@ -74,10 +74,10 @@ public async Task ListAllProjects_Async([FromQuery] ListAllProjec [Route("Count/All")] [SwaggerResponse(200, "Project", typeof(int))] [SwaggerResponse(400, "Invalid request data.")] - public async Task CountAllProjects_Async([FromQuery] CountAllProjectsQuery request, CancellationToken cancellationToken) + public async Task CountAllProjectsAsync([FromQuery] CountAllProjectsQuery request, CancellationToken cancellationToken) { var project = await sender.Send(request, cancellationToken); - return Ok(project); + return Ok(project.Value); } } From 0571480615f531ee49ddcab97ae8e5fa387079bf Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 27 Jan 2025 10:32:45 +0000 Subject: [PATCH 33/47] Update Namespace for projects in progress view model --- .../List/ProjectsInProgress/AllProjectsInProgress.cshtml | 2 +- .../List/ProjectsInProgress/ProjectsInProgressViewModel.cs | 2 +- .../List/ProjectsInProgress/_ProjectsInProgressLayout.cshtml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml index 38974d0b..19478264 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/AllProjectsInProgress.cshtml @@ -1,5 +1,5 @@ @page "/projects/all/in-progress/all" -@using Dfe.Complete.Pages.Projects.List +@using Dfe.Complete.Pages.Projects.List.ProjectsInProgress @model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.ProjectsInProgressInProgressViewModel @{ Layout = "List/ProjectsInProgress/_ProjectsInProgressLayout"; diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModel.cs b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModel.cs index a6de5476..16fa1c5e 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModel.cs +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModel.cs @@ -5,7 +5,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; -namespace Dfe.Complete.Pages.Projects.List; +namespace Dfe.Complete.Pages.Projects.List.ProjectsInProgress; public class ProjectsInProgressViewModel : PageModel { diff --git a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/_ProjectsInProgressLayout.cshtml b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/_ProjectsInProgressLayout.cshtml index f4d60b78..d4c5c166 100644 --- a/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/_ProjectsInProgressLayout.cshtml +++ b/src/Frontend/Dfe.Complete/Pages/Projects/List/ProjectsInProgress/_ProjectsInProgressLayout.cshtml @@ -1,4 +1,4 @@ -@model Dfe.Complete.Pages.Projects.List.ProjectsInProgressViewModel +@model Dfe.Complete.Pages.Projects.List.ProjectsInProgress.ProjectsInProgressViewModel @{ Layout = "List/_AllProjectsLayout"; } From 809a9f38c5ca813402f9ce04fc5f3373f4cd2c2e Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 27 Jan 2025 10:33:13 +0000 Subject: [PATCH 34/47] Add unit tests for pagination models --- .../Pages/Pagination/PaginationModelTests.cs | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 src/Tests/Dfe.Complete.Tests/Pages/Pagination/PaginationModelTests.cs diff --git a/src/Tests/Dfe.Complete.Tests/Pages/Pagination/PaginationModelTests.cs b/src/Tests/Dfe.Complete.Tests/Pages/Pagination/PaginationModelTests.cs new file mode 100644 index 00000000..dd091ed6 --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests/Pages/Pagination/PaginationModelTests.cs @@ -0,0 +1,181 @@ +using Dfe.Complete.Pages.Pagination; + +namespace Dfe.Complete.Tests.Pages.Pagination; + +public class PaginationModelTests +{ + [Fact] + public void Constructor_WithValidInputs_InitialisesPropertiesCorrectly() + { + // Arrange + var url = "https://example.com"; + var pageNumber = 2; + var recordCount = 50; + var pageSize = 10; + var elementIdPrefix = "test-"; + + // Act + var model = new PaginationModel(url, pageNumber, recordCount, pageSize, elementIdPrefix); + + // Assert + Assert.Equal(url, model.Url); + Assert.Equal(pageNumber, model.CurrentPageNumber); + Assert.Equal(recordCount, model.RecordCount); + Assert.Equal(5, model.TotalPages); // 50 records with page size 10 = 5 pages + Assert.True(model.HasPrevious); + Assert.True(model.HasNext); + Assert.Equal(pageNumber - 1, model.Previous); + Assert.Equal(pageNumber + 1, model.Next); + Assert.Equal(elementIdPrefix, model.ElementIdPrefix); + Assert.Equal("test-", model.Prefix); + Assert.Contains(1, model.PagesToDisplay); + Assert.Contains(pageNumber, model.PagesToDisplay); + Assert.Contains(5, model.PagesToDisplay); + Assert.Contains(1, model.PagesToDisplay); + Assert.Contains(3, model.PagesToDisplay); + } + + [Fact] + public void PaginationModel_OnFirstPage_HasCorrectProperties() + { + // Arrange + var url = "https://example.com"; + var pageNumber = 1; + var recordCount = 20; + var pageSize = 5; + + // Act + var model = new PaginationModel(url, pageNumber, recordCount, pageSize); + + // Assert + Assert.False(model.HasPrevious); + Assert.Null(model.Previous); + Assert.Null(model.PreviousPageLink); + Assert.True(model.HasNext); + Assert.Equal(2, model.Next); + Assert.Equal(4, model.TotalPages); // 20 records with page size 5 = 4 pages + Assert.Equal(new List { 1, 2, 4 }, model.PagesToDisplay); + } + + [Fact] + public void PaginationModel_OnLastPage_HasCorrectProperties() + { + // Arrange + var url = "https://example.com"; + var pageNumber = 5; + var recordCount = 50; + var pageSize = 10; + + // Act + var model = new PaginationModel(url, pageNumber, recordCount, pageSize); + + // Assert + Assert.True(model.HasPrevious); + Assert.Equal(4, model.Previous); + Assert.False(model.HasNext); + Assert.Null(model.Next); + Assert.Null(model.NextPageLink); + Assert.Equal(new List { 1, 4, 5 }, model.PagesToDisplay); + } + + [Theory] + [InlineData(null)] + [InlineData("")] + public void Prefix_WhenElementIdPrefixIsNullOrEmpty_ReturnsEmptyString(string? elementPrefix) + { + // Arrange + var url = "https://example.com"; + var pageNumber = 1; + var recordCount = 10; + var pageSize = 5; + + // Act + var modelWithPrefix = new PaginationModel(url, pageNumber, recordCount, pageSize, elementPrefix); + + // Assert + Assert.Equal(string.Empty, modelWithPrefix.Prefix); + } + + [Fact] + public void NextPageLink_CorrectlyFormatsUrl() + { + // Arrange + var url = "https://example.com"; + var pageNumber = 2; + var recordCount = 50; + var pageSize = 10; + + // Act + var model = new PaginationModel(url, pageNumber, recordCount, pageSize); + + // Assert + Assert.Equal("https://example.com?pageNumber=3", model.NextPageLink); + } + + [Fact] + public void PreviousPageLink_CorrectlyFormatsUrl() + { + // Arrange + var url = "https://example.com"; + var pageNumber = 2; + var recordCount = 50; + var pageSize = 10; + + // Act + var model = new PaginationModel(url, pageNumber, recordCount, pageSize); + + // Assert + Assert.Equal("https://example.com?pageNumber=1", model.PreviousPageLink); + } + + [Fact] + public void PaginationContainerId_FormatsCorrectlyWithPrefix() + { + // Arrange + var url = "https://example.com"; + var pageNumber = 1; + var recordCount = 10; + var pageSize = 5; + var elementIdPrefix = "custom-"; + + // Act + var model = new PaginationModel(url, pageNumber, recordCount, pageSize, elementIdPrefix); + + // Assert + Assert.Equal("custom-pagination-container", model.PaginationContainerId); + } + + [Fact] + public void NextButtonId_FormatsCorrectlyWithPrefix() + { + // Arrange + var url = "https://example.com"; + var pageNumber = 1; + var recordCount = 10; + var pageSize = 5; + var elementIdPrefix = "custom-"; + + // Act + var model = new PaginationModel(url, pageNumber, recordCount, pageSize, elementIdPrefix); + + // Assert + Assert.Equal("custom-next-page", model.NextButtonId); + } + + [Fact] + public void PreviousButtonId_FormatsCorrectlyWithPrefix() + { + // Arrange + var url = "https://example.com"; + var pageNumber = 1; + var recordCount = 10; + var pageSize = 5; + var elementIdPrefix = "custom-"; + + // Act + var model = new PaginationModel(url, pageNumber, recordCount, pageSize, elementIdPrefix); + + // Assert + Assert.Equal("custom-previous-page", model.PreviousButtonId); + } +} From 37990067b77e4b43b642e01f66d6d36fac9f3e69 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 27 Jan 2025 10:33:26 +0000 Subject: [PATCH 35/47] Add unit tests for DateOnly extensions --- .../Extensions/DateOnlyExtensionsTests.cs | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 src/Tests/Dfe.Complete.Tests/Extensions/DateOnlyExtensionsTests.cs diff --git a/src/Tests/Dfe.Complete.Tests/Extensions/DateOnlyExtensionsTests.cs b/src/Tests/Dfe.Complete.Tests/Extensions/DateOnlyExtensionsTests.cs new file mode 100644 index 00000000..543a9c71 --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests/Extensions/DateOnlyExtensionsTests.cs @@ -0,0 +1,71 @@ +using Dfe.Complete.Extensions; + +namespace Dfe.Complete.Tests.Extensions; + +public class DateOnlyExtensionsTests +{ + [Theory] + [InlineData(1,2,2025, "01/02/2025")] + [InlineData(31,12,9999, "31/12/9999")] + [InlineData(1,1,1, "01/01/0001")] + [InlineData(10,9,1990, "10/09/1990")] + public void ToUkDateString_ReturnsTheCorrectString(int date, int month, int year, string expected) + { + Assert.Equal(expected, new DateOnly(year, month, date).ToUkDateString()); + } + + [Theory] + [InlineData(1,2,2025, "1 February 2025")] + [InlineData(31,12,9999, "31 December 9999")] + [InlineData(1,1,1, "1 January 0001")] + [InlineData(10,9,1990, "10 September 1990")] + public void ToDateString_ReturnsTheCorrectString_WithoutDayOfTheWeek(int date, int month, int year, string expected) + { + Assert.Equal(expected, new DateOnly(year, month, date).ToDateString()); + } + + [Theory] + [InlineData(1,2,2025, "Saturday 1 February 2025")] + [InlineData(31,12,9999, "Friday 31 December 9999")] + [InlineData(1,1,1, "Monday 1 January 0001")] + [InlineData(10,9,1990, "Monday 10 September 1990")] + public void ToDateString_ReturnsTheCorrectString_WithDayOfTheWeek(int date, int month, int year, string expected) + { + Assert.Equal(expected, new DateOnly(year, month, date).ToDateString(true)); + } + + [Fact] + public void ToDateString_ReturnsEmptyString_WhenDateOnlyIsNull() + { + DateOnly? test = null; + Assert.Equal(string.Empty, test.ToDateString(true)); + } + + [Theory] + [InlineData(1,2,2025, 0, 2)] + [InlineData(31,12,9998, 1, 1, 9999)] + [InlineData(1,1,1, 10, 11)] + [InlineData(10,9,1990, 200, 5, 2007)] + public void FirstOfMonth_ReturnsTheCorrectDateOnly(int testDate, int testMonth, int testYear, int monthsToAdd, int expectedMonth, int? expectedYear = null) + { + Assert.Equal(new DateOnly(expectedYear ?? testYear, expectedMonth, 1), new DateOnly(testYear, testMonth, testDate).FirstOfMonth(monthsToAdd)); + } + + [Theory] + [InlineData(1,2,2025, "Feb 2025")] + [InlineData(31,12,9999, "Dec 9999")] + [InlineData(1,1,1, "Jan 0001")] + [InlineData(10,9,1990, "Sept 1990")] + public void ToDateMonthYearString_ReturnsTheCorrectString_WithDayOfTheWeek(int date, int month, int year, string expected) + { + DateOnly? testDate = new DateOnly(year, month, date); + Assert.Equal(expected, testDate.ToDateMonthYearString()); + } + + [Fact] + public void ToDateMonthYearString_ReturnsEmptyString_WhenDateOnlyIsNull() + { + DateOnly? test = null; + Assert.Equal(string.Empty, test.ToDateMonthYearString()); + } +} \ No newline at end of file From 63d29dd33087e736546bdd2f07c0a0bf682d5801 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 27 Jan 2025 10:33:46 +0000 Subject: [PATCH 36/47] Add unit tests for Projects in Progress view model --- .../ProjectsInProgressViewModelTests.cs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/Tests/Dfe.Complete.Tests/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModelTests.cs diff --git a/src/Tests/Dfe.Complete.Tests/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModelTests.cs b/src/Tests/Dfe.Complete.Tests/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModelTests.cs new file mode 100644 index 00000000..bcdec3f5 --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests/Pages/Projects/List/ProjectsInProgress/ProjectsInProgressViewModelTests.cs @@ -0,0 +1,49 @@ +using DfE.CoreLibs.Testing.AutoFixture.Attributes; +using Dfe.Complete.Application.Projects.Model; +using Dfe.Complete.Constants; +using Dfe.Complete.Domain.Enums; +using Dfe.Complete.Pages.Projects.List.ProjectsInProgress; +using Dfe.Complete.Tests.Common.Customizations.Models; + +namespace Dfe.Complete.Tests.Pages.Projects.List.ProjectsInProgress; + +public class ProjectsInProgressViewModelTests +{ + [Theory] + [CustomAutoData(typeof(ListAllProjectResultModelCustomization))] + public void GetProjectSummaryUrl_ShouldReturnConversionTaskListUrl_WhenProjectTypeIsConversion(IFixture fixture) + { + // Arrange + var project = fixture.Customize(new ListAllProjectResultModelCustomization + { + ProjectType = ProjectType.Conversion + }).Create(); + + string expectedUrl = string.Format(RouteConstants.ConversionProjectTaskList, project.ProjectId); + + // Act + var result = ProjectsInProgressViewModel.GetProjectSummaryUrl(project); + + // Assert + Assert.Equal(expectedUrl, result); + } + + [Theory] + [CustomAutoData(typeof(ListAllProjectResultModelCustomization))] + public void GetProjectSummaryUrl_ShouldReturnTransferTaskListUrl_WhenProjectTypeIsTransfer(IFixture fixture) + { + // Arrange + var project = fixture.Customize(new ListAllProjectResultModelCustomization + { + ProjectType = ProjectType.Transfer + }).Create(); + + string expectedUrl = string.Format(RouteConstants.TransferProjectTaskList, project.ProjectId); + + // Act + var result = ProjectsInProgressViewModel.GetProjectSummaryUrl(project); + + // Assert + Assert.Equal(expectedUrl, result); + } +} \ No newline at end of file From cef3944c8b3bde7b3a3d2868e694523f6ddea7b1 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 27 Jan 2025 10:34:12 +0000 Subject: [PATCH 37/47] Update API integration tests, add tests for the new projects endpoints --- .../Controllers/ProjectsControllerTests.cs | 117 ++++++++++-------- 1 file changed, 66 insertions(+), 51 deletions(-) diff --git a/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs b/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs index 104e1f2a..9acc0bb7 100644 --- a/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs +++ b/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs @@ -1,16 +1,15 @@ using System.Net; -using Dfe.Complete.Application.Projects.Queries.CountAllProjects; -using Dfe.Complete.Application.Projects.Queries.ListAllProjects; +using AutoFixture; +using Dfe.Complete.Api.Tests.Integration.Customizations; using Dfe.Complete.Client.Contracts; +using Dfe.Complete.Domain.Entities; using Dfe.Complete.Infrastructure.Database; -using Dfe.Complete.Tests.Common.Customizations; using Dfe.Complete.Tests.Common.Customizations.Commands; using Dfe.Complete.Tests.Common.Customizations.Models; -using Dfe.Complete.Tests.Common.Customizations.Queries; using DfE.CoreLibs.Testing.AutoFixture.Attributes; -using DfE.CoreLibs.Testing.AutoFixture.Customizations; using DfE.CoreLibs.Testing.Mocks.WebApplicationFactory; using Microsoft.EntityFrameworkCore; +using Project = Dfe.Complete.Domain.Entities.Project; namespace Dfe.Complete.Api.Tests.Integration.Controllers; @@ -22,22 +21,23 @@ public class ProjectsControllerTests public async Task CreateProject_Async_ShouldCreateConversionProject( CustomWebApplicationDbContextFactory factory, CreateConversionProjectCommand createConversionProjectCommand, - ICreateProjectClient createProjectClient) + IProjectsClient projectsClient) { //todo: when auth is done, add this back in // factory.TestClaims = [new Claim(ClaimTypes.Role, "API.Write")]; var testUserAdId = createConversionProjectCommand.UserAdId; - + var dbContext = factory.GetDbContext(); var testUser = await dbContext.Users.FirstOrDefaultAsync(); + Assert.NotNull(testUser); testUser.ActiveDirectoryUserId = testUserAdId; dbContext.Users.Update(testUser); await dbContext.SaveChangesAsync(); - - var result = await createProjectClient.Projects_CreateProject_Async(createConversionProjectCommand); + + var result = await projectsClient.CreateProjectAsync(createConversionProjectCommand); Assert.NotNull(result); Assert.IsType(result); @@ -46,9 +46,9 @@ public async Task CreateProject_Async_ShouldCreateConversionProject( [Theory] [CustomAutoData(typeof(CustomWebApplicationDbContextFactoryCustomization))] public async Task CreateProject_WithNullRequest_ThrowsException( - CustomWebApplicationDbContextFactory factory, + // CustomWebApplicationDbContextFactory factory, CreateConversionProjectCommand createConversionProjectCommand, - ICreateProjectClient createProjectClient) + IProjectsClient projectsClient) { //todo: when auth is done, add this back in // factory.TestClaims = [new Claim(ClaimTypes.Role, "API.Write")]; @@ -56,63 +56,78 @@ public async Task CreateProject_WithNullRequest_ThrowsException( createConversionProjectCommand.Urn = null; //todo: change exception type? - var exception = await Assert.ThrowsAsync(async () => - await createProjectClient.Projects_CreateProject_Async(createConversionProjectCommand)); - + var exception = await Assert.ThrowsAsync(async () => + await projectsClient.CreateProjectAsync(createConversionProjectCommand)); + Assert.Equal(HttpStatusCode.BadRequest, (HttpStatusCode)exception.StatusCode); } [Theory] - [CustomAutoData(typeof(CountAllProjectsQueryCustomization), typeof(ProjectCustomization), typeof(DateOnlyCustomization))] - public async Task CountAllProjects_Async_ShouldReturnCorrectNumber( + [CustomAutoData(typeof(CustomWebApplicationDbContextFactoryCustomization), typeof(EstablishmentsCustomization))] + public async Task ListAllProjects_Async_ShouldReturnList( CustomWebApplicationDbContextFactory factory, - CountAllProjectsQuery countAllProjectsQuery, - ICountAllProjectsClient countAllProjectsClient) + IProjectsClient projectsClient, + IFixture fixture) { //todo: when auth is done, add this back in - + // Arrange var dbContext = factory.GetDbContext(); - - dbContext.Projects.AddRangeAsync() + var testUser = await dbContext.Users.FirstAsync(); + var establishments = fixture.CreateMany(50).ToList(); + await dbContext.GiasEstablishments.AddRangeAsync(establishments); + var projects = establishments.Select(establishment => + { + var project = fixture.Customize(new ProjectCustomization + { + RegionalDeliveryOfficerId = testUser.Id, + CaseworkerId = testUser.Id, + AssignedToId = testUser.Id + }) + .Create(); + project.Urn = establishment.Urn ?? project.Urn; + return project; + }).ToList(); + await dbContext.Projects.AddRangeAsync(projects); - // dbContext.Users.Update(testUser); - // await dbContext.SaveChangesAsync(); + await dbContext.SaveChangesAsync(); - var result = await countAllProjectsClient.Projects_CountAllProjects_Async( - (ProjectState?)countAllProjectsQuery.ProjectStatus, (ProjectType?)countAllProjectsQuery.Type); + // Act + var results = await projectsClient.ListAllProjectsAsync( + null, null, 0, 50); - // Assert.NotNull(result); - Assert.IsType(result); - } + // Assert + Assert.NotNull(results); + Assert.Equal(50, results.Count); + foreach (var result in results) + { + var project = projects.Find(p => p.Id.Value == result.ProjectId?.Value); + var establishment = establishments.Find(e => e.Urn?.Value == result.Urn?.Value); - [Theory] - [CustomAutoData(typeof(ListAllProjectsQueryCustomization))] - public async Task ListAllProjects_Async_ShouldReturnList( - ListAllProjectsQuery listAllProjectsQuery, - IListAllProjectsClient listAllProjectsClient) - { - //todo: when auth is done, add this back in - // factory.TestClaims = [new Claim(ClaimTypes.Role, "API.Write")]; + Assert.NotNull(result.EstablishmentName); + Assert.Equal(establishment?.Name, result.EstablishmentName); - // var testUserAdId = createConversionProjectCommand.UserAdId; + Assert.NotNull(result.ProjectId); + Assert.Equal(project?.Id.Value, result.ProjectId.Value); - // var dbContext = factory.GetDbContext(); + Assert.NotNull(result.Urn); + Assert.Equal(project?.Urn.Value, result.Urn.Value); + Assert.Equal(establishment?.Urn?.Value, result.Urn.Value); - // var testUser = await dbContext.Users.FirstOrDefaultAsync(); - // testUser.ActiveDirectoryUserId = testUserAdId; + Assert.NotNull(result.ConversionOrTransferDate); + Assert.Equal(project?.SignificantDate, new DateOnly(result.ConversionOrTransferDate.Value.Year, + result.ConversionOrTransferDate.Value.Month, result.ConversionOrTransferDate.Value.Day)); - // dbContext.Users.Update(testUser); - // await dbContext.SaveChangesAsync(); + Assert.NotNull(result.State); + Assert.Equal(project?.State.ToString(), result.State.ToString()); - var result = await listAllProjectsClient.Projects_ListAllProjects_Async( - (ProjectState?)listAllProjectsQuery.ProjectStatus, (ProjectType?)listAllProjectsQuery.Type, - listAllProjectsQuery.Page, listAllProjectsQuery.Count); + Assert.NotNull(result.ProjectType); + Assert.Equal(project?.Type?.ToString(), result.ProjectType.Value.ToString()); - Assert.NotNull(result); - Assert.IsAssignableFrom>(result); - foreach (var item in result) - { - //Do something to match all the results + Assert.Equal(project?.IncomingTrustUkprn == null, result.IsFormAMAT); + + Assert.NotNull(result.AssignedToFullName); + Assert.Equal($"{project?.AssignedTo?.FirstName} {project?.AssignedTo?.LastName}", + result.AssignedToFullName); } } -} \ No newline at end of file +} From 99d47d51a5ef5d6d0872021b03a5ca073047f44d Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 27 Jan 2025 13:26:59 +0000 Subject: [PATCH 38/47] Remove caching from CountAllProjectsQueryHandlerTests --- .../Project/CountAllProjectsQueryHandlerTests.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs b/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs index 041a4675..3751cd20 100644 --- a/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs +++ b/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs @@ -31,19 +31,19 @@ public async Task Handle_ShouldReturnCorrectCount( // Arrange var expected = listAllProjectsQueryModels.Count; - var cacheKey = $"Project_{CacheKeyHelper.GenerateHashedCacheKey(query.ToString())}"; + // var cacheKey = $"Project_{CacheKeyHelper.GenerateHashedCacheKey(query.ToString())}"; var mock = listAllProjectsQueryModels.BuildMock(); mockEstablishmentQueryService.ListAllProjects(query.ProjectStatus, query.Type) .Returns(mock); - mockCacheService.GetOrAddAsync(cacheKey, Arg.Any>>>(), Arg.Any()) - .Returns(callInfo => - { - var callback = callInfo.ArgAt>>>(1); - return callback(); - }); + // mockCacheService.GetOrAddAsync(cacheKey, Arg.Any>>>(), Arg.Any()) + // .Returns(callInfo => + // { + // var callback = callInfo.ArgAt>>>(1); + // return callback(); + // }); // Act var result = await handler.Handle(query, default); @@ -51,7 +51,7 @@ public async Task Handle_ShouldReturnCorrectCount( // Assert Assert.Equal(expected, result.Value); - await mockCacheService.Received(1).GetOrAddAsync(cacheKey, Arg.Any>>>(), nameof(CountAllProjectsQueryHandler)); + // await mockCacheService.Received(1).GetOrAddAsync(cacheKey, Arg.Any>>>(), nameof(CountAllProjectsQueryHandler)); } } } From 48fdcf0e38d71d5d91c2eebdfe3e5633d360695a Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Mon, 27 Jan 2025 14:07:36 +0000 Subject: [PATCH 39/47] Fix the create null project test and add in the CountProjects test --- .../Controllers/ProjectsControllerTests.cs | 41 ++++++++++++++++++- ...ustomWebApplicationFactoryCustomization.cs | 9 ++-- .../OpenApiTests/OpenApiDocumentTests.cs | 1 + .../CsvExport/Builders/FormAMatTests.cs | 12 ------ 4 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs b/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs index 758d29a8..0e279070 100644 --- a/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs +++ b/src/Tests/Dfe.Complete.Api.Tests.Integration/Controllers/ProjectsControllerTests.cs @@ -46,7 +46,7 @@ public async Task CreateProject_Async_ShouldCreateConversionProject( [Theory] [CustomAutoData(typeof(CustomWebApplicationDbContextFactoryCustomization))] public async Task CreateProject_WithNullRequest_ThrowsException( - // CustomWebApplicationDbContextFactory factory, + CustomWebApplicationDbContextFactory factory, CreateConversionProjectCommand createConversionProjectCommand, IProjectsClient projectsClient) { @@ -61,6 +61,45 @@ public async Task CreateProject_WithNullRequest_ThrowsException( Assert.Equal(HttpStatusCode.BadRequest, (HttpStatusCode)exception.StatusCode); } + + [Theory] + [CustomAutoData(typeof(CustomWebApplicationDbContextFactoryCustomization), typeof(EstablishmentsCustomization))] + public async Task CountAllProjects_Async_ShouldReturnCorrectNumber( + CustomWebApplicationDbContextFactory factory, + IProjectsClient projectsClient, + IFixture fixture) + { + //todo: when auth is done, add this back in + + var dbContext = factory.GetDbContext(); + + var testUser = await dbContext.Users.FirstAsync(); + var establishments = fixture.CreateMany(50).ToList(); + await dbContext.GiasEstablishments.AddRangeAsync(establishments); + var projects = establishments.Select(establishment => + { + var project = fixture.Customize(new ProjectCustomization + { + RegionalDeliveryOfficerId = testUser.Id, + CaseworkerId = testUser.Id, + AssignedToId = testUser.Id + }) + .Create(); + project.Urn = establishment.Urn ?? project.Urn; + return project; + }).ToList(); + await dbContext.Projects.AddRangeAsync(projects); + + await dbContext.SaveChangesAsync(); + + // dbContext.Users.Update(testUser); + // await dbContext.SaveChangesAsync(); + + var result = await projectsClient.CountAllProjectsAsync(null, null); + + // Assert.NotNull(result); + Assert.Equal(50, result); + } [Theory] [CustomAutoData(typeof(CustomWebApplicationDbContextFactoryCustomization), typeof(EstablishmentsCustomization))] diff --git a/src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationFactoryCustomization.cs b/src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationFactoryCustomization.cs index c3e9b187..dc19dde4 100644 --- a/src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationFactoryCustomization.cs +++ b/src/Tests/Dfe.Complete.Api.Tests.Integration/Customizations/CustomWebApplicationFactoryCustomization.cs @@ -1,16 +1,13 @@ +using System.Net.Http.Headers; +using System.Security.Claims; using AutoFixture; using DfE.CoreLibs.Testing.Mocks.Authentication; using DfE.CoreLibs.Testing.Mocks.WebApplicationFactory; -using Dfe.Complete.Api.Client.Extensions; -using Dfe.Complete.Client; -using Dfe.Complete.Client.Contracts; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using System.Net.Http.Headers; -using System.Security.Claims; -namespace Dfe.Complete.Tests.Common.Customizations +namespace Dfe.Complete.Api.Tests.Integration.Customizations { public class CustomWebApplicationFactoryCustomization : ICustomization where TProgram : class { diff --git a/src/Tests/Dfe.Complete.Api.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs b/src/Tests/Dfe.Complete.Api.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs index 71d71baf..b9f58125 100644 --- a/src/Tests/Dfe.Complete.Api.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs +++ b/src/Tests/Dfe.Complete.Api.Tests.Integration/OpenApiTests/OpenApiDocumentTests.cs @@ -2,6 +2,7 @@ using DfE.CoreLibs.Testing.Mocks.WebApplicationFactory; using Dfe.Complete.Tests.Common.Customizations; using System.Net; +using Dfe.Complete.Api.Tests.Integration.Customizations; using Microsoft.VisualStudio.TestPlatform.TestHost; namespace Dfe.Complete.Api.Tests.Integration.OpenApiTests; diff --git a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/FormAMatTests.cs b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/FormAMatTests.cs index 186dbbb9..60b048aa 100644 --- a/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/FormAMatTests.cs +++ b/src/Tests/Dfe.Complete.Application.Tests/Services/CsvExport/Builders/FormAMatTests.cs @@ -12,17 +12,5 @@ namespace Dfe.Complete.Application.Tests.Services.CsvExport.Builders public class FormAMatTests { - [Theory] - [CustomAutoData(typeof(ProjectCustomization), typeof(DateOnlyCustomization))] - - public void Build_When_Conversion(Project project) - { - project.Type = ProjectType.Conversion; - var builder = new FormAMat(); - - var result = builder.Build(new ConversionCsvModel(project, null, null)); - - Assert.Equal("", result); - } } } From 97c099120a6f91230180f0f2b3ac49b428a0e9fb Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Tue, 28 Jan 2025 10:50:53 +0000 Subject: [PATCH 40/47] Upgrade xunit runners to the same version across the solution --- src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj | 2 +- .../Dfe.Complete.UserContext.Tests.csproj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj b/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj index 40e19839..92ff140d 100644 --- a/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj +++ b/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj @@ -18,7 +18,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj b/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj index e2323474..8e3f74c1 100644 --- a/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj +++ b/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj @@ -16,7 +16,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 07bfd6e89ce745ac93153a7b4038ed447088af5f Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Tue, 28 Jan 2025 11:22:24 +0000 Subject: [PATCH 41/47] Downgrade xunit to previous version --- .../Dfe.Complete.Api.Tests.Integration.csproj | 2 +- .../Dfe.Complete.Domain.Tests/Dfe.Complete.Domain.Tests.csproj | 2 +- .../Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj | 2 +- src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj | 2 +- .../Dfe.Complete.UserContext.Tests.csproj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj b/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj index 1332dda9..56b89ebf 100644 --- a/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj +++ b/src/Tests/Dfe.Complete.Api.Tests.Integration/Dfe.Complete.Api.Tests.Integration.csproj @@ -12,7 +12,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Tests/Dfe.Complete.Domain.Tests/Dfe.Complete.Domain.Tests.csproj b/src/Tests/Dfe.Complete.Domain.Tests/Dfe.Complete.Domain.Tests.csproj index 3c3a9b7c..13923743 100644 --- a/src/Tests/Dfe.Complete.Domain.Tests/Dfe.Complete.Domain.Tests.csproj +++ b/src/Tests/Dfe.Complete.Domain.Tests/Dfe.Complete.Domain.Tests.csproj @@ -10,7 +10,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj b/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj index c96a6d17..cf55ee43 100644 --- a/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj +++ b/src/Tests/Dfe.Complete.Tests.Common/Dfe.Complete.Tests.Common.csproj @@ -30,7 +30,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj b/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj index 92ff140d..69758fa7 100644 --- a/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj +++ b/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj @@ -17,7 +17,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj b/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj index 8e3f74c1..aee05534 100644 --- a/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj +++ b/src/Tests/Dfe.Complete.UserContext.Tests/Dfe.Complete.UserContext.Tests.csproj @@ -15,7 +15,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 9d54940323ced09121b90502373c4e8518fdd9cb Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Tue, 28 Jan 2025 12:44:49 +0000 Subject: [PATCH 42/47] Add static culture for frontend app and unit tests --- src/Frontend/Dfe.Complete/wwwroot/web.config | 8 ++++++++ src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj | 4 ++++ src/Tests/Dfe.Complete.Tests/xunit.runner.json | 4 ++++ 3 files changed, 16 insertions(+) create mode 100644 src/Frontend/Dfe.Complete/wwwroot/web.config create mode 100644 src/Tests/Dfe.Complete.Tests/xunit.runner.json diff --git a/src/Frontend/Dfe.Complete/wwwroot/web.config b/src/Frontend/Dfe.Complete/wwwroot/web.config new file mode 100644 index 00000000..424baab8 --- /dev/null +++ b/src/Frontend/Dfe.Complete/wwwroot/web.config @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj b/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj index 69758fa7..a680eb03 100644 --- a/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj +++ b/src/Tests/Dfe.Complete.Tests/Dfe.Complete.Tests.csproj @@ -33,4 +33,8 @@ + + + + diff --git a/src/Tests/Dfe.Complete.Tests/xunit.runner.json b/src/Tests/Dfe.Complete.Tests/xunit.runner.json new file mode 100644 index 00000000..e4ddd283 --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests/xunit.runner.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "culture": "en-GB" +} \ No newline at end of file From fb698c515cbf0ddc5758739a17e2323e96ca48dd Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Tue, 28 Jan 2025 14:56:48 +0000 Subject: [PATCH 43/47] Add explicit culture to the DateOnly and DateTime extensions --- .../Extensions/DateOnlyExtensions.cs | 24 +++++++++++-------- .../Extensions/DateTimeExtensions.cs | 18 ++++++++------ 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs b/src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs index 7113396c..466fbe45 100644 --- a/src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs +++ b/src/Frontend/Dfe.Complete/Extensions/DateOnlyExtensions.cs @@ -1,27 +1,31 @@ +using System.Globalization; using Dfe.Complete.Constants; namespace Dfe.Complete.Extensions { public static class DateOnlyExtensions { - public static string ToUkDateString(this DateOnly dateTime) => dateTime.ToString(DateFormatConstants.DateUkFormat); + // State the explicit culture so that the functions work correctly in all environments + // TODO: Fix global culture + private static readonly CultureInfo GbCulture = new("en-GB"); + public static string ToUkDateString(this DateOnly dateOnly) => dateOnly.ToString(DateFormatConstants.DateUkFormat, GbCulture); - public static string ToDateString(this DateOnly? dateTime, bool includeDayOfWeek = false) + public static string ToDateString(this DateOnly? dateOnly, bool includeDayOfWeek = false) { - if (!dateTime.HasValue) + if (!dateOnly.HasValue) { return string.Empty; } - return ToDateString(dateTime.Value, includeDayOfWeek); + return ToDateString(dateOnly.Value, includeDayOfWeek); } - public static string ToDateString(this DateOnly dateTime, bool includeDayOfWeek = false) + public static string ToDateString(this DateOnly dateOnly, bool includeDayOfWeek = false) { if (includeDayOfWeek) { - return dateTime.ToString(DateFormatConstants.DateWithDayOfTheWeek); + return dateOnly.ToString(DateFormatConstants.DateWithDayOfTheWeek, GbCulture); } - return dateTime.ToString(DateFormatConstants.DateWithoutDayOfTheWeek); + return dateOnly.ToString(DateFormatConstants.DateWithoutDayOfTheWeek, GbCulture); } public static DateOnly FirstOfMonth(this DateOnly thisMonth, int monthsToAdd) @@ -32,14 +36,14 @@ public static DateOnly FirstOfMonth(this DateOnly thisMonth, int monthsToAdd) return new DateOnly(thisMonth.Year + yearsToAdd, month, 1); } - public static string ToDateMonthYearString(this DateOnly? dateTime) + public static string ToDateMonthYearString(this DateOnly? dateOnly) { - if (!dateTime.HasValue) + if (!dateOnly.HasValue) { return string.Empty; } - return dateTime.Value.ToString(DateFormatConstants.MonthAndYearFormat); + return dateOnly.Value.ToString(DateFormatConstants.MonthAndYearFormat, GbCulture); } } } \ No newline at end of file diff --git a/src/Frontend/Dfe.Complete/Extensions/DateTimeExtensions.cs b/src/Frontend/Dfe.Complete/Extensions/DateTimeExtensions.cs index 71877a99..7a435f54 100644 --- a/src/Frontend/Dfe.Complete/Extensions/DateTimeExtensions.cs +++ b/src/Frontend/Dfe.Complete/Extensions/DateTimeExtensions.cs @@ -1,10 +1,14 @@ +using System.Globalization; using Dfe.Complete.Constants; namespace Dfe.Complete.Extensions { public static class DateTimeExtensions { - public static string ToUkDateString(this DateTime dateTime) => dateTime.ToString(DateFormatConstants.DateUkFormat); + // State the explicit culture so that the functions work correctly in all environments + // TODO: Fix global culture + private static readonly CultureInfo GbCulture = new("en-GB"); + public static string ToUkDateString(this DateTime dateTime) => dateTime.ToString(DateFormatConstants.DateUkFormat, GbCulture); public static string ToDateString(this DateTime? dateTime, bool includeDayOfWeek = false) { @@ -19,9 +23,9 @@ public static string ToDateString(this DateTime dateTime, bool includeDayOfWeek { if (includeDayOfWeek) { - return dateTime.ToString(DateFormatConstants.DateWithDayOfTheWeek); + return dateTime.ToString(DateFormatConstants.DateWithDayOfTheWeek, GbCulture); } - return dateTime.ToString(DateFormatConstants.DateWithoutDayOfTheWeek); + return dateTime.ToString(DateFormatConstants.DateWithoutDayOfTheWeek, GbCulture); } public static DateTime FirstOfMonth(this DateTime thisMonth, int monthsToAdd) @@ -32,14 +36,14 @@ public static DateTime FirstOfMonth(this DateTime thisMonth, int monthsToAdd) return new DateTime(thisMonth.Year + yearsToAdd, month, 1); } - public static string ToDateMonthYearString(this DateTime? dateTime) - { + public static string ToDateMonthYearString(this DateTime? dateTime) + { if (!dateTime.HasValue) { return string.Empty; } - return dateTime.Value.ToString(DateFormatConstants.MonthAndYearFormat); - } + return dateTime.Value.ToString(DateFormatConstants.MonthAndYearFormat, GbCulture); + } } } \ No newline at end of file From a8e80e6e8bc4fd785868f96f917220cea5d9f878 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Tue, 28 Jan 2025 16:10:40 +0000 Subject: [PATCH 44/47] Add coverage for DateTimeExtensions and DateOnlyExtensions --- .../Extensions/DateOnlyExtensionsTests.cs | 12 ++-- .../Extensions/DateTimeExtensionsTests.cs | 72 +++++++++++++++++++ 2 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 src/Tests/Dfe.Complete.Tests/Extensions/DateTimeExtensionsTests.cs diff --git a/src/Tests/Dfe.Complete.Tests/Extensions/DateOnlyExtensionsTests.cs b/src/Tests/Dfe.Complete.Tests/Extensions/DateOnlyExtensionsTests.cs index 543a9c71..de0ea5e3 100644 --- a/src/Tests/Dfe.Complete.Tests/Extensions/DateOnlyExtensionsTests.cs +++ b/src/Tests/Dfe.Complete.Tests/Extensions/DateOnlyExtensionsTests.cs @@ -33,12 +33,14 @@ public void ToDateString_ReturnsTheCorrectString_WithDayOfTheWeek(int date, int { Assert.Equal(expected, new DateOnly(year, month, date).ToDateString(true)); } - - [Fact] - public void ToDateString_ReturnsEmptyString_WhenDateOnlyIsNull() + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void ToDateString_ReturnsEmptyString_WhenDateOnlyIsNull(bool includeDayOfWeek) { DateOnly? test = null; - Assert.Equal(string.Empty, test.ToDateString(true)); + Assert.Equal(string.Empty, test.ToDateString(includeDayOfWeek)); } [Theory] @@ -46,6 +48,8 @@ public void ToDateString_ReturnsEmptyString_WhenDateOnlyIsNull() [InlineData(31,12,9998, 1, 1, 9999)] [InlineData(1,1,1, 10, 11)] [InlineData(10,9,1990, 200, 5, 2007)] + [InlineData(10, 12, 2025, 0, 12)] + public void FirstOfMonth_ReturnsTheCorrectDateOnly(int testDate, int testMonth, int testYear, int monthsToAdd, int expectedMonth, int? expectedYear = null) { Assert.Equal(new DateOnly(expectedYear ?? testYear, expectedMonth, 1), new DateOnly(testYear, testMonth, testDate).FirstOfMonth(monthsToAdd)); diff --git a/src/Tests/Dfe.Complete.Tests/Extensions/DateTimeExtensionsTests.cs b/src/Tests/Dfe.Complete.Tests/Extensions/DateTimeExtensionsTests.cs new file mode 100644 index 00000000..19e66cd7 --- /dev/null +++ b/src/Tests/Dfe.Complete.Tests/Extensions/DateTimeExtensionsTests.cs @@ -0,0 +1,72 @@ +using Dfe.Complete.Extensions; + +namespace Dfe.Complete.Tests.Extensions; + +public class DateTimeExtensionsTests +{ + [Theory] + [InlineData(1,2,2025, "01/02/2025")] + [InlineData(31,12,9999, "31/12/9999")] + [InlineData(1,1,1, "01/01/0001")] + [InlineData(10,9,1990, "10/09/1990")] + public void ToUkDateString_ReturnsTheCorrectString(int date, int month, int year, string expected) + { + Assert.Equal(expected, new DateTime(year, month, date).ToUkDateString()); + } + + [Theory] + [InlineData(1,2,2025, "1 February 2025")] + [InlineData(31,12,9999, "31 December 9999")] + [InlineData(1,1,1, "1 January 0001")] + [InlineData(10,9,1990, "10 September 1990")] + public void ToDateString_ReturnsTheCorrectString_WithoutDayOfTheWeek(int date, int month, int year, string expected) + { + Assert.Equal(expected, new DateTime(year, month, date).ToDateString()); + } + + [Theory] + [InlineData(1,2,2025, "Saturday 1 February 2025")] + [InlineData(31,12,9999, "Friday 31 December 9999")] + [InlineData(1,1,1, "Monday 1 January 0001")] + [InlineData(10,9,1990, "Monday 10 September 1990")] + public void ToDateString_ReturnsTheCorrectString_WithDayOfTheWeek(int date, int month, int year, string expected) + { + Assert.Equal(expected, new DateTime(year, month, date).ToDateString(true)); + } + + [Fact] + public void ToDateString_ReturnsEmptyString_WhenDateTimeIsNull() + { + DateTime? test = null; + Assert.Equal(string.Empty, test.ToDateString(true)); + } + + [Theory] + [InlineData(1,2,2025, 0, 2)] + [InlineData(31,12,9998, 1, 1, 9999)] + [InlineData(1,1,1, 10, 11)] + [InlineData(10,9,1990, 200, 5, 2007)] + [InlineData(10, 12, 2025, 0, 12)] + public void FirstOfMonth_ReturnsTheCorrectDateTime(int testDate, int testMonth, int testYear, int monthsToAdd, int expectedMonth, int? expectedYear = null) + { + Assert.Equal(new DateTime(expectedYear ?? testYear, expectedMonth, 1), new DateTime(testYear, testMonth, testDate).FirstOfMonth(monthsToAdd)); + } + + [Theory] + [InlineData(1,2,2025, "Feb 2025")] + [InlineData(31,12,9999, "Dec 9999")] + [InlineData(1,1,1, "Jan 0001")] + [InlineData(10,9,1990, "Sept 1990")] + public void ToDateMonthYearString_ReturnsTheCorrectString_WithDayOfTheWeek(int date, int month, int year, string expected) + { + DateTime? testDate = new DateTime(year, month, date); + Assert.Equal(expected, testDate.ToDateMonthYearString()); + } + + [Fact] + public void ToDateMonthYearString_ReturnsEmptyString_WhenDateTimeIsNull() + { + DateTime? test = null; + Assert.Equal(string.Empty, test.ToDateMonthYearString()); + } +} \ No newline at end of file From c3141fad8f66cc7a73d529f97cdcfb3058cf5d52 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Tue, 28 Jan 2025 16:15:39 +0000 Subject: [PATCH 45/47] Exclude GenericDbContextFactory from code coverage --- .../Database/GenericDbContextFactory.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Core/Dfe.Complete.Infrastructure/Database/GenericDbContextFactory.cs b/src/Core/Dfe.Complete.Infrastructure/Database/GenericDbContextFactory.cs index b77f8667..fe0ebe45 100644 --- a/src/Core/Dfe.Complete.Infrastructure/Database/GenericDbContextFactory.cs +++ b/src/Core/Dfe.Complete.Infrastructure/Database/GenericDbContextFactory.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using MediatR; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; @@ -6,6 +7,7 @@ namespace Dfe.Complete.Infrastructure.Database { + [ExcludeFromCodeCoverage] public class GenericDbContextFactory : IDesignTimeDbContextFactory where TContext : DbContext { public TContext CreateDbContext(string[] args) From 5b10960a88d5ee03bdb4ae7c63a6170dcb4b9a84 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Tue, 28 Jan 2025 16:16:28 +0000 Subject: [PATCH 46/47] Add Tests for error handling for count and list all project queries --- .../CountAllProjectsQueryHandlerTests.cs | 40 ++++++++++++------- .../ListAllProjectsQueryHandlerTests.cs | 33 ++++++++++++--- 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs b/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs index 3751cd20..55a6873e 100644 --- a/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs +++ b/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/CountAllProjectsQueryHandlerTests.cs @@ -2,14 +2,12 @@ using DfE.CoreLibs.Testing.AutoFixture.Attributes; using NSubstitute; using DfE.CoreLibs.Testing.AutoFixture.Customizations; -using DfE.CoreLibs.Caching.Interfaces; -using Dfe.Complete.Application.Common.Models; -using DfE.CoreLibs.Caching.Helpers; using Dfe.Complete.Application.Projects.Interfaces; using Dfe.Complete.Application.Projects.Model; using Dfe.Complete.Application.Projects.Queries.CountAllProjects; using Dfe.Complete.Tests.Common.Customizations.Models; using MockQueryable; +using NSubstitute.ExceptionExtensions; namespace Dfe.Complete.Application.Tests.QueryHandlers.Project { @@ -23,7 +21,6 @@ public class CountAllProjectsQueryHandlerTests typeof(DateOnlyCustomization))] public async Task Handle_ShouldReturnCorrectCount( [Frozen] IListAllProjectsQueryService mockEstablishmentQueryService, - [Frozen] ICacheService mockCacheService, CountAllProjectsQueryHandler handler, CountAllProjectsQuery query, List listAllProjectsQueryModels) @@ -31,27 +28,42 @@ public async Task Handle_ShouldReturnCorrectCount( // Arrange var expected = listAllProjectsQueryModels.Count; - // var cacheKey = $"Project_{CacheKeyHelper.GenerateHashedCacheKey(query.ToString())}"; - var mock = listAllProjectsQueryModels.BuildMock(); mockEstablishmentQueryService.ListAllProjects(query.ProjectStatus, query.Type) .Returns(mock); - // mockCacheService.GetOrAddAsync(cacheKey, Arg.Any>>>(), Arg.Any()) - // .Returns(callInfo => - // { - // var callback = callInfo.ArgAt>>>(1); - // return callback(); - // }); - // Act var result = await handler.Handle(query, default); // Assert Assert.Equal(expected, result.Value); + } + + [Theory] + [CustomAutoData( + typeof(OmitCircularReferenceCustomization), + typeof(ListAllProjectsQueryModelCustomization), + typeof(DateOnlyCustomization))] + public async Task Handle_ShouldReturnUnsuccessful_WhenAnErrorOccurs( + [Frozen] IListAllProjectsQueryService mockEstablishmentQueryService, + CountAllProjectsQueryHandler handler) + { + // Arrange + var errorMessage = "This is a test"; + + var query = new CountAllProjectsQuery(null, null); + + mockEstablishmentQueryService.ListAllProjects(query.ProjectStatus, query.Type) + .Throws(new Exception(errorMessage)); - // await mockCacheService.Received(1).GetOrAddAsync(cacheKey, Arg.Any>>>(), nameof(CountAllProjectsQueryHandler)); + // Act + var result = await handler.Handle(query, default); + + // Assert + Assert.NotNull(result); + Assert.False(result.IsSuccess); + Assert.Equal(errorMessage, result.Error); } } } diff --git a/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/ListAllProjectsQueryHandlerTests.cs b/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/ListAllProjectsQueryHandlerTests.cs index 3317572e..a58ff40e 100644 --- a/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/ListAllProjectsQueryHandlerTests.cs +++ b/src/Tests/Dfe.Complete.Application.Tests/QueryHandlers/Project/ListAllProjectsQueryHandlerTests.cs @@ -3,16 +3,12 @@ using DfE.CoreLibs.Testing.AutoFixture.Attributes; using NSubstitute; using DfE.CoreLibs.Testing.AutoFixture.Customizations; -using DfE.CoreLibs.Caching.Interfaces; -using Dfe.Complete.Application.Common.Models; -using DfE.CoreLibs.Caching.Helpers; using Dfe.Complete.Application.Projects.Interfaces; using Dfe.Complete.Application.Projects.Model; -using Dfe.Complete.Application.Projects.Queries.CountAllProjects; using Dfe.Complete.Application.Projects.Queries.ListAllProjects; using Dfe.Complete.Tests.Common.Customizations.Models; -using Dfe.Complete.Tests.Common.Customizations.Queries; using MockQueryable; +using NSubstitute.ExceptionExtensions; namespace Dfe.Complete.Application.Tests.QueryHandlers.Project { @@ -134,7 +130,34 @@ public async Task Handle_ShouldReturnCorrectList_WhenAllPagesAreSkipped( // Assert Assert.NotNull(result); + Assert.True(result.IsSuccess); Assert.Equal(0, result.Value?.Count); } + + [Theory] + [CustomAutoData( + typeof(OmitCircularReferenceCustomization), + typeof(ListAllProjectsQueryModelCustomization), + typeof(DateOnlyCustomization))] + public async Task Handle_ShouldReturnUnsuccessful_WhenAnErrorOccurs( + [Frozen] IListAllProjectsQueryService mockEstablishmentQueryService, + ListAllProjectsQueryHandler handler) + { + // Arrange + var errorMessage = "This is a test"; + + var query = new ListAllProjectsQuery(null, null); + + mockEstablishmentQueryService.ListAllProjects(query.ProjectStatus, query.Type) + .Throws(new Exception(errorMessage)); + + // Act + var result = await handler.Handle(query, default); + + // Assert + Assert.NotNull(result); + Assert.False(result.IsSuccess); + Assert.Equal(errorMessage, result.Error); + } } } \ No newline at end of file From e23effff0ac8db4f9270121b8e3b38e782c2f7d0 Mon Sep 17 00:00:00 2001 From: Nick Warms Date: Wed, 29 Jan 2025 10:36:38 +0000 Subject: [PATCH 47/47] Change projectDto state field to use project state enum --- .../Generated/Contracts.g.cs | 33 ++++++++++--------- .../Generated/swagger.json | 31 +++++++++-------- .../Projects/Models/ProjectDto.cs | 2 +- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs index 79854432..162ee90c 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs +++ b/src/Api/Dfe.Complete.Api.Client/Generated/Contracts.g.cs @@ -347,7 +347,8 @@ public partial class ProjectDto public string? NewTrustName { get; set; } = default!; [Newtonsoft.Json.JsonProperty("state", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] - public int? State { get; set; } = default!; + [Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))] + public ProjectState? State { get; set; } = default!; [Newtonsoft.Json.JsonProperty("prepareId", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)] public int? PrepareId { get; set; } = default!; @@ -526,6 +527,21 @@ public static ContactId FromJson(string data) } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] + public enum ProjectState + { + + [System.Runtime.Serialization.EnumMember(Value = @"Active")] + Active = 0, + + [System.Runtime.Serialization.EnumMember(Value = @"Completed")] + Completed = 1, + + [System.Runtime.Serialization.EnumMember(Value = @"Cancelled")] + Cancelled = 2, + + } + [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class ProjectGroupId { @@ -851,21 +867,6 @@ public static Project FromJson(string data) } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] - public enum ProjectState - { - - [System.Runtime.Serialization.EnumMember(Value = @"Active")] - Active = 0, - - [System.Runtime.Serialization.EnumMember(Value = @"Completed")] - Completed = 1, - - [System.Runtime.Serialization.EnumMember(Value = @"Cancelled")] - Cancelled = 2, - - } - [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0))")] public partial class Contact { diff --git a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json index 632c953c..aada1f1f 100644 --- a/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json +++ b/src/Api/Dfe.Complete.Api.Client/Generated/swagger.json @@ -521,8 +521,7 @@ "nullable": true }, "state": { - "type": "integer", - "format": "int32" + "$ref": "#/components/schemas/ProjectState" }, "prepareId": { "type": "integer", @@ -683,6 +682,20 @@ } } }, + "ProjectState": { + "type": "string", + "description": "", + "x-enumNames": [ + "Active", + "Completed", + "Cancelled" + ], + "enum": [ + "Active", + "Completed", + "Cancelled" + ] + }, "ProjectGroupId": { "type": "object", "additionalProperties": false, @@ -1138,20 +1151,6 @@ } ] }, - "ProjectState": { - "type": "string", - "description": "", - "x-enumNames": [ - "Active", - "Completed", - "Cancelled" - ], - "enum": [ - "Active", - "Completed", - "Cancelled" - ] - }, "Contact": { "type": "object", "additionalProperties": false, diff --git a/src/Core/Dfe.Complete.Application/Projects/Models/ProjectDto.cs b/src/Core/Dfe.Complete.Application/Projects/Models/ProjectDto.cs index 0a1878da..26c5c3a7 100644 --- a/src/Core/Dfe.Complete.Application/Projects/Models/ProjectDto.cs +++ b/src/Core/Dfe.Complete.Application/Projects/Models/ProjectDto.cs @@ -72,7 +72,7 @@ public class ProjectDto public string? NewTrustName { get; set; } - public int State { get; set; } + public ProjectState State { get; set; } public int? PrepareId { get; set; }
    School or academy URNConversion or transfer dateProject typeTransfer date Form a MAT project? Assigned to
    @project.Urn.Value @project.ConversionOrTransferDate.ToDateMonthYearString()@project.ProjectType.ToString() @project.IsFormAMAT.ToYesNoString() @project.AssignedTo?.FirstName @project.AssignedTo?.LastName
    @project.ConversionOrTransferDate.ToDateMonthYearString() @project.ProjectType.ToString() @project.IsFormAMAT.ToYesNoString()@project.AssignedTo?.FirstName @project.AssignedTo?.LastName@project.AssignedToFullName
    @project.Urn.Value @project.ConversionOrTransferDate.ToDateMonthYearString() @project.IsFormAMAT.ToYesNoString()@project.AssignedTo?.FirstName @project.AssignedTo?.LastName@project.AssignedToFullName
    @project.Urn.Value @project.ConversionOrTransferDate.ToDateMonthYearString() @project.IsFormAMAT.ToYesNoString()@project.AssignedTo?.FirstName @project.AssignedTo?.LastName@project.AssignedToFullName