From b2bb67384ae8c486c4578b94b0c1858274198bc7 Mon Sep 17 00:00:00 2001 From: Sukhvinder Bhullar Date: Fri, 11 Oct 2024 16:20:47 +0100 Subject: [PATCH] Added validation for blank or empty files and project id duplication --- .../Integration/BulkEditApiTests.cs | 140 +++++++++++++++++- .../UseCases/BulkEdit/TestScaffoldClasses.cs | 26 ++-- .../Validation/DateValidationCommandTests.cs | 4 +- .../LACodeValidationCommandTests.cs | 8 +- .../ProjectStatusValidationCommandTests.cs | 8 +- .../BulkEdit/BulkEditHeaderRegister.cs | 8 +- .../UseCases/BulkEdit/BulkEditValidation.cs | 8 +- .../UseCases/BulkEdit/HeaderType.cs | 2 +- .../UseCases/BulkEdit/IValidationCommand.cs | 16 +- .../Validations/DateValidationCommand.cs | 4 +- .../Validations/LACodeValidationCommand.cs | 6 +- .../Validations/ProjectIdValidationCommand.cs | 26 +++- .../ProjectStatusValidationCommand.cs | 10 +- .../Validations/TextValidationCommand.cs | 6 +- .../Project/BulkEditFileValidatorTests.cs | 28 ++++ .../BulkEdit/BulkEditFileValidator.cs | 17 +++ 16 files changed, 261 insertions(+), 56 deletions(-) diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/Integration/BulkEditApiTests.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/Integration/BulkEditApiTests.cs index b32407e78..6e196d4f5 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/Integration/BulkEditApiTests.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/Integration/BulkEditApiTests.cs @@ -49,7 +49,7 @@ public async Task BasicValidationTest() FileRowIndex = 1, Columns = new List { - new ColumnInfo { ColumnIndex = 0, Value = projectId.ToString() }, + new ColumnInfo { ColumnIndex = 0, Value = projectId }, new ColumnInfo { ColumnIndex = 2, Value = NewOpeningDate}, } } @@ -71,8 +71,8 @@ public async Task BasicValidationTest() new() { ColumnIndex = 0, - CurrentValue = projectId.ToString(), - NewValue = projectId.ToString() + CurrentValue = projectId, + NewValue = projectId } , new() @@ -111,7 +111,7 @@ public async Task BasicSaveTest() FileRowIndex = 1, Columns = new List { - new ColumnInfo { ColumnIndex = 0, Value = projectId.ToString() }, + new ColumnInfo { ColumnIndex = 0, Value = projectId }, new ColumnInfo { ColumnIndex = 2, Value = NewOpeningDate.ToString("dd/MM/yyyy")}, } } @@ -171,7 +171,7 @@ public async Task ProjectIDMissingTest() FileRowIndex = 2, Columns = new List { - new ColumnInfo { ColumnIndex = 0, Value = projectId.ToString() }, + new ColumnInfo { ColumnIndex = 0, Value = projectId }, new ColumnInfo { ColumnIndex = 2, Value = NewOpeningDate }, } } @@ -212,8 +212,8 @@ public async Task ProjectIDMissingTest() new() { ColumnIndex = 0, - CurrentValue = projectId.ToString(), - NewValue = projectId.ToString(), + CurrentValue = projectId, + NewValue = projectId, }, new() { @@ -225,6 +225,130 @@ public async Task ProjectIDMissingTest() } + + [Fact] + public async Task ProjectIDDuplicatedTest() + { + var project = DatabaseModelBuilder.BuildProject(); + var correctProject = DatabaseModelBuilder.BuildProject(); + + var projectId = project.ProjectStatusProjectId; + var ExistingOpeningDate = project.ProjectStatusActualOpeningDate; + var NewOpeningDate = "01/01/2021"; + + var correctProjectId = correctProject.ProjectStatusProjectId; + + using var context = _testFixture.GetContext(); + context.Kpi.Add(project); + context.Kpi.Add(correctProject); + await context.SaveChangesAsync(); + + var bulkValidateRequest = new BulkEditRequest + { + Headers = new List + { + new HeaderInfo { Name = HeaderNames.ProjectId, Index = 0 }, + new HeaderInfo { Name = HeaderNames.OpeningDate, Index = 2 }, + }, + Rows = new List + { + new RowInfo + { + FileRowIndex = 1, + Columns = new List + { + new ColumnInfo { ColumnIndex = 0, Value = projectId }, + new ColumnInfo { ColumnIndex = 2, Value = NewOpeningDate }, + } + }, + new RowInfo + { + FileRowIndex = 2, + Columns = new List + { + new ColumnInfo { ColumnIndex = 0, Value = correctProjectId }, + new ColumnInfo { ColumnIndex = 2, Value = NewOpeningDate }, + } + }, + new RowInfo + { + FileRowIndex = 3, + Columns = new List + { + new ColumnInfo { ColumnIndex = 0, Value = projectId }, + new ColumnInfo { ColumnIndex = 2, Value = NewOpeningDate }, + } + } + } + }; + + var response = await _testFixture.Client.PostAsync($"api/v1/bulkedit/validate", bulkValidateRequest.ConvertToJson()); + + response.EnsureSuccessStatusCode(); + + var result = await response.Content.ReadFromJsonAsync>(); + + result.Data.Headers.Should().BeEquivalentTo(bulkValidateRequest.Headers); + + var resultRow = result.Data.ValidationResultRows.FirstOrDefault(x => x.FileRowIndex == 1); + + resultRow.Columns.Should().BeEquivalentTo(new List + { + new() + { + ColumnIndex = 0, + CurrentValue = projectId, + NewValue = projectId, + Error = "The project ID must be unique" + }, + new() + { + ColumnIndex = 2, + CurrentValue = ExistingOpeningDate.Value.ToString("dd/MM/yyyy"), + NewValue = NewOpeningDate + } + }); + + var resultValidRow = result.Data.ValidationResultRows.FirstOrDefault(x => x.FileRowIndex == 2); + + resultValidRow.Columns.Should().BeEquivalentTo(new List + { + new() + { + ColumnIndex = 0, + CurrentValue = correctProjectId, + NewValue = correctProjectId, + }, + new() + { + ColumnIndex = 2, + CurrentValue = correctProject.ProjectStatusActualOpeningDate.Value.ToString("dd/MM/yyyy"), + NewValue = NewOpeningDate + } + }); + + var resultDuplicatedRow = result.Data.ValidationResultRows.FirstOrDefault(x => x.FileRowIndex == 3); + + resultDuplicatedRow.Columns.Should().BeEquivalentTo(new List + { + new() + { + ColumnIndex = 0, + CurrentValue = projectId, + NewValue = projectId, + Error = "The project ID must be unique" + }, + new() + { + ColumnIndex = 2, + CurrentValue = ExistingOpeningDate.Value.ToString("dd/MM/yyyy"), + NewValue = NewOpeningDate + } + }); + } + + + [Fact] public async Task LocalAuthorityValidationTest() { @@ -440,7 +564,7 @@ public async Task IfBlankDoNotChangeTest() FileRowIndex = 1, Columns = new List { - new ColumnInfo { ColumnIndex = 0, Value = projectId.ToString() }, + new ColumnInfo { ColumnIndex = 0, Value = projectId }, new ColumnInfo { ColumnIndex = 1, Value = string.Empty }, } } diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/TestScaffoldClasses.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/TestScaffoldClasses.cs index e86b54438..c96f3a6cc 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/TestScaffoldClasses.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/TestScaffoldClasses.cs @@ -6,7 +6,7 @@ namespace Dfe.ManageFreeSchoolProjects.API.Tests.UseCases.BulkEdit { - internal record TestDto: IBulkEditDto + internal record TestDto : IBulkEditDto { public string ProjectId { get; set; } public string TestData { get; set; } @@ -79,11 +79,11 @@ public List> GetHeaders() { return new() { - new() { Name = ProjectId, Type = new TestProjectIdValidation(), DataInteraction = new TestInteraction(x => x.ProjectId, (x, t) => t.ProjectId = x) }, - new() { Name = HeaderOneName, Type = new TestValidation(), DataInteraction = new TestInteraction(x => x.TestData, (x, t) => t.TestData = x) }, - new() { Name = HeaderTwoName, Type = new TestValidation(), DataInteraction = new TestInteraction(x => x.OtherTestData, (x, t) => t.OtherTestData = x) }, - new() { Name = HeaderData, Type = new DataDependencyValidation(), DataInteraction = new TestInteraction(x => x.DependantTestData, (x, t) => t.DependantTestData = x) }, - new() { Name = FormattedName, Type = new TestValidation(), DataInteraction = new TestFormattedInteraction(x => x.TestData, (x, t) => t.TestData = x) }, + new() { Name = ProjectId, Validation = new TestProjectIdValidation(), DataInteraction = new TestInteraction(x => x.ProjectId, (x, t) => t.ProjectId = x) }, + new() { Name = HeaderOneName, Validation = new TestValidation(), DataInteraction = new TestInteraction(x => x.TestData, (x, t) => t.TestData = x) }, + new() { Name = HeaderTwoName, Validation = new TestValidation(), DataInteraction = new TestInteraction(x => x.OtherTestData, (x, t) => t.OtherTestData = x) }, + new() { Name = HeaderData, Validation = new DataDependencyValidation(), DataInteraction = new TestInteraction(x => x.DependantTestData, (x, t) => t.DependantTestData = x) }, + new() { Name = FormattedName, Validation = new TestValidation(), DataInteraction = new TestFormattedInteraction(x => x.TestData, (x, t) => t.TestData = x) }, }; } } @@ -93,7 +93,7 @@ internal class TestProjectIdValidation : IValidationCommand internal const string ValidationMessage = "TriggeredValidation"; internal const string ValidInput = "Valid"; - public ValidationResult Execute(TestDto data, string value) + public ValidationResult Execute(ValidationCommandParameters parameters) { return new ValidationResult() { @@ -107,12 +107,12 @@ internal class TestValidation : IValidationCommand internal const string ValidationMessage = "TriggeredValidation"; internal const string ValidInput = "Valid"; - public ValidationResult Execute(TestDto data, string value) + public ValidationResult Execute(ValidationCommandParameters parameters) { return new ValidationResult() { - IsValid = value == ValidInput, - ErrorMessage = value == ValidInput ? null : ValidationMessage + IsValid = parameters.Value == ValidInput, + ErrorMessage = parameters.Value == ValidInput ? null : ValidationMessage }; } } @@ -122,12 +122,12 @@ internal class DataDependencyValidation : IValidationCommand { internal const string DataValidationMessage = "Triggered data validation"; - public ValidationResult Execute(TestDto data, string value) + public ValidationResult Execute(ValidationCommandParameters parameters) { return new ValidationResult() { - IsValid = data.DataForValidation == value, - ErrorMessage = data.DataForValidation == value ? null : DataValidationMessage + IsValid = parameters.Data.DataForValidation == parameters.Value, + ErrorMessage = parameters.Data.DataForValidation == parameters.Value ? null : DataValidationMessage }; } } diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/DateValidationCommandTests.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/DateValidationCommandTests.cs index affd301ed..01bb08896 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/DateValidationCommandTests.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/DateValidationCommandTests.cs @@ -13,7 +13,7 @@ public class DateValidationCommandTests public void DateValidationFails(string date, string error) { var dateValidation = new DateValidationCommand(); - var validationResult = dateValidation.Execute(null, date); + var validationResult = dateValidation.Execute(new() { Data = null, Value = date }); validationResult.IsValid.Should().BeFalse(); validationResult.ErrorMessage.Should().Be(error); @@ -27,7 +27,7 @@ public void DateValidationFails(string date, string error) public void DateValidationPasses(string date) { var dateValidation = new DateValidationCommand(); - var validationResult = dateValidation.Execute(null, date); + var validationResult = dateValidation.Execute(new() { Data = null, Value = date }); validationResult.IsValid.Should().BeTrue(); validationResult.ErrorMessage.Should().BeNull(); diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/LACodeValidationCommandTests.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/LACodeValidationCommandTests.cs index 98b047e11..ba7c46e1a 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/LACodeValidationCommandTests.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/LACodeValidationCommandTests.cs @@ -29,9 +29,9 @@ public void Execute_WhenValidLACode_ReturnsValidResult() }); var command = new LACodeValidationCommand(localAuthorityCache); - + // Act - var result = command.Execute(null, "123"); + var result = command.Execute(new() { Data = null, Value = "123"}); // Assert result.IsValid.Should().BeTrue(); @@ -53,7 +53,7 @@ public void Execute_WhenInvalidLACode_ReturnsInvalidResult() var command = new LACodeValidationCommand(localAuthorityCache); // Act - var result = command.Execute(null, "456"); + var result = command.Execute(new() { Data = null, Value = "456" }); // Assert result.IsValid.Should().BeFalse(); @@ -77,7 +77,7 @@ public void Execute_WhenInvalidShortLACode_ReturnsInvalidResult() var command = new LACodeValidationCommand(localAuthorityCache); // Act - var result = command.Execute(null, "12"); + var result = command.Execute(new() { Data = null, Value = "12" }); // Assert result.IsValid.Should().BeFalse(); diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/ProjectStatusValidationCommandTests.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/ProjectStatusValidationCommandTests.cs index afcdb43f1..6421894ea 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/ProjectStatusValidationCommandTests.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API.Tests/UseCases/BulkEdit/Validation/ProjectStatusValidationCommandTests.cs @@ -18,7 +18,7 @@ public void StatusValidationFails(string status, string error) { var projectStatusValidation = new ProjectStatusValidationCommand(); var dto = new BulkEditDto { ApplicationWave = "Any Wave" }; - var validationResult = projectStatusValidation.Execute(dto, status); + var validationResult = projectStatusValidation.Execute(new() { Data = dto, Value = status }); validationResult.IsValid.Should().BeFalse(); validationResult.ErrorMessage.Should().Be(error); @@ -41,7 +41,7 @@ public void StatusValidationPasses(string status) { var projectStatusValidation = new ProjectStatusValidationCommand(); var dto = new BulkEditDto { ApplicationWave = "Any Wave" }; - var validationResult = projectStatusValidation.Execute(dto, status); + var validationResult = projectStatusValidation.Execute(new() { Data = dto, Value = status }); validationResult.IsValid.Should().BeTrue(); validationResult.ErrorMessage.Should().BeNull(); @@ -65,7 +65,7 @@ public void PresumptionFailsOnCertainStatus(string status, bool pass) { var projectStatusValidation = new ProjectStatusValidationCommand(); var dto = new BulkEditDto { ApplicationWave = "FS - Presumption" }; - var validationResult = projectStatusValidation.Execute(dto, status); + var validationResult = projectStatusValidation.Execute(new() { Data = dto, Value = status }); validationResult.IsValid.Should().Be(pass); @@ -98,7 +98,7 @@ public void PresumptionFailsOnCertainStatus(string status, bool pass) public void IgnoresProjectIfNotFoundCertainStatus(string status, bool pass) { var projectStatusValidation = new ProjectStatusValidationCommand(); - var validationResult = projectStatusValidation.Execute(null, status); + var validationResult = projectStatusValidation.Execute(new() { Data = null, Value = status }); validationResult.IsValid.Should().Be(pass); diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/BulkEditHeaderRegister.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/BulkEditHeaderRegister.cs index 351313d23..200fd0200 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/BulkEditHeaderRegister.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/BulkEditHeaderRegister.cs @@ -14,10 +14,10 @@ public List> GetHeaders() { return new List> { - new() { Name = HeaderNames.ProjectId, Type = new ProjectIdValidationCommand(), DataInteraction = new ProjectIdInteraction() }, - new() { Name = HeaderNames.OpeningDate, Type = new DateValidationCommand(), DataInteraction = new OpeningDateInteraction() }, - new() { Name = HeaderNames.ProjectStatus, Type = new ProjectStatusValidationCommand(), DataInteraction = new ProjectStatusInteraction() }, - new() { Name = HeaderNames.LocalAuthority, Type = new LACodeValidationCommand(localAuthorityCache), DataInteraction = new LACodeInteraction(localAuthorityCache) }, + new() { Name = HeaderNames.ProjectId, Validation = new ProjectIdValidationCommand(), DataInteraction = new ProjectIdInteraction() }, + new() { Name = HeaderNames.OpeningDate, Validation = new DateValidationCommand(), DataInteraction = new OpeningDateInteraction() }, + new() { Name = HeaderNames.ProjectStatus, Validation = new ProjectStatusValidationCommand(), DataInteraction = new ProjectStatusInteraction() }, + new() { Name = HeaderNames.LocalAuthority, Validation = new LACodeValidationCommand(localAuthorityCache), DataInteraction = new LACodeInteraction(localAuthorityCache) }, }; } diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/BulkEditValidation.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/BulkEditValidation.cs index 3099ae3b8..b615bb796 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/BulkEditValidation.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/BulkEditValidation.cs @@ -49,7 +49,13 @@ public async Task Execute(BulkEditRequest request) } var header = headerMap[column.ColumnIndex]; - var validationResult = header.Type.Execute(currentRow, column.Value); + var validationResult = header.Validation.Execute(new ValidationCommandParameters() + { + Value = column.Value, + Request = request, + CurrentRowIndex = row.FileRowIndex, + Data = currentRow + }); var currentValue = IsNotNullOrEmpty(currentRow) ? header.DataInteraction.GetFromDto(currentRow) : ""; if (!validationResult.IsValid) { diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/HeaderType.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/HeaderType.cs index 107e75048..d2f815b60 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/HeaderType.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/HeaderType.cs @@ -13,7 +13,7 @@ public record HeaderType where T : IBulkEditDto { public string Name { get; set; } - public IValidationCommand Type { get; set; } + public IValidationCommand Validation { get; set; } public IHeaderDataInteraction DataInteraction { get; set; } } diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/IValidationCommand.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/IValidationCommand.cs index 3a7035d59..09587cd7a 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/IValidationCommand.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/IValidationCommand.cs @@ -1,8 +1,18 @@ -namespace Dfe.ManageFreeSchoolProjects.API.UseCases.BulkEdit +using Dfe.ManageFreeSchoolProjects.API.Contracts.BulkEdit; + +namespace Dfe.ManageFreeSchoolProjects.API.UseCases.BulkEdit { - public interface IValidationCommand where TDto : IBulkEditDto + public interface IValidationCommand where TDto : IBulkEditDto + { + ValidationResult Execute(ValidationCommandParameters parameters); + } + + public record ValidationCommandParameters() where TDto : IBulkEditDto { - ValidationResult Execute(TDto data, string value); + public string Value { get; set; } + public BulkEditRequest Request { get; set; } + public int CurrentRowIndex { get; set; } + public TDto Data { get; set; } } public record ValidationResult() diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/DateValidationCommand.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/DateValidationCommand.cs index 51bd2c2d8..a367b8904 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/DateValidationCommand.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/DateValidationCommand.cs @@ -4,10 +4,10 @@ namespace Dfe.ManageFreeSchoolProjects.API.UseCases.BulkEdit.Validations { public class DateValidationCommand() : IValidationCommand { - public ValidationResult Execute(BulkEditDto data, string value) + public ValidationResult Execute(ValidationCommandParameters parameters) { var culture = CultureInfo.CreateSpecificCulture("en-GB"); - var IsValid = DateTime.TryParse(value, culture, out var date); + var IsValid = DateTime.TryParse(parameters.Value, culture, out var date); if (!IsValid) { diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/LACodeValidationCommand.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/LACodeValidationCommand.cs index ddb980295..35116b8ad 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/LACodeValidationCommand.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/LACodeValidationCommand.cs @@ -4,9 +4,9 @@ namespace Dfe.ManageFreeSchoolProjects.API.UseCases.BulkEdit.Validations { public class LACodeValidationCommand(ILocalAuthorityCache localAuthorityCache) : IValidationCommand { - public ValidationResult Execute(BulkEditDto data, string value) + public ValidationResult Execute(ValidationCommandParameters parameters) { - if(value.Length < 3) + if(parameters.Value.Length < 3) { return new ValidationResult { @@ -15,7 +15,7 @@ public ValidationResult Execute(BulkEditDto data, string value) }; } - var isValid = localAuthorityCache.GetLocalAuthorities().Exists(x => x.LACode == value); + var isValid = localAuthorityCache.GetLocalAuthorities().Exists(x => x.LACode == parameters.Value); return new ValidationResult { diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/ProjectIdValidationCommand.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/ProjectIdValidationCommand.cs index 0bc9d4dd8..304918b95 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/ProjectIdValidationCommand.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/ProjectIdValidationCommand.cs @@ -1,10 +1,14 @@ -namespace Dfe.ManageFreeSchoolProjects.API.UseCases.BulkEdit.Validations +using Azure.Core; +using Dfe.ManageFreeSchoolProjects.API.Contracts.BulkEdit; +using DocumentFormat.OpenXml.Spreadsheet; + +namespace Dfe.ManageFreeSchoolProjects.API.UseCases.BulkEdit.Validations { public class ProjectIdValidationCommand() : IValidationCommand { - public ValidationResult Execute(BulkEditDto data, string value) + public ValidationResult Execute(ValidationCommandParameters parameters) { - if(data == null) + if (parameters.Data == null) { return new ValidationResult { @@ -13,6 +17,22 @@ public ValidationResult Execute(BulkEditDto data, string value) }; } + var IdColumnIndex = parameters.Request.Headers.Find(x => string.Compare(x.Name, HeaderNames.ProjectId, true) == 0).Index; + + var duplicated = parameters.Request.Rows + .Where(row => row.FileRowIndex != parameters.CurrentRowIndex) + .Any(row => parameters.Value == row.Columns[IdColumnIndex].Value); + + if (duplicated) + { + return new ValidationResult + { + IsValid = false, + ErrorMessage = "The project ID must be unique" + }; + } + + return new ValidationResult { IsValid = true diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/ProjectStatusValidationCommand.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/ProjectStatusValidationCommand.cs index e94d89ce4..e0967315b 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/ProjectStatusValidationCommand.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/ProjectStatusValidationCommand.cs @@ -6,7 +6,7 @@ namespace Dfe.ManageFreeSchoolProjects.API.UseCases.BulkEdit.Validations { public class ProjectStatusValidationCommand() : IValidationCommand { - public ValidationResult Execute(BulkEditDto data, string value) + public ValidationResult Execute(ValidationCommandParameters parameters) { HashSet PresumptionOnly = @@ -20,19 +20,19 @@ public ValidationResult Execute(BulkEditDto data, string value) ProjectStatus? status = null; - if(value.Equals("cancelled in pre-opening", StringComparison.OrdinalIgnoreCase)) + if(parameters.Value.Equals("cancelled in pre-opening", StringComparison.OrdinalIgnoreCase)) { status = ProjectStatus.Cancelled; } if (status == null) { - status = GetStatusUsingDatabaseName(value); + status = GetStatusUsingDatabaseName(parameters.Value); } if (status == null) { - status = GetStatusFromDescription(value); + status = GetStatusFromDescription(parameters.Value); } if (status == null) @@ -44,7 +44,7 @@ public ValidationResult Execute(BulkEditDto data, string value) }; } - if (data != null && data.ApplicationWave == "FS - Presumption" && !PresumptionOnly.Contains(status.Value)) + if (parameters.Data != null && parameters.Data.ApplicationWave == "FS - Presumption" && !PresumptionOnly.Contains(status.Value)) { return new ValidationResult { diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/TextValidationCommand.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/TextValidationCommand.cs index b13c2794c..31226d675 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/TextValidationCommand.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.API/UseCases/BulkEdit/Validations/TextValidationCommand.cs @@ -2,12 +2,12 @@ { public class TextValidationCommand(int maxLength) : IValidationCommand { - public ValidationResult Execute(BulkEditDto data, string value) + public ValidationResult Execute(ValidationCommandParameters parameters) { return new ValidationResult { - IsValid = value.Length <= maxLength, - ErrorMessage = value.Length <= maxLength ? null : $"Value exceeds maximum length of {maxLength}." + IsValid = parameters.Value.Length <= maxLength, + ErrorMessage = parameters.Value.Length <= maxLength ? null : $"Value exceeds maximum length of {maxLength}." }; } } diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.Tests/Project/BulkEditFileValidatorTests.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.Tests/Project/BulkEditFileValidatorTests.cs index 3037a1cfe..529c9e845 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.Tests/Project/BulkEditFileValidatorTests.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects.Tests/Project/BulkEditFileValidatorTests.cs @@ -56,5 +56,33 @@ public void FailsWhenGivenUnsupportedColumn() result.IsValid.Should().BeFalse(); result.ErrorMessage.Should().Be("File has an invalid column header: LocalPlace"); } + + [Fact] + public void FailsWhenGivenEmptyTable() + { + + var table = new DataTable(); + + var validator = new BulkEditFileValidator(); + var result = validator.Validate(table); + + result.IsValid.Should().BeFalse(); + result.ErrorMessage.Should().Be("The selected file must have project data in it"); + } + + [Fact] + public void FailsWhenGivenEmptyRows() + { + + var table = new DataTable(); + table.Columns.Add(HeaderNames.ProjectId); + table.Columns.Add(HeaderNames.OpeningDate); + + var validator = new BulkEditFileValidator(); + var result = validator.Validate(table); + + result.IsValid.Should().BeFalse(); + result.ErrorMessage.Should().Be("The selected file must have project data in it"); + } } } diff --git a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects/Services/BulkEdit/BulkEditFileValidator.cs b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects/Services/BulkEdit/BulkEditFileValidator.cs index 2f3ee05ba..ace076d4c 100644 --- a/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects/Services/BulkEdit/BulkEditFileValidator.cs +++ b/Dfe.ManageFreeSchoolProjects/Dfe.ManageFreeSchoolProjects/Services/BulkEdit/BulkEditFileValidator.cs @@ -20,6 +20,14 @@ public class BulkEditFileValidator : IBulkEditFileValidator { public FileValidationResult Validate(DataTable table) { + if(table.Columns.Count == 0) + { + return new FileValidationResult + { + IsValid = false, + ErrorMessage = "The selected file must have project data in it" + }; + } foreach (DataColumn column in table.Columns) { @@ -40,6 +48,15 @@ public FileValidationResult Validate(DataTable table) ErrorMessage = $"File has an invalid column header: {column.ColumnName}" }; } + + if (table.Rows.Count == 0) + { + return new FileValidationResult + { + IsValid = false, + ErrorMessage = "The selected file must have project data in it" + }; + } } return new FileValidationResult