updating countries
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
using ApplicationLayer.GeneralExceptions;
|
||||||
|
|
||||||
|
namespace Infrastructure.Database.GeneralExceptions
|
||||||
|
{
|
||||||
|
/// Exception to throw when can't complete some action on entity(delete or something) because it's needed for other entities
|
||||||
|
public class EntityUsedInDatabaseException(string message) : ApiException(message);
|
||||||
|
}
|
||||||
@@ -3,4 +3,8 @@ using Domains.LocationDomain;
|
|||||||
|
|
||||||
namespace ApplicationLayer.Services.Locations.NeededServices;
|
namespace ApplicationLayer.Services.Locations.NeededServices;
|
||||||
|
|
||||||
public interface ICitiesRepository : IGenericRepository<City>;
|
public interface ICitiesRepository : IGenericRepository<City>
|
||||||
|
{
|
||||||
|
/// Get <see cref="City"/> by name and country identifier
|
||||||
|
Task<City?> GetByNameAsync(Guid requestId, string existingCity, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ namespace ApplicationLayer.Services.Locations.NeededServices;
|
|||||||
public interface ICountriesRepository : IGenericRepository<Country>
|
public interface ICountriesRepository : IGenericRepository<Country>
|
||||||
{
|
{
|
||||||
/// Gets country by name
|
/// Gets country by name
|
||||||
/// <param name="countryName">Name of country to seek</param>
|
Task<Country?> FindByNameAsync(string countryName, CancellationToken cancellationToken);
|
||||||
/// <param name="cancellationToken">Cancellation Token</param>
|
|
||||||
/// <returns>Country or null if not found</returns>
|
/// Gets country by identifier
|
||||||
Task<Country?> FindByName(string countryName, CancellationToken cancellationToken);
|
Task<Country?> FindByIdAsync(Guid id, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
using Infrastructure.Database.GeneralExceptions;
|
||||||
|
|
||||||
|
namespace ApplicationLayer.Services.Locations.RequestHandlers.Exceptions
|
||||||
|
{
|
||||||
|
public class CityCanNotBeDeletedException(string cityName)
|
||||||
|
: EntityUsedInDatabaseException($"{cityName} can not be deleted because some applicants live or work in it");
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
using Domains.LocationDomain;
|
||||||
|
using Infrastructure.Database.GeneralExceptions;
|
||||||
|
|
||||||
|
namespace ApplicationLayer.Services.Locations.RequestHandlers.Exceptions
|
||||||
|
{
|
||||||
|
public class CountryNotFoundException(string countryName) : EntityNotFoundException<Country>($"Country {countryName} is not supported.");
|
||||||
|
}
|
||||||
@@ -10,7 +10,10 @@ namespace ApplicationLayer.Services.Locations.RequestHandlers
|
|||||||
/// <returns>List of available countries</returns>
|
/// <returns>List of available countries</returns>
|
||||||
Task<List<Country>> HandleGetRequestAsync(CancellationToken cancellationToken);
|
Task<List<Country>> HandleGetRequestAsync(CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// Handles add country requests
|
/// Handles <see cref="AddCountryRequest"/>
|
||||||
Task AddCountryAsync(AddCountryRequest request, CancellationToken cancellationToken);
|
Task AddCountryAsync(AddCountryRequest request, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// Handles <see cref="UpdateCountryRequest"/>
|
||||||
|
Task UpdateCountryAsync(UpdateCountryRequest request, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using ApplicationLayer.InfrastructureServicesInterfaces;
|
using ApplicationLayer.InfrastructureServicesInterfaces;
|
||||||
|
using ApplicationLayer.Services.Applicants.NeededServices;
|
||||||
using ApplicationLayer.Services.Locations.NeededServices;
|
using ApplicationLayer.Services.Locations.NeededServices;
|
||||||
using ApplicationLayer.Services.Locations.RequestHandlers.Exceptions;
|
using ApplicationLayer.Services.Locations.RequestHandlers.Exceptions;
|
||||||
using ApplicationLayer.Services.Locations.Requests;
|
using ApplicationLayer.Services.Locations.Requests;
|
||||||
@@ -7,7 +8,11 @@ using Domains.LocationDomain;
|
|||||||
namespace ApplicationLayer.Services.Locations.RequestHandlers
|
namespace ApplicationLayer.Services.Locations.RequestHandlers
|
||||||
{
|
{
|
||||||
/// <inheritdoc cref="ILocationRequestsHandler"/>
|
/// <inheritdoc cref="ILocationRequestsHandler"/>
|
||||||
public class LocationRequestsHandler(ICountriesRepository countries, IUnitOfWork unitOfWork) : ILocationRequestsHandler
|
public class LocationRequestsHandler(
|
||||||
|
ICountriesRepository countries,
|
||||||
|
ICitiesRepository cities,
|
||||||
|
IApplicantsRepository applicants,
|
||||||
|
IUnitOfWork unitOfWork) : ILocationRequestsHandler
|
||||||
{
|
{
|
||||||
async Task<List<Country>> ILocationRequestsHandler.HandleGetRequestAsync(CancellationToken cancellationToken)
|
async Task<List<Country>> ILocationRequestsHandler.HandleGetRequestAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@@ -16,7 +21,7 @@ namespace ApplicationLayer.Services.Locations.RequestHandlers
|
|||||||
|
|
||||||
async Task ILocationRequestsHandler.AddCountryAsync(AddCountryRequest request, CancellationToken cancellationToken)
|
async Task ILocationRequestsHandler.AddCountryAsync(AddCountryRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (await countries.FindByName(request.CountryName, cancellationToken) is not null)
|
if (await countries.FindByNameAsync(request.CountryName, cancellationToken) is not null)
|
||||||
{
|
{
|
||||||
throw new CountryAlreadyExists(request.CountryName);
|
throw new CountryAlreadyExists(request.CountryName);
|
||||||
}
|
}
|
||||||
@@ -38,5 +43,49 @@ namespace ApplicationLayer.Services.Locations.RequestHandlers
|
|||||||
|
|
||||||
await unitOfWork.SaveAsync(cancellationToken);
|
await unitOfWork.SaveAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async Task ILocationRequestsHandler.UpdateCountryAsync(UpdateCountryRequest request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (await countries.FindByNameAsync(request.CountryName, cancellationToken) is not null)
|
||||||
|
{
|
||||||
|
throw new CountryAlreadyExists(request.CountryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var country = await countries.FindByIdAsync(request.Id, cancellationToken);
|
||||||
|
if (country is null)
|
||||||
|
{
|
||||||
|
throw new CountryNotFoundException(request.CountryName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingCities = country.Cities;
|
||||||
|
var citiesToAdd = request.Cities.Except(existingCities.Select(c => c.Name)).ToList();
|
||||||
|
var citiesToRemove = existingCities.Where(c => !request.Cities.Contains(c.Name));
|
||||||
|
var applicantsList = await applicants.GetAllAsync(cancellationToken);
|
||||||
|
|
||||||
|
//todo mapper
|
||||||
|
foreach (var city in citiesToRemove)
|
||||||
|
{
|
||||||
|
if (applicantsList.All(a => a.CityOfBirth.Id != city.Id && a.PlaceOfWork.Address.City.Id != city.Id))
|
||||||
|
{
|
||||||
|
cities.Remove(city);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new CityCanNotBeDeletedException(city.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var city in citiesToAdd)
|
||||||
|
{
|
||||||
|
await cities.AddAsync(new City { Name = city, Country = country }, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
country.Name = request.CountryName;
|
||||||
|
country.IsSchengen = request.IsSchengen;
|
||||||
|
|
||||||
|
await countries.UpdateAsync(country, cancellationToken);
|
||||||
|
|
||||||
|
await unitOfWork.SaveAsync(cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
namespace ApplicationLayer.Services.Locations.Requests
|
||||||
|
{
|
||||||
|
public record UpdateCountryRequest(Guid Id, string CountryName, bool IsSchengen, string[] Cities);
|
||||||
|
}
|
||||||
@@ -12,4 +12,7 @@ public sealed class CitiesRepository(IGenericReader reader, IGenericWriter write
|
|||||||
{
|
{
|
||||||
return base.LoadDomain().Include(c => c.Country);
|
return base.LoadDomain().Include(c => c.Country);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Task<City?> ICitiesRepository.GetByNameAsync(Guid countryId, string cityName, CancellationToken cancellationToken)
|
||||||
|
=> LoadDomain().SingleOrDefaultAsync(c => c.Country.Id == countryId && c.Name == cityName, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,9 +13,15 @@ public sealed class CountriesRepository(IGenericReader reader, IGenericWriter wr
|
|||||||
return base.LoadDomain().Include(c => c.Cities);
|
return base.LoadDomain().Include(c => c.Cities);
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task<Country?> ICountriesRepository.FindByName(string countryName, CancellationToken cancellationToken)
|
async Task<Country?> ICountriesRepository.FindByNameAsync(string countryName, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var result = await LoadDomain().SingleOrDefaultAsync(c => c.Name == countryName, cancellationToken);
|
var result = await LoadDomain().SingleOrDefaultAsync(c => c.Name == countryName, cancellationToken);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async Task<Country?> ICountriesRepository.FindByIdAsync(Guid id, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var result = await LoadDomain().SingleOrDefaultAsync(c => c.Id == id, cancellationToken);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace SchengenVisaApi.Controllers
|
|||||||
{
|
{
|
||||||
/// Controller for <see cref="Domains.LocationDomain"/>
|
/// Controller for <see cref="Domains.LocationDomain"/>
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("countries")]
|
[Route("locations")]
|
||||||
public class LocationsController(ILocationRequestsHandler requestsHandler) : ControllerBase
|
public class LocationsController(ILocationRequestsHandler requestsHandler) : ControllerBase
|
||||||
{
|
{
|
||||||
/// Return countries with cities from DB
|
/// Return countries with cities from DB
|
||||||
@@ -35,5 +35,19 @@ namespace SchengenVisaApi.Controllers
|
|||||||
await requestsHandler.AddCountryAsync(request, cancellationToken);
|
await requestsHandler.AddCountryAsync(request, cancellationToken);
|
||||||
return Ok();
|
return Ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Updates country with cities in DB
|
||||||
|
[HttpPut]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
|
||||||
|
[Route("country")]
|
||||||
|
[Authorize(policy: PolicyConstants.AdminPolicy)]
|
||||||
|
public async Task<IActionResult> UpdateCountry(UpdateCountryRequest request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await requestsHandler.UpdateCountryAsync(request, cancellationToken);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,11 @@ namespace SchengenVisaApi.ExceptionFilters
|
|||||||
problemDetails.Title = "Can not add cities with one name to one country";
|
problemDetails.Title = "Can not add cities with one name to one country";
|
||||||
problemDetails.Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1";
|
problemDetails.Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.1";
|
||||||
break;
|
break;
|
||||||
|
case EntityUsedInDatabaseException:
|
||||||
|
problemDetails.Status = StatusCodes.Status409Conflict;
|
||||||
|
problemDetails.Title = "entity is used by someone";
|
||||||
|
problemDetails.Type = "https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.8";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
problemDetails.Status = StatusCodes.Status400BadRequest;
|
problemDetails.Status = StatusCodes.Status400BadRequest;
|
||||||
problemDetails.Title = "Bad request";
|
problemDetails.Title = "Bad request";
|
||||||
|
|||||||
Reference in New Issue
Block a user