From d3ea6114d6d3dcfbca9d7e2f4c524f353c198983 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Tue, 4 Nov 2025 16:09:28 -0500 Subject: [PATCH 01/16] Updated Version, .NET & Packages --- .../JMayer.Example.ASPReact.Server.csproj | 6 +++--- TestProject/TestProject.csproj | 6 +++--- jmayer.example.aspreact.client/package-lock.json | 4 ++-- jmayer.example.aspreact.client/package.json | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj index 06da9a0..491d520 100644 --- a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj +++ b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj @@ -1,19 +1,19 @@  - net8.0 + net9.0 enable enable ..\jmayer.example.aspreact.client npm run dev https://localhost:5173 - 1.2.0 + 9.0.0 - 8.*-* + 9.0.10 diff --git a/TestProject/TestProject.csproj b/TestProject/TestProject.csproj index 59b2426..dfd99db 100644 --- a/TestProject/TestProject.csproj +++ b/TestProject/TestProject.csproj @@ -1,13 +1,13 @@ - net8.0 + net9.0 enable enable false true - 1.2.0 + 9.0.0 @@ -16,7 +16,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/jmayer.example.aspreact.client/package-lock.json b/jmayer.example.aspreact.client/package-lock.json index d49d563..04436bd 100644 --- a/jmayer.example.aspreact.client/package-lock.json +++ b/jmayer.example.aspreact.client/package-lock.json @@ -1,12 +1,12 @@ { "name": "jmayer.example.aspreact.client", - "version": "0.0.0", + "version": "9.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "jmayer.example.aspreact.client", - "version": "0.0.0", + "version": "9.0.0", "dependencies": { "globals": "^16.2.0", "primeflex": "^4.0.0", diff --git a/jmayer.example.aspreact.client/package.json b/jmayer.example.aspreact.client/package.json index 97d47fd..9e63522 100644 --- a/jmayer.example.aspreact.client/package.json +++ b/jmayer.example.aspreact.client/package.json @@ -1,7 +1,7 @@ { "name": "jmayer.example.aspreact.client", "private": true, - "version": "0.0.0", + "version": "9.0.0", "type": "module", "scripts": { "dev": "vite", From 15229b50299fbf3c3607fe5d4b8045b361173fe3 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Thu, 6 Nov 2025 09:54:23 -0500 Subject: [PATCH 02/16] Changes based on JMayer library changes --- .../Airlines/Airline.cs | 7 +- .../Airlines/AirlineController.cs | 4 +- .../Airlines/AirlineDataLayer.cs | 11 +- .../Airlines/AirlineEqualityComparer.cs | 2 +- .../Airlines/IAirlineDataLayer.cs | 2 +- .../FlightScheduleExampleBuilder.cs | 4 +- .../Flights/CodeShareEqualityComparer.cs | 2 +- .../Flights/Flight.cs | 2 +- .../Flights/FlightController.cs | 4 +- .../Flights/FlightDataLayer.cs | 12 +- .../Flights/FlightEqualityComparer.cs | 2 +- .../Flights/IFlightDataLayer.cs | 2 +- JMayer.Example.ASPReact.Server/Gates/Gate.cs | 8 +- .../Gates/GateController.cs | 6 +- .../Gates/GateDataLayer.cs | 2 +- .../Gates/GateEqualityComparer.cs | 2 +- .../Gates/IGateDataLayer.cs | 2 +- .../JMayer.Example.ASPReact.Server.csproj | 2 +- .../ISortDestinationDataLayer.cs | 2 +- .../SortDestinations/SortDestination.cs | 8 +- .../SortDestinationController.cs | 6 +- .../SortDestinationDataLayer.cs | 2 +- .../SortDestinationEqualityComparer.cs | 2 +- JMayer.Example.ASPReact.sln | 12 ++ TestProject/Airlines/AirlineDataLayer.cs | 2 +- .../Airlines/AirlineWebRequestUnitTest.cs | 109 +++++---------- TestProject/Flights/FlightDataLayer.cs | 2 +- .../Flights/FlightWebRequestUnitTest.cs | 127 ++++++------------ TestProject/Gates/GateDataLyaer.cs | 2 +- TestProject/Gates/GateWebRequestUnitTest.cs | 6 +- .../SortDestinationDataLayer.cs | 2 +- .../SortDestinationWebRequestUnitTest.cs | 6 +- TestProject/TestProject.csproj | 2 +- 33 files changed, 164 insertions(+), 202 deletions(-) diff --git a/JMayer.Example.ASPReact.Server/Airlines/Airline.cs b/JMayer.Example.ASPReact.Server/Airlines/Airline.cs index bcf9280..6f50f42 100644 --- a/JMayer.Example.ASPReact.Server/Airlines/Airline.cs +++ b/JMayer.Example.ASPReact.Server/Airlines/Airline.cs @@ -6,7 +6,7 @@ namespace JMayer.Example.ASPReact.Server.Airlines; /// /// The class represents an airline and its codes. /// -public class Airline : UserEditableDataObject +public class Airline : DataObject { /// /// The property gets/sets the IATA code assigned by the IATA organization. @@ -21,6 +21,11 @@ public class Airline : UserEditableDataObject [RegularExpression("^[A-Z]{3}$", ErrorMessage = "The ICAO must be 3 capital letters.")] public string ICAO { get; set; } = string.Empty; + /// + /// Overridden to add Required data annotation. + [Required] + public override string? Name { get => base.Name; set => base.Name = value; } + /// /// The property gets/sets the number code assigned by the IATA organization. /// diff --git a/JMayer.Example.ASPReact.Server/Airlines/AirlineController.cs b/JMayer.Example.ASPReact.Server/Airlines/AirlineController.cs index d098e0a..2a70aa5 100644 --- a/JMayer.Example.ASPReact.Server/Airlines/AirlineController.cs +++ b/JMayer.Example.ASPReact.Server/Airlines/AirlineController.cs @@ -1,4 +1,4 @@ -using JMayer.Web.Mvc.Controller; +using JMayer.Web.Mvc.Controller.Api; using Microsoft.AspNetCore.Mvc; namespace JMayer.Example.ASPReact.Server.Airlines; @@ -8,7 +8,7 @@ namespace JMayer.Example.ASPReact.Server.Airlines; /// [Route("api/[controller]")] [ApiController] -public class AirlineController : UserEditableController +public class AirlineController : StandardCRUDController { /// public AirlineController(IAirlineDataLayer dataLayer, ILogger logger) : base(dataLayer, logger) { } diff --git a/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs b/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs index 2c58588..eeb9679 100644 --- a/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs +++ b/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs @@ -6,8 +6,13 @@ namespace JMayer.Example.ASPReact.Server.Airlines; /// /// The class manages CRUD interactions with the database for an airline. /// -public class AirlineDataLayer : UserEditableDataLayer, IAirlineDataLayer +public class AirlineDataLayer : StandardCRUDDataLayer, IAirlineDataLayer { + /// + /// The default constructor. + /// + public AirlineDataLayer() => IsUniqueNameRequired = true; + /// /// /// Overriden to check the ICAO is unique and the number code is unique (expect for 000). @@ -16,12 +21,12 @@ public override async Task> ValidateAsync(Airline dataObj { List validationResults = await base.ValidateAsync(dataObject, cancellationToken); - if (dataObject.ICAO != null && await ExistAsync(obj => obj.Integer64ID != dataObject.Integer64ID && obj.ICAO == dataObject.ICAO, cancellationToken) == true) + if (dataObject.ICAO is not null && await ExistAsync(obj => obj.Integer64ID != dataObject.Integer64ID && obj.ICAO == dataObject.ICAO, cancellationToken) is true) { validationResults.Add(new ValidationResult("The ICAO must be unique.", [nameof(Airline.ICAO)])); } - if (dataObject.NumberCode != Airline.ZeroNumberCode && await ExistAsync(obj => obj.Integer64ID != dataObject.Integer64ID && obj.NumberCode == dataObject.NumberCode, cancellationToken) == true) + if (dataObject.NumberCode is not Airline.ZeroNumberCode && await ExistAsync(obj => obj.Integer64ID != dataObject.Integer64ID && obj.NumberCode == dataObject.NumberCode, cancellationToken) is true) { validationResults.Add(new ValidationResult("The number code must be unique unless the code is 000.", [nameof(Airline.NumberCode)])); } diff --git a/JMayer.Example.ASPReact.Server/Airlines/AirlineEqualityComparer.cs b/JMayer.Example.ASPReact.Server/Airlines/AirlineEqualityComparer.cs index 9de47b2..98c0fc4 100644 --- a/JMayer.Example.ASPReact.Server/Airlines/AirlineEqualityComparer.cs +++ b/JMayer.Example.ASPReact.Server/Airlines/AirlineEqualityComparer.cs @@ -43,7 +43,7 @@ public AirlineEqualityComparer(bool excludeCreatedOn, bool excludeID, bool exclu /// public bool Equals(Airline? x, Airline? y) { - if (x == null || y == null) + if (x is null || y is null) { return false; } diff --git a/JMayer.Example.ASPReact.Server/Airlines/IAirlineDataLayer.cs b/JMayer.Example.ASPReact.Server/Airlines/IAirlineDataLayer.cs index 0f856e8..e61e27d 100644 --- a/JMayer.Example.ASPReact.Server/Airlines/IAirlineDataLayer.cs +++ b/JMayer.Example.ASPReact.Server/Airlines/IAirlineDataLayer.cs @@ -5,6 +5,6 @@ namespace JMayer.Example.ASPReact.Server.Airlines; /// /// The interface for interacting with an airline collection in a database using CRUD operations. /// -public interface IAirlineDataLayer : IUserEditableDataLayer +public interface IAirlineDataLayer : IStandardCRUDDataLayer { } diff --git a/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs b/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs index 7f295b6..27d4861 100644 --- a/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs +++ b/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs @@ -111,10 +111,10 @@ private void BuildFlights() DepartTime = departTime, FlightNumber = flightNumber.ToString().PadLeft(4, '0'), GateID = gates[gateIndex].Integer64ID, - GateName = gates[gateIndex].Name, + GateName = gates[gateIndex].Name ?? string.Empty, Name = $"{airline.IATA}{flightNumber.ToString().PadLeft(4, '0')}", SortDestinationID = sortDestinations[sortDestinationIndex].Integer64ID, - SortDestinationName = sortDestinations[sortDestinationIndex].Name, + SortDestinationName = sortDestinations[sortDestinationIndex].Name ?? string.Empty, }); flightNumber++; diff --git a/JMayer.Example.ASPReact.Server/Flights/CodeShareEqualityComparer.cs b/JMayer.Example.ASPReact.Server/Flights/CodeShareEqualityComparer.cs index e4ac96c..480e294 100644 --- a/JMayer.Example.ASPReact.Server/Flights/CodeShareEqualityComparer.cs +++ b/JMayer.Example.ASPReact.Server/Flights/CodeShareEqualityComparer.cs @@ -10,7 +10,7 @@ public class CodeShareEqualityComparer : IEqualityComparer /// public bool Equals(CodeShare? x, CodeShare? y) { - if (x == null || y == null) + if (x is null || y is null) { return false; } diff --git a/JMayer.Example.ASPReact.Server/Flights/Flight.cs b/JMayer.Example.ASPReact.Server/Flights/Flight.cs index 0d8b098..eee80b0 100644 --- a/JMayer.Example.ASPReact.Server/Flights/Flight.cs +++ b/JMayer.Example.ASPReact.Server/Flights/Flight.cs @@ -6,7 +6,7 @@ namespace JMayer.Example.ASPReact.Server.Flights; /// /// The class represents a flight in the flight schedule. /// -public class Flight : UserEditableDataObject +public class Flight : DataObject { /// /// The property gets/sets the IATA code for the airline for the flight. diff --git a/JMayer.Example.ASPReact.Server/Flights/FlightController.cs b/JMayer.Example.ASPReact.Server/Flights/FlightController.cs index a728b8e..4edb18f 100644 --- a/JMayer.Example.ASPReact.Server/Flights/FlightController.cs +++ b/JMayer.Example.ASPReact.Server/Flights/FlightController.cs @@ -1,4 +1,4 @@ -using JMayer.Web.Mvc.Controller; +using JMayer.Web.Mvc.Controller.Api; using Microsoft.AspNetCore.Mvc; namespace JMayer.Example.ASPReact.Server.Flights; @@ -8,7 +8,7 @@ namespace JMayer.Example.ASPReact.Server.Flights; /// [Route("api/[controller]")] [ApiController] -public class FlightController : UserEditableController +public class FlightController : StandardCRUDController { /// public FlightController(IFlightDataLayer dataLayer, ILogger logger) : base(dataLayer, logger) { } diff --git a/JMayer.Example.ASPReact.Server/Flights/FlightDataLayer.cs b/JMayer.Example.ASPReact.Server/Flights/FlightDataLayer.cs index 2a86c59..0de67ed 100644 --- a/JMayer.Example.ASPReact.Server/Flights/FlightDataLayer.cs +++ b/JMayer.Example.ASPReact.Server/Flights/FlightDataLayer.cs @@ -9,7 +9,7 @@ namespace JMayer.Example.ASPReact.Server.Flights; /// /// The class manages CRUD interactions with the database for a flight. /// -public class FlightDataLayer : UserEditableDataLayer, IFlightDataLayer +public class FlightDataLayer : StandardCRUDDataLayer, IFlightDataLayer { /// /// Used to access the airline data. @@ -62,30 +62,30 @@ public override async Task> ValidateAsync(Flight dataObje { List validationResults = await base.ValidateAsync(dataObject, cancellationToken); - if (await _airlineDataLayer.ExistAsync(obj => obj.Integer64ID == dataObject.AirlineID, cancellationToken) == false) + if (await _airlineDataLayer.ExistAsync(obj => obj.Integer64ID == dataObject.AirlineID, cancellationToken) is false) { validationResults.Add(new ValidationResult($"The {dataObject.AirlineID} airline was not found in the data store.", [nameof(Flight.AirlineID)])); } - if (await _gateDataLayer.ExistAsync(obj => obj.Integer64ID == dataObject.GateID, cancellationToken) == false) + if (await _gateDataLayer.ExistAsync(obj => obj.Integer64ID == dataObject.GateID, cancellationToken) is false) { validationResults.Add(new ValidationResult($"The {dataObject.GateID} gate was not found in the data store.", [nameof(Flight.GateID)])); } - if (await _sortDestinationDataLayer.ExistAsync(obj => obj.Integer64ID == dataObject.SortDestinationID, cancellationToken) == false) + if (await _sortDestinationDataLayer.ExistAsync(obj => obj.Integer64ID == dataObject.SortDestinationID, cancellationToken) is false) { validationResults.Add(new ValidationResult($"The {dataObject.SortDestinationID} sort destination was not found in the data store.", [nameof(Flight.SortDestinationID)])); } foreach (var codeShare in dataObject.CodeShares) { - if (await _airlineDataLayer.ExistAsync(obj => obj.Integer64ID == codeShare.AirlineID, cancellationToken) == false) + if (await _airlineDataLayer.ExistAsync(obj => obj.Integer64ID == codeShare.AirlineID, cancellationToken) is false) { validationResults.Add(new ValidationResult($"The {codeShare.AirlineID} airline for the codeshare was not found in the data store.", [nameof(CodeShare.AirlineID)])); } } - if (await ExistAsync(obj => obj.Integer64ID != dataObject.Integer64ID && obj.AirlineID == dataObject.AirlineID && obj.FlightNumber == dataObject.FlightNumber && obj.Destination == dataObject.Destination, cancellationToken) == true) + if (await ExistAsync(obj => obj.Integer64ID != dataObject.Integer64ID && obj.AirlineID == dataObject.AirlineID && obj.FlightNumber == dataObject.FlightNumber && obj.Destination == dataObject.Destination, cancellationToken) is true) { validationResults.Add(new ValidationResult("The flight already exists in the schedule.", [nameof(Flight.FlightNumber)])); } diff --git a/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs b/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs index 5473acf..3f5faaa 100644 --- a/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs +++ b/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs @@ -43,7 +43,7 @@ public FlightEqualityComparer(bool excludeCreatedOn, bool excludeID, bool exclud /// public bool Equals(Flight? x, Flight? y) { - if (x == null || y == null) + if (x is null || y is null) { return false; } diff --git a/JMayer.Example.ASPReact.Server/Flights/IFlightDataLayer.cs b/JMayer.Example.ASPReact.Server/Flights/IFlightDataLayer.cs index 826f9cf..05b371f 100644 --- a/JMayer.Example.ASPReact.Server/Flights/IFlightDataLayer.cs +++ b/JMayer.Example.ASPReact.Server/Flights/IFlightDataLayer.cs @@ -5,6 +5,6 @@ namespace JMayer.Example.ASPReact.Server.Flights; /// /// The interface for interacting with a flight collection in a database using CRUD operations. /// -public interface IFlightDataLayer : IUserEditableDataLayer +public interface IFlightDataLayer : IStandardCRUDDataLayer { } diff --git a/JMayer.Example.ASPReact.Server/Gates/Gate.cs b/JMayer.Example.ASPReact.Server/Gates/Gate.cs index e269d93..b6af321 100644 --- a/JMayer.Example.ASPReact.Server/Gates/Gate.cs +++ b/JMayer.Example.ASPReact.Server/Gates/Gate.cs @@ -1,12 +1,18 @@ using JMayer.Data.Data; +using System.ComponentModel.DataAnnotations; namespace JMayer.Example.ASPReact.Server.Gates; /// /// The class represents a gate in an airport. /// -public class Gate : UserEditableDataObject +public class Gate : DataObject { + /// + /// Overridden to add Required data annotation. + [Required] + public override string? Name { get => base.Name; set => base.Name = value; } + /// /// The default constructor. /// diff --git a/JMayer.Example.ASPReact.Server/Gates/GateController.cs b/JMayer.Example.ASPReact.Server/Gates/GateController.cs index 0040ac6..a867714 100644 --- a/JMayer.Example.ASPReact.Server/Gates/GateController.cs +++ b/JMayer.Example.ASPReact.Server/Gates/GateController.cs @@ -1,15 +1,17 @@ -using JMayer.Web.Mvc.Controller; +using JMayer.Web.Mvc.Controller.Api; using Microsoft.AspNetCore.Mvc; using System.Net; namespace JMayer.Example.ASPReact.Server.Gates; +#warning Should I use the NoAction attribute over returning a MethodNotAllowed status? + /// /// The class manages HTTP requests for CRUD operations associated with a gate in a database. /// [Route("api/[controller]")] [ApiController] -public class GateController : UserEditableController +public class GateController : StandardCRUDController { /// public GateController(IGateDataLayer dataLayer, ILogger logger) : base(dataLayer, logger) { } diff --git a/JMayer.Example.ASPReact.Server/Gates/GateDataLayer.cs b/JMayer.Example.ASPReact.Server/Gates/GateDataLayer.cs index a734527..3b0405c 100644 --- a/JMayer.Example.ASPReact.Server/Gates/GateDataLayer.cs +++ b/JMayer.Example.ASPReact.Server/Gates/GateDataLayer.cs @@ -5,6 +5,6 @@ namespace JMayer.Example.ASPReact.Server.Gates; /// /// The class manages CRUD interactions with the database for a gate. /// -public class GateDataLayer : UserEditableDataLayer, IGateDataLayer +public class GateDataLayer : StandardCRUDDataLayer, IGateDataLayer { } diff --git a/JMayer.Example.ASPReact.Server/Gates/GateEqualityComparer.cs b/JMayer.Example.ASPReact.Server/Gates/GateEqualityComparer.cs index be7e0c0..0555749 100644 --- a/JMayer.Example.ASPReact.Server/Gates/GateEqualityComparer.cs +++ b/JMayer.Example.ASPReact.Server/Gates/GateEqualityComparer.cs @@ -43,7 +43,7 @@ public GateEqualityComparer(bool excludeCreatedOn, bool excludeID, bool excludeL /// public bool Equals(Gate? x, Gate? y) { - if (x == null || y == null) + if (x is null || y is null) { return false; } diff --git a/JMayer.Example.ASPReact.Server/Gates/IGateDataLayer.cs b/JMayer.Example.ASPReact.Server/Gates/IGateDataLayer.cs index c0f9dd4..44ad6f5 100644 --- a/JMayer.Example.ASPReact.Server/Gates/IGateDataLayer.cs +++ b/JMayer.Example.ASPReact.Server/Gates/IGateDataLayer.cs @@ -5,6 +5,6 @@ namespace JMayer.Example.ASPReact.Server.Gates; /// /// The interface for interacting with a gate collection in a database using CRUD operations. /// -public interface IGateDataLayer : IUserEditableDataLayer +public interface IGateDataLayer : IStandardCRUDDataLayer { } diff --git a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj index 491d520..7437407 100644 --- a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj +++ b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj @@ -11,7 +11,6 @@ - 9.0.10 @@ -19,6 +18,7 @@ + false diff --git a/JMayer.Example.ASPReact.Server/SortDestinations/ISortDestinationDataLayer.cs b/JMayer.Example.ASPReact.Server/SortDestinations/ISortDestinationDataLayer.cs index 1dc576f..862ed22 100644 --- a/JMayer.Example.ASPReact.Server/SortDestinations/ISortDestinationDataLayer.cs +++ b/JMayer.Example.ASPReact.Server/SortDestinations/ISortDestinationDataLayer.cs @@ -5,6 +5,6 @@ namespace JMayer.Example.ASPReact.Server.SortDestinations; /// /// The interface for interacting with a sort destination collection in a database using CRUD operations. /// -public interface ISortDestinationDataLayer : IUserEditableDataLayer +public interface ISortDestinationDataLayer : IStandardCRUDDataLayer { } diff --git a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestination.cs b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestination.cs index 91905ab..1428ef1 100644 --- a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestination.cs +++ b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestination.cs @@ -1,12 +1,18 @@ using JMayer.Data.Data; +using System.ComponentModel.DataAnnotations; namespace JMayer.Example.ASPReact.Server.SortDestinations; /// /// The class represents a sort destination in the baggage handling system. /// -public class SortDestination : UserEditableDataObject +public class SortDestination : DataObject { + /// + /// Overridden to add Required data annotation. + [Required] + public override string? Name { get => base.Name; set => base.Name = value; } + /// /// The default constructor. /// diff --git a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs index 262c4c6..1bacd30 100644 --- a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs +++ b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs @@ -1,15 +1,17 @@ -using JMayer.Web.Mvc.Controller; +using JMayer.Web.Mvc.Controller.Api; using Microsoft.AspNetCore.Mvc; using System.Net; namespace JMayer.Example.ASPReact.Server.SortDestinations; +#warning Should I use the NoAction attribute over returning a MethodNotAllowed status? + /// /// The class manages HTTP requests for CRUD operations associated with a sort destination in a database. /// [Route("api/[controller]")] [ApiController] -public class SortDestinationController : UserEditableController +public class SortDestinationController : StandardCRUDController { /// public SortDestinationController(ISortDestinationDataLayer dataLayer, ILogger logger) : base(dataLayer, logger) { } diff --git a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationDataLayer.cs b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationDataLayer.cs index ae697c2..f796f17 100644 --- a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationDataLayer.cs +++ b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationDataLayer.cs @@ -5,6 +5,6 @@ namespace JMayer.Example.ASPReact.Server.SortDestinations; /// /// The class manages CRUD interactions with the database for a sort destination. /// -public class SortDestinationDataLayer : UserEditableDataLayer, ISortDestinationDataLayer +public class SortDestinationDataLayer : StandardCRUDDataLayer, ISortDestinationDataLayer { } diff --git a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationEqualityComparer.cs b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationEqualityComparer.cs index c78f75f..20605db 100644 --- a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationEqualityComparer.cs +++ b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationEqualityComparer.cs @@ -43,7 +43,7 @@ public SortDestinationEqualityComparer(bool excludeCreatedOn, bool excludeID, bo /// public bool Equals(SortDestination? x, SortDestination? y) { - if (x == null || y == null) + if (x is null || y is null) { return false; } diff --git a/JMayer.Example.ASPReact.sln b/JMayer.Example.ASPReact.sln index ff83c02..ab5595c 100644 --- a/JMayer.Example.ASPReact.sln +++ b/JMayer.Example.ASPReact.sln @@ -9,6 +9,10 @@ Project("{54A90642-561A-4BB1-A94E-469ADEE60C69}") = "jmayer.example.aspreact.cli EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{DDEDF8AE-D81E-4019-8164-77F1588D3320}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JMayer.Data", "..\JMayer-Data-Library\JMayer.Data\JMayer.Data.csproj", "{5E2E9979-417E-9AC0-DE5B-ED439D651066}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JMayer.Web.Mvc", "..\JMayer-Web-Mvc-Library\JMayer.Web.Mvc\JMayer.Web.Mvc.csproj", "{185EFF51-282E-8F0A-7CE5-41B2029C51D3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -29,6 +33,14 @@ Global {DDEDF8AE-D81E-4019-8164-77F1588D3320}.Debug|Any CPU.Build.0 = Debug|Any CPU {DDEDF8AE-D81E-4019-8164-77F1588D3320}.Release|Any CPU.ActiveCfg = Release|Any CPU {DDEDF8AE-D81E-4019-8164-77F1588D3320}.Release|Any CPU.Build.0 = Release|Any CPU + {5E2E9979-417E-9AC0-DE5B-ED439D651066}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5E2E9979-417E-9AC0-DE5B-ED439D651066}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5E2E9979-417E-9AC0-DE5B-ED439D651066}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5E2E9979-417E-9AC0-DE5B-ED439D651066}.Release|Any CPU.Build.0 = Release|Any CPU + {185EFF51-282E-8F0A-7CE5-41B2029C51D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {185EFF51-282E-8F0A-7CE5-41B2029C51D3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {185EFF51-282E-8F0A-7CE5-41B2029C51D3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {185EFF51-282E-8F0A-7CE5-41B2029C51D3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/TestProject/Airlines/AirlineDataLayer.cs b/TestProject/Airlines/AirlineDataLayer.cs index 7d6814b..20df032 100644 --- a/TestProject/Airlines/AirlineDataLayer.cs +++ b/TestProject/Airlines/AirlineDataLayer.cs @@ -6,7 +6,7 @@ namespace TestProject.Airlines; /// /// The class manages CRUD interactions with a remote server for an airline. /// -internal class AirlineDataLayer : UserEditableDataLayer +internal class AirlineDataLayer : StandardCRUDDataLayer { /// public AirlineDataLayer(HttpClient httpClient) : base(httpClient) { } diff --git a/TestProject/Airlines/AirlineWebRequestUnitTest.cs b/TestProject/Airlines/AirlineWebRequestUnitTest.cs index 8b3de24..c57fb1e 100644 --- a/TestProject/Airlines/AirlineWebRequestUnitTest.cs +++ b/TestProject/Airlines/AirlineWebRequestUnitTest.cs @@ -3,6 +3,7 @@ using JMayer.Example.ASPReact.Server.Airlines; using Microsoft.AspNetCore.Mvc.Testing; using System.Net; +using System.Security.Cryptography.Xml; namespace TestProject.Airlines; @@ -111,13 +112,10 @@ public async Task VerifyAddAirlineBadIATAFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Equal("The IATA must be 2 alphanumeric characters; letters must be capitalized.", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.IATA), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.IATA)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.IATA)]); + Assert.Equal("The IATA must be 2 alphanumeric characters; letters must be capitalized.", operationResult.ValidationErrors[nameof(Airline.IATA)][0]); } /// @@ -147,13 +145,10 @@ public async Task VerifyAddAirlineBadICAOFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Equal("The ICAO must be 3 capital letters.", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.ICAO), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.ICAO)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.ICAO)]); + Assert.Equal("The ICAO must be 3 capital letters.", operationResult.ValidationErrors[nameof(Airline.ICAO)][0]); } /// @@ -183,13 +178,10 @@ public async Task VerifyAddAirlineBadNumberCodeFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Equal("The number code must be 3 digits.", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.NumberCode), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.NumberCode)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.NumberCode)]); + Assert.Equal("The number code must be 3 digits.", operationResult.ValidationErrors[nameof(Airline.NumberCode)][0]); } /// @@ -233,13 +225,10 @@ public async Task VerifyAddDuplicateAirlineICAOFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("ICAO must be unique", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.ICAO), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.ICAO)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.ICAO)]); + Assert.Equal("The ICAO must be unique.", operationResult.ValidationErrors[nameof(Airline.ICAO)][0]); } /// @@ -283,13 +272,10 @@ public async Task VerifyAddDuplicateAirlineNameFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("name already exists", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.Name), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.Name)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.Name)]); + Assert.Equal("The Add Duplicate Name Test name already exists in the data store.", operationResult.ValidationErrors[nameof(Airline.Name)][0]); } /// @@ -333,13 +319,10 @@ public async Task VerifyAddDuplicateAirlineNumberCodeFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("number code must be unique", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.NumberCode), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.NumberCode)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.NumberCode)]); + Assert.Equal("The number code must be unique unless the code is 000.", operationResult.ValidationErrors[nameof(Airline.NumberCode)][0]); } /// @@ -549,13 +532,10 @@ public async Task VerifyUpdateAirlineBadIATAFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Equal("The IATA must be 2 alphanumeric characters; letters must be capitalized.", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.IATA), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.IATA)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.IATA)]); + Assert.Equal("The IATA must be 2 alphanumeric characters; letters must be capitalized.", operationResult.ValidationErrors[nameof(Airline.IATA)][0]); } else { @@ -595,13 +575,10 @@ public async Task VerifyUpdateAirlineBadICAOFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Equal("The ICAO must be 3 capital letters.", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.ICAO), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.ICAO)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.ICAO)]); + Assert.Equal("The ICAO must be 3 capital letters.", operationResult.ValidationErrors[nameof(Airline.ICAO)][0]); } else { @@ -641,13 +618,10 @@ public async Task VerifyUpdateAirlineBadNumberCodeFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Equal("The number code must be 3 digits.", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.NumberCode), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.NumberCode)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.NumberCode)]); + Assert.Equal("The number code must be 3 digits.", operationResult.ValidationErrors[nameof(Airline.NumberCode)][0]); } else { @@ -745,13 +719,10 @@ public async Task VerifyUpdateDuplicateAirlineICAOFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("ICAO must be unique", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.ICAO), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.ICAO)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.ICAO)]); + Assert.Equal("The ICAO must be unique.", operationResult.ValidationErrors[nameof(Airline.ICAO)][0]); } else { @@ -805,13 +776,10 @@ public async Task VerifyUpdateDuplicateAirlineNameFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("name already exists", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.Name), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.Name)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.Name)]); + Assert.Equal($"The {airline.Name} name already exists in the data store.", operationResult.ValidationErrors[nameof(Airline.Name)][0]); } else { @@ -865,13 +833,10 @@ public async Task VerifyUpdateDuplicateAirlineNumberCodeFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("number code must be unique", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Airline.NumberCode), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.NumberCode)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.NumberCode)]); + Assert.Equal("The number code must be unique unless the code is 000.", operationResult.ValidationErrors[nameof(Airline.NumberCode)][0]); } else { diff --git a/TestProject/Flights/FlightDataLayer.cs b/TestProject/Flights/FlightDataLayer.cs index 893e54c..62d0656 100644 --- a/TestProject/Flights/FlightDataLayer.cs +++ b/TestProject/Flights/FlightDataLayer.cs @@ -6,7 +6,7 @@ namespace TestProject.Flights; /// /// The class manages CRUD interactions with a remote server for a flight. /// -internal class FlightDataLayer : UserEditableDataLayer +internal class FlightDataLayer : StandardCRUDDataLayer { /// public FlightDataLayer(HttpClient httpClient) : base(httpClient) { } diff --git a/TestProject/Flights/FlightWebRequestUnitTest.cs b/TestProject/Flights/FlightWebRequestUnitTest.cs index 176b707..4854577 100644 --- a/TestProject/Flights/FlightWebRequestUnitTest.cs +++ b/TestProject/Flights/FlightWebRequestUnitTest.cs @@ -6,6 +6,7 @@ using JMayer.Example.ASPReact.Server.SortDestinations; using Microsoft.AspNetCore.Mvc.Testing; using System.Net; +using System.Security.Cryptography.Xml; using TestProject.Gates; namespace TestProject.Flights; @@ -283,13 +284,10 @@ public async Task VerifyAddFlightBadDestinationFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Equal("The city must be 3 capital letters.", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.Destination), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.Destination)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.Destination)]); + Assert.Equal("The city must be 3 capital letters.", operationResult.ValidationErrors[nameof(Flight.Destination)][0]); } /// @@ -322,13 +320,10 @@ public async Task VerifyAddFlightBadFlightNumberFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Equal("The flight number must be 4 digits or 4 digits and a capital letter.", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.FlightNumber), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.FlightNumber)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.FlightNumber)]); + Assert.Equal("The flight number must be 4 digits or 4 digits and a capital letter.", operationResult.ValidationErrors[nameof(Flight.FlightNumber)][0]); } /// @@ -378,13 +373,10 @@ public async Task VerifyAddDuplicateFlightFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("flight already exists", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.FlightNumber), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.FlightNumber)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.FlightNumber)]); + Assert.Equal("The flight already exists in the schedule.", operationResult.ValidationErrors[nameof(Flight.FlightNumber)][0]); } /// @@ -480,13 +472,10 @@ public async Task VerifyAddFlightFailureAirlineNotFound() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("airline was not found", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.AirlineID), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.AirlineID)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.AirlineID)]); + Assert.Equal($"The {BadAirlineID} airline was not found in the data store.", operationResult.ValidationErrors[nameof(Flight.AirlineID)][0]); } /// @@ -520,13 +509,10 @@ public async Task VerifyAddFlightFailureCodeShareAirlineNotFound() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("airline for the codeshare was not found", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(CodeShare.AirlineID), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.AirlineID)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.AirlineID)]); + Assert.Equal($"The {BadAirlineID} airline for the codeshare was not found in the data store.", operationResult.ValidationErrors[nameof(Flight.AirlineID)][0]); } /// @@ -559,13 +545,10 @@ public async Task VerifyAddFlightFailureGateNotFound() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("gate was not found", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.GateID), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.GateID)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.GateID)]); + Assert.Equal($"The {BadGateID} gate was not found in the data store.", operationResult.ValidationErrors[nameof(Flight.GateID)][0]); } /// @@ -598,13 +581,10 @@ public async Task VerifyAddFlightFailureSortDestinationNotFound() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("sort destination was not found", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.SortDestinationID), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.SortDestinationID)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.SortDestinationID)]); + Assert.Equal($"The {BadSortDestinationID} sort destination was not found in the data store.", operationResult.ValidationErrors[nameof(Flight.SortDestinationID)][0]); } /// @@ -826,13 +806,10 @@ public async Task VerifyUpdateDuplicateFlightFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("flight already exists", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.FlightNumber), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.FlightNumber)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.FlightNumber)]); + Assert.Equal("The flight already exists in the schedule.", operationResult.ValidationErrors[nameof(Flight.FlightNumber)][0]); } else { @@ -966,13 +943,10 @@ public async Task VerifyUpdateFlightAirlineNotFoundFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("airline was not found", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.AirlineID), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.AirlineID)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.AirlineID)]); + Assert.Equal($"The {BadAirlineID} airline was not found in the data store.", operationResult.ValidationErrors[nameof(Flight.AirlineID)][0]); } else { @@ -1015,13 +989,10 @@ public async Task VerifyUpdateFlightBadDestinationFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Equal("The city must be 3 capital letters.", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.Destination), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.Destination)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.Destination)]); + Assert.Equal("The city must be 3 capital letters.", operationResult.ValidationErrors[nameof(Flight.Destination)][0]); } else { @@ -1064,13 +1035,10 @@ public async Task VerifyUpdateFlightBadFlightNumberFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Equal("The flight number must be 4 digits or 4 digits and a capital letter.", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.FlightNumber), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.FlightNumber)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.FlightNumber)]); + Assert.Equal("The flight number must be 4 digits or 4 digits and a capital letter.", operationResult.ValidationErrors[nameof(Flight.FlightNumber)][0]); } else { @@ -1123,13 +1091,10 @@ public async Task VerifyUpdateFlightCodeShareAirlineNotFoundFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("airline for the codeshare was not found", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(CodeShare.AirlineID), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.AirlineID)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.AirlineID)]); + Assert.Equal($"The {BadAirlineID} airline for the codeshare was not found in the data store.", operationResult.ValidationErrors[nameof(Flight.AirlineID)][0]); } else { @@ -1181,13 +1146,10 @@ public async Task VerifyUpdateFlightGateNotFoundFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("gate was not found", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.GateID), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.GateID)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.GateID)]); + Assert.Equal($"The {BadGateID} gate was not found in the data store.", operationResult.ValidationErrors[nameof(Flight.GateID)][0]); } else { @@ -1239,13 +1201,10 @@ public async Task VerifyUpdateFlightSortDestinationNotFoundFailure() //A bad request status was returned. Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - //A validation error was returned. - Assert.NotNull(operationResult.ServerSideValidationResult); - Assert.Single(operationResult.ServerSideValidationResult.Errors); - //The correct error was returned. - Assert.Contains("sort destination was not found", operationResult.ServerSideValidationResult.Errors[0].ErrorMessage); - Assert.Equal(nameof(Flight.SortDestinationID), operationResult.ServerSideValidationResult.Errors[0].PropertyName); + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.SortDestinationID)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.SortDestinationID)]); + Assert.Equal($"The {BadSortDestinationID} sort destination was not found in the data store.", operationResult.ValidationErrors[nameof(Flight.SortDestinationID)][0]); } else { diff --git a/TestProject/Gates/GateDataLyaer.cs b/TestProject/Gates/GateDataLyaer.cs index 7dc342d..5deab23 100644 --- a/TestProject/Gates/GateDataLyaer.cs +++ b/TestProject/Gates/GateDataLyaer.cs @@ -6,7 +6,7 @@ namespace TestProject.Gates; /// /// The class manages CRUD interactions with a remote server for an airline. /// -internal class GateDataLyaer : UserEditableDataLayer +internal class GateDataLyaer : StandardCRUDDataLayer { /// public GateDataLyaer(HttpClient httpClient) : base(httpClient) { } diff --git a/TestProject/Gates/GateWebRequestUnitTest.cs b/TestProject/Gates/GateWebRequestUnitTest.cs index bed4063..4092271 100644 --- a/TestProject/Gates/GateWebRequestUnitTest.cs +++ b/TestProject/Gates/GateWebRequestUnitTest.cs @@ -47,7 +47,7 @@ public async Task VerifyAddGateNotAllowed() //No gate or server side validation result was returned. Assert.Null(operationResult.DataObject); - Assert.Null(operationResult.ServerSideValidationResult); + Assert.Empty(operationResult.ValidationErrors); //A method not allowed status was returned. Assert.Equal(HttpStatusCode.MethodNotAllowed, operationResult.StatusCode); @@ -83,7 +83,7 @@ public async Task VerifyDeleteGateNotAllowed() //No gate or server side validation result was returned. Assert.Null(operationResult.DataObject); - Assert.Null(operationResult.ServerSideValidationResult); + Assert.Empty(operationResult.ValidationErrors); //A method not allowed status was returned. Assert.Equal(HttpStatusCode.MethodNotAllowed, operationResult.StatusCode); @@ -167,7 +167,7 @@ public async Task VerifyUpdateGateNotAllowed() //No gate or server side validation result was returned. Assert.Null(operationResult.DataObject); - Assert.Null(operationResult.ServerSideValidationResult); + Assert.Empty(operationResult.ValidationErrors); //A method not allowed status was returned. Assert.Equal(HttpStatusCode.MethodNotAllowed, operationResult.StatusCode); diff --git a/TestProject/SortDestinations/SortDestinationDataLayer.cs b/TestProject/SortDestinations/SortDestinationDataLayer.cs index 3f93ce7..6269f00 100644 --- a/TestProject/SortDestinations/SortDestinationDataLayer.cs +++ b/TestProject/SortDestinations/SortDestinationDataLayer.cs @@ -6,7 +6,7 @@ namespace TestProject.SortDestinations; /// /// The class manages CRUD interactions with a remote server for a sort destination. /// -internal class SortDestinationDataLayer : UserEditableDataLayer +internal class SortDestinationDataLayer : StandardCRUDDataLayer { /// public SortDestinationDataLayer(HttpClient httpClient) : base(httpClient) { } diff --git a/TestProject/SortDestinations/SortDestinationWebRequestUnitTest.cs b/TestProject/SortDestinations/SortDestinationWebRequestUnitTest.cs index a884a7d..38838b8 100644 --- a/TestProject/SortDestinations/SortDestinationWebRequestUnitTest.cs +++ b/TestProject/SortDestinations/SortDestinationWebRequestUnitTest.cs @@ -47,7 +47,7 @@ public async Task VerifyAddSortDestinationNotAllowed() //No sort destination or server side validation result was returned. Assert.Null(operationResult.DataObject); - Assert.Null(operationResult.ServerSideValidationResult); + Assert.Empty(operationResult.ValidationErrors); //A method not allowed status was returned. Assert.Equal(HttpStatusCode.MethodNotAllowed, operationResult.StatusCode); @@ -83,7 +83,7 @@ public async Task VerifyDeleteSortDestinationNotAllowed() //No sort destination or server side validation result was returned. Assert.Null(operationResult.DataObject); - Assert.Null(operationResult.ServerSideValidationResult); + Assert.Empty(operationResult.ValidationErrors); //A method not allowed status was returned. Assert.Equal(HttpStatusCode.MethodNotAllowed, operationResult.StatusCode); @@ -167,7 +167,7 @@ public async Task VerifyUpdateSortDestinationNotAllowed() //No sort destination or server side validation result was returned. Assert.Null(operationResult.DataObject); - Assert.Null(operationResult.ServerSideValidationResult); + Assert.Empty(operationResult.ValidationErrors); //A method not allowed status was returned. Assert.Equal(HttpStatusCode.MethodNotAllowed, operationResult.StatusCode); diff --git a/TestProject/TestProject.csproj b/TestProject/TestProject.csproj index dfd99db..e2d61a2 100644 --- a/TestProject/TestProject.csproj +++ b/TestProject/TestProject.csproj @@ -15,7 +15,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -27,6 +26,7 @@ + From 8acfcd4f50f7d99e9d3dd03fb14b3ed3ffc86784 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:18:58 -0500 Subject: [PATCH 03/16] Code cleanup on the unit tests --- .../Flights/FlightEqualityComparer.cs | 2 +- .../Airlines/AirlineWebRequestUnitTest.cs | 55 +++-------- .../Flights/FlightWebRequestUnitTest.cs | 98 +++++++------------ 3 files changed, 48 insertions(+), 107 deletions(-) diff --git a/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs b/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs index 3f5faaa..2fa5a2d 100644 --- a/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs +++ b/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs @@ -56,7 +56,7 @@ public bool Equals(Flight? x, Flight? y) { for (int index = 0; index < x.CodeShares.Count; index++) { - if (new CodeShareEqualityComparer().Equals(x.CodeShares[index], y.CodeShares[index]) == false) + if (new CodeShareEqualityComparer().Equals(x.CodeShares[index], y.CodeShares[index]) is false) { return false; } diff --git a/TestProject/Airlines/AirlineWebRequestUnitTest.cs b/TestProject/Airlines/AirlineWebRequestUnitTest.cs index c57fb1e..8eec3a4 100644 --- a/TestProject/Airlines/AirlineWebRequestUnitTest.cs +++ b/TestProject/Airlines/AirlineWebRequestUnitTest.cs @@ -3,7 +3,6 @@ using JMayer.Example.ASPReact.Server.Airlines; using Microsoft.AspNetCore.Mvc.Testing; using System.Net; -using System.Security.Cryptography.Xml; namespace TestProject.Airlines; @@ -201,12 +200,7 @@ public async Task VerifyAddDuplicateAirlineICAOFailure() NumberCode = Airline.ZeroNumberCode, Name = "Add Duplicate ICAO Test 1", }); - - if (!operationResult.IsSuccessStatusCode) - { - Assert.Fail("Failed to create the first airline."); - return; - } + Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); operationResult = await dataLayer.CreateAsync(new Airline() { @@ -248,12 +242,7 @@ public async Task VerifyAddDuplicateAirlineNameFailure() NumberCode = Airline.ZeroNumberCode, Name = "Add Duplicate Name Test", }); - - if (!operationResult.IsSuccessStatusCode) - { - Assert.Fail("Failed to create the first airline."); - return; - } + Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); operationResult = await dataLayer.CreateAsync(new Airline() { @@ -295,12 +284,7 @@ public async Task VerifyAddDuplicateAirlineNumberCodeFailure() NumberCode = "999", Name = "Add Duplicate Number Code Test 1", }); - - if (!operationResult.IsSuccessStatusCode) - { - Assert.Fail("Failed to create the first airline."); - return; - } + Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); operationResult = await dataLayer.CreateAsync(new Airline() { @@ -357,7 +341,7 @@ public async Task VerifyDeleteAirline() NumberCode = Airline.ZeroNumberCode, }); - if (operationResult.DataObject is Airline airline) + if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) { operationResult = await dataLayer.DeleteAsync(airline); Assert.True(operationResult.IsSuccessStatusCode); @@ -435,7 +419,7 @@ public async Task VerifyGetSingleAirlineWithId() }; OperationResult operationResult = await dataLayer.CreateAsync(airline); - if (operationResult.DataObject is Airline createdAirline) + if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline createdAirline) { Airline? verifyAirline = await dataLayer.GetSingleAsync(createdAirline.Integer64ID); Assert.NotNull(verifyAirline); @@ -518,7 +502,7 @@ public async Task VerifyUpdateAirlineBadIATAFailure() Name = "Update Bad IATA Code Test", }); - if (operationResult.DataObject is Airline airline) + if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) { airline.IATA = BadFormattedIATACode; operationResult = await dataLayer.UpdateAsync(airline); @@ -561,7 +545,7 @@ public async Task VerifyUpdateAirlineBadICAOFailure() Name = "Update Bad ICAO Code Test", }); - if (operationResult.DataObject is Airline airline) + if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) { airline.ICAO = BadFormattedICAOCode; operationResult = await dataLayer.UpdateAsync(airline); @@ -604,7 +588,7 @@ public async Task VerifyUpdateAirlineBadNumberCodeFailure() Name = "Update Bad Number Code Test", }); - if (operationResult.DataObject is Airline airline) + if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) { airline.NumberCode = BadFormattedNumberCode; operationResult = await dataLayer.UpdateAsync(airline); @@ -647,7 +631,7 @@ public async Task VerifyUpdateAirlineOldDataConflict() NumberCode = Airline.ZeroNumberCode, }); - if (operationResult.DataObject is Airline firstAirline) + if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline firstAirline) { Airline secondAirline = new(firstAirline); @@ -690,12 +674,7 @@ public async Task VerifyUpdateDuplicateAirlineICAOFailure() NumberCode = Airline.ZeroNumberCode, Name = "Update Duplicate ICAO Test 1", }); - - if (!operationResult.IsSuccessStatusCode) - { - Assert.Fail("Failed to create the first airline."); - return; - } + Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); operationResult = await dataLayer.CreateAsync(new Airline() { @@ -747,12 +726,7 @@ public async Task VerifyUpdateDuplicateAirlineNameFailure() NumberCode = Airline.ZeroNumberCode, Name = "Update Duplicate Name Test 1", }); - - if (!operationResult.IsSuccessStatusCode) - { - Assert.Fail("Failed to create the first airline."); - return; - } + Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); operationResult = await dataLayer.CreateAsync(new Airline() { @@ -804,12 +778,7 @@ public async Task VerifyUpdateDuplicateAirlineNumberCodeFailure() NumberCode = "997", Name = "Update Duplicate Number Code Test Test 1", }); - - if (!operationResult.IsSuccessStatusCode) - { - Assert.Fail("Failed to create the first airline."); - return; - } + Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); operationResult = await dataLayer.CreateAsync(new Airline() { diff --git a/TestProject/Flights/FlightWebRequestUnitTest.cs b/TestProject/Flights/FlightWebRequestUnitTest.cs index 4854577..9a515c4 100644 --- a/TestProject/Flights/FlightWebRequestUnitTest.cs +++ b/TestProject/Flights/FlightWebRequestUnitTest.cs @@ -6,7 +6,6 @@ using JMayer.Example.ASPReact.Server.SortDestinations; using Microsoft.AspNetCore.Mvc.Testing; using System.Net; -using System.Security.Cryptography.Xml; using TestProject.Gates; namespace TestProject.Flights; @@ -182,27 +181,28 @@ public class FlightWebRequestUnitTest : IClassFixtureA list of CodeShare objects. private async Task> CreateCodeSharesAsync(string? codeshareCommaSeparatedList) { + if (codeshareCommaSeparatedList is null) + { + return []; + } + List codeshares = []; + string[] splitCodeShares = codeshareCommaSeparatedList.Split(',', StringSplitOptions.RemoveEmptyEntries); - if (codeshareCommaSeparatedList != null) + foreach (var codeshareString in splitCodeShares) { - string[] splitCodeShares = codeshareCommaSeparatedList.Split([','], StringSplitOptions.RemoveEmptyEntries); + string airlineIATACode = codeshareString.Substring(0, 2); + string flightNumber = codeshareString.Substring(2); - foreach (var codeshareString in splitCodeShares) - { - string airlineIATACode = codeshareString.Substring(0, 2); - string flightNumber = codeshareString.Substring(2); + Airline? airline = await GetAirlineByIATAAsync(airlineIATACode); - Airline? airline = await GetAirlineByIATAAsync(airlineIATACode); - - if (airline != null) + if (airline is not null) + { + codeshares.Add(new CodeShare() { - codeshares.Add(new CodeShare() - { - AirlineID = airline.Integer64ID, - FlightNumber = flightNumber, - }); - } + AirlineID = airline.Integer64ID, + FlightNumber = flightNumber, + }); } } @@ -220,7 +220,6 @@ private async Task> CreateCodeSharesAsync(string? codeshareComma Airlines.AirlineDataLayer dataLayer = new(httpClient); List? airlines = await dataLayer.GetAllAsync(); - return airlines?.FirstOrDefault(obj => obj.IATA == iata); } @@ -235,7 +234,6 @@ private async Task> CreateCodeSharesAsync(string? codeshareComma GateDataLyaer dataLayer = new(httpClient); List? gates = await dataLayer.GetAllAsync(); - return gates?.FirstOrDefault(obj => obj.Name == name); } @@ -250,7 +248,6 @@ private async Task> CreateCodeSharesAsync(string? codeshareComma SortDestinations.SortDestinationDataLayer dataLayer = new(httpClient); List? sortDestinations = await dataLayer.GetAllAsync(); - return sortDestinations?.FirstOrDefault(obj => obj.Name == name); } @@ -346,12 +343,7 @@ public async Task VerifyAddDuplicateFlightFailure() Destination = DefaultAirportCode, SortDestinationID = DefaultSortDestinationID, }); - - if (!operationResult.IsSuccessStatusCode) - { - Assert.Fail("Failed to create the first flight."); - return; - } + Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first flight."); operationResult = await dataLayer.CreateAsync(new Flight() { @@ -400,26 +392,23 @@ public async Task VerifyAddFlight(string gateName, string airlineIATA, string fl Gate? gate = await GetGateAsync(gateName); - if (gate == null) + if (gate is null) { Assert.Fail("Failed to retrieve the gate."); - return; } Airline? airline = await GetAirlineByIATAAsync(airlineIATA); - if (airline == null) + if (airline is null) { Assert.Fail("Failed to retrieve the airline."); - return; } SortDestination? sortDestination = await GetSortDestinationAsync(sortDestinationName); - if (sortDestination == null) + if (sortDestination is null) { Assert.Fail("Failed to retrieve the sort destination."); - return; } List codeshares = await CreateCodeSharesAsync(codeshareCommaSeparatedList); @@ -613,10 +602,9 @@ public async Task VerifyDeleteAirlineCascade() Airline? airline = await CreateAirlineAsync(AirlineCascadeDelete); - if (airline == null) + if (airline is null) { Assert.Fail("Failed to create the airline."); - return; } OperationResult operationResult = await dataLayer.CreateAsync(new Flight() @@ -629,26 +617,20 @@ public async Task VerifyDeleteAirlineCascade() Destination = DefaultAirportCode, SortDestinationID = DefaultSortDestinationID, }); + Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the flight."); - if (!operationResult.IsSuccessStatusCode) - { - Assert.Fail("Failed to create the flight."); - return; - } - - await new Airlines.AirlineDataLayer(httpClient).DeleteAsync(airline); + operationResult = await new Airlines.AirlineDataLayer(httpClient).DeleteAsync(airline); + Assert.True(operationResult.IsSuccessStatusCode, "Failed to delete the airline."); List? flights = await dataLayer.GetAllAsync(); - if (flights == null) + if (flights is null) { Assert.Fail("Failed to query the flights."); } - else - { - flights = flights.Where(obj => obj.AirlineID == airline.Integer64ID).ToList(); - Assert.Empty(flights); - } + + flights = [.. flights.Where(obj => obj.AirlineID == airline.Integer64ID)]; + Assert.Empty(flights); } /// @@ -672,7 +654,7 @@ public async Task VerifyDeleteFlight() SortDestinationID = DefaultSortDestinationID, }); - if (operationResult.DataObject is Flight flight) + if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Flight flight) { operationResult = await dataLayer.DeleteAsync(flight); Assert.True(operationResult.IsSuccessStatusCode); @@ -692,7 +674,6 @@ public async Task VerifyGetAllFlights() { HttpClient httpClient = _factory.CreateClient(); FlightDataLayer dataLayer = new(httpClient); - List? flights = await dataLayer.GetAllAsync(); //Flights must have been returned. @@ -709,7 +690,6 @@ public async Task VerifyGetAllListViewFlights() { HttpClient httpClient = _factory.CreateClient(); FlightDataLayer dataLayer = new(httpClient); - List? flights = await dataLayer.GetAllListViewAsync(); //List view flights must have been returned. @@ -765,12 +745,7 @@ public async Task VerifyUpdateDuplicateFlightFailure() Destination = DefaultAirportCode, SortDestinationID = DefaultSortDestinationID, }); - - if (!operationResult.IsSuccessStatusCode) - { - Assert.Fail("Failed to create the first flight."); - return; - } + Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first flight."); operationResult = await dataLayer.CreateAsync(new Flight() { @@ -838,26 +813,23 @@ public async Task VerifyUpdateFlight(string gateName, string airlineIATA, string Gate? gate = await GetGateAsync(gateName); - if (gate == null) + if (gate is null) { Assert.Fail("Failed to retrieve the gate."); - return; } Airline? airline = await GetAirlineByIATAAsync(airlineIATA); - if (airline == null) + if (airline is null) { Assert.Fail("Failed to retrieve the airline."); - return; } SortDestination? sortDestination = await GetSortDestinationAsync(sortDestinationName); - if (sortDestination == null) + if (sortDestination is null) { Assert.Fail("Failed to retrieve the sort destination."); - return; } OperationResult operationResult = await dataLayer.CreateAsync(new Flight() @@ -975,7 +947,7 @@ public async Task VerifyUpdateFlightBadDestinationFailure() SortDestinationID = DefaultSortDestinationID, }); - if (operationResult.DataObject is Flight flight) + if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Flight flight) { flight.Destination = BadFormatttedDestination; operationResult = await dataLayer.UpdateAsync(flight); @@ -1021,7 +993,7 @@ public async Task VerifyUpdateFlightBadFlightNumberFailure() SortDestinationID = DefaultSortDestinationID, }); - if (operationResult.DataObject is Flight flight) + if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Flight flight) { flight.FlightNumber = BadFormatttedFlightNumber; operationResult = await dataLayer.UpdateAsync(flight); From fb46938516f66bfe2f1ea255fc66392299b34605 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:25:14 -0500 Subject: [PATCH 04/16] Switched to NoAction Attribute --- .../Gates/GateController.cs | 19 ++++++++++--------- .../SortDestinationController.cs | 19 ++++++++++--------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/JMayer.Example.ASPReact.Server/Gates/GateController.cs b/JMayer.Example.ASPReact.Server/Gates/GateController.cs index a867714..ed83b17 100644 --- a/JMayer.Example.ASPReact.Server/Gates/GateController.cs +++ b/JMayer.Example.ASPReact.Server/Gates/GateController.cs @@ -1,11 +1,8 @@ using JMayer.Web.Mvc.Controller.Api; using Microsoft.AspNetCore.Mvc; -using System.Net; namespace JMayer.Example.ASPReact.Server.Gates; -#warning Should I use the NoAction attribute over returning a MethodNotAllowed status? - /// /// The class manages HTTP requests for CRUD operations associated with a gate in a database. /// @@ -21,9 +18,10 @@ public GateController(IGateDataLayer dataLayer, ILogger logger) /// Overriden to prevent the creation of new gates. The example will auto generate some default gates /// and the client side will only retrieve them but not edit them. /// + [NonAction] public override Task CreateAsync([FromBody] Gate dataObject) { - return Task.FromResult((IActionResult)StatusCode((int)HttpStatusCode.MethodNotAllowed)); + return base.CreateAsync(dataObject); } /// @@ -31,9 +29,10 @@ public override Task CreateAsync([FromBody] Gate dataObject) /// Overriden to prevent the deletion of gates. The example will auto generate some default gates /// and the client side will only retrieve them but not edit them. /// - public override Task DeleteAsync(long integerID) + [NonAction] + public override Task DeleteAsync(long id) { - return Task.FromResult((IActionResult)StatusCode((int)HttpStatusCode.MethodNotAllowed)); + return base.DeleteAsync(id); } /// @@ -41,9 +40,10 @@ public override Task DeleteAsync(long integerID) /// Overriden to prevent the deletion of gates. The example will auto generate some default gates /// and the client side will only retrieve them but not edit them. /// - public override Task DeleteAsync(string stringID) + [NonAction] + public override Task DeleteAsync(string id) { - return Task.FromResult((IActionResult)StatusCode((int)HttpStatusCode.MethodNotAllowed)); + return base.DeleteAsync(id); } /// @@ -51,8 +51,9 @@ public override Task DeleteAsync(string stringID) /// Overriden to prevent the updating of gates. The example will auto generate some default gates /// and the client side will only retrieve them but not edit them. /// + [NonAction] public override Task UpdateAsync([FromBody] Gate dataObject) { - return Task.FromResult((IActionResult)StatusCode((int)HttpStatusCode.MethodNotAllowed)); + return base.UpdateAsync(dataObject); } } diff --git a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs index 1bacd30..0bb3f7a 100644 --- a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs +++ b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs @@ -1,11 +1,8 @@ using JMayer.Web.Mvc.Controller.Api; using Microsoft.AspNetCore.Mvc; -using System.Net; namespace JMayer.Example.ASPReact.Server.SortDestinations; -#warning Should I use the NoAction attribute over returning a MethodNotAllowed status? - /// /// The class manages HTTP requests for CRUD operations associated with a sort destination in a database. /// @@ -21,9 +18,10 @@ public SortDestinationController(ISortDestinationDataLayer dataLayer, ILogger + [NonAction] public override Task CreateAsync([FromBody] SortDestination dataObject) { - return Task.FromResult((IActionResult)StatusCode((int)HttpStatusCode.MethodNotAllowed)); + return base.CreateAsync(dataObject); } /// @@ -31,9 +29,10 @@ public override Task CreateAsync([FromBody] SortDestination dataO /// Overriden to prevent the deletion of sort destinations. The example will auto generate some default sort destinations /// and the client side will only retrieve them but not edit them. /// - public override Task DeleteAsync(long integerID) + [NonAction] + public override Task DeleteAsync(long id) { - return Task.FromResult((IActionResult)StatusCode((int)HttpStatusCode.MethodNotAllowed)); + return base.DeleteAsync(id); } /// @@ -41,9 +40,10 @@ public override Task DeleteAsync(long integerID) /// Overriden to prevent the deletion of sort destinations. The example will auto generate some default sort destinations /// and the client side will only retrieve them but not edit them. /// - public override Task DeleteAsync(string stringID) + [NonAction] + public override Task DeleteAsync(string id) { - return Task.FromResult((IActionResult)StatusCode((int)HttpStatusCode.MethodNotAllowed)); + return base.DeleteAsync(id); } /// @@ -51,8 +51,9 @@ public override Task DeleteAsync(string stringID) /// Overriden to prevent the updating of sort destinations. The example will auto generate some default sort destinations /// and the client side will only retrieve them but not edit them. /// + [NonAction] public override Task UpdateAsync([FromBody] SortDestination dataObject) { - return Task.FromResult((IActionResult)StatusCode((int)HttpStatusCode.MethodNotAllowed)); + return base.UpdateAsync(dataObject); } } From 644d26382f4dfb6a6ca0b25567c3a4a2c0534370 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Thu, 6 Nov 2025 10:33:55 -0500 Subject: [PATCH 05/16] Missing equality test for SortDestinationID --- .../Flights/FlightEqualityComparer.cs | 3 ++- .../Flights/FlightEqualityComparerUnitTest.cs | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs b/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs index 2fa5a2d..6951b2f 100644 --- a/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs +++ b/JMayer.Example.ASPReact.Server/Flights/FlightEqualityComparer.cs @@ -72,7 +72,8 @@ public bool Equals(Flight? x, Flight? y) && (_excludeID || x.Integer64ID == y.Integer64ID) && (_excludeLastEditedOn || x.LastEditedBy == y.LastEditedBy) && x.Name == y.Name - && x.Destination == y.Destination; + && x.Destination == y.Destination + && x.SortDestinationID == y.SortDestinationID; } /// diff --git a/TestProject/Flights/FlightEqualityComparerUnitTest.cs b/TestProject/Flights/FlightEqualityComparerUnitTest.cs index 09dbe47..863e578 100644 --- a/TestProject/Flights/FlightEqualityComparerUnitTest.cs +++ b/TestProject/Flights/FlightEqualityComparerUnitTest.cs @@ -42,6 +42,11 @@ public class FlightEqualityComparerUnitTest /// private const string Name = "A Name"; + /// + /// The constant for the sort destination ID. + /// + private const long SortDestinationID = 3; + /// /// The method verifies equality failure when the AirlineID property is different between the two objects. /// @@ -202,12 +207,28 @@ public void VerifyFailureOneIsNull() Integer64ID = ID, LastEditedOn = DateTime.Now, Name = Name, + SortDestinationID = SortDestinationID, }; Assert.False(new FlightEqualityComparer().Equals(flight, null)); Assert.False(new FlightEqualityComparer().Equals(null, flight)); } + /// + /// The method verifies equality failure when the SortDestinationID property is different between the two objects. + /// + [Fact] + public void VerifyFailureSortDestinationID() + { + Flight flight1 = new() + { + SortDestinationID = SortDestinationID, + }; + Flight flight2 = new(); + + Assert.False(new FlightEqualityComparer().Equals(flight1, flight2)); + } + /// /// The method verifies equality success when two objects (different references) are compared. /// @@ -227,6 +248,7 @@ public void VerifySuccess() Integer64ID = ID, LastEditedOn = DateTime.Now, Name = Name, + SortDestinationID = SortDestinationID, }; Flight flight2 = new(flight1); @@ -253,6 +275,7 @@ public void VerifySuccessExcludeCreatedOn() Integer64ID = ID, LastEditedOn = DateTime.Now, Name = Name, + SortDestinationID = SortDestinationID, }; Flight flight2 = new(flight1) { @@ -282,6 +305,7 @@ public void VerifySuccessExcludeID() Integer64ID = ID, LastEditedOn = DateTime.Now, Name = Name, + SortDestinationID = SortDestinationID, }; Flight flight2 = new(flight1) { @@ -311,6 +335,7 @@ public void VerifySuccessExcludeLastEditedOn() Integer64ID = ID, LastEditedOn = DateTime.Now, Name = Name, + SortDestinationID = SortDestinationID, }; Flight flight2 = new(flight1) { From 99a48d08636f07ed229e8cd838b33199b54b6a0d Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Thu, 6 Nov 2025 12:50:29 -0500 Subject: [PATCH 06/16] Renamed tests for consistency --- .../Flights/FlightWebRequestUnitTest.cs | 152 +++++++++--------- 1 file changed, 76 insertions(+), 76 deletions(-) diff --git a/TestProject/Flights/FlightWebRequestUnitTest.cs b/TestProject/Flights/FlightWebRequestUnitTest.cs index 9a515c4..0679f81 100644 --- a/TestProject/Flights/FlightWebRequestUnitTest.cs +++ b/TestProject/Flights/FlightWebRequestUnitTest.cs @@ -251,78 +251,6 @@ private async Task> CreateCodeSharesAsync(string? codeshareComma return sortDestinations?.FirstOrDefault(obj => obj.Name == name); } - /// - /// The method verifies the server will return a failure if the flight destination is badly formatted when adding a new flight. - /// - /// A Task object for the async. - [Fact] - public async Task VerifyAddFlightBadDestinationFailure() - { - HttpClient httpClient = _factory.CreateClient(); - FlightDataLayer dataLayer = new(httpClient); - - OperationResult operationResult = await dataLayer.CreateAsync(new Flight() - { - AirlineID = DefaultAirlineID, - DepartTime = DateTime.Now.TimeOfDay, - FlightNumber = "0000", - GateID = DefaultGateID, - Name = "Add Bad Destination Test", - Destination = BadFormatttedDestination, - SortDestinationID = DefaultSortDestinationID, - }); - - //The operation must have failed. - Assert.False(operationResult.IsSuccessStatusCode, "The operation should have failed."); - - //No airline was returned. - Assert.Null(operationResult.DataObject); - - //A bad request status was returned. - Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - - //The correct error was returned. - Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.Destination)); - Assert.Single(operationResult.ValidationErrors[nameof(Flight.Destination)]); - Assert.Equal("The city must be 3 capital letters.", operationResult.ValidationErrors[nameof(Flight.Destination)][0]); - } - - /// - /// The method verifies the server will return a failure if the flight number is badly formatted when adding a new flight. - /// - /// A Task object for the async. - [Fact] - public async Task VerifyAddFlightBadFlightNumberFailure() - { - HttpClient httpClient = _factory.CreateClient(); - FlightDataLayer dataLayer = new(httpClient); - - OperationResult operationResult = await dataLayer.CreateAsync(new Flight() - { - AirlineID = DefaultAirlineID, - DepartTime = DateTime.Now.TimeOfDay, - FlightNumber = BadFormatttedFlightNumber, - GateID = DefaultGateID, - Name = "Add Bad Flight Number Test", - Destination = DefaultAirportCode, - SortDestinationID = DefaultSortDestinationID, - }); - - //The operation must have failed. - Assert.False(operationResult.IsSuccessStatusCode, "The operation should have failed."); - - //No airline was returned. - Assert.Null(operationResult.DataObject); - - //A bad request status was returned. - Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); - - //The correct error was returned. - Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.FlightNumber)); - Assert.Single(operationResult.ValidationErrors[nameof(Flight.FlightNumber)]); - Assert.Equal("The flight number must be 4 digits or 4 digits and a capital letter.", operationResult.ValidationErrors[nameof(Flight.FlightNumber)][0]); - } - /// /// The method verifies the server will return a failure if the flight already exists when adding a new flight. /// @@ -436,7 +364,7 @@ public async Task VerifyAddFlight(string gateName, string airlineIATA, string fl /// /// A Task object for the async. [Fact] - public async Task VerifyAddFlightFailureAirlineNotFound() + public async Task VerifyAddFlightAirlineNotFoundFailure() { HttpClient httpClient = _factory.CreateClient(); FlightDataLayer dataLayer = new(httpClient); @@ -467,12 +395,84 @@ public async Task VerifyAddFlightFailureAirlineNotFound() Assert.Equal($"The {BadAirlineID} airline was not found in the data store.", operationResult.ValidationErrors[nameof(Flight.AirlineID)][0]); } + /// + /// The method verifies the server will return a failure if the flight destination is badly formatted when adding a new flight. + /// + /// A Task object for the async. + [Fact] + public async Task VerifyAddFlightBadDestinationFailure() + { + HttpClient httpClient = _factory.CreateClient(); + FlightDataLayer dataLayer = new(httpClient); + + OperationResult operationResult = await dataLayer.CreateAsync(new Flight() + { + AirlineID = DefaultAirlineID, + DepartTime = DateTime.Now.TimeOfDay, + FlightNumber = "0000", + GateID = DefaultGateID, + Name = "Add Bad Destination Test", + Destination = BadFormatttedDestination, + SortDestinationID = DefaultSortDestinationID, + }); + + //The operation must have failed. + Assert.False(operationResult.IsSuccessStatusCode, "The operation should have failed."); + + //No airline was returned. + Assert.Null(operationResult.DataObject); + + //A bad request status was returned. + Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); + + //The correct error was returned. + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.Destination)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.Destination)]); + Assert.Equal("The city must be 3 capital letters.", operationResult.ValidationErrors[nameof(Flight.Destination)][0]); + } + + /// + /// The method verifies the server will return a failure if the flight number is badly formatted when adding a new flight. + /// + /// A Task object for the async. + [Fact] + public async Task VerifyAddFlightBadFlightNumberFailure() + { + HttpClient httpClient = _factory.CreateClient(); + FlightDataLayer dataLayer = new(httpClient); + + OperationResult operationResult = await dataLayer.CreateAsync(new Flight() + { + AirlineID = DefaultAirlineID, + DepartTime = DateTime.Now.TimeOfDay, + FlightNumber = BadFormatttedFlightNumber, + GateID = DefaultGateID, + Name = "Add Bad Flight Number Test", + Destination = DefaultAirportCode, + SortDestinationID = DefaultSortDestinationID, + }); + + //The operation must have failed. + Assert.False(operationResult.IsSuccessStatusCode, "The operation should have failed."); + + //No airline was returned. + Assert.Null(operationResult.DataObject); + + //A bad request status was returned. + Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); + + //The correct error was returned. + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Flight.FlightNumber)); + Assert.Single(operationResult.ValidationErrors[nameof(Flight.FlightNumber)]); + Assert.Equal("The flight number must be 4 digits or 4 digits and a capital letter.", operationResult.ValidationErrors[nameof(Flight.FlightNumber)][0]); + } + /// /// The method verifies the server will return a failure if the codeshare airline ID doesn't exist when adding a new flight. /// /// A Task object for the async. [Fact] - public async Task VerifyAddFlightFailureCodeShareAirlineNotFound() + public async Task VerifyAddFlightCodeShareAirlineNotFoundFailure() { HttpClient httpClient = _factory.CreateClient(); FlightDataLayer dataLayer = new(httpClient); @@ -509,7 +509,7 @@ public async Task VerifyAddFlightFailureCodeShareAirlineNotFound() /// /// A Task object for the async. [Fact] - public async Task VerifyAddFlightFailureGateNotFound() + public async Task VerifyAddFlightGateNotFoundFailure() { HttpClient httpClient = _factory.CreateClient(); FlightDataLayer dataLayer = new(httpClient); @@ -545,7 +545,7 @@ public async Task VerifyAddFlightFailureGateNotFound() /// /// A Task object for the async. [Fact] - public async Task VerifyAddFlightFailureSortDestinationNotFound() + public async Task VerifyAddFlightSortDestinationNotFoundFailure() { HttpClient httpClient = _factory.CreateClient(); FlightDataLayer dataLayer = new(httpClient); From fb11d277fd9a2b4bfcb1aba4dfede2c5b6efa473 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Thu, 6 Nov 2025 13:35:25 -0500 Subject: [PATCH 07/16] Added sort destination to airline; backend --- .../Airlines/Airline.cs | 12 + .../Airlines/AirlineDataLayer.cs | 20 +- .../Airlines/AirlineEqualityComparer.cs | 3 +- .../FlightScheduleExampleBuilder.cs | 29 ++- .../AirlineEqualityComparerUnitTest.cs | 25 +++ .../Airlines/AirlineWebRequestUnitTest.cs | 211 +++++++++++++++--- .../Flights/FlightWebRequestUnitTest.cs | 1 + 7 files changed, 257 insertions(+), 44 deletions(-) diff --git a/JMayer.Example.ASPReact.Server/Airlines/Airline.cs b/JMayer.Example.ASPReact.Server/Airlines/Airline.cs index 6f50f42..ee9ff32 100644 --- a/JMayer.Example.ASPReact.Server/Airlines/Airline.cs +++ b/JMayer.Example.ASPReact.Server/Airlines/Airline.cs @@ -33,6 +33,16 @@ public class Airline : DataObject [RegularExpression("^\\d{3}$", ErrorMessage = "The number code must be 3 digits.")] public string NumberCode { get; set; } = ZeroNumberCode; + /// + /// The property gets/sets the id for the default sort destintion assigned to the airline. + /// + public long SortDestinationID { get; set; } + + /// + /// The property gets/sets the name of the default sort destination assigned to the airline. + /// + public string SortDestinationName { get; set; } = string.Empty; + /// /// The constant for the zero number code. /// @@ -59,6 +69,8 @@ public override void MapProperties(DataObject dataObject) IATA = airline.IATA; ICAO = airline.ICAO; NumberCode = airline.NumberCode; + SortDestinationID = airline.SortDestinationID; + SortDestinationName = airline.SortDestinationName; } } } diff --git a/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs b/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs index eeb9679..1af3aee 100644 --- a/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs +++ b/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs @@ -1,4 +1,5 @@ using JMayer.Data.Database.DataLayer.MemoryStorage; +using JMayer.Example.ASPReact.Server.SortDestinations; using System.ComponentModel.DataAnnotations; namespace JMayer.Example.ASPReact.Server.Airlines; @@ -9,9 +10,19 @@ namespace JMayer.Example.ASPReact.Server.Airlines; public class AirlineDataLayer : StandardCRUDDataLayer, IAirlineDataLayer { /// - /// The default constructor. + /// The property gets/sets the data layer for accessing the sort desintation data. /// - public AirlineDataLayer() => IsUniqueNameRequired = true; + private readonly ISortDestinationDataLayer _sortDestinationDataLayer; + + /// + /// The dependency injection constructor. + /// + /// + public AirlineDataLayer(ISortDestinationDataLayer sortDestinationDataLayer) + { + _sortDestinationDataLayer = sortDestinationDataLayer; + IsUniqueNameRequired = true; + } /// /// @@ -26,6 +37,11 @@ public override async Task> ValidateAsync(Airline dataObj validationResults.Add(new ValidationResult("The ICAO must be unique.", [nameof(Airline.ICAO)])); } + if (await _sortDestinationDataLayer.ExistAsync(obj => obj.Integer64ID == dataObject.SortDestinationID, cancellationToken) is false) + { + validationResults.Add(new ValidationResult($"The {dataObject.SortDestinationID} sort destination was not found in the data store.", [nameof(Airline.SortDestinationID)])); + } + if (dataObject.NumberCode is not Airline.ZeroNumberCode && await ExistAsync(obj => obj.Integer64ID != dataObject.Integer64ID && obj.NumberCode == dataObject.NumberCode, cancellationToken) is true) { validationResults.Add(new ValidationResult("The number code must be unique unless the code is 000.", [nameof(Airline.NumberCode)])); diff --git a/JMayer.Example.ASPReact.Server/Airlines/AirlineEqualityComparer.cs b/JMayer.Example.ASPReact.Server/Airlines/AirlineEqualityComparer.cs index 98c0fc4..f01a796 100644 --- a/JMayer.Example.ASPReact.Server/Airlines/AirlineEqualityComparer.cs +++ b/JMayer.Example.ASPReact.Server/Airlines/AirlineEqualityComparer.cs @@ -55,7 +55,8 @@ public bool Equals(Airline? x, Airline? y) && (_excludeID || x.Integer64ID == y.Integer64ID) && (_excludeLastEditedOn || x.LastEditedBy == y.LastEditedBy) && x.Name == y.Name - && x.NumberCode == y.NumberCode; + && x.NumberCode == y.NumberCode + && x.SortDestinationID == y.SortDestinationID; } /// diff --git a/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs b/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs index 27d4861..b2cc92d 100644 --- a/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs +++ b/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs @@ -18,7 +18,7 @@ public class FlightScheduleExampleBuilder /// /// The property gets/sets the data layer used to interact with airlines. /// - public IAirlineDataLayer AirlineDataLayer { get; init; } = new AirlineDataLayer(); + public IAirlineDataLayer AirlineDataLayer { get; init; } /// /// The property gets/sets the data layer used to ineract with the flights. @@ -35,11 +35,17 @@ public class FlightScheduleExampleBuilder /// public ISortDestinationDataLayer SortDestinationDataLayer { get; init; } = new SortDestinationDataLayer(); + /// + /// The constant for the southwest IATA code. + /// + private const string SouthwestIataCode = "WN"; + /// /// The default constructor. /// public FlightScheduleExampleBuilder() { + AirlineDataLayer = new AirlineDataLayer(SortDestinationDataLayer); FlightDataLayer = new FlightDataLayer(AirlineDataLayer, GateDataLayer, SortDestinationDataLayer); GenerateAirportCodes(); } @@ -49,9 +55,9 @@ public FlightScheduleExampleBuilder() /// public void Build() { + BuildSortDestinations(); BuildAirlines(); BuildGates(); - BuildSortDestinations(); BuildFlights(); } @@ -60,12 +66,16 @@ public void Build() /// private void BuildAirlines() { + List sortDestinations = SortDestinationDataLayer.GetAllAsync().Result; + _ = AirlineDataLayer.CreateAsync(new Airline() { IATA = "AA", ICAO = "AAL", Name = "American Airlines", NumberCode = "001", + SortDestinationID = sortDestinations[0].Integer64ID, + SortDestinationName = sortDestinations[0].Name ?? string.Empty, }); _ = AirlineDataLayer.CreateAsync(new Airline() { @@ -73,6 +83,8 @@ private void BuildAirlines() ICAO = "DAL", Name = "Delta Air Lines", NumberCode = "006", + SortDestinationID = sortDestinations[1].Integer64ID, + SortDestinationName = sortDestinations[1].Name ?? string.Empty, }); _ = AirlineDataLayer.CreateAsync(new Airline() { @@ -80,6 +92,8 @@ private void BuildAirlines() ICAO = "SWA", Name = "Southwest Airlines", NumberCode = "526", + SortDestinationID = sortDestinations[2].Integer64ID, + SortDestinationName = sortDestinations[2].Name ?? string.Empty, }); } @@ -92,9 +106,9 @@ private void BuildFlights() List gates = GateDataLayer.GetAllAsync().Result; List sortDestinations = SortDestinationDataLayer.GetAllAsync().Result; + bool useMU4 = false; int flightNumber = 1000; int gateIndex = 0; - int sortDestinationIndex = 0; TimeSpan departTime = new(4, 0, 0); TimeSpan operationalEnd = new(22, 0, 0); @@ -113,13 +127,12 @@ private void BuildFlights() GateID = gates[gateIndex].Integer64ID, GateName = gates[gateIndex].Name ?? string.Empty, Name = $"{airline.IATA}{flightNumber.ToString().PadLeft(4, '0')}", - SortDestinationID = sortDestinations[sortDestinationIndex].Integer64ID, - SortDestinationName = sortDestinations[sortDestinationIndex].Name ?? string.Empty, + SortDestinationID = airline.IATA == SouthwestIataCode && useMU4 ? sortDestinations[3].Integer64ID : airline.SortDestinationID, + SortDestinationName = airline.IATA == SouthwestIataCode && useMU4 ? sortDestinations[3].Name ?? string.Empty : airline.SortDestinationName, }); flightNumber++; gateIndex++; - sortDestinationIndex++; departTime = departTime.Add(TimeSpan.FromMinutes(10)); if (gateIndex > gates.Count - 1) @@ -127,9 +140,9 @@ private void BuildFlights() gateIndex = 0; } - if (sortDestinationIndex > sortDestinations.Count - 1) + if (airline.IATA == SouthwestIataCode) { - sortDestinationIndex = 0; + useMU4 = !useMU4; } } } diff --git a/TestProject/Airlines/AirlineEqualityComparerUnitTest.cs b/TestProject/Airlines/AirlineEqualityComparerUnitTest.cs index ee63ff3..eb508f4 100644 --- a/TestProject/Airlines/AirlineEqualityComparerUnitTest.cs +++ b/TestProject/Airlines/AirlineEqualityComparerUnitTest.cs @@ -37,6 +37,11 @@ public class AirlineEqualityComparerUnitTest /// private const string NumberCode = "999"; + /// + /// The constant for the sort destination ID. + /// + private const long SortDestinationID = 2; + /// /// The method verifies equality failure when two nulls are compared. /// @@ -149,12 +154,28 @@ public void VerifyFailureOneIsNull() LastEditedOn = DateTime.Now, Name = Name, NumberCode = NumberCode, + SortDestinationID = SortDestinationID, }; Assert.False(new AirlineEqualityComparer().Equals(airline, null)); Assert.False(new AirlineEqualityComparer().Equals(null, airline)); } + /// + /// The method verifies equality failure when the SortDestinationID property is different between the two objects. + /// + [Fact] + public void VerifyFailureSortDestinationID() + { + Airline airline1 = new() + { + SortDestinationID = SortDestinationID, + }; + Airline airline2 = new(); + + Assert.False(new AirlineEqualityComparer().Equals(airline1, airline2)); + } + /// /// The method verifies equality success when two objects (different references) are compared. /// @@ -171,6 +192,7 @@ public void VerifySuccess() LastEditedOn = DateTime.Now, Name = Name, NumberCode = NumberCode, + SortDestinationID = SortDestinationID, }; Airline airline2 = new(airline1); @@ -194,6 +216,7 @@ public void VerifySuccessExcludeCreatedOn() LastEditedOn = DateTime.Now, Name = Name, NumberCode = NumberCode, + SortDestinationID = SortDestinationID, }; Airline airline2 = new(airline1) { @@ -220,6 +243,7 @@ public void VerifySuccessExcludeID() LastEditedOn = DateTime.Now, Name = Name, NumberCode = NumberCode, + SortDestinationID = SortDestinationID, }; Airline airline2 = new(airline1) { @@ -246,6 +270,7 @@ public void VerifySuccessExcludeLastEditedOn() LastEditedOn = DateTime.Now, Name = Name, NumberCode = NumberCode, + SortDestinationID = SortDestinationID, }; Airline airline2 = new(airline1) { diff --git a/TestProject/Airlines/AirlineWebRequestUnitTest.cs b/TestProject/Airlines/AirlineWebRequestUnitTest.cs index 8eec3a4..9344af4 100644 --- a/TestProject/Airlines/AirlineWebRequestUnitTest.cs +++ b/TestProject/Airlines/AirlineWebRequestUnitTest.cs @@ -1,6 +1,7 @@ using JMayer.Data.Data; using JMayer.Data.HTTP.DataLayer; using JMayer.Example.ASPReact.Server.Airlines; +using JMayer.Example.ASPReact.Server.SortDestinations; using Microsoft.AspNetCore.Mvc.Testing; using System.Net; @@ -35,6 +36,11 @@ public class AirlineWebRequestUnitTest : IClassFixture private const string BadFormattedNumberCode = "9999"; + /// + /// The constant for a bad sort destination ID. + /// + private const long BadSortDestinationID = 99; + /// /// The constant for a default IATA code. /// @@ -45,12 +51,31 @@ public class AirlineWebRequestUnitTest : IClassFixture private const string DefaultICAOCode = "ZZZ"; + /// + /// The constant for the default sort destination ID. + /// + private const long DefaultSortDestinationID = 1; + /// /// The dependency injection constructor. /// /// The factory for the web application. public AirlineWebRequestUnitTest(WebApplicationFactory factory) => _factory = factory; + /// + /// The method returns the sort destination. + /// + /// The name to search for. + /// The sort destination or null if not found. + private async Task GetSortDestinationAsync(string name) + { + HttpClient httpClient = _factory.CreateClient(); + SortDestinations.SortDestinationDataLayer dataLayer = new(httpClient); + + List? sortDestinations = await dataLayer.GetAllAsync(); + return sortDestinations?.FirstOrDefault(obj => obj.Name == name); + } + /// /// The method verifies the HTTP data layer can request an airline to be created by the server and the server can successfully process the request. /// @@ -59,16 +84,24 @@ public class AirlineWebRequestUnitTest : IClassFixtureThe IATA code assigned to the airline by the IATA Organization. /// The ICAO code assigned to the airline by the International Aviation Organization. /// The number code assigned to the airline by the IATA Organization. + /// The default sort destination for the airline. /// A Task object for the async. [Theory] - [InlineData("Alaska Airlines", "Alaska", "AS", "ASA", "027")] - [InlineData("Allegiant Air", "Allegiant", "G4", "AAY", "268")] - [InlineData("Jetblue Airways", "Jetblue", "B6", "JBU", "279")] - public async Task VerifyAddAirline(string name, string description, string iata, string icao, string numberCode) + [InlineData("Alaska Airlines", "Alaska", "AS", "ASA", "027", "MU1")] + [InlineData("Allegiant Air", "Allegiant", "G4", "AAY", "268", "MU2")] + [InlineData("Jetblue Airways", "Jetblue", "B6", "JBU", "279", "MU3")] + public async Task VerifyAddAirline(string name, string description, string iata, string icao, string numberCode, string sortDestinationName) { HttpClient httpClient = _factory.CreateClient(); AirlineDataLayer dataLayer = new(httpClient); + SortDestination? sortDestination = await GetSortDestinationAsync(sortDestinationName); + + if (sortDestination is null) + { + Assert.Fail("Failed to retrieve the sort destination."); + } + Airline airline = new() { Description = description, @@ -76,6 +109,8 @@ public async Task VerifyAddAirline(string name, string description, string iata, ICAO = icao, Name = name, NumberCode = numberCode, + SortDestinationID = sortDestination.Integer64ID, + SortDestinationName = sortDestination.Name ?? string.Empty, }; OperationResult operationResult = await dataLayer.CreateAsync(airline); @@ -100,6 +135,7 @@ public async Task VerifyAddAirlineBadIATAFailure() ICAO = DefaultICAOCode, NumberCode = Airline.ZeroNumberCode, Name = "Add Bad IATA Code Test", + SortDestinationID = DefaultSortDestinationID, }); //The operation must have failed. @@ -133,6 +169,7 @@ public async Task VerifyAddAirlineBadICAOFailure() ICAO = BadFormattedICAOCode, NumberCode = Airline.ZeroNumberCode, Name = "Add Bad ICAO Code Test", + SortDestinationID = DefaultSortDestinationID, }); //The operation must have failed. @@ -166,6 +203,7 @@ public async Task VerifyAddAirlineBadNumberCodeFailure() ICAO = DefaultICAOCode, NumberCode = BadFormattedNumberCode, Name = "Add Bad Number Code Test", + SortDestinationID = DefaultSortDestinationID, }); //The operation must have failed. @@ -199,6 +237,7 @@ public async Task VerifyAddDuplicateAirlineICAOFailure() ICAO = "ZZC", NumberCode = Airline.ZeroNumberCode, Name = "Add Duplicate ICAO Test 1", + SortDestinationID = DefaultSortDestinationID, }); Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); @@ -208,6 +247,7 @@ public async Task VerifyAddDuplicateAirlineICAOFailure() ICAO = "ZZC", NumberCode = Airline.ZeroNumberCode, Name = "Add Duplicate ICAO Test New", + SortDestinationID = DefaultSortDestinationID, }); //The operation must have failed. @@ -241,6 +281,7 @@ public async Task VerifyAddDuplicateAirlineNameFailure() ICAO = "ZZA", NumberCode = Airline.ZeroNumberCode, Name = "Add Duplicate Name Test", + SortDestinationID = DefaultSortDestinationID, }); Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); @@ -250,6 +291,7 @@ public async Task VerifyAddDuplicateAirlineNameFailure() ICAO = "ZZB", NumberCode = Airline.ZeroNumberCode, Name = "Add Duplicate Name Test", + SortDestinationID = DefaultSortDestinationID, }); //The operation must have failed. @@ -283,6 +325,7 @@ public async Task VerifyAddDuplicateAirlineNumberCodeFailure() ICAO = "ZZD", NumberCode = "999", Name = "Add Duplicate Number Code Test 1", + SortDestinationID = DefaultSortDestinationID, }); Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); @@ -292,6 +335,7 @@ public async Task VerifyAddDuplicateAirlineNumberCodeFailure() ICAO = "ZZE", NumberCode = "999", Name = "Add Duplicate Number Code Test New", + SortDestinationID = DefaultSortDestinationID, }); //The operation must have failed. @@ -309,6 +353,40 @@ public async Task VerifyAddDuplicateAirlineNumberCodeFailure() Assert.Equal("The number code must be unique unless the code is 000.", operationResult.ValidationErrors[nameof(Airline.NumberCode)][0]); } + /// + /// The method verifies the server will return a failure if the sort destination ID doesn't exist when adding a new airline. + /// + /// A Task object for the async. + [Fact] + public async Task VerifyAddAirlineSortDestinationNotFoundFailure() + { + HttpClient httpClient = _factory.CreateClient(); + AirlineDataLayer dataLayer = new(httpClient); + + OperationResult operationResult = await dataLayer.CreateAsync(new Airline() + { + IATA = "ZF", + ICAO = "ZZF", + NumberCode = "999", + Name = "Add Airline Sort Destination Not Found Test", + SortDestinationID = BadSortDestinationID, + }); + + //The operation must have failed. + Assert.False(operationResult.IsSuccessStatusCode, "The operation should have failed."); + + //No airline was returned. + Assert.Null(operationResult.DataObject); + + //A bad request status was returned. + Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); + + //The correct error was returned. + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.SortDestinationID)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.SortDestinationID)]); + Assert.Equal($"The {BadSortDestinationID} sort destination was not found in the data store.", operationResult.ValidationErrors[nameof(Airline.SortDestinationID)][0]); + } + /// /// The method verifies the HTTP data layer can request the count from the server and the server can successfully process the request. /// @@ -335,10 +413,11 @@ public async Task VerifyDeleteAirline() OperationResult operationResult = await dataLayer.CreateAsync(new Airline() { - IATA = "ZF", - ICAO = "ZZF", + IATA = "ZG", + ICAO = "ZZG", Name = "Delete Airline Test", NumberCode = Airline.ZeroNumberCode, + SortDestinationID = DefaultSortDestinationID, }); if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) @@ -412,10 +491,11 @@ public async Task VerifyGetSingleAirlineWithId() Airline airline = new() { - IATA = "ZG", - ICAO = "ZZG", + IATA = "ZH", + ICAO = "ZZH", Name = "Get Single Airline Test", NumberCode = Airline.ZeroNumberCode, + SortDestinationID = DefaultSortDestinationID, }; OperationResult operationResult = await dataLayer.CreateAsync(airline); @@ -443,16 +523,24 @@ public async Task VerifyGetSingleAirlineWithId() /// The new ICAO code assigned to the airline by the International Aviation Organization. /// The orginal number code assigned to the airline by the IATA Organization. /// The new number code assigned to the airline by the IATA Organization. + /// The default sort destination for the airline. /// A Task object for the async. [Theory] - [InlineData("Air Canada", "Spirit Airlines", "Canada", "Spirit", "AC", "NK", "ACA", "NKS", "014", "487")] - [InlineData("British Airways", "Silver Airways", "British", "Silver", "BA", "3M", "BAW", "SIL", "125", "449")] - [InlineData("Flair Airlines", "Frontier Airlines", "Flair", "Frontier", "F8", "F9", "FLE", "FFT", "418", "422")] - public async Task VerifyUpdateAirline(string originalName, string newName, string originalDescription, string newDescription, string originalIata, string newIata, string originalIcao, string newIcao, string originalNumberCode, string newNumberCode) + [InlineData("Air Canada", "Spirit Airlines", "Canada", "Spirit", "AC", "NK", "ACA", "NKS", "014", "487", "MU2")] + [InlineData("British Airways", "Silver Airways", "British", "Silver", "BA", "3M", "BAW", "SIL", "125", "449", "MU3")] + [InlineData("Flair Airlines", "Frontier Airlines", "Flair", "Frontier", "F8", "F9", "FLE", "FFT", "418", "422", "MU4")] + public async Task VerifyUpdateAirline(string originalName, string newName, string originalDescription, string newDescription, string originalIata, string newIata, string originalIcao, string newIcao, string originalNumberCode, string newNumberCode, string sortDestinationName) { HttpClient httpClient = _factory.CreateClient(); AirlineDataLayer dataLayer = new(httpClient); + SortDestination? sortDestination = await GetSortDestinationAsync(sortDestinationName); + + if (sortDestination is null) + { + Assert.Fail("Failed to retrieve the sort destination."); + } + OperationResult operationResult = await dataLayer.CreateAsync(new Airline() { Description = originalDescription, @@ -460,6 +548,7 @@ public async Task VerifyUpdateAirline(string originalName, string newName, strin ICAO = originalIcao, Name = originalName, NumberCode = originalNumberCode, + SortDestinationID = DefaultSortDestinationID, }); if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline createdAirline) @@ -471,6 +560,8 @@ public async Task VerifyUpdateAirline(string originalName, string newName, strin ICAO = newIcao, Name = newName, NumberCode = newNumberCode, + SortDestinationID = sortDestination.Integer64ID, + SortDestinationName = sortDestination.Name ?? string.Empty, }; operationResult = await dataLayer.UpdateAsync(updatedAirline); @@ -496,10 +587,11 @@ public async Task VerifyUpdateAirlineBadIATAFailure() OperationResult operationResult = await dataLayer.CreateAsync(new Airline() { - IATA = "ZH", - ICAO = "ZZH", + IATA = "ZI", + ICAO = "ZZI", NumberCode = Airline.ZeroNumberCode, Name = "Update Bad IATA Code Test", + SortDestinationID = DefaultSortDestinationID, }); if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) @@ -539,10 +631,11 @@ public async Task VerifyUpdateAirlineBadICAOFailure() OperationResult operationResult = await dataLayer.CreateAsync(new Airline() { - IATA = "ZI", - ICAO = "ZZI", + IATA = "ZJ", + ICAO = "ZZJ", NumberCode = Airline.ZeroNumberCode, Name = "Update Bad ICAO Code Test", + SortDestinationID = DefaultSortDestinationID, }); if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) @@ -582,10 +675,11 @@ public async Task VerifyUpdateAirlineBadNumberCodeFailure() OperationResult operationResult = await dataLayer.CreateAsync(new Airline() { - IATA = "ZJ", - ICAO = "ZZJ", + IATA = "ZK", + ICAO = "ZZK", NumberCode = Airline.ZeroNumberCode, Name = "Update Bad Number Code Test", + SortDestinationID = DefaultSortDestinationID, }); if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) @@ -625,10 +719,11 @@ public async Task VerifyUpdateAirlineOldDataConflict() OperationResult operationResult = await dataLayer.CreateAsync(new Airline() { - IATA = "ZK", - ICAO = "ZZK", + IATA = "ZL", + ICAO = "ZZL", Name = "Old Data Airline Test", NumberCode = Airline.ZeroNumberCode, + SortDestinationID = DefaultSortDestinationID, }); if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline firstAirline) @@ -658,11 +753,11 @@ public async Task VerifyUpdateAirlineOldDataConflict() } /// - /// The method verifies the server will return a failure if the airline ICAO already exists when adding updating an airline. + /// The method verifies the server will return a failure if the sort destination ID doesn't exist when adding a new airline. /// /// A Task object for the async. [Fact] - public async Task VerifyUpdateDuplicateAirlineICAOFailure() + public async Task VerifyUpdateAirlineSortDestinationNotFoundFailure() { HttpClient httpClient = _factory.CreateClient(); AirlineDataLayer dataLayer = new(httpClient); @@ -671,22 +766,68 @@ public async Task VerifyUpdateDuplicateAirlineICAOFailure() { IATA = "ZM", ICAO = "ZZM", + Name = "Update Airline Sort Destination Not Found Test", + NumberCode = Airline.ZeroNumberCode, + SortDestinationID = DefaultSortDestinationID, + }); + + if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) + { + airline.SortDestinationID = BadSortDestinationID; + operationResult = await dataLayer.UpdateAsync(airline); + + //The operation must have failed. + Assert.False(operationResult.IsSuccessStatusCode, "The operation should have failed."); + + //No airline was returned. + Assert.Null(operationResult.DataObject); + + //A bad request status was returned. + Assert.Equal(HttpStatusCode.BadRequest, operationResult.StatusCode); + + //The correct error was returned. + Assert.Contains(operationResult.ValidationErrors, obj => obj.Key == nameof(Airline.SortDestinationID)); + Assert.Single(operationResult.ValidationErrors[nameof(Airline.SortDestinationID)]); + Assert.Equal($"The {BadSortDestinationID} sort destination was not found in the data store.", operationResult.ValidationErrors[nameof(Airline.SortDestinationID)][0]); + } + else + { + Assert.Fail("Failed to create the airline."); + } + } + + /// + /// The method verifies the server will return a failure if the airline ICAO already exists when adding updating an airline. + /// + /// A Task object for the async. + [Fact] + public async Task VerifyUpdateDuplicateAirlineICAOFailure() + { + HttpClient httpClient = _factory.CreateClient(); + AirlineDataLayer dataLayer = new(httpClient); + + OperationResult operationResult = await dataLayer.CreateAsync(new Airline() + { + IATA = "ZN", + ICAO = "ZZN", NumberCode = Airline.ZeroNumberCode, Name = "Update Duplicate ICAO Test 1", + SortDestinationID = DefaultSortDestinationID, }); Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); operationResult = await dataLayer.CreateAsync(new Airline() { - IATA = "ZN", - ICAO = "ZZN", + IATA = "ZO", + ICAO = "ZZO", NumberCode = Airline.ZeroNumberCode, Name = "Update Duplicate ICAO Test 2", + SortDestinationID = DefaultSortDestinationID, }); if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) { - airline.ICAO = "ZZM"; + airline.ICAO = "ZZN"; operationResult = await dataLayer.UpdateAsync(airline); //The operation must have failed. @@ -721,19 +862,21 @@ public async Task VerifyUpdateDuplicateAirlineNameFailure() OperationResult operationResult = await dataLayer.CreateAsync(new Airline() { - IATA = "ZO", - ICAO = "ZZO", + IATA = "ZP", + ICAO = "ZZP", NumberCode = Airline.ZeroNumberCode, Name = "Update Duplicate Name Test 1", + SortDestinationID = DefaultSortDestinationID, }); Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); operationResult = await dataLayer.CreateAsync(new Airline() { - IATA = "ZP", - ICAO = "ZZP", + IATA = "ZQ", + ICAO = "ZZQ", NumberCode = Airline.ZeroNumberCode, Name = "Update Duplicate Name Test 2", + SortDestinationID = DefaultSortDestinationID, }); if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) @@ -773,19 +916,21 @@ public async Task VerifyUpdateDuplicateAirlineNumberCodeFailure() OperationResult operationResult = await dataLayer.CreateAsync(new Airline() { - IATA = "ZQ", - ICAO = "ZZQ", + IATA = "ZR", + ICAO = "ZZR", NumberCode = "997", Name = "Update Duplicate Number Code Test Test 1", + SortDestinationID = DefaultSortDestinationID, }); Assert.True(operationResult.IsSuccessStatusCode, "Failed to create the first airline."); operationResult = await dataLayer.CreateAsync(new Airline() { - IATA = "ZR", - ICAO = "ZZR", + IATA = "ZS", + ICAO = "ZZS", NumberCode = "998", Name = "Update Duplicate Number Code Test 2", + SortDestinationID = DefaultSortDestinationID, }); if (operationResult.IsSuccessStatusCode && operationResult.DataObject is Airline airline) diff --git a/TestProject/Flights/FlightWebRequestUnitTest.cs b/TestProject/Flights/FlightWebRequestUnitTest.cs index 0679f81..cb5d420 100644 --- a/TestProject/Flights/FlightWebRequestUnitTest.cs +++ b/TestProject/Flights/FlightWebRequestUnitTest.cs @@ -169,6 +169,7 @@ public class FlightWebRequestUnitTest : IClassFixture Date: Thu, 6 Nov 2025 13:59:31 -0500 Subject: [PATCH 08/16] Added sort destination to airline; frontend --- .../airline/AirlineAddEditDialog.jsx | 45 ++++++++++++++++++- .../src/components/airline/AirlinePage.jsx | 1 + .../src/datalayers/AirlineDataLayer.jsx | 2 + 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/jmayer.example.aspreact.client/src/components/airline/AirlineAddEditDialog.jsx b/jmayer.example.aspreact.client/src/components/airline/AirlineAddEditDialog.jsx index e1c3f4e..abdc431 100644 --- a/jmayer.example.aspreact.client/src/components/airline/AirlineAddEditDialog.jsx +++ b/jmayer.example.aspreact.client/src/components/airline/AirlineAddEditDialog.jsx @@ -1,8 +1,10 @@ import React, { useState, useEffect } from 'react'; import { Button } from 'primereact/button'; import { Dialog } from 'primereact/dialog'; +import { Dropdown } from 'primereact/dropdown'; import { InputText } from 'primereact/inputtext'; import { useAirlineDataLayer } from '../../datalayers/AirlineDataLayer.jsx'; +import { useSortDestinationDataLayer } from '../../datalayers/SortDestinationDataLayer.jsx'; //The function returns the dialog for adding or updating an airline. //@param {object} props The properties accepted by the component. @@ -16,8 +18,16 @@ export default function AirlineAddEditDialog({ newRecord, airline, setAirline, v const [icaoValidationError, setIcaoValidationError] = useState(''); const [nameValidationError, setNameValidationError] = useState(''); const [numberCodeValidationError, setNumberCodeValidationError] = useState(''); + const [sortDestinationValidationError, setSortDestinationValidationError] = useState(''); const { addAirline, addAirlineServerSideResult, addAirlineSuccess, updateAirline, updateAirlineServerSideResult, updateAirlineSuccess } = useAirlineDataLayer(); + const { sortDestinations, getSortDestinations } = useSortDestinationDataLayer(); + //Load the destinations when the component mounts. + useEffect(() => { + getSortDestinations(); + }, []); + + //Handle state changes based on add/update operations. useEffect(() => { //Hide the dialog on a successful add or update. if (addAirlineSuccess || updateAirlineSuccess) { @@ -40,6 +50,7 @@ export default function AirlineAddEditDialog({ newRecord, airline, setAirline, v setIcaoValidationError(''); setNameValidationError(''); setNumberCodeValidationError(''); + setSortDestinationValidationError(''); hide(); }; @@ -49,8 +60,9 @@ export default function AirlineAddEditDialog({ newRecord, airline, setAirline, v const icoaPass = validateICOA(); const namePass = validateName(); const numberCodePass = validateNumberCode(); + const sortDestinationPass = validateSortDestination(); - return iataPass && icoaPass && namePass && numberCodePass; + return iataPass && icoaPass && namePass && numberCodePass && sortDestinationPass; }; //The function processes the server side validation result and sets any validation errors. @@ -70,6 +82,9 @@ export default function AirlineAddEditDialog({ newRecord, airline, setAirline, v case 'NumberCode': setNumberCodeValidationError(error.errorMessage); break; + case 'SortDestinationID': + setSortDestinationValidationError(error.errorMessage); + break; } } }; @@ -119,6 +134,16 @@ export default function AirlineAddEditDialog({ newRecord, airline, setAirline, v }); }; + //The function updates the sort destination field with the value selected by the user. + //@param {sortDestination} sortDestination The sort desstination the user selected. + const setSortDestination = (sortDestination) => { + setAirline({ + ...airline, + sortDestinationID: sortDestination.integer64ID, + sortDestinationName: sortDestination.name + }); + } + //The function returns if the airline's IATA field passed validation. const validateIATA = () => { const pattern = /^[A-Z0-9]{2}$/; @@ -183,6 +208,19 @@ export default function AirlineAddEditDialog({ newRecord, airline, setAirline, v return !error; }; + //The function returns if the airline's sort destination field passed validation. + const validateSortDestination = () => { + let error = ''; + + if (airline.sortDestinationID === 0) { + error = 'The sort destination is required.'; + } + + setSortDestinationValidationError(error); + + return !error; + }; + //The footer for the dialog. const footer = ( @@ -218,6 +256,11 @@ export default function AirlineAddEditDialog({ newRecord, airline, setAirline, v validateNumberCode()} onChange={(e) => setNumberCode(e.target.value)} /> {numberCodeValidationError && {numberCodeValidationError}} +
+ + element.integer64ID == airline.sortDestinationID)} options={sortDestinations} optionLabel="name" filter placeholder="Select a default sort destination for the airline's baggage" onBlur={(e) => validateSortDestination()} onChange={(e) => setSortDestination(e.value)} /> + {sortDestinationValidationError && {sortDestinationValidationError}} +
); diff --git a/jmayer.example.aspreact.client/src/components/airline/AirlinePage.jsx b/jmayer.example.aspreact.client/src/components/airline/AirlinePage.jsx index f72ea5e..7ecf1d1 100644 --- a/jmayer.example.aspreact.client/src/components/airline/AirlinePage.jsx +++ b/jmayer.example.aspreact.client/src/components/airline/AirlinePage.jsx @@ -86,6 +86,7 @@ export default function AirlinePage() { + diff --git a/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx b/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx index 46763a4..4389fab 100644 --- a/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx +++ b/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx @@ -9,6 +9,8 @@ const initialAirline = { iata: '', icao: '', numberCode: '', + sortDestinationID: 0, + sortDestinationName: '', }; export default initialAirline; From 85c67d49326a6b1c23d7c4c72d809736df027b86 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Thu, 6 Nov 2025 14:35:14 -0500 Subject: [PATCH 09/16] ValidationProblemDetails now returned from server --- .../airline/AirlineAddEditDialog.jsx | 36 +++++++++---------- .../flightSchedule/FlightAddEditDialog.jsx | 28 +++++++-------- .../src/datalayers/AirlineDataLayer.jsx | 31 +++++++++------- .../src/datalayers/DataLayerHelper.jsx | 25 ------------- .../src/datalayers/FlightDataLayer.jsx | 31 +++++++++------- 5 files changed, 68 insertions(+), 83 deletions(-) delete mode 100644 jmayer.example.aspreact.client/src/datalayers/DataLayerHelper.jsx diff --git a/jmayer.example.aspreact.client/src/components/airline/AirlineAddEditDialog.jsx b/jmayer.example.aspreact.client/src/components/airline/AirlineAddEditDialog.jsx index abdc431..7ea8b9d 100644 --- a/jmayer.example.aspreact.client/src/components/airline/AirlineAddEditDialog.jsx +++ b/jmayer.example.aspreact.client/src/components/airline/AirlineAddEditDialog.jsx @@ -14,13 +14,13 @@ import { useSortDestinationDataLayer } from '../../datalayers/SortDestinationDat //@param {bool} props.visible Used to control if the dialog is visible or not. //@param {function} props.hide Used to hide the dialog. export default function AirlineAddEditDialog({ newRecord, airline, setAirline, visible, hide }) { + const { addAirline, addAirlineValidationProblemDetails, addAirlineSuccess, updateAirline, updateAirlineValidationProblemDetails, updateAirlineSuccess } = useAirlineDataLayer(); + const { sortDestinations, getSortDestinations } = useSortDestinationDataLayer(); const [iataValidationError, setIataValidationError] = useState(''); const [icaoValidationError, setIcaoValidationError] = useState(''); const [nameValidationError, setNameValidationError] = useState(''); const [numberCodeValidationError, setNumberCodeValidationError] = useState(''); const [sortDestinationValidationError, setSortDestinationValidationError] = useState(''); - const { addAirline, addAirlineServerSideResult, addAirlineSuccess, updateAirline, updateAirlineServerSideResult, updateAirlineSuccess } = useAirlineDataLayer(); - const { sortDestinations, getSortDestinations } = useSortDestinationDataLayer(); //Load the destinations when the component mounts. useEffect(() => { @@ -34,15 +34,15 @@ export default function AirlineAddEditDialog({ newRecord, airline, setAirline, v hide(); } - //Handle displays server side errors. - if (addAirlineServerSideResult !== null) { - processServerSideValidationResult(addAirlineServerSideResult); + //Handle displayings server side errors. + if (addAirlineValidationProblemDetails !== null) { + processValidationProblemDetails(addAirlineValidationProblemDetails); } - else if (updateAirlineServerSideResult !== null) { - processServerSideValidationResult(updateAirlineServerSideResult); + else if (updateAirlineValidationProblemDetails !== null) { + processValidationProblemDetails(updateAirlineValidationProblemDetails); } - }, [addAirlineServerSideResult, addAirlineSuccess, updateAirlineServerSideResult, updateAirlineSuccess]); + }, [addAirlineValidationProblemDetails, addAirlineSuccess, updateAirlineValidationProblemDetails, updateAirlineSuccess]); //The function clears the validation and closes the dialog. const closeDialog = () => { @@ -65,25 +65,25 @@ export default function AirlineAddEditDialog({ newRecord, airline, setAirline, v return iataPass && icoaPass && namePass && numberCodePass && sortDestinationPass; }; - //The function processes the server side validation result and sets any validation errors. - //@param {object} serverSideValidationResult What the server found wrong with the user input. - const processServerSideValidationResult = (serverSideValidationResult) => { - for (const error of serverSideValidationResult.errors) { - switch (error.propertyName) { + //The function processes the validation problem details returned by the server. + //@param {object} result The validation problem details returned by the server. + const processValidationProblemDetails = (details) => { + for (const key in details.errors) { + switch (key) { case 'Name': - setNameValidationError(error.errorMessage); + setNameValidationError(details.errors[key][0]); break; case 'IATA': - setIataValidationError(error.errorMessage); + setIataValidationError(details.errors[key][0]); break; case 'ICAO': - setIcaoValidationError(error.errorMessage); + setIcaoValidationError(details.errors[key][0]); break; case 'NumberCode': - setNumberCodeValidationError(error.errorMessage); + setNumberCodeValidationError(details.errors[key][0]); break; case 'SortDestinationID': - setSortDestinationValidationError(error.errorMessage); + setSortDestinationValidationError(details.errors[key][0]); break; } } diff --git a/jmayer.example.aspreact.client/src/components/flightSchedule/FlightAddEditDialog.jsx b/jmayer.example.aspreact.client/src/components/flightSchedule/FlightAddEditDialog.jsx index c7241bb..21e7e5b 100644 --- a/jmayer.example.aspreact.client/src/components/flightSchedule/FlightAddEditDialog.jsx +++ b/jmayer.example.aspreact.client/src/components/flightSchedule/FlightAddEditDialog.jsx @@ -17,7 +17,7 @@ import { useSortDestinationDataLayer } from '../../datalayers/SortDestinationDat //@param {function} props.hide Used to hide the dialog. export default function FlightAddEditDialog({ newRecord, flight, setFlight, visible, hide }) { const { airlines, getAirlines } = useAirlineDataLayer(); - const { addFlight, addFlightServerSideResult, addFlightSuccess, updateFlight, updateFlightServerSideResult, updateFlightSuccess } = useFlightDataLayer(); + const { addFlight, addFlightValidationProblemDetails, addFlightSuccess, updateFlight, updateFlightValidationProblemDetails, updateFlightSuccess } = useFlightDataLayer(); const { gates, getGates } = useGateDataLayer(); const { sortDestinations, getSortDestinations } = useSortDestinationDataLayer(); const [airlineValidationError, setAirlineValidationError] = useState(''); @@ -51,15 +51,15 @@ export default function FlightAddEditDialog({ newRecord, flight, setFlight, visi hide(); } - //Handle displays server side errors. - if (addFlightServerSideResult !== null) { - processServerSideValidationResult(addFlightServerSideResult); + //Handle displayings server side errors. + if (addFlightValidationProblemDetails !== null) { + processValidationProblemDetails(addFlightValidationProblemDetails); } - else if (updateFlightServerSideResult !== null) { - processServerSideValidationResult(updateFlightServerSideResult); + else if (updateFlightValidationProblemDetails !== null) { + processValidationProblemDetails(updateFlightValidationProblemDetails); } - }, [addFlightServerSideResult, addFlightSuccess, updateFlightServerSideResult, updateFlightSuccess]); + }, [addFlightValidationProblemDetails, addFlightSuccess, updateFlightValidationProblemDetails, updateFlightSuccess]); //The function clears the validation and closes the dialog. const closeDialog = () => { @@ -82,16 +82,16 @@ export default function FlightAddEditDialog({ newRecord, flight, setFlight, visi return airlinePass && destinationPass && gatePass && flightNumberPass && sortDestinaitonPass; }; - //The function processes the server side validation result and sets any validation errors. - //@param {object} serverSideValidationResult What the server found wrong with the user input. - const processServerSideValidationResult = (serverSideValidationResult) => { - for (const error of serverSideValidationResult.errors) { - switch (error.propertyName) { + //The function processes the validation problem details returned by the server. + //@param {object} result The validation problem details returned by the server. + const processValidationProblemDetails = (details) => { + for (const key in details.errors) { + switch (key) { case 'Destination': - setDestinationValidationError(error.errorMessage); + setDestinationValidationError(details.errors[key][0]); break; case 'FlightNumber': - setFlightNumberValidationError(error.errorMessage); + setFlightNumberValidationError(details.errors[key][0]); break; } } diff --git a/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx b/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx index 4389fab..08aef90 100644 --- a/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx +++ b/jmayer.example.aspreact.client/src/datalayers/AirlineDataLayer.jsx @@ -1,6 +1,5 @@ import { useState } from 'react'; import { useError } from '../components/errorDialog/ErrorProvider.jsx'; -import { shapeValidationResult } from './DataLayerHelper.jsx'; //Defines the initial airline object. const initialAirline = { @@ -19,10 +18,10 @@ export function useAirlineDataLayer() { const { showError } = useError(); const [airlines, setAirlines] = useState([]); const [addAirlineSuccess, setAddAirlineSuccess] = useState(false); - const [addAirlineServerSideResult, setAddAirlineServerSideResult] = useState(null); + const [addAirlineValidationProblemDetails, setAddAirlineValidationProblemDetails] = useState(null); const [deleteAirlineSuccess, setDeleteAirlineSuccess] = useState(false); const [updateAirlineSuccess, setUpdateAirlineSuccess] = useState(false); - const [updateAirlineServerSideResult, setUpdateAirlineServerSideResult] = useState(null); + const [updateAirlineValidationProblemDetails, setUpdateAirlineValidationProblemDetails] = useState(null); //The function adds an airline to the server. //@param {object} airline The airline to add. @@ -40,8 +39,11 @@ export function useAirlineDataLayer() { if (response.ok) { setAddAirlineSuccess(true); } - else if (response.status == 400) { - response.json().then(serverSideValidationResult => setAddAirlineServerSideResult(shapeValidationResult(serverSideValidationResult))); + else if (response.status === 400) { + response.json().then(validationProblemDetails => setAddAirlineValidationProblemDetails(validationProblemDetails)); + } + else if (response.status === 500) { + response.json().then(problemDetails => showError(problemDetails.detail)); } else { showError('Failed to create the airline because of an error on the server.'); @@ -52,11 +54,11 @@ export function useAirlineDataLayer() { //The function clears the states before an operation. const clearStates = () => { - setAddAirlineServerSideResult(null); setAddAirlineSuccess(false); + setAddAirlineValidationProblemDetails(null); setDeleteAirlineSuccess(false); - setUpdateAirlineServerSideResult(null); setUpdateAirlineSuccess(false); + setUpdateAirlineValidationProblemDetails(null); }; //The function deletes an airline from the server. @@ -71,6 +73,9 @@ export function useAirlineDataLayer() { if (response.ok) { setDeleteAirlineSuccess(true); } + else if (response.status === 500) { + response.json().then(problemDetails => showError(problemDetails.detail)); + } else { showError('Failed to delete the airline because of an error on the server.'); } @@ -102,11 +107,11 @@ export function useAirlineDataLayer() { if (response.ok) { setUpdateAirlineSuccess(true); } - else if (response.status == 400) { - response.json().then(serverSideValidationResult => setUpdateAirlineServerSideResult(shapeValidationResult(serverSideValidationResult))); + else if (response.status === 400) { + response.json().then(validationProblemDetails => setUpdateAirlineValidationProblemDetails(validationProblemDetails)); } - else if (response.status == 409) { - showError('The submitted data was detected to be out of date; please refresh and try again.'); + else if (response.status == 409 || response.status === 500) { + response.json().then(problemDetails => showError(problemDetails.detail)); } else { showError('Failed to update the airline because of an error on the server.'); @@ -119,12 +124,12 @@ export function useAirlineDataLayer() { airlines, getAirlines, addAirline, - addAirlineServerSideResult, + addAirlineValidationProblemDetails, addAirlineSuccess, deleteAirline, deleteAirlineSuccess, updateAirline, - updateAirlineServerSideResult, + updateAirlineValidationProblemDetails, updateAirlineSuccess }; }; \ No newline at end of file diff --git a/jmayer.example.aspreact.client/src/datalayers/DataLayerHelper.jsx b/jmayer.example.aspreact.client/src/datalayers/DataLayerHelper.jsx deleted file mode 100644 index dfd25d7..0000000 --- a/jmayer.example.aspreact.client/src/datalayers/DataLayerHelper.jsx +++ /dev/null @@ -1,25 +0,0 @@ -//The function shapes the validation result into a C# ServerSideValidationResult object. This is done -//because the server can send a ValidationProblemDetails object instead of a ServerSideValidationResult object; -//ASP.NET core checks data annotations and sends a ValidationProblemDetails object on failure. -export function shapeValidationResult(result) { - if (!Array.isArray(result.errors)) { - let newResult = { - errors: [] - }; - - //errors will be a dictionary where the property name is the key and the value is a list of error messages. - //The loop will convert the dictionary into an array of objects where each object is a property name and error message. - for (let key in result.errors) { - for (const errorMessage of result.errors[key]) { - newResult.errors.push({ - propertyName: key, - errorMessage: errorMessage - }); - } - } - - result = newResult; - } - - return result; -} \ No newline at end of file diff --git a/jmayer.example.aspreact.client/src/datalayers/FlightDataLayer.jsx b/jmayer.example.aspreact.client/src/datalayers/FlightDataLayer.jsx index 8675dd4..a5cdb1a 100644 --- a/jmayer.example.aspreact.client/src/datalayers/FlightDataLayer.jsx +++ b/jmayer.example.aspreact.client/src/datalayers/FlightDataLayer.jsx @@ -1,6 +1,5 @@ import { useState } from 'react'; import { useError } from '../components/errorDialog/ErrorProvider.jsx'; -import { shapeValidationResult } from './DataLayerHelper.jsx'; //Defines the initial flight object. const initialFlight = { @@ -22,11 +21,11 @@ export default initialFlight; export function useFlightDataLayer() { const { showError } = useError(); const [flights, setFlights] = useState([]); - const [addFlightServerSideResult, setAddFlightServerSideResult] = useState(null); const [addFlightSuccess, setAddFlightSuccess] = useState(false); + const [addFlightValidationProblemDetails, setAddFlightValidationProblemDetails] = useState(null); const [deleteFlightSuccess, setDeleteFlightSuccess] = useState(false); - const [updateFlightServerSideResult, setUpdateFlightServerSideResult] = useState(null); const [updateFlightSuccess, setUpdateFlightSuccess] = useState(false); + const [updateFlightValidationProblemDetails, setUpdateFlightValidationProblemDetails] = useState(null); //The function adds a flight to the server. //@param {object} flight The flight to add. @@ -46,8 +45,11 @@ export function useFlightDataLayer() { if (response.ok) { setAddFlightSuccess(true); } - else if (response.status == 400) { - response.json().then(serverSideValidationResult => setAddFlightServerSideResult(shapeValidationResult(serverSideValidationResult))); + else if (response.status === 400) { + response.json().then(validationProblemDetails => setAddFlightValidationProblemDetails(validationProblemDetails)); + } + else if (response.status === 500) { + response.json().then(problemDetails => showError(problemDetails.detail)); } else { showError('Failed to create the flight because of an error on the server.'); @@ -58,11 +60,11 @@ export function useFlightDataLayer() { //The function clears the states before an operation. const clearStates = () => { - setAddFlightServerSideResult(null); setAddFlightSuccess(false); + setAddFlightValidationProblemDetails(null); setDeleteFlightSuccess(false); - setUpdateFlightServerSideResult(null); setUpdateFlightSuccess(false); + setUpdateFlightValidationProblemDetails(null); }; //The function deletes a flight from the server. @@ -77,6 +79,9 @@ export function useFlightDataLayer() { if (response.ok) { setDeleteFlightSuccess(true); } + else if (response.status === 500) { + response.json().then(problemDetails => showError(problemDetails.detail)); + } else { showError('Failed to delete the flight because of an error on the server.'); } @@ -120,11 +125,11 @@ export function useFlightDataLayer() { if (response.ok) { setUpdateFlightSuccess(true); } - else if (response.status == 400) { - response.json().then(serverSideValidationResult => setUpdateFlightServerSideResult(shapeValidationResult(serverSideValidationResult))); + else if (response.status === 400) { + response.json().then(validationProblemDetails => setUpdateFlightValidationProblemDetails(validationProblemDetails)); } - else if (response.status == 409) { - showError('The submitted data was detected to be out of date; please refresh and try again.'); + else if (response.status == 409 || response.status === 500) { + response.json().then(problemDetails => showError(problemDetails.detail)); } else { showError('Failed to update the flight because of an error on the server.'); @@ -137,12 +142,12 @@ export function useFlightDataLayer() { flights, getFlights, addFlight, - addFlightServerSideResult, + addFlightValidationProblemDetails, addFlightSuccess, deleteFlight, deleteFlightSuccess, updateFlight, - updateFlightServerSideResult, + updateFlightValidationProblemDetails, updateFlightSuccess }; }; \ No newline at end of file From 07970a48b89cbe29ef12b21f4e366f6c6585b928 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Sat, 8 Nov 2025 09:13:32 -0500 Subject: [PATCH 10/16] Added more package info --- .../JMayer.Example.ASPReact.Server.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj index 7437407..90fab84 100644 --- a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj +++ b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj @@ -8,6 +8,9 @@ npm run dev https://localhost:5173 9.0.0 + jmayer913 + jmayer913 + https://github.com/jmayer913/JMayer-Example-ASPReact From 30dd5e55dec03d7723503aaf9185b702c309e371 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Sun, 16 Nov 2025 12:46:45 -0500 Subject: [PATCH 11/16] Turned on odd data detection --- JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs | 4 +++- JMayer.Example.ASPReact.Server/Flights/FlightDataLayer.cs | 2 ++ TestProject/TestProject.csproj | 1 - 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs b/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs index 1af3aee..9349797 100644 --- a/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs +++ b/JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs @@ -20,8 +20,10 @@ public class AirlineDataLayer : StandardCRUDDataLayer, IAirlineDataLaye /// public AirlineDataLayer(ISortDestinationDataLayer sortDestinationDataLayer) { - _sortDestinationDataLayer = sortDestinationDataLayer; + IsOldDataObjectDetectionEnabled = true; IsUniqueNameRequired = true; + + _sortDestinationDataLayer = sortDestinationDataLayer; } /// diff --git a/JMayer.Example.ASPReact.Server/Flights/FlightDataLayer.cs b/JMayer.Example.ASPReact.Server/Flights/FlightDataLayer.cs index 0de67ed..c99c7d7 100644 --- a/JMayer.Example.ASPReact.Server/Flights/FlightDataLayer.cs +++ b/JMayer.Example.ASPReact.Server/Flights/FlightDataLayer.cs @@ -34,6 +34,8 @@ public class FlightDataLayer : StandardCRUDDataLayer, IFlightDataLayer /// Used to access the sort desintation data. public FlightDataLayer(IAirlineDataLayer airlineDataLayer, IGateDataLayer gateDataLayer, ISortDestinationDataLayer sortDestinationDataLayer) { + IsOldDataObjectDetectionEnabled = true; + _airlineDataLayer = airlineDataLayer; _gateDataLayer = gateDataLayer; _sortDestinationDataLayer = sortDestinationDataLayer; diff --git a/TestProject/TestProject.csproj b/TestProject/TestProject.csproj index e2d61a2..7c3fe80 100644 --- a/TestProject/TestProject.csproj +++ b/TestProject/TestProject.csproj @@ -26,7 +26,6 @@ - From 5dacaf86cea11e58c70d63a19aea4cf5807bad45 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Thu, 20 Nov 2025 16:23:45 -0500 Subject: [PATCH 12/16] Updated .NET Version in Workflow --- .github/workflows/mainworkflow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/mainworkflow.yml b/.github/workflows/mainworkflow.yml index c8c3466..ba28f55 100644 --- a/.github/workflows/mainworkflow.yml +++ b/.github/workflows/mainworkflow.yml @@ -20,7 +20,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 8.0.x + dotnet-version: 9.0.x - name: Restore dependencies run: dotnet restore - name: Build From 63cabd5e7d6c9091a491216471e84e7a049fa721 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:56:15 -0500 Subject: [PATCH 13/16] Fixed conflict issues with swagger --- .../Airlines/AirlineController.cs | 16 ++++++++++++++++ .../Flights/FlightController.cs | 16 ++++++++++++++++ .../Gates/GateController.cs | 8 ++++++++ .../SortDestinationController.cs | 8 ++++++++ 4 files changed, 48 insertions(+) diff --git a/JMayer.Example.ASPReact.Server/Airlines/AirlineController.cs b/JMayer.Example.ASPReact.Server/Airlines/AirlineController.cs index 2a70aa5..30946ac 100644 --- a/JMayer.Example.ASPReact.Server/Airlines/AirlineController.cs +++ b/JMayer.Example.ASPReact.Server/Airlines/AirlineController.cs @@ -12,4 +12,20 @@ public class AirlineController : StandardCRUDController public AirlineController(IAirlineDataLayer dataLayer, ILogger logger) : base(dataLayer, logger) { } + + /// + /// Overridden to hide the string version of this. The client doesn't use it and swagger complains about a conflict when its exposed. + [NonAction] + public override Task DeleteAsync(string id) + { + return base.DeleteAsync(id); + } + + /// + /// Overridden to hide the string version of this. The client doesn't use it and swagger complains about a conflict when its exposed. + [NonAction] + public override Task GetSingleAsync(string id) + { + return base.GetSingleAsync(id); + } } diff --git a/JMayer.Example.ASPReact.Server/Flights/FlightController.cs b/JMayer.Example.ASPReact.Server/Flights/FlightController.cs index 4edb18f..d91aa99 100644 --- a/JMayer.Example.ASPReact.Server/Flights/FlightController.cs +++ b/JMayer.Example.ASPReact.Server/Flights/FlightController.cs @@ -12,4 +12,20 @@ public class FlightController : StandardCRUDController { /// public FlightController(IFlightDataLayer dataLayer, ILogger logger) : base(dataLayer, logger) { } + + /// + /// Overridden to hide the string version of this. The client doesn't use it and swagger complains about a conflict when its exposed. + [NonAction] + public override Task DeleteAsync(string id) + { + return base.DeleteAsync(id); + } + + /// + /// Overridden to hide the string version of this. The client doesn't use it and swagger complains about a conflict when its exposed. + [NonAction] + public override Task GetSingleAsync(string id) + { + return base.GetSingleAsync(id); + } } diff --git a/JMayer.Example.ASPReact.Server/Gates/GateController.cs b/JMayer.Example.ASPReact.Server/Gates/GateController.cs index ed83b17..c5b5236 100644 --- a/JMayer.Example.ASPReact.Server/Gates/GateController.cs +++ b/JMayer.Example.ASPReact.Server/Gates/GateController.cs @@ -46,6 +46,14 @@ public override Task DeleteAsync(string id) return base.DeleteAsync(id); } + /// + /// Overridden to hide the string version of this. The client doesn't use it and swagger complains about a conflict when its exposed. + [NonAction] + public override Task GetSingleAsync(string id) + { + return base.GetSingleAsync(id); + } + /// /// /// Overriden to prevent the updating of gates. The example will auto generate some default gates diff --git a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs index 0bb3f7a..343ec06 100644 --- a/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs +++ b/JMayer.Example.ASPReact.Server/SortDestinations/SortDestinationController.cs @@ -46,6 +46,14 @@ public override Task DeleteAsync(string id) return base.DeleteAsync(id); } + /// + /// Overridden to hide the string version of this. The client doesn't use it and swagger complains about a conflict when its exposed. + [NonAction] + public override Task GetSingleAsync(string id) + { + return base.GetSingleAsync(id); + } + /// /// /// Overriden to prevent the updating of sort destinations. The example will auto generate some default sort destinations From 1c880b1df59fb99656ccbd787d059adb3b94bfde Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Mon, 24 Nov 2025 12:34:21 -0500 Subject: [PATCH 14/16] Example Builder Changes Increased sort destinations to 6; two for each airline. Flight will be set to the airline's default sort destination or its alternative sort destination. Switched from rolling gate to two gates for each airline. --- .../FlightScheduleExampleBuilder.cs | 105 +++++++++++++----- 1 file changed, 80 insertions(+), 25 deletions(-) diff --git a/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs b/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs index b2cc92d..87b0a91 100644 --- a/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs +++ b/JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs @@ -20,6 +20,16 @@ public class FlightScheduleExampleBuilder ///
public IAirlineDataLayer AirlineDataLayer { get; init; } + /// + /// The constant for the American Airline IATA code. + /// + private const string AmericanIataCode = "AA"; + + /// + /// The constant for the Delta IATA code. + /// + private const string DeltaIataCode = "DL"; + /// /// The property gets/sets the data layer used to ineract with the flights. /// @@ -36,7 +46,7 @@ public class FlightScheduleExampleBuilder public ISortDestinationDataLayer SortDestinationDataLayer { get; init; } = new SortDestinationDataLayer(); /// - /// The constant for the southwest IATA code. + /// The constant for the Southwest IATA code. /// private const string SouthwestIataCode = "WN"; @@ -70,7 +80,7 @@ private void BuildAirlines() _ = AirlineDataLayer.CreateAsync(new Airline() { - IATA = "AA", + IATA = AmericanIataCode, ICAO = "AAL", Name = "American Airlines", NumberCode = "001", @@ -79,21 +89,21 @@ private void BuildAirlines() }); _ = AirlineDataLayer.CreateAsync(new Airline() { - IATA = "DL", + IATA = DeltaIataCode, ICAO = "DAL", Name = "Delta Air Lines", NumberCode = "006", - SortDestinationID = sortDestinations[1].Integer64ID, - SortDestinationName = sortDestinations[1].Name ?? string.Empty, + SortDestinationID = sortDestinations[2].Integer64ID, + SortDestinationName = sortDestinations[2].Name ?? string.Empty, }); _ = AirlineDataLayer.CreateAsync(new Airline() { - IATA = "WN", + IATA = SouthwestIataCode, ICAO = "SWA", Name = "Southwest Airlines", NumberCode = "526", - SortDestinationID = sortDestinations[2].Integer64ID, - SortDestinationName = sortDestinations[2].Name ?? string.Empty, + SortDestinationID = sortDestinations[4].Integer64ID, + SortDestinationName = sortDestinations[4].Name ?? string.Empty, }); } @@ -106,9 +116,16 @@ private void BuildFlights() List gates = GateDataLayer.GetAllAsync().Result; List sortDestinations = SortDestinationDataLayer.GetAllAsync().Result; - bool useMU4 = false; + bool altAmericanGate = false; + bool altAmericanSortDestination = false; + + bool altDeltaGate = false; + bool altDeltaSortDestination = false; + + bool altSouthwestGate = false; + bool altSouthWestSortDestination = false; + int flightNumber = 1000; - int gateIndex = 0; TimeSpan departTime = new(4, 0, 0); TimeSpan operationalEnd = new(22, 0, 0); @@ -116,6 +133,47 @@ private void BuildFlights() { foreach (Airline airline in airlines) { + Gate gate; + SortDestination? altSortDestination = null; + + //Determine which gate is used for the airline and if the alternative sort destination is used. + if (airline.IATA is AmericanIataCode) + { + gate = altAmericanGate ? gates[1] : gates[0]; + + if (altAmericanSortDestination) + { + altSortDestination = sortDestinations[1]; + } + + altAmericanGate = !altAmericanGate; + altAmericanSortDestination = !altAmericanSortDestination; + } + else if (airline.IATA is DeltaIataCode) + { + gate = altDeltaGate ? gates[3] : gates[2]; + + if (altDeltaSortDestination) + { + altSortDestination = sortDestinations[3]; + } + + altDeltaGate = !altDeltaGate; + altDeltaSortDestination = !altDeltaSortDestination; + } + else + { + gate = altSouthwestGate ? gates[5] : gates[4]; + + if (altSouthWestSortDestination) + { + altSortDestination = sortDestinations[5]; + } + + altSouthwestGate = !altSouthwestGate; + altSouthWestSortDestination = !altSouthWestSortDestination; + } + _ = FlightDataLayer.CreateAsync(new Flight() { AirlineIATACode = airline.IATA, @@ -124,26 +182,15 @@ private void BuildFlights() Destination = _airportCodes[new Random(DateTime.Now.Millisecond).Next(0, _airportCodes.Count - 1)], DepartTime = departTime, FlightNumber = flightNumber.ToString().PadLeft(4, '0'), - GateID = gates[gateIndex].Integer64ID, - GateName = gates[gateIndex].Name ?? string.Empty, + GateID = gate.Integer64ID, + GateName = gate.Name ?? string.Empty, Name = $"{airline.IATA}{flightNumber.ToString().PadLeft(4, '0')}", - SortDestinationID = airline.IATA == SouthwestIataCode && useMU4 ? sortDestinations[3].Integer64ID : airline.SortDestinationID, - SortDestinationName = airline.IATA == SouthwestIataCode && useMU4 ? sortDestinations[3].Name ?? string.Empty : airline.SortDestinationName, + SortDestinationID = altSortDestination is not null ? altSortDestination.Integer64ID : airline.SortDestinationID, + SortDestinationName = altSortDestination is not null ? altSortDestination.Name ?? string.Empty : airline.SortDestinationName, }); flightNumber++; - gateIndex++; departTime = departTime.Add(TimeSpan.FromMinutes(10)); - - if (gateIndex > gates.Count - 1) - { - gateIndex = 0; - } - - if (airline.IATA == SouthwestIataCode) - { - useMU4 = !useMU4; - } } } } @@ -202,6 +249,14 @@ private void BuildSortDestinations() { Name = "MU4", }); + _ = SortDestinationDataLayer.CreateAsync(new SortDestination() + { + Name = "MU5", + }); + _ = SortDestinationDataLayer.CreateAsync(new SortDestination() + { + Name = "MU6", + }); } /// From 3f3633e5520ddc89b5c99b66818c0c1f4e6ae670 Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:42:39 -0500 Subject: [PATCH 15/16] Updated the Packages --- .../JMayer.Example.ASPReact.Server.csproj | 2 +- TestProject/TestProject.csproj | 4 +- .../package-lock.json | 105 +++++++++--------- 3 files changed, 55 insertions(+), 56 deletions(-) diff --git a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj index 90fab84..fe4fe43 100644 --- a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj +++ b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj @@ -15,7 +15,7 @@ - 9.0.10 + 9.0.11 diff --git a/TestProject/TestProject.csproj b/TestProject/TestProject.csproj index 7c3fe80..8c45acc 100644 --- a/TestProject/TestProject.csproj +++ b/TestProject/TestProject.csproj @@ -15,8 +15,8 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/jmayer.example.aspreact.client/package-lock.json b/jmayer.example.aspreact.client/package-lock.json index 04436bd..e92703b 100644 --- a/jmayer.example.aspreact.client/package-lock.json +++ b/jmayer.example.aspreact.client/package-lock.json @@ -716,12 +716,12 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -730,21 +730,21 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.0.tgz", - "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "dependencies": { - "@eslint/core": "^0.16.0" + "@eslint/core": "^0.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", - "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.15" @@ -789,9 +789,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.37.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.37.0.tgz", - "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", + "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -801,21 +801,21 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", - "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "dependencies": { - "@eslint/core": "^0.16.0", + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -1227,17 +1227,17 @@ "dev": true }, "node_modules/@types/react": { - "version": "19.2.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", - "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", + "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "dependencies": { - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "19.2.2", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.2.tgz", - "integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "peerDependencies": { "@types/react": "^19.2.0" @@ -1677,9 +1677,9 @@ } }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==" }, "node_modules/data-view-buffer": { "version": "1.0.2", @@ -2060,24 +2060,23 @@ } }, "node_modules/eslint": { - "version": "9.37.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.37.0.tgz", - "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", + "version": "9.39.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", + "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.4.0", - "@eslint/core": "^0.16.0", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.37.0", - "@eslint/plugin-kit": "^0.4.0", + "@eslint/js": "9.39.1", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", @@ -2479,9 +2478,9 @@ } }, "node_modules/globals": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", - "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "engines": { "node": ">=18" }, @@ -3582,9 +3581,9 @@ } }, "node_modules/react-router": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.4.tgz", - "integrity": "sha512-SD3G8HKviFHg9xj7dNODUKDFgpG4xqD5nhyd0mYoB5iISepuZAvzSr8ywxgxKJ52yRzf/HWtVHc9AWwoTbljvA==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.9.6.tgz", + "integrity": "sha512-Y1tUp8clYRXpfPITyuifmSoE2vncSME18uVLgaqyxh9H35JWpIfzHo+9y3Fzh5odk/jxPW29IgLgzcdwxGqyNA==", "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" @@ -3603,11 +3602,11 @@ } }, "node_modules/react-router-dom": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.4.tgz", - "integrity": "sha512-f30P6bIkmYvnHHa5Gcu65deIXoA2+r3Eb6PJIAddvsT9aGlchMatJ51GgpU470aSqRRbFX22T70yQNUGuW3DfA==", + "version": "7.9.6", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.9.6.tgz", + "integrity": "sha512-2MkC2XSXq6HjGcihnx1s0DBWQETI4mlis4Ux7YTLvP67xnGxCvq+BcCQSO81qQHVUTM1V53tl4iVVaY5sReCOA==", "dependencies": { - "react-router": "7.9.4" + "react-router": "7.9.6" }, "engines": { "node": ">=20.0.0" @@ -4257,9 +4256,9 @@ } }, "node_modules/vite": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.0.tgz", - "integrity": "sha512-oLnWs9Hak/LOlKjeSpOwD6JMks8BeICEdYMJBf6P4Lac/pO9tKiv/XhXnAM7nNfSkZahjlCZu9sS50zL8fSnsw==", + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", + "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", From 74ae4c06cd20f43e70e665ac094084f24f77e28a Mon Sep 17 00:00:00 2001 From: jmayer913 <72579603+jmayer913@users.noreply.github.com> Date: Tue, 23 Dec 2025 14:37:27 -0500 Subject: [PATCH 16/16] Switched back to package reference --- .../JMayer.Example.ASPReact.Server.csproj | 2 +- JMayer.Example.ASPReact.sln | 12 ------------ 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj index 0ca4b45..c8372a6 100644 --- a/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj +++ b/JMayer.Example.ASPReact.Server/JMayer.Example.ASPReact.Server.csproj @@ -14,6 +14,7 @@ + 9.0.11 @@ -21,7 +22,6 @@ - false diff --git a/JMayer.Example.ASPReact.sln b/JMayer.Example.ASPReact.sln index ab5595c..ff83c02 100644 --- a/JMayer.Example.ASPReact.sln +++ b/JMayer.Example.ASPReact.sln @@ -9,10 +9,6 @@ Project("{54A90642-561A-4BB1-A94E-469ADEE60C69}") = "jmayer.example.aspreact.cli EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestProject", "TestProject\TestProject.csproj", "{DDEDF8AE-D81E-4019-8164-77F1588D3320}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JMayer.Data", "..\JMayer-Data-Library\JMayer.Data\JMayer.Data.csproj", "{5E2E9979-417E-9AC0-DE5B-ED439D651066}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "JMayer.Web.Mvc", "..\JMayer-Web-Mvc-Library\JMayer.Web.Mvc\JMayer.Web.Mvc.csproj", "{185EFF51-282E-8F0A-7CE5-41B2029C51D3}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -33,14 +29,6 @@ Global {DDEDF8AE-D81E-4019-8164-77F1588D3320}.Debug|Any CPU.Build.0 = Debug|Any CPU {DDEDF8AE-D81E-4019-8164-77F1588D3320}.Release|Any CPU.ActiveCfg = Release|Any CPU {DDEDF8AE-D81E-4019-8164-77F1588D3320}.Release|Any CPU.Build.0 = Release|Any CPU - {5E2E9979-417E-9AC0-DE5B-ED439D651066}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5E2E9979-417E-9AC0-DE5B-ED439D651066}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5E2E9979-417E-9AC0-DE5B-ED439D651066}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5E2E9979-417E-9AC0-DE5B-ED439D651066}.Release|Any CPU.Build.0 = Release|Any CPU - {185EFF51-282E-8F0A-7CE5-41B2029C51D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {185EFF51-282E-8F0A-7CE5-41B2029C51D3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {185EFF51-282E-8F0A-7CE5-41B2029C51D3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {185EFF51-282E-8F0A-7CE5-41B2029C51D3}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE