-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement controller for accessing database with airports with follow…
…ing middleware and filters: - caching - validation of input IATA code - global exception handler - invalid request to database filter - URI disconitinuation filter Use CQRS pattern to decouple any logic from controllers by using MediatR NuGet package. Add Repository pattern to decouple access to database from CQRS pattern.
- Loading branch information
ivan_nizic
committed
Dec 2, 2024
1 parent
e254787
commit f77c02f
Showing
22 changed files
with
425 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 3 additions & 3 deletions
6
...ghtScanner.Domain/Models/AirportEntity.cs → ...tScanner.Domain/Entities/AirportEntity.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
source/FlightScanner.Domain/Exceptions/InvalidResponseException.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
namespace FlightScanner.Domain.Exceptions; | ||
|
||
public class InvalidResponseException : Exception | ||
{ | ||
public InvalidResponseException(string? message) : base(message) | ||
{ | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
namespace FlightScanner.Domain.Exceptions; | ||
|
||
public class NotFoundException : Exception | ||
{ | ||
public NotFoundException(Type typeName, string objectId) | ||
: base($"{typeName} with id {objectId} was not found!") | ||
{ | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
source/FlightScanner.Domain/Repositories/IAirportRepository.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
| ||
using FlightScanner.Domain.Entities; | ||
|
||
namespace FlightScanner.Domain.Repositories; | ||
|
||
public interface IAirportRepository | ||
{ | ||
Task<AirportEntity> GetAirportWithIataCode(string iataCode, CancellationToken cancellationToken); | ||
} |
6 changes: 3 additions & 3 deletions
6
source/FlightScanner.Persistence/Configurations/AirportEntityConfiguration.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
source/FlightScanner.Persistence/Repositories/AirportRepository.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
using FlightScanner.Domain.Entities; | ||
using FlightScanner.Domain.Exceptions; | ||
using FlightScanner.Domain.Repositories; | ||
using FlightScanner.Persistence.Database; | ||
using Microsoft.EntityFrameworkCore; | ||
|
||
namespace FlightScanner.Persistence.Repositories; | ||
|
||
public class AirportRepository : IAirportRepository | ||
{ | ||
private readonly AirportsDbContext _airportsDbContext; | ||
|
||
public AirportRepository(AirportsDbContext airportsDbContext) | ||
{ | ||
_airportsDbContext = airportsDbContext; | ||
} | ||
|
||
public async Task<AirportEntity> GetAirportWithIataCode(string iataCode, CancellationToken cancellationToken) | ||
{ | ||
var airport = await _airportsDbContext | ||
.Airports | ||
.AsNoTracking() | ||
.AsQueryable() | ||
.FirstOrDefaultAsync( | ||
predicate: airport => string.Equals(airport.IataCode, iataCode.ToUpper()), | ||
cancellationToken: cancellationToken); | ||
|
||
if (airport == null) | ||
{ | ||
throw new NotFoundException(typeof(AirportEntity), iataCode); | ||
} | ||
|
||
return airport; | ||
} | ||
} |
38 changes: 38 additions & 0 deletions
38
source/FlightScanner.WebApi/Controllers/AirportCodesController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using FlightScanner.Domain.Entities; | ||
using FlightScanner.WebApi.Filters; | ||
using FlightScanner.WebApi.Validation; | ||
using FlightsScanner.Application.Airports.Queries.GetAirport; | ||
using MediatR; | ||
using Microsoft.AspNetCore.Mvc; | ||
using System.Net.Mime; | ||
|
||
namespace FlightScanner.WebApi.Controllers; | ||
|
||
[ApiController] | ||
[Route("api/v2")] | ||
[AirportCodesVersionDiscontinuationResourceFilter] | ||
public class AirportCodesController : ControllerBase | ||
{ | ||
private readonly ISender _sender; | ||
|
||
public AirportCodesController(ISender sender) | ||
{ | ||
_sender = sender; | ||
} | ||
|
||
[Produces(MediaTypeNames.Application.Json)] | ||
[ProducesResponseType(StatusCodes.Status201Created, Type = typeof(AirportEntity))] | ||
[HttpGet] | ||
[Route("airport")] | ||
[CountriesWithClosedAirTrafficFilter] | ||
public async Task<IActionResult> GetAirport( | ||
[FromQuery][IataCodeValidation] string iataCode, | ||
CancellationToken cancellationToken) | ||
{ | ||
var response = await _sender.Send( | ||
request: new GetAirportQuery(iataCode), | ||
cancellationToken: cancellationToken); | ||
|
||
return Ok(response); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
source/FlightScanner.WebApi/Filters/AirportCodesVersionDiscontinuationResourceFilter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
using Microsoft.AspNetCore.Mvc.Filters; | ||
using Microsoft.AspNetCore.Mvc; | ||
|
||
namespace FlightScanner.WebApi.Filters; | ||
|
||
public class AirportCodesVersionDiscontinuationResourceFilter : Attribute, IResourceFilter | ||
{ | ||
private const string OUTDATED_API_VERSION = "v1"; | ||
|
||
public void OnResourceExecuted(ResourceExecutedContext context) | ||
{ | ||
|
||
} | ||
|
||
public void OnResourceExecuting(ResourceExecutingContext context) | ||
{ | ||
var uriPath = context.HttpContext.Request.Path; | ||
|
||
if (!string.IsNullOrEmpty(uriPath.Value) && uriPath.Value.Contains(OUTDATED_API_VERSION, StringComparison.CurrentCultureIgnoreCase)) | ||
{ | ||
var errorObject = new | ||
{ | ||
Versioning = new[] | ||
{ | ||
$"API version {OUTDATED_API_VERSION} is expired. Please use the latest version." | ||
} | ||
}; | ||
|
||
context.Result = new BadRequestObjectResult(errorObject); | ||
} | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
source/FlightScanner.WebApi/Filters/CountriesWithClosedAirTrafficFilter.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using Microsoft.AspNetCore.Mvc.Filters; | ||
using Microsoft.AspNetCore.Mvc; | ||
using FlightScanner.Domain.Entities; | ||
|
||
namespace FlightScanner.WebApi.Filters; | ||
|
||
public class CountriesWithClosedAirTrafficFilter : ActionFilterAttribute | ||
{ | ||
private readonly string[] _ukrainianAirportIataCodes = new string[] | ||
{ | ||
"UCK", | ||
"UDJ", | ||
"UMY", | ||
}; | ||
|
||
public override void OnActionExecuted(ActionExecutedContext context) | ||
{ | ||
base.OnActionExecuted(context); | ||
|
||
if (context.Result is not OkObjectResult result) | ||
{ | ||
return; | ||
} | ||
|
||
if (result.Value is not AirportEntity airport) | ||
{ | ||
return; | ||
} | ||
|
||
if (_ukrainianAirportIataCodes.Any(ukrainianAirportIataCode => string.Equals(ukrainianAirportIataCode, airport.IataCode, StringComparison.OrdinalIgnoreCase))) | ||
{ | ||
context.ModelState.AddModelError( | ||
key: "Forbidden IATA code", | ||
errorMessage: $"Airport with IATA code {airport.IataCode} is currently closed due to war conditions!"); | ||
|
||
var problemDetails = new ValidationProblemDetails(context.ModelState) | ||
{ | ||
Status = StatusCodes.Status406NotAcceptable | ||
}; | ||
|
||
context.Result = new BadRequestObjectResult(problemDetails); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
23 changes: 23 additions & 0 deletions
23
source/FlightScanner.WebApi/Validation/IataCodeValidation.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
using System.ComponentModel.DataAnnotations; | ||
|
||
namespace FlightScanner.WebApi.Validation; | ||
|
||
public class IataCodeValidation : ValidationAttribute | ||
{ | ||
private const int IATA_CODE_LENGTH = 3; | ||
|
||
protected override ValidationResult? IsValid(object? value, ValidationContext validationContext) | ||
{ | ||
if (value is not string iataCode) | ||
{ | ||
return new ValidationResult($"Received input {value} is not text!"); | ||
} | ||
|
||
if (iataCode.Length != IATA_CODE_LENGTH) | ||
{ | ||
return new ValidationResult($"IATA code has invalid length of {iataCode.Length}. IATA code should have {IATA_CODE_LENGTH} text characters."); | ||
} | ||
|
||
return ValidationResult.Success; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.