Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/mainworkflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
19 changes: 18 additions & 1 deletion JMayer.Example.ASPReact.Server/Airlines/Airline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace JMayer.Example.ASPReact.Server.Airlines;
/// <summary>
/// The class represents an airline and its codes.
/// </summary>
public class Airline : UserEditableDataObject
public class Airline : DataObject
{
/// <summary>
/// The property gets/sets the IATA code assigned by the IATA organization.
Expand All @@ -21,13 +21,28 @@ public class Airline : UserEditableDataObject
[RegularExpression("^[A-Z]{3}$", ErrorMessage = "The ICAO must be 3 capital letters.")]
public string ICAO { get; set; } = string.Empty;

/// <inheritdoc/>
/// <remarks>Overridden to add Required data annotation.</remarks>
[Required]
public override string? Name { get => base.Name; set => base.Name = value; }

/// <summary>
/// The property gets/sets the number code assigned by the IATA organization.
/// </summary>
[Required]
[RegularExpression("^\\d{3}$", ErrorMessage = "The number code must be 3 digits.")]
public string NumberCode { get; set; } = ZeroNumberCode;

/// <summary>
/// The property gets/sets the id for the default sort destintion assigned to the airline.
/// </summary>
public long SortDestinationID { get; set; }

/// <summary>
/// The property gets/sets the name of the default sort destination assigned to the airline.
/// </summary>
public string SortDestinationName { get; set; } = string.Empty;

/// <summary>
/// The constant for the zero number code.
/// </summary>
Expand All @@ -54,6 +69,8 @@ public override void MapProperties(DataObject dataObject)
IATA = airline.IATA;
ICAO = airline.ICAO;
NumberCode = airline.NumberCode;
SortDestinationID = airline.SortDestinationID;
SortDestinationName = airline.SortDestinationName;
}
}
}
20 changes: 18 additions & 2 deletions JMayer.Example.ASPReact.Server/Airlines/AirlineController.cs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -8,8 +8,24 @@ namespace JMayer.Example.ASPReact.Server.Airlines;
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class AirlineController : UserEditableController<Airline, IAirlineDataLayer>
public class AirlineController : StandardCRUDController<Airline, IAirlineDataLayer>
{
/// <inheritdoc/>
public AirlineController(IAirlineDataLayer dataLayer, ILogger<AirlineController> logger) : base(dataLayer, logger) { }

/// <inheritdoc/>
/// <remarks>Overridden to hide the string version of this. The client doesn't use it and swagger complains about a conflict when its exposed.</remarks>
[NonAction]
public override Task<IActionResult> DeleteAsync(string id)
{
return base.DeleteAsync(id);
}

/// <inheritdoc/>
/// <remarks>Overridden to hide the string version of this. The client doesn't use it and swagger complains about a conflict when its exposed.</remarks>
[NonAction]
public override Task<IActionResult> GetSingleAsync(string id)
{
return base.GetSingleAsync(id);
}
}
29 changes: 26 additions & 3 deletions JMayer.Example.ASPReact.Server/Airlines/AirlineDataLayer.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
using JMayer.Data.Database.DataLayer.MemoryStorage;
using JMayer.Example.ASPReact.Server.SortDestinations;
using System.ComponentModel.DataAnnotations;

namespace JMayer.Example.ASPReact.Server.Airlines;

/// <summary>
/// The class manages CRUD interactions with the database for an airline.
/// </summary>
public class AirlineDataLayer : UserEditableDataLayer<Airline>, IAirlineDataLayer
public class AirlineDataLayer : StandardCRUDDataLayer<Airline>, IAirlineDataLayer
{
/// <summary>
/// The property gets/sets the data layer for accessing the sort desintation data.
/// </summary>
private readonly ISortDestinationDataLayer _sortDestinationDataLayer;

/// <summary>
/// The dependency injection constructor.
/// </summary>
/// <param name="sortDestinationDataLayer"></param>
public AirlineDataLayer(ISortDestinationDataLayer sortDestinationDataLayer)
{
IsOldDataObjectDetectionEnabled = true;
IsUniqueNameRequired = true;

_sortDestinationDataLayer = sortDestinationDataLayer;
}

/// <inheritdoc/>
/// <remarks>
/// Overriden to check the ICAO is unique and the number code is unique (expect for 000).
Expand All @@ -16,12 +34,17 @@ public override async Task<List<ValidationResult>> ValidateAsync(Airline dataObj
{
List<ValidationResult> 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 (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)]));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public AirlineEqualityComparer(bool excludeCreatedOn, bool excludeID, bool exclu
/// <inheritdoc/>
public bool Equals(Airline? x, Airline? y)
{
if (x == null || y == null)
if (x is null || y is null)
{
return false;
}
Expand All @@ -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;
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ namespace JMayer.Example.ASPReact.Server.Airlines;
/// <summary>
/// The interface for interacting with an airline collection in a database using CRUD operations.
/// </summary>
public interface IAirlineDataLayer : IUserEditableDataLayer<Airline>
public interface IAirlineDataLayer : IStandardCRUDDataLayer<Airline>
{
}
114 changes: 91 additions & 23 deletions JMayer.Example.ASPReact.Server/FlightScheduleExampleBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,17 @@ public class FlightScheduleExampleBuilder
/// <summary>
/// The property gets/sets the data layer used to interact with airlines.
/// </summary>
public IAirlineDataLayer AirlineDataLayer { get; init; } = new AirlineDataLayer();
public IAirlineDataLayer AirlineDataLayer { get; init; }

/// <summary>
/// The constant for the American Airline IATA code.
/// </summary>
private const string AmericanIataCode = "AA";

/// <summary>
/// The constant for the Delta IATA code.
/// </summary>
private const string DeltaIataCode = "DL";

/// <summary>
/// The property gets/sets the data layer used to ineract with the flights.
Expand All @@ -35,11 +45,17 @@ public class FlightScheduleExampleBuilder
/// </summary>
public ISortDestinationDataLayer SortDestinationDataLayer { get; init; } = new SortDestinationDataLayer();

/// <summary>
/// The constant for the Southwest IATA code.
/// </summary>
private const string SouthwestIataCode = "WN";

/// <summary>
/// The default constructor.
/// </summary>
public FlightScheduleExampleBuilder()
{
AirlineDataLayer = new AirlineDataLayer(SortDestinationDataLayer);
FlightDataLayer = new FlightDataLayer(AirlineDataLayer, GateDataLayer, SortDestinationDataLayer);
GenerateAirportCodes();
}
Expand All @@ -49,9 +65,9 @@ public FlightScheduleExampleBuilder()
/// </summary>
public void Build()
{
BuildSortDestinations();
BuildAirlines();
BuildGates();
BuildSortDestinations();
BuildFlights();
}

Expand All @@ -60,26 +76,34 @@ public void Build()
/// </summary>
private void BuildAirlines()
{
List<SortDestination> sortDestinations = SortDestinationDataLayer.GetAllAsync().Result;

_ = AirlineDataLayer.CreateAsync(new Airline()
{
IATA = "AA",
IATA = AmericanIataCode,
ICAO = "AAL",
Name = "American Airlines",
NumberCode = "001",
SortDestinationID = sortDestinations[0].Integer64ID,
SortDestinationName = sortDestinations[0].Name ?? string.Empty,
});
_ = AirlineDataLayer.CreateAsync(new Airline()
{
IATA = "DL",
IATA = DeltaIataCode,
ICAO = "DAL",
Name = "Delta Air Lines",
NumberCode = "006",
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[4].Integer64ID,
SortDestinationName = sortDestinations[4].Name ?? string.Empty,
});
}

Expand All @@ -92,16 +116,64 @@ private void BuildFlights()
List<Gate> gates = GateDataLayer.GetAllAsync().Result;
List<SortDestination> sortDestinations = SortDestinationDataLayer.GetAllAsync().Result;

bool altAmericanGate = false;
bool altAmericanSortDestination = false;

bool altDeltaGate = false;
bool altDeltaSortDestination = false;

bool altSouthwestGate = false;
bool altSouthWestSortDestination = false;

int flightNumber = 1000;
int gateIndex = 0;
int sortDestinationIndex = 0;
TimeSpan departTime = new(4, 0, 0);
TimeSpan operationalEnd = new(22, 0, 0);

while (departTime < operationalEnd)
{
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,
Expand All @@ -110,27 +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,
GateID = gate.Integer64ID,
GateName = gate.Name ?? string.Empty,
Name = $"{airline.IATA}{flightNumber.ToString().PadLeft(4, '0')}",
SortDestinationID = sortDestinations[sortDestinationIndex].Integer64ID,
SortDestinationName = sortDestinations[sortDestinationIndex].Name,
SortDestinationID = altSortDestination is not null ? altSortDestination.Integer64ID : airline.SortDestinationID,
SortDestinationName = altSortDestination is not null ? altSortDestination.Name ?? string.Empty : airline.SortDestinationName,
});

flightNumber++;
gateIndex++;
sortDestinationIndex++;
departTime = departTime.Add(TimeSpan.FromMinutes(10));

if (gateIndex > gates.Count - 1)
{
gateIndex = 0;
}

if (sortDestinationIndex > sortDestinations.Count - 1)
{
sortDestinationIndex = 0;
}
}
}
}
Expand Down Expand Up @@ -189,6 +249,14 @@ private void BuildSortDestinations()
{
Name = "MU4",
});
_ = SortDestinationDataLayer.CreateAsync(new SortDestination()
{
Name = "MU5",
});
_ = SortDestinationDataLayer.CreateAsync(new SortDestination()
{
Name = "MU6",
});
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class CodeShareEqualityComparer : IEqualityComparer<CodeShare>
/// <inheritdoc/>
public bool Equals(CodeShare? x, CodeShare? y)
{
if (x == null || y == null)
if (x is null || y is null)
{
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion JMayer.Example.ASPReact.Server/Flights/Flight.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace JMayer.Example.ASPReact.Server.Flights;
/// <summary>
/// The class represents a flight in the flight schedule.
/// </summary>
public class Flight : UserEditableDataObject
public class Flight : DataObject
{
/// <summary>
/// The property gets/sets the IATA code for the airline for the flight.
Expand Down
Loading