diff --git a/src/Application/Addresses/AddressDto.cs b/src/Application/Addresses/AddressDto.cs new file mode 100644 index 0000000..5d936bf --- /dev/null +++ b/src/Application/Addresses/AddressDto.cs @@ -0,0 +1,58 @@ +using cuqmbr.TravelGuide.Application.Common.Mappings; +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Application.Addresses; + +public sealed class AddressDto : IMapFrom
+{ + public Guid Uuid { get; set; } + + public string Name { get; set; } + + public double Longitude { get; set; } + + public double Latitude { get; set; } + + public string VehicleType { get; set; } + + public Guid CountryUuid { get; set; } + + public string CountryName { get; set; } + + public Guid RegionUuid { get; set; } + + public string RegionName { get; set; } + + public Guid CityUuid { get; set; } + + public string CityName { get; set; } + + public void Mapping(MappingProfile profile) + { + profile.CreateMap() + .ForMember( + d => d.Uuid, + opt => opt.MapFrom(s => s.Guid)) + .ForMember( + d => d.VehicleType, + opt => opt.MapFrom(s => s.VehicleType.Name)) + .ForMember( + d => d.CountryUuid, + opt => opt.MapFrom(s => s.City.Region.Country.Guid)) + .ForMember( + d => d.CountryName, + opt => opt.MapFrom(s => s.City.Region.Country.Name)) + .ForMember( + d => d.RegionUuid, + opt => opt.MapFrom(s => s.City.Region.Guid)) + .ForMember( + d => d.RegionName, + opt => opt.MapFrom(s => s.City.Region.Name)) + .ForMember( + d => d.CityUuid, + opt => opt.MapFrom(s => s.City.Guid)) + .ForMember( + d => d.CityName, + opt => opt.MapFrom(s => s.City.Name)); + } +} diff --git a/src/Application/Addresses/Commands/AddAddress/AddAddressCommand.cs b/src/Application/Addresses/Commands/AddAddress/AddAddressCommand.cs new file mode 100644 index 0000000..2b1a2f5 --- /dev/null +++ b/src/Application/Addresses/Commands/AddAddress/AddAddressCommand.cs @@ -0,0 +1,17 @@ +using cuqmbr.TravelGuide.Domain.Enums; +using MediatR; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.AddAddress; + +public record AddAddressCommand : IRequest +{ + public string Name { get; set; } + + public double Longitude { get; set; } + + public double Latitude { get; set; } + + public VehicleType VehicleType { get; set; } + + public Guid CityGuid { get; set; } +} diff --git a/src/Application/Addresses/Commands/AddAddress/AddAddressCommandAuthorizer.cs b/src/Application/Addresses/Commands/AddAddress/AddAddressCommandAuthorizer.cs new file mode 100644 index 0000000..eb8ae61 --- /dev/null +++ b/src/Application/Addresses/Commands/AddAddress/AddAddressCommandAuthorizer.cs @@ -0,0 +1,31 @@ +using cuqmbr.TravelGuide.Application.Common.Authorization; +using cuqmbr.TravelGuide.Application.Common.Interfaces.Services; +using cuqmbr.TravelGuide.Application.Common.Models; +using MediatR.Behaviors.Authorization; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.AddAddress; + +public class AddAddressCommandAuthorizer : + AbstractRequestAuthorizer +{ + private readonly SessionUserService _sessionUserService; + + public AddAddressCommandAuthorizer(SessionUserService sessionUserService) + { + _sessionUserService = sessionUserService; + } + + public override void BuildPolicy(AddAddressCommand request) + { + UseRequirement(new MustBeAuthenticatedRequirement + { + IsAuthenticated= _sessionUserService.IsAuthenticated + }); + + UseRequirement(new MustBeInRolesRequirement + { + RequiredRoles = [IdentityRole.Administrator], + UserRoles = _sessionUserService.Roles + }); + } +} diff --git a/src/Application/Addresses/Commands/AddAddress/AddAddressCommandHandler.cs b/src/Application/Addresses/Commands/AddAddress/AddAddressCommandHandler.cs new file mode 100644 index 0000000..5aaf4f9 --- /dev/null +++ b/src/Application/Addresses/Commands/AddAddress/AddAddressCommandHandler.cs @@ -0,0 +1,64 @@ +using MediatR; +using cuqmbr.TravelGuide.Application.Common.Interfaces.Persistence; +using cuqmbr.TravelGuide.Domain.Entities; +using AutoMapper; +using cuqmbr.TravelGuide.Application.Common.Exceptions; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.AddAddress; + +public class AddAddressCommandHandler : + IRequestHandler +{ + private readonly UnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public AddAddressCommandHandler( + UnitOfWork unitOfWork, + IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task Handle( + AddAddressCommand request, + CancellationToken cancellationToken) + { + var entity = await _unitOfWork.AddressRepository.GetOneAsync( + e => e.Name == request.Name && e.City.Guid == request.CityGuid, + cancellationToken); + + if (entity != null) + { + throw new DuplicateEntityException( + "Address with given name already exists."); + } + + var parentEntity = await _unitOfWork.CityRepository.GetOneAsync( + e => e.Guid == request.CityGuid, e => e.Region.Country, + cancellationToken); + + if (parentEntity == null) + { + throw new NotFoundException( + $"Parent entity with Guid: {request.CityGuid} not found."); + } + + entity = new Address() + { + Name = request.Name, + Longitude = request.Longitude, + Latitude = request.Latitude, + VehicleType = request.VehicleType, + CityId = parentEntity.Id + }; + + entity = await _unitOfWork.AddressRepository.AddOneAsync( + entity, cancellationToken); + + await _unitOfWork.SaveAsync(cancellationToken); + _unitOfWork.Dispose(); + + return _mapper.Map(entity); + } +} diff --git a/src/Application/Addresses/Commands/AddAddress/AddAddressCommandValidator.cs b/src/Application/Addresses/Commands/AddAddress/AddAddressCommandValidator.cs new file mode 100644 index 0000000..2ca5840 --- /dev/null +++ b/src/Application/Addresses/Commands/AddAddress/AddAddressCommandValidator.cs @@ -0,0 +1,65 @@ +using cuqmbr.TravelGuide.Application.Common.Interfaces.Services; +using cuqmbr.TravelGuide.Domain.Enums; +using FluentValidation; +using Microsoft.Extensions.Localization; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.AddAddress; + +public class AddAddressCommandValidator : AbstractValidator +{ + public AddAddressCommandValidator( + IStringLocalizer localizer, + CultureService cultureService) + { + RuleFor(v => v.Name) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]) + .MaximumLength(64) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.MaximumLength"], + 64)); + + RuleFor(v => v.Latitude) + .GreaterThanOrEqualTo(-90) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + -90)) + .LessThanOrEqualTo(90) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.LessThanOrEqualTo"], + 90)); + + RuleFor(v => v.Longitude) + .GreaterThanOrEqualTo(-180) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + -180)) + .LessThanOrEqualTo(180) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.LessThanOrEqualTo"], + 180)); + + RuleFor(v => v.VehicleType) + .Must((v, vt) => VehicleType.Enumerations.ContainsValue(vt)) + .WithMessage( + String.Format( + localizer["FluentValidation.MustBeInEnum"], + String.Join( + ", ", + VehicleType.Enumerations.Values.Select(e => e.Name)))); + + RuleFor(v => v.CityGuid) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]); + } +} diff --git a/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommand.cs b/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommand.cs new file mode 100644 index 0000000..87241a4 --- /dev/null +++ b/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.DeleteAddress; + +public record DeleteAddressCommand : IRequest +{ + public Guid Guid { get; set; } +} diff --git a/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandAuthorizer.cs b/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandAuthorizer.cs new file mode 100644 index 0000000..e8e04ca --- /dev/null +++ b/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandAuthorizer.cs @@ -0,0 +1,31 @@ +using cuqmbr.TravelGuide.Application.Common.Authorization; +using cuqmbr.TravelGuide.Application.Common.Interfaces.Services; +using cuqmbr.TravelGuide.Application.Common.Models; +using MediatR.Behaviors.Authorization; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.DeleteAddress; + +public class DeleteAddressCommandAuthorizer : + AbstractRequestAuthorizer +{ + private readonly SessionUserService _sessionUserService; + + public DeleteAddressCommandAuthorizer(SessionUserService sessionUserService) + { + _sessionUserService = sessionUserService; + } + + public override void BuildPolicy(DeleteAddressCommand request) + { + UseRequirement(new MustBeAuthenticatedRequirement + { + IsAuthenticated= _sessionUserService.IsAuthenticated + }); + + UseRequirement(new MustBeInRolesRequirement + { + RequiredRoles = [IdentityRole.Administrator], + UserRoles = _sessionUserService.Roles + }); + } +} diff --git a/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandHandler.cs b/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandHandler.cs new file mode 100644 index 0000000..428b7fa --- /dev/null +++ b/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandHandler.cs @@ -0,0 +1,34 @@ +using MediatR; +using cuqmbr.TravelGuide.Application.Common.Interfaces.Persistence; +using cuqmbr.TravelGuide.Application.Common.Exceptions; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.DeleteAddress; + +public class DeleteAddressCommandHandler : IRequestHandler +{ + private readonly UnitOfWork _unitOfWork; + + public DeleteAddressCommandHandler(UnitOfWork unitOfWork) + { + _unitOfWork = unitOfWork; + } + + public async Task Handle( + DeleteAddressCommand request, + CancellationToken cancellationToken) + { + var entity = await _unitOfWork.AddressRepository.GetOneAsync( + e => e.Guid == request.Guid, cancellationToken); + + if (entity == null) + { + throw new NotFoundException(); + } + + await _unitOfWork.AddressRepository.DeleteOneAsync( + entity, cancellationToken); + + await _unitOfWork.SaveAsync(cancellationToken); + _unitOfWork.Dispose(); + } +} diff --git a/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandValidator.cs b/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandValidator.cs new file mode 100644 index 0000000..10cec3c --- /dev/null +++ b/src/Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using Microsoft.Extensions.Localization; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.DeleteAddress; + +public class DeleteAddressCommandValidator : AbstractValidator +{ + public DeleteAddressCommandValidator(IStringLocalizer localizer) + { + RuleFor(v => v.Guid) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]); + } +} diff --git a/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommand.cs b/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommand.cs new file mode 100644 index 0000000..b44f8cf --- /dev/null +++ b/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommand.cs @@ -0,0 +1,19 @@ +using MediatR; +using cuqmbr.TravelGuide.Domain.Enums; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.UpdateAddress; + +public record UpdateAddressCommand : IRequest +{ + public Guid Guid { get; set; } + + public string Name { get; set; } + + public double Longitude { get; set; } + + public double Latitude { get; set; } + + public VehicleType VehicleType { get; set; } + + public Guid CityGuid { get; set; } +} diff --git a/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandAuthorizer.cs b/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandAuthorizer.cs new file mode 100644 index 0000000..1063a38 --- /dev/null +++ b/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandAuthorizer.cs @@ -0,0 +1,31 @@ +using cuqmbr.TravelGuide.Application.Common.Authorization; +using cuqmbr.TravelGuide.Application.Common.Interfaces.Services; +using cuqmbr.TravelGuide.Application.Common.Models; +using MediatR.Behaviors.Authorization; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.UpdateAddress; + +public class UpdateAddressCommandAuthorizer : + AbstractRequestAuthorizer +{ + private readonly SessionUserService _sessionUserService; + + public UpdateAddressCommandAuthorizer(SessionUserService sessionUserService) + { + _sessionUserService = sessionUserService; + } + + public override void BuildPolicy(UpdateAddressCommand request) + { + UseRequirement(new MustBeAuthenticatedRequirement + { + IsAuthenticated= _sessionUserService.IsAuthenticated + }); + + UseRequirement(new MustBeInRolesRequirement + { + RequiredRoles = [IdentityRole.Administrator], + UserRoles = _sessionUserService.Roles + }); + } +} diff --git a/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandHandler.cs b/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandHandler.cs new file mode 100644 index 0000000..eba7089 --- /dev/null +++ b/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandHandler.cs @@ -0,0 +1,58 @@ +using MediatR; +using cuqmbr.TravelGuide.Application.Common.Interfaces.Persistence; +using AutoMapper; +using cuqmbr.TravelGuide.Application.Common.Exceptions; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.UpdateAddress; + +public class UpdateAddressCommandHandler : + IRequestHandler +{ + private readonly UnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public UpdateAddressCommandHandler( + UnitOfWork unitOfWork, + IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task Handle( + UpdateAddressCommand request, + CancellationToken cancellationToken) + { + var entity = await _unitOfWork.AddressRepository.GetOneAsync( + e => e.Guid == request.Guid, e => e.City.Region.Country, + cancellationToken); + + if (entity == null) + { + throw new NotFoundException(); + } + + var parentEntity = await _unitOfWork.CityRepository.GetOneAsync( + e => e.Guid == request.CityGuid, cancellationToken); + + if (parentEntity == null) + { + throw new NotFoundException( + $"Parent entity with Guid: {request.CityGuid} not found."); + } + + entity.Name = request.Name; + entity.Longitude = request.Longitude; + entity.Latitude = request.Latitude; + entity.VehicleType = request.VehicleType; + entity.CityId = parentEntity.Id; + + entity = await _unitOfWork.AddressRepository.UpdateOneAsync( + entity, cancellationToken); + + await _unitOfWork.SaveAsync(cancellationToken); + _unitOfWork.Dispose(); + + return _mapper.Map(entity); + } +} diff --git a/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandValidator.cs b/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandValidator.cs new file mode 100644 index 0000000..59770c9 --- /dev/null +++ b/src/Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandValidator.cs @@ -0,0 +1,31 @@ +using cuqmbr.TravelGuide.Application.Common.Interfaces.Services; +using FluentValidation; +using Microsoft.Extensions.Localization; + +namespace cuqmbr.TravelGuide.Application.Addresses.Commands.UpdateAddress; + +public class UpdateAddressCommandValidator : AbstractValidator +{ + public UpdateAddressCommandValidator( + IStringLocalizer localizer, + CultureService cultureService) + { + RuleFor(v => v.Guid) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]); + + RuleFor(v => v.Name) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]) + .MaximumLength(64) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.MaximumLength"], + 64)); + + RuleFor(v => v.CityGuid) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]); + } +} diff --git a/src/Application/Addresses/Queries/GetAddress/GetAddressQuery.cs b/src/Application/Addresses/Queries/GetAddress/GetAddressQuery.cs new file mode 100644 index 0000000..fa665c4 --- /dev/null +++ b/src/Application/Addresses/Queries/GetAddress/GetAddressQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace cuqmbr.TravelGuide.Application.Addresses.Queries.GetAddress; + +public record GetAddressQuery : IRequest +{ + public Guid Guid { get; set; } +} diff --git a/src/Application/Addresses/Queries/GetAddress/GetAddressQueryAuthorizer.cs b/src/Application/Addresses/Queries/GetAddress/GetAddressQueryAuthorizer.cs new file mode 100644 index 0000000..545dd25 --- /dev/null +++ b/src/Application/Addresses/Queries/GetAddress/GetAddressQueryAuthorizer.cs @@ -0,0 +1,31 @@ +using cuqmbr.TravelGuide.Application.Common.Authorization; +using cuqmbr.TravelGuide.Application.Common.Interfaces.Services; +using cuqmbr.TravelGuide.Application.Common.Models; +using MediatR.Behaviors.Authorization; + +namespace cuqmbr.TravelGuide.Application.Addresses.Queries.GetAddress; + +public class GetAddressQueryAuthorizer : + AbstractRequestAuthorizer +{ + private readonly SessionUserService _sessionUserService; + + public GetAddressQueryAuthorizer(SessionUserService sessionUserService) + { + _sessionUserService = sessionUserService; + } + + public override void BuildPolicy(GetAddressQuery request) + { + UseRequirement(new MustBeAuthenticatedRequirement + { + IsAuthenticated= _sessionUserService.IsAuthenticated + }); + + UseRequirement(new MustBeInRolesRequirement + { + RequiredRoles = [IdentityRole.Administrator], + UserRoles = _sessionUserService.Roles + }); + } +} diff --git a/src/Application/Addresses/Queries/GetAddress/GetAddressQueryHandler.cs b/src/Application/Addresses/Queries/GetAddress/GetAddressQueryHandler.cs new file mode 100644 index 0000000..f0f5a86 --- /dev/null +++ b/src/Application/Addresses/Queries/GetAddress/GetAddressQueryHandler.cs @@ -0,0 +1,39 @@ +using MediatR; +using cuqmbr.TravelGuide.Application.Common.Interfaces.Persistence; +using cuqmbr.TravelGuide.Application.Common.Exceptions; +using AutoMapper; + +namespace cuqmbr.TravelGuide.Application.Addresses.Queries.GetAddress; + +public class GetAddressQueryHandler : + IRequestHandler +{ + private readonly UnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public GetAddressQueryHandler( + UnitOfWork unitOfWork, + IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task Handle( + GetAddressQuery request, + CancellationToken cancellationToken) + { + var entity = await _unitOfWork.AddressRepository.GetOneAsync( + e => e.Guid == request.Guid, e => e.City.Region.Country, + cancellationToken); + + _unitOfWork.Dispose(); + + if (entity == null) + { + throw new NotFoundException(); + } + + return _mapper.Map(entity); + } +} diff --git a/src/Application/Addresses/Queries/GetAddress/GetAddressQueryValidator.cs b/src/Application/Addresses/Queries/GetAddress/GetAddressQueryValidator.cs new file mode 100644 index 0000000..5e97a2e --- /dev/null +++ b/src/Application/Addresses/Queries/GetAddress/GetAddressQueryValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using Microsoft.Extensions.Localization; + +namespace cuqmbr.TravelGuide.Application.Addresses.Queries.GetAddress; + +public class GetAddressQueryValidator : AbstractValidator +{ + public GetAddressQueryValidator(IStringLocalizer localizer) + { + RuleFor(v => v.Guid) + .NotEmpty() + .WithMessage(localizer["FluentValidation.NotEmpty"]); + } +} diff --git a/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQuery.cs b/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQuery.cs new file mode 100644 index 0000000..deaae5a --- /dev/null +++ b/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQuery.cs @@ -0,0 +1,21 @@ +using cuqmbr.TravelGuide.Application.Common.Models; +using MediatR; + +namespace cuqmbr.TravelGuide.Application.Addresses.Queries.GetAddressesPage; + +public record GetAddressesPageQuery : IRequest> +{ + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; + + public string Search { get; set; } = String.Empty; + + public string Sort { get; set; } = String.Empty; + + public Guid? CountryGuid { get; set; } + + public Guid? RegionGuid { get; set; } + + public Guid? CityGuid { get; set; } +} diff --git a/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQueryAuthorizer.cs b/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQueryAuthorizer.cs new file mode 100644 index 0000000..b8d21da --- /dev/null +++ b/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQueryAuthorizer.cs @@ -0,0 +1,31 @@ +using cuqmbr.TravelGuide.Application.Common.Authorization; +using cuqmbr.TravelGuide.Application.Common.Interfaces.Services; +using cuqmbr.TravelGuide.Application.Common.Models; +using MediatR.Behaviors.Authorization; + +namespace cuqmbr.TravelGuide.Application.Addresses.Queries.GetAddressesPage; + +public class GetAddressesPageQueryAuthorizer : + AbstractRequestAuthorizer +{ + private readonly SessionUserService _sessionUserService; + + public GetAddressesPageQueryAuthorizer(SessionUserService sessionUserService) + { + _sessionUserService = sessionUserService; + } + + public override void BuildPolicy(GetAddressesPageQuery request) + { + UseRequirement(new MustBeAuthenticatedRequirement + { + IsAuthenticated= _sessionUserService.IsAuthenticated + }); + + UseRequirement(new MustBeInRolesRequirement + { + RequiredRoles = [IdentityRole.Administrator], + UserRoles = _sessionUserService.Roles + }); + } +} diff --git a/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQueryHandler.cs b/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQueryHandler.cs new file mode 100644 index 0000000..0be0bca --- /dev/null +++ b/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQueryHandler.cs @@ -0,0 +1,59 @@ +using MediatR; +using cuqmbr.TravelGuide.Application.Common.Interfaces.Persistence; +using AutoMapper; +using cuqmbr.TravelGuide.Application.Common.Models; +using cuqmbr.TravelGuide.Application.Common.Extensions; + +namespace cuqmbr.TravelGuide.Application.Addresses.Queries.GetAddressesPage; + +public class GetAddressesPageQueryHandler : + IRequestHandler> +{ + private readonly UnitOfWork _unitOfWork; + private readonly IMapper _mapper; + + public GetAddressesPageQueryHandler( + UnitOfWork unitOfWork, + IMapper mapper) + { + _unitOfWork = unitOfWork; + _mapper = mapper; + } + + public async Task> Handle( + GetAddressesPageQuery request, + CancellationToken cancellationToken) + { + var paginatedList = await _unitOfWork.AddressRepository.GetPageAsync( + e => + (e.Name.ToLower().Contains(request.Search.ToLower()) || + e.City.Name.ToLower().Contains(request.Search.ToLower()) || + e.City.Region.Name.ToLower().Contains(request.Search.ToLower()) || + e.City.Region.Country.Name.ToLower().Contains(request.Search.ToLower())) && + (request.CityGuid != null + ? e.City.Guid == request.CityGuid + : true) && + (request.RegionGuid != null + ? e.City.Region.Guid == request.RegionGuid + : true) && + (request.CountryGuid != null + ? e.City.Region.Country.Guid == request.CountryGuid + : true), + e => e.City.Region.Country, + request.PageNumber, request.PageSize, + cancellationToken); + + var mappedItems = _mapper + .ProjectTo(paginatedList.Items.AsQueryable()); + + mappedItems = QueryableExtension + .ApplySort(mappedItems, request.Sort); + + _unitOfWork.Dispose(); + + return new PaginatedList( + mappedItems.ToList(), + paginatedList.TotalCount, request.PageNumber, + request.PageSize); + } +} diff --git a/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQueryValidator.cs b/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQueryValidator.cs new file mode 100644 index 0000000..af43f25 --- /dev/null +++ b/src/Application/Addresses/Queries/GetAddressesPage/GetAddressesPageQueryValidator.cs @@ -0,0 +1,43 @@ +using cuqmbr.TravelGuide.Application.Common.Interfaces.Services; +using FluentValidation; +using Microsoft.Extensions.Localization; + +namespace cuqmbr.TravelGuide.Application.Addresses.Queries.GetAddressesPage; + +public class GetAddressesPageQueryValidator : AbstractValidator +{ + public GetAddressesPageQueryValidator( + IStringLocalizer localizer, + CultureService cultureService) + { + RuleFor(v => v.PageNumber) + .GreaterThanOrEqualTo(1) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + 1)); + + RuleFor(v => v.PageSize) + .GreaterThanOrEqualTo(1) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.GreaterThanOrEqualTo"], + 1)) + .LessThanOrEqualTo(50) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.LessThanOrEqualTo"], + 50)); + + RuleFor(v => v.Search) + .MaximumLength(64) + .WithMessage( + String.Format( + cultureService.Culture, + localizer["FluentValidation.MaximumLength"], + 64)); + } +} diff --git a/src/Application/Addresses/ViewModels/AddAddressViewModel.cs b/src/Application/Addresses/ViewModels/AddAddressViewModel.cs new file mode 100644 index 0000000..904431c --- /dev/null +++ b/src/Application/Addresses/ViewModels/AddAddressViewModel.cs @@ -0,0 +1,14 @@ +namespace cuqmbr.TravelGuide.Application.Addresses.ViewModels; + +public sealed class AddAddressViewModel +{ + public string Name { get; set; } + + public double Longitude { get; set; } + + public double Latitude { get; set; } + + public string VehicleType { get; set; } + + public Guid CityUuid { get; set; } +} diff --git a/src/Application/Addresses/ViewModels/GetAddressesPageFilterViewModel.cs b/src/Application/Addresses/ViewModels/GetAddressesPageFilterViewModel.cs new file mode 100644 index 0000000..610f6c9 --- /dev/null +++ b/src/Application/Addresses/ViewModels/GetAddressesPageFilterViewModel.cs @@ -0,0 +1,10 @@ +namespace cuqmbr.TravelGuide.Application.Addresses.ViewModels; + +public sealed class GetAddressesPageFilterViewModel +{ + public Guid? CountryUuid { get; set; } + + public Guid? RegionUuid { get; set; } + + public Guid? CityUuid { get; set; } +} diff --git a/src/Application/Addresses/ViewModels/UpdateAddressViewModel.cs b/src/Application/Addresses/ViewModels/UpdateAddressViewModel.cs new file mode 100644 index 0000000..6bcb4e4 --- /dev/null +++ b/src/Application/Addresses/ViewModels/UpdateAddressViewModel.cs @@ -0,0 +1,14 @@ +namespace cuqmbr.TravelGuide.Application.Addresses.ViewModels; + +public sealed class UpdateAddressViewModel +{ + public string Name { get; set; } + + public double Longitude { get; set; } + + public double Latitude { get; set; } + + public string VehicleType { get; set; } + + public Guid CityUuid { get; set; } +} diff --git a/src/Application/Cities/Commands/AddCity/AddCityCommand.cs b/src/Application/Cities/Commands/AddCity/AddCityCommand.cs index 3530aec..72152a3 100644 --- a/src/Application/Cities/Commands/AddCity/AddCityCommand.cs +++ b/src/Application/Cities/Commands/AddCity/AddCityCommand.cs @@ -6,5 +6,5 @@ public record AddCityCommand : IRequest { public string Name { get; set; } - public Guid RegionUuid { get; set; } + public Guid RegionGuid { get; set; } } diff --git a/src/Application/Cities/Commands/AddCity/AddCityCommandHandler.cs b/src/Application/Cities/Commands/AddCity/AddCityCommandHandler.cs index fa93931..51584b4 100644 --- a/src/Application/Cities/Commands/AddCity/AddCityCommandHandler.cs +++ b/src/Application/Cities/Commands/AddCity/AddCityCommandHandler.cs @@ -25,7 +25,7 @@ public class AddCityCommandHandler : CancellationToken cancellationToken) { var entity = await _unitOfWork.CityRepository.GetOneAsync( - e => e.Name == request.Name && e.Region.Guid == request.RegionUuid, + e => e.Name == request.Name && e.Region.Guid == request.RegionGuid, cancellationToken); if (entity != null) @@ -35,13 +35,13 @@ public class AddCityCommandHandler : } var parentEntity = await _unitOfWork.RegionRepository.GetOneAsync( - e => e.Guid == request.RegionUuid, e => e.Country, + e => e.Guid == request.RegionGuid, e => e.Country, cancellationToken); if (parentEntity == null) { throw new NotFoundException( - $"Parent entity with Guid: {request.RegionUuid} not found."); + $"Parent entity with Guid: {request.RegionGuid} not found."); } entity = new City() diff --git a/src/Application/Cities/Commands/AddCity/AddCityCommandValidator.cs b/src/Application/Cities/Commands/AddCity/AddCityCommandValidator.cs index e764345..2a0a931 100644 --- a/src/Application/Cities/Commands/AddCity/AddCityCommandValidator.cs +++ b/src/Application/Cities/Commands/AddCity/AddCityCommandValidator.cs @@ -20,7 +20,7 @@ public class AddCityCommandValidator : AbstractValidator localizer["FluentValidation.MaximumLength"], 64)); - RuleFor(v => v.RegionUuid) + RuleFor(v => v.RegionGuid) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]); } diff --git a/src/Application/Cities/Commands/DeleteCity/DeleteCityCommand.cs b/src/Application/Cities/Commands/DeleteCity/DeleteCityCommand.cs index a8ee4d0..94dbf43 100644 --- a/src/Application/Cities/Commands/DeleteCity/DeleteCityCommand.cs +++ b/src/Application/Cities/Commands/DeleteCity/DeleteCityCommand.cs @@ -4,5 +4,5 @@ namespace cuqmbr.TravelGuide.Application.Cities.Commands.DeleteCity; public record DeleteCityCommand : IRequest { - public Guid Uuid { get; set; } + public Guid Guid { get; set; } } diff --git a/src/Application/Cities/Commands/DeleteCity/DeleteCityCommandHandler.cs b/src/Application/Cities/Commands/DeleteCity/DeleteCityCommandHandler.cs index 26dc751..700334d 100644 --- a/src/Application/Cities/Commands/DeleteCity/DeleteCityCommandHandler.cs +++ b/src/Application/Cities/Commands/DeleteCity/DeleteCityCommandHandler.cs @@ -18,7 +18,7 @@ public class DeleteCityCommandHandler : IRequestHandler CancellationToken cancellationToken) { var entity = await _unitOfWork.CityRepository.GetOneAsync( - e => e.Guid == request.Uuid, cancellationToken); + e => e.Guid == request.Guid, cancellationToken); if (entity == null) { diff --git a/src/Application/Cities/Commands/DeleteCity/DeleteCityCommandValidator.cs b/src/Application/Cities/Commands/DeleteCity/DeleteCityCommandValidator.cs index 292d274..582aa0b 100644 --- a/src/Application/Cities/Commands/DeleteCity/DeleteCityCommandValidator.cs +++ b/src/Application/Cities/Commands/DeleteCity/DeleteCityCommandValidator.cs @@ -7,7 +7,7 @@ public class DeleteCityCommandValidator : AbstractValidator { public DeleteCityCommandValidator(IStringLocalizer localizer) { - RuleFor(v => v.Uuid) + RuleFor(v => v.Guid) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]); } diff --git a/src/Application/Cities/Commands/UpdateCity/UpdateCityCommand.cs b/src/Application/Cities/Commands/UpdateCity/UpdateCityCommand.cs index 992cfc1..acafa27 100644 --- a/src/Application/Cities/Commands/UpdateCity/UpdateCityCommand.cs +++ b/src/Application/Cities/Commands/UpdateCity/UpdateCityCommand.cs @@ -4,9 +4,9 @@ namespace cuqmbr.TravelGuide.Application.Cities.Commands.UpdateCity; public record UpdateCityCommand : IRequest { - public Guid Uuid { get; set; } + public Guid Guid { get; set; } public string Name { get; set; } - public Guid RegionUuid { get; set; } + public Guid RegionGuid { get; set; } } diff --git a/src/Application/Cities/Commands/UpdateCity/UpdateCityCommandHandler.cs b/src/Application/Cities/Commands/UpdateCity/UpdateCityCommandHandler.cs index 67a788f..5fecbc5 100644 --- a/src/Application/Cities/Commands/UpdateCity/UpdateCityCommandHandler.cs +++ b/src/Application/Cities/Commands/UpdateCity/UpdateCityCommandHandler.cs @@ -24,7 +24,7 @@ public class UpdateCityCommandHandler : CancellationToken cancellationToken) { var entity = await _unitOfWork.CityRepository.GetOneAsync( - e => e.Guid == request.Uuid, e => e.Region.Country, + e => e.Guid == request.Guid, e => e.Region.Country, cancellationToken); if (entity == null) @@ -33,12 +33,12 @@ public class UpdateCityCommandHandler : } var parentEntity = await _unitOfWork.RegionRepository.GetOneAsync( - e => e.Guid == request.RegionUuid, cancellationToken); + e => e.Guid == request.RegionGuid, cancellationToken); if (parentEntity == null) { throw new NotFoundException( - $"Parent entity with Guid: {request.RegionUuid} not found."); + $"Parent entity with Guid: {request.RegionGuid} not found."); } entity.Name = request.Name; diff --git a/src/Application/Cities/Commands/UpdateCity/UpdateCityCommandValidator.cs b/src/Application/Cities/Commands/UpdateCity/UpdateCityCommandValidator.cs index c70d946..b813ca2 100644 --- a/src/Application/Cities/Commands/UpdateCity/UpdateCityCommandValidator.cs +++ b/src/Application/Cities/Commands/UpdateCity/UpdateCityCommandValidator.cs @@ -10,7 +10,7 @@ public class UpdateCityCommandValidator : AbstractValidator IStringLocalizer localizer, CultureService cultureService) { - RuleFor(v => v.Uuid) + RuleFor(v => v.Guid) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]); @@ -24,7 +24,7 @@ public class UpdateCityCommandValidator : AbstractValidator localizer["FluentValidation.MaximumLength"], 64)); - RuleFor(v => v.RegionUuid) + RuleFor(v => v.RegionGuid) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]); } diff --git a/src/Application/Cities/Queries/GetCitiesPage/GetCitiesPageQuery.cs b/src/Application/Cities/Queries/GetCitiesPage/GetCitiesPageQuery.cs index 23e2c7d..2fdcac5 100644 --- a/src/Application/Cities/Queries/GetCitiesPage/GetCitiesPageQuery.cs +++ b/src/Application/Cities/Queries/GetCitiesPage/GetCitiesPageQuery.cs @@ -13,7 +13,7 @@ public record GetCitiesPageQuery : IRequest> public string Sort { get; set; } = String.Empty; - public Guid? CountryUuid { get; set; } + public Guid? CountryGuid { get; set; } - public Guid? RegionUuid { get; set; } + public Guid? RegionGuid { get; set; } } diff --git a/src/Application/Cities/Queries/GetCitiesPage/GetCitiesPageQueryHandler.cs b/src/Application/Cities/Queries/GetCitiesPage/GetCitiesPageQueryHandler.cs index 91b5daa..3d4d568 100644 --- a/src/Application/Cities/Queries/GetCitiesPage/GetCitiesPageQueryHandler.cs +++ b/src/Application/Cities/Queries/GetCitiesPage/GetCitiesPageQueryHandler.cs @@ -29,11 +29,11 @@ public class GetCitiesPageQueryHandler : (e.Name.ToLower().Contains(request.Search.ToLower()) || e.Region.Name.ToLower().Contains(request.Search.ToLower()) || e.Region.Country.Name.ToLower().Contains(request.Search.ToLower())) && - (request.RegionUuid != null - ? e.Region.Guid == request.RegionUuid + (request.RegionGuid != null + ? e.Region.Guid == request.RegionGuid : true) && - (request.CountryUuid != null - ? e.Region.Country.Guid == request.CountryUuid + (request.CountryGuid != null + ? e.Region.Country.Guid == request.CountryGuid : true), e => e.Region.Country, request.PageNumber, request.PageSize, diff --git a/src/Application/Cities/Queries/GetCity/GetCityQuery.cs b/src/Application/Cities/Queries/GetCity/GetCityQuery.cs index 1c6f9bd..4f2c898 100644 --- a/src/Application/Cities/Queries/GetCity/GetCityQuery.cs +++ b/src/Application/Cities/Queries/GetCity/GetCityQuery.cs @@ -4,5 +4,5 @@ namespace cuqmbr.TravelGuide.Application.Cities.Queries.GetCity; public record GetCityQuery : IRequest { - public Guid Uuid { get; set; } + public Guid Guid { get; set; } } diff --git a/src/Application/Cities/Queries/GetCity/GetCityQueryHandler.cs b/src/Application/Cities/Queries/GetCity/GetCityQueryHandler.cs index 954f646..68e3aec 100644 --- a/src/Application/Cities/Queries/GetCity/GetCityQueryHandler.cs +++ b/src/Application/Cities/Queries/GetCity/GetCityQueryHandler.cs @@ -24,7 +24,7 @@ public class GetCityQueryHandler : CancellationToken cancellationToken) { var entity = await _unitOfWork.CityRepository.GetOneAsync( - e => e.Guid == request.Uuid, e => e.Region.Country, + e => e.Guid == request.Guid, e => e.Region.Country, cancellationToken); _unitOfWork.Dispose(); diff --git a/src/Application/Cities/Queries/GetCity/GetCityQueryValidator.cs b/src/Application/Cities/Queries/GetCity/GetCityQueryValidator.cs index 894166b..1c7fbb5 100644 --- a/src/Application/Cities/Queries/GetCity/GetCityQueryValidator.cs +++ b/src/Application/Cities/Queries/GetCity/GetCityQueryValidator.cs @@ -7,7 +7,7 @@ public class GetCityQueryValidator : AbstractValidator { public GetCityQueryValidator(IStringLocalizer localizer) { - RuleFor(v => v.Uuid) + RuleFor(v => v.Guid) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]); } diff --git a/src/Application/Cities/ViewModels/AddCityViewModel.cs b/src/Application/Cities/ViewModels/AddCityViewModel.cs new file mode 100644 index 0000000..ad60681 --- /dev/null +++ b/src/Application/Cities/ViewModels/AddCityViewModel.cs @@ -0,0 +1,8 @@ +namespace cuqmbr.TravelGuide.Application.Cities.ViewModels; + +public sealed class AddCityViewModel +{ + public string Name { get; set; } + + public Guid RegionGuid { get; set; } +} diff --git a/src/Application/Cities/ViewModels/UpdateCityViewModel.cs b/src/Application/Cities/ViewModels/UpdateCityViewModel.cs new file mode 100644 index 0000000..05f7e11 --- /dev/null +++ b/src/Application/Cities/ViewModels/UpdateCityViewModel.cs @@ -0,0 +1,8 @@ +namespace cuqmbr.TravelGuide.Application.Cities.ViewModels; + +public sealed class UpdateCityViewModel +{ + public string Name { get; set; } + + public Guid RegionUuid { get; set; } +} diff --git a/src/Application/Common/Interfaces/Persistence/Repositories/AddressRepository.cs b/src/Application/Common/Interfaces/Persistence/Repositories/AddressRepository.cs new file mode 100644 index 0000000..3dfcbf4 --- /dev/null +++ b/src/Application/Common/Interfaces/Persistence/Repositories/AddressRepository.cs @@ -0,0 +1,6 @@ +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Application.Common.Interfaces + .Persistence.Repositories; + +public interface AddressRepository : BaseRepository
{ } diff --git a/src/Application/Common/Interfaces/Persistence/UnitOfWork.cs b/src/Application/Common/Interfaces/Persistence/UnitOfWork.cs index 9c34925..78d0a07 100644 --- a/src/Application/Common/Interfaces/Persistence/UnitOfWork.cs +++ b/src/Application/Common/Interfaces/Persistence/UnitOfWork.cs @@ -10,6 +10,8 @@ public interface UnitOfWork : IDisposable CityRepository CityRepository { get; } + AddressRepository AddressRepository { get; } + int Save(); Task SaveAsync(CancellationToken cancellationToken); diff --git a/src/Application/Countries/ViewModels/AddCountryViewModel.cs b/src/Application/Countries/ViewModels/AddCountryViewModel.cs new file mode 100644 index 0000000..f244738 --- /dev/null +++ b/src/Application/Countries/ViewModels/AddCountryViewModel.cs @@ -0,0 +1,6 @@ +namespace cuqmbr.TravelGuide.Application.Countries.ViewModels; + +public sealed class AddCountryViewModel +{ + public string Name { get; set; } +} diff --git a/src/Application/Countries/ViewModels/UpdateCountryViewModel.cs b/src/Application/Countries/ViewModels/UpdateCountryViewModel.cs new file mode 100644 index 0000000..20fe7f5 --- /dev/null +++ b/src/Application/Countries/ViewModels/UpdateCountryViewModel.cs @@ -0,0 +1,6 @@ +namespace cuqmbr.TravelGuide.Application.Countries.ViewModels; + +public sealed class UpdateCountryViewModel +{ + public string Name { get; set; } +} diff --git a/src/Application/Regions/Commands/AddRegion/AddRegionCommand.cs b/src/Application/Regions/Commands/AddRegion/AddRegionCommand.cs index cc9c48f..e3369a0 100644 --- a/src/Application/Regions/Commands/AddRegion/AddRegionCommand.cs +++ b/src/Application/Regions/Commands/AddRegion/AddRegionCommand.cs @@ -6,5 +6,5 @@ public record AddRegionCommand : IRequest { public string Name { get; set; } - public Guid CountryUuid { get; set; } + public Guid CountryGuid { get; set; } } diff --git a/src/Application/Regions/Commands/AddRegion/AddRegionCommandHandler.cs b/src/Application/Regions/Commands/AddRegion/AddRegionCommandHandler.cs index 87357bd..0b5000e 100644 --- a/src/Application/Regions/Commands/AddRegion/AddRegionCommandHandler.cs +++ b/src/Application/Regions/Commands/AddRegion/AddRegionCommandHandler.cs @@ -25,7 +25,7 @@ public class AddRegionCommandHandler : CancellationToken cancellationToken) { var entity = await _unitOfWork.RegionRepository.GetOneAsync( - e => e.Name == request.Name && e.Country.Guid == request.CountryUuid, + e => e.Name == request.Name && e.Country.Guid == request.CountryGuid, cancellationToken); if (entity != null) @@ -35,12 +35,12 @@ public class AddRegionCommandHandler : } var parentEntity = await _unitOfWork.CountryRepository.GetOneAsync( - e => e.Guid == request.CountryUuid, cancellationToken); + e => e.Guid == request.CountryGuid, cancellationToken); if (parentEntity == null) { throw new NotFoundException( - $"Parent entity with Guid: {request.CountryUuid} not found."); + $"Parent entity with Guid: {request.CountryGuid} not found."); } entity = new Region() diff --git a/src/Application/Regions/Commands/AddRegion/AddRegionCommandValidator.cs b/src/Application/Regions/Commands/AddRegion/AddRegionCommandValidator.cs index 6cbc97d..c35227d 100644 --- a/src/Application/Regions/Commands/AddRegion/AddRegionCommandValidator.cs +++ b/src/Application/Regions/Commands/AddRegion/AddRegionCommandValidator.cs @@ -20,7 +20,7 @@ public class AddRegionCommandValidator : AbstractValidator localizer["FluentValidation.MaximumLength"], 64)); - RuleFor(v => v.CountryUuid) + RuleFor(v => v.CountryGuid) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]); } diff --git a/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommand.cs b/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommand.cs index b6839cd..2e43800 100644 --- a/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommand.cs +++ b/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommand.cs @@ -4,5 +4,5 @@ namespace cuqmbr.TravelGuide.Application.Regions.Commands.DeleteRegion; public record DeleteRegionCommand : IRequest { - public Guid Uuid { get; set; } + public Guid Guid { get; set; } } diff --git a/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommandHandler.cs b/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommandHandler.cs index b52384b..aba2cf7 100644 --- a/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommandHandler.cs +++ b/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommandHandler.cs @@ -18,7 +18,7 @@ public class DeleteRegionCommandHandler : IRequestHandler CancellationToken cancellationToken) { var entity = await _unitOfWork.RegionRepository.GetOneAsync( - e => e.Guid == request.Uuid, cancellationToken); + e => e.Guid == request.Guid, cancellationToken); if (entity == null) { diff --git a/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommandValidator.cs b/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommandValidator.cs index 5bfaf0b..1b62b86 100644 --- a/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommandValidator.cs +++ b/src/Application/Regions/Commands/DeleteRegion/DeleteRegionCommandValidator.cs @@ -7,7 +7,7 @@ public class DeleteRegionCommandValidator : AbstractValidator v.Uuid) + RuleFor(v => v.Guid) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]); } diff --git a/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommand.cs b/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommand.cs index 1ff6fb2..2bbe7c3 100644 --- a/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommand.cs +++ b/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommand.cs @@ -4,9 +4,9 @@ namespace cuqmbr.TravelGuide.Application.Regions.Commands.UpdateRegion; public record UpdateRegionCommand : IRequest { - public Guid Uuid { get; set; } + public Guid Guid { get; set; } public string Name { get; set; } - public Guid CountryUuid { get; set; } + public Guid CountryGuid { get; set; } } diff --git a/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommandHandler.cs b/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommandHandler.cs index f5fd163..5f8d461 100644 --- a/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommandHandler.cs +++ b/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommandHandler.cs @@ -24,7 +24,7 @@ public class UpdateRegionCommandHandler : CancellationToken cancellationToken) { var entity = await _unitOfWork.RegionRepository.GetOneAsync( - e => e.Guid == request.Uuid, cancellationToken); + e => e.Guid == request.Guid, cancellationToken); if (entity == null) { @@ -32,12 +32,12 @@ public class UpdateRegionCommandHandler : } var parentEntity = await _unitOfWork.CountryRepository.GetOneAsync( - e => e.Guid == request.CountryUuid, cancellationToken); + e => e.Guid == request.CountryGuid, cancellationToken); if (parentEntity == null) { throw new NotFoundException( - $"Parent entity with Guid: {request.CountryUuid} not found."); + $"Parent entity with Guid: {request.CountryGuid} not found."); } entity.Name = request.Name; diff --git a/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommandValidator.cs b/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommandValidator.cs index a259cd5..6cb3dfb 100644 --- a/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommandValidator.cs +++ b/src/Application/Regions/Commands/UpdateRegion/UpdateRegionCommandValidator.cs @@ -10,7 +10,7 @@ public class UpdateRegionCommandValidator : AbstractValidator v.Uuid) + RuleFor(v => v.Guid) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]); @@ -24,7 +24,7 @@ public class UpdateRegionCommandValidator : AbstractValidator v.CountryUuid) + RuleFor(v => v.CountryGuid) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]); } diff --git a/src/Application/Regions/Queries/GetRegion/GetRegionQuery.cs b/src/Application/Regions/Queries/GetRegion/GetRegionQuery.cs index 32b5f5d..eb0431e 100644 --- a/src/Application/Regions/Queries/GetRegion/GetRegionQuery.cs +++ b/src/Application/Regions/Queries/GetRegion/GetRegionQuery.cs @@ -4,5 +4,5 @@ namespace cuqmbr.TravelGuide.Application.Regions.Queries.GetRegion; public record GetRegionQuery : IRequest { - public Guid Uuid { get; set; } + public Guid Guid { get; set; } } diff --git a/src/Application/Regions/Queries/GetRegion/GetRegionQueryHandler.cs b/src/Application/Regions/Queries/GetRegion/GetRegionQueryHandler.cs index 1af903e..c50a2df 100644 --- a/src/Application/Regions/Queries/GetRegion/GetRegionQueryHandler.cs +++ b/src/Application/Regions/Queries/GetRegion/GetRegionQueryHandler.cs @@ -24,7 +24,7 @@ public class GetRegionQueryHandler : CancellationToken cancellationToken) { var entity = await _unitOfWork.RegionRepository.GetOneAsync( - e => e.Guid == request.Uuid, e => e.Country, + e => e.Guid == request.Guid, e => e.Country, cancellationToken); _unitOfWork.Dispose(); diff --git a/src/Application/Regions/Queries/GetRegion/GetRegionQueryValidator.cs b/src/Application/Regions/Queries/GetRegion/GetRegionQueryValidator.cs index 90e7f09..e6fc08d 100644 --- a/src/Application/Regions/Queries/GetRegion/GetRegionQueryValidator.cs +++ b/src/Application/Regions/Queries/GetRegion/GetRegionQueryValidator.cs @@ -7,7 +7,7 @@ public class GetRegionQueryValidator : AbstractValidator { public GetRegionQueryValidator(IStringLocalizer localizer) { - RuleFor(v => v.Uuid) + RuleFor(v => v.Guid) .NotEmpty() .WithMessage(localizer["FluentValidation.NotEmpty"]); } diff --git a/src/Application/Regions/Queries/GetRegionsPage/GetRegionsPageQuery.cs b/src/Application/Regions/Queries/GetRegionsPage/GetRegionsPageQuery.cs index d618dd6..9d8ad93 100644 --- a/src/Application/Regions/Queries/GetRegionsPage/GetRegionsPageQuery.cs +++ b/src/Application/Regions/Queries/GetRegionsPage/GetRegionsPageQuery.cs @@ -13,5 +13,5 @@ public record GetRegionsPageQuery : IRequest> public string Sort { get; set; } = String.Empty; - public Guid? CountryUuid { get; set; } + public Guid? CountryGuid { get; set; } } diff --git a/src/Application/Regions/Queries/GetRegionsPage/GetRegionsPageQueryHandler.cs b/src/Application/Regions/Queries/GetRegionsPage/GetRegionsPageQueryHandler.cs index 1e40663..42c1143 100644 --- a/src/Application/Regions/Queries/GetRegionsPage/GetRegionsPageQueryHandler.cs +++ b/src/Application/Regions/Queries/GetRegionsPage/GetRegionsPageQueryHandler.cs @@ -28,8 +28,8 @@ public class GetRegionsPageQueryHandler : e => (e.Name.ToLower().Contains(request.Search.ToLower()) || e.Country.Name.ToLower().Contains(request.Search.ToLower())) && - (request.CountryUuid != null - ? e.Country.Guid == request.CountryUuid + (request.CountryGuid != null + ? e.Country.Guid == request.CountryGuid : true), e => e.Country, request.PageNumber, request.PageSize, diff --git a/src/Application/Regions/ViewModels/AddRegionViewModel.cs b/src/Application/Regions/ViewModels/AddRegionViewModel.cs new file mode 100644 index 0000000..4b1273c --- /dev/null +++ b/src/Application/Regions/ViewModels/AddRegionViewModel.cs @@ -0,0 +1,8 @@ +namespace cuqmbr.TravelGuide.Application.Regions.ViewModels; + +public sealed class AddRegionViewModel +{ + public string Name { get; set; } + + public Guid CountryUuid { get; set; } +} diff --git a/src/Application/Regions/ViewModels/UpdateRegionViewModel.cs b/src/Application/Regions/ViewModels/UpdateRegionViewModel.cs new file mode 100644 index 0000000..e3025df --- /dev/null +++ b/src/Application/Regions/ViewModels/UpdateRegionViewModel.cs @@ -0,0 +1,8 @@ +namespace cuqmbr.TravelGuide.Application.Regions.ViewModels; + +public sealed class UpdateRegionViewModel +{ + public string Name { get; set; } + + public Guid CountryUuid { get; set; } +} diff --git a/src/Application/Resources/Localization/en-US.json b/src/Application/Resources/Localization/en-US.json index 481cb0f..cec7742 100644 --- a/src/Application/Resources/Localization/en-US.json +++ b/src/Application/Resources/Localization/en-US.json @@ -3,7 +3,8 @@ "MaximumLength": "Must less than {0:G} characters.", "NotEmpty": "Must not be empty.", "GreaterThanOrEqualTo": "Must be greater than or equal to {0:G}.", - "LessThanOrEqualTo": "Must be less than or equal to {0:G}." + "LessThanOrEqualTo": "Must be less than or equal to {0:G}.", + "MustBeInEnum": "Must be one of the following: {0}." }, "ExceptionHandling": { "ValidationException": { diff --git a/src/Configuration/Identity/Configuration.cs b/src/Configuration/Identity/Configuration.cs index d9c1d2a..d066380 100644 --- a/src/Configuration/Identity/Configuration.cs +++ b/src/Configuration/Identity/Configuration.cs @@ -45,7 +45,7 @@ public static class Configuration .AddEntityFrameworkStores() .AddDefaultTokenProviders(); - if (configuration.Datastore.Initialize) + if (configuration.Datastore.Migrate) { using var dbContextServiceProvider = services.BuildServiceProvider(); PostgreSqlInitializer.Initialize(dbContextServiceProvider); diff --git a/src/Domain/Entities/Address.cs b/src/Domain/Entities/Address.cs index 81e017b..1c00718 100644 --- a/src/Domain/Entities/Address.cs +++ b/src/Domain/Entities/Address.cs @@ -7,11 +7,11 @@ public sealed class Address : EntityBase public string Name { get; set; } // TODO: Implement coordinates using NetTopologySuite - // public double Longitude { get; set; } - // - // public double Latitude { get; set; } + public double Longitude { get; set; } - // public VehicleType VehicleType { get; set; } + public double Latitude { get; set; } + + public VehicleType VehicleType { get; set; } public long CityId { get; set; } @@ -19,6 +19,5 @@ public sealed class Address : EntityBase public City City { get; set; } - public ICollection AddressRoutes { get; set; } + // public ICollection AddressRoutes { get; set; } } - diff --git a/src/Domain/Enums/VehicleType.cs b/src/Domain/Enums/VehicleType.cs index a107370..30402d1 100644 --- a/src/Domain/Enums/VehicleType.cs +++ b/src/Domain/Enums/VehicleType.cs @@ -5,24 +5,24 @@ namespace cuqmbr.TravelGuide.Domain.Enums; public abstract class VehicleType : Enumeration { - public static readonly VehicleType Bus = new BusVehicleType(); - public static readonly VehicleType Train = new TrainVehicleType(); - public static readonly VehicleType Aircraft = new AircraftVehicleType(); + public static readonly VehicleType Bus = new BusVehicleType(); + public static readonly VehicleType Train = new TrainVehicleType(); + public static readonly VehicleType Aircraft = new AircraftVehicleType(); - protected VehicleType(int value, string name) : base(value, name) { } + protected VehicleType(int value, string name) : base(value, name) { } - private sealed class BusVehicleType : VehicleType - { - public BusVehicleType() : base(0, "bus") { } - } + private sealed class BusVehicleType : VehicleType + { + public BusVehicleType() : base(0, "bus") { } + } - private sealed class TrainVehicleType : VehicleType - { - public TrainVehicleType() : base(1, "train") { } - } + private sealed class TrainVehicleType : VehicleType + { + public TrainVehicleType() : base(1, "train") { } + } - private sealed class AircraftVehicleType : VehicleType - { - public AircraftVehicleType() : base(2, "aircraft") { } - } + private sealed class AircraftVehicleType : VehicleType + { + public AircraftVehicleType() : base(2, "aircraft") { } + } } diff --git a/src/HttpApi/Controllers/AddressesController.cs b/src/HttpApi/Controllers/AddressesController.cs new file mode 100644 index 0000000..4d34676 --- /dev/null +++ b/src/HttpApi/Controllers/AddressesController.cs @@ -0,0 +1,196 @@ +using Microsoft.AspNetCore.Mvc; +using Swashbuckle.AspNetCore.Annotations; +using cuqmbr.TravelGuide.Application.Common.Models; +using cuqmbr.TravelGuide.Application.Common.ViewModels; +using cuqmbr.TravelGuide.Domain.Enums; +using cuqmbr.TravelGuide.Application.Addresses; +using cuqmbr.TravelGuide.Application.Addresses.Commands.AddAddress; +using cuqmbr.TravelGuide.Application.Addresses.Queries.GetAddressesPage; +using cuqmbr.TravelGuide.Application.Addresses.Queries.GetAddress; +using cuqmbr.TravelGuide.Application.Addresses.Commands.UpdateAddress; +using cuqmbr.TravelGuide.Application.Addresses.Commands.DeleteAddress; +using cuqmbr.TravelGuide.Application.Addresses.ViewModels; + +namespace cuqmbr.TravelGuide.HttpApi.Controllers; + +[Route("addresses")] +public class AddressesController : ControllerBase +{ + [HttpPost] + [SwaggerOperation("Add an address")] + [SwaggerResponse( + StatusCodes.Status201Created, "Object successfuly created", + typeof(AddressDto))] + [SwaggerResponse( + StatusCodes.Status400BadRequest, "Object already exists", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status400BadRequest, "Input data validation error", + typeof(HttpValidationProblemDetails))] + [SwaggerResponse( + StatusCodes.Status401Unauthorized, "Unauthorized to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status403Forbidden, + "Not enough privileges to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status404NotFound, "Parent object not found", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status500InternalServerError, "Internal server error", + typeof(ProblemDetails))] + public async Task> Add( + [FromBody] AddAddressViewModel viewModel, + CancellationToken cancellationToken) + { + return StatusCode( + StatusCodes.Status201Created, + await Mediator.Send( + new AddAddressCommand() + { + Name = viewModel.Name, + Longitude = viewModel.Longitude, + Latitude = viewModel.Latitude, + VehicleType = VehicleType.FromName(viewModel.VehicleType), + CityGuid = viewModel.CityUuid + }, + cancellationToken)); + } + + [HttpGet] + [SwaggerOperation("Get a list of all addresses")] + [SwaggerResponse( + StatusCodes.Status200OK, "Request successful", + typeof(PaginatedList))] + [SwaggerResponse( + StatusCodes.Status400BadRequest, "Input data validation error", + typeof(HttpValidationProblemDetails))] + [SwaggerResponse( + StatusCodes.Status401Unauthorized, "Unauthorized to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status403Forbidden, + "Not enough privileges to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status500InternalServerError, "Internal server error", + typeof(ProblemDetails))] + public async Task> GetPage( + [FromQuery] PageQuery pageQuery, [FromQuery] SearchQuery searchQuery, + [FromQuery] SortQuery sortQuery, + [FromQuery] GetAddressesPageFilterViewModel filterQuery, + CancellationToken cancellationToken) + { + return await Mediator.Send( + new GetAddressesPageQuery() + { + PageNumber = pageQuery.PageNumber, + PageSize = pageQuery.PageSize, + Search = searchQuery.Search, + Sort = sortQuery.Sort, + CountryGuid = filterQuery.CountryUuid, + RegionGuid = filterQuery.RegionUuid, + CityGuid = filterQuery.CityUuid + }, + cancellationToken); + } + + [HttpGet("{uuid:guid}")] + [SwaggerOperation("Get an address by uuid")] + [SwaggerResponse( + StatusCodes.Status200OK, "Request successful", typeof(AddressDto))] + [SwaggerResponse( + StatusCodes.Status400BadRequest, "Input data validation error", + typeof(HttpValidationProblemDetails))] + [SwaggerResponse( + StatusCodes.Status401Unauthorized, "Unauthorized to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status403Forbidden, + "Not enough privileges to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status404NotFound, "Object not found", typeof(AddressDto))] + [SwaggerResponse( + StatusCodes.Status500InternalServerError, "Internal server error", + typeof(ProblemDetails))] + public async Task Get( + [FromRoute] Guid uuid, + CancellationToken cancellationToken) + { + return await Mediator.Send(new GetAddressQuery() { Guid = uuid }, + cancellationToken); + } + + [HttpPut("{uuid:guid}")] + [SwaggerOperation("Update an address")] + [SwaggerResponse( + StatusCodes.Status200OK, "Request successful", typeof(AddressDto))] + [SwaggerResponse( + StatusCodes.Status400BadRequest, "Object already exists", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status400BadRequest, "Input data validation error", + typeof(HttpValidationProblemDetails))] + [SwaggerResponse( + StatusCodes.Status401Unauthorized, "Unauthorized to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status403Forbidden, + "Not enough privileges to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status404NotFound, "Object not found", typeof(AddressDto))] + [SwaggerResponse( + StatusCodes.Status404NotFound, "Parent object not found", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status500InternalServerError, "Internal server error", + typeof(ProblemDetails))] + public async Task Update( + [FromRoute] Guid uuid, + [FromBody] UpdateAddressViewModel viewModel, + CancellationToken cancellationToken) + { + return await Mediator.Send( + new UpdateAddressCommand() + { + Guid = uuid, + Name = viewModel.Name, + Longitude = viewModel.Longitude, + Latitude = viewModel.Latitude, + VehicleType = VehicleType.FromName(viewModel.VehicleType), + CityGuid = viewModel.CityUuid + }, + cancellationToken); + } + + [HttpDelete("{uuid:guid}")] + [SwaggerOperation("Delete an address")] + [SwaggerResponse(StatusCodes.Status204NoContent, "Request successful")] + [SwaggerResponse( + StatusCodes.Status400BadRequest, "Input data validation error", + typeof(HttpValidationProblemDetails))] + [SwaggerResponse( + StatusCodes.Status401Unauthorized, "Unauthorized to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status403Forbidden, + "Not enough privileges to perform an action", + typeof(ProblemDetails))] + [SwaggerResponse( + StatusCodes.Status404NotFound, "Object not found", typeof(AddressDto))] + [SwaggerResponse( + StatusCodes.Status500InternalServerError, "Internal server error", + typeof(ProblemDetails))] + public async Task Delete( + [FromRoute] Guid uuid, + CancellationToken cancellationToken) + { + await Mediator.Send( + new DeleteAddressCommand() { Guid = uuid }, + cancellationToken); + return StatusCode(StatusCodes.Status204NoContent); + } +} diff --git a/src/HttpApi/Controllers/CitiesController.cs b/src/HttpApi/Controllers/CitiesController.cs index 5286fba..086a5ad 100644 --- a/src/HttpApi/Controllers/CitiesController.cs +++ b/src/HttpApi/Controllers/CitiesController.cs @@ -16,7 +16,7 @@ namespace cuqmbr.TravelGuide.HttpApi.Controllers; public class CitiesController : ControllerBase { [HttpPost] - [SwaggerOperation("Create a city")] + [SwaggerOperation("Add a city")] [SwaggerResponse( StatusCodes.Status201Created, "Object successfuly created", typeof(CityDto))] @@ -40,12 +40,18 @@ public class CitiesController : ControllerBase StatusCodes.Status500InternalServerError, "Internal server error", typeof(ProblemDetails))] public async Task> Add( - [FromBody] AddCityCommand command, + [FromBody] AddCityViewModel viewModel, CancellationToken cancellationToken) { return StatusCode( StatusCodes.Status201Created, - await Mediator.Send(command, cancellationToken)); + await Mediator.Send( + new AddCityCommand() + { + Name = viewModel.Name, + RegionGuid = viewModel.RegionGuid + }, + cancellationToken)); } [HttpGet] @@ -79,14 +85,14 @@ public class CitiesController : ControllerBase PageSize = pageQuery.PageSize, Search = searchQuery.Search, Sort = sortQuery.Sort, - CountryUuid = filterQuery.CountryUuid, - RegionUuid = filterQuery.RegionUuid + CountryGuid = filterQuery.CountryUuid, + RegionGuid = filterQuery.RegionUuid }, cancellationToken); } [HttpGet("{uuid:guid}")] - [SwaggerOperation("Get city by uuid")] + [SwaggerOperation("Get a city by uuid")] [SwaggerResponse( StatusCodes.Status200OK, "Request successful", typeof(CityDto))] [SwaggerResponse( @@ -108,12 +114,12 @@ public class CitiesController : ControllerBase [FromRoute] Guid uuid, CancellationToken cancellationToken) { - return await Mediator.Send(new GetCityQuery() { Uuid = uuid }, + return await Mediator.Send(new GetCityQuery() { Guid = uuid }, cancellationToken); } [HttpPut("{uuid:guid}")] - [SwaggerOperation("Update city")] + [SwaggerOperation("Update a city")] [SwaggerResponse( StatusCodes.Status200OK, "Request successful", typeof(CityDto))] [SwaggerResponse( @@ -139,15 +145,21 @@ public class CitiesController : ControllerBase typeof(ProblemDetails))] public async Task Update( [FromRoute] Guid uuid, - [FromBody] UpdateCityCommand command, + [FromBody] UpdateCityViewModel viewModel, CancellationToken cancellationToken) { - command.Uuid = uuid; - return await Mediator.Send(command, cancellationToken); + return await Mediator.Send( + new UpdateCityCommand() + { + Guid = uuid, + Name= viewModel.Name, + RegionGuid = viewModel.RegionUuid + }, + cancellationToken); } [HttpDelete("{uuid:guid}")] - [SwaggerOperation("Delete city")] + [SwaggerOperation("Delete a city")] [SwaggerResponse(StatusCodes.Status204NoContent, "Request successful")] [SwaggerResponse( StatusCodes.Status400BadRequest, "Input data validation error", @@ -169,7 +181,7 @@ public class CitiesController : ControllerBase CancellationToken cancellationToken) { await Mediator.Send( - new DeleteCityCommand() { Uuid = uuid }, + new DeleteCityCommand() { Guid = uuid }, cancellationToken); return StatusCode(StatusCodes.Status204NoContent); } diff --git a/src/HttpApi/Controllers/CountriesController.cs b/src/HttpApi/Controllers/CountriesController.cs index d40fa01..76dae58 100644 --- a/src/HttpApi/Controllers/CountriesController.cs +++ b/src/HttpApi/Controllers/CountriesController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; using cuqmbr.TravelGuide.Application.Common.Models; using cuqmbr.TravelGuide.Application.Common.ViewModels; +using cuqmbr.TravelGuide.Application.Countries.ViewModels; using cuqmbr.TravelGuide.Application.Countries; using cuqmbr.TravelGuide.Application.Countries.Commands.AddCountry; using cuqmbr.TravelGuide.Application.Countries.Queries.GetCountriesPage; @@ -15,7 +16,7 @@ namespace cuqmbr.TravelGuide.HttpApi.Controllers; public class CountriesController : ControllerBase { [HttpPost] - [SwaggerOperation("Create a country")] + [SwaggerOperation("Add a country")] [SwaggerResponse( StatusCodes.Status201Created, "Object successfuly created", typeof(CountryDto))] @@ -36,12 +37,17 @@ public class CountriesController : ControllerBase StatusCodes.Status500InternalServerError, "Internal server error", typeof(ProblemDetails))] public async Task> Add( - [FromBody] AddCountryCommand command, + [FromBody] AddCountryViewModel viewModel, CancellationToken cancellationToken) { return StatusCode( StatusCodes.Status201Created, - await Mediator.Send(command, cancellationToken)); + await Mediator.Send( + new AddCountryCommand() + { + Name = viewModel.Name + }, + cancellationToken)); } [HttpGet] @@ -78,7 +84,7 @@ public class CountriesController : ControllerBase } [HttpGet("{uuid:guid}")] - [SwaggerOperation("Get country by uuid")] + [SwaggerOperation("Get a country by uuid")] [SwaggerResponse( StatusCodes.Status200OK, "Request successful", typeof(CountryDto))] [SwaggerResponse( @@ -105,7 +111,7 @@ public class CountriesController : ControllerBase } [HttpPut("{uuid:guid}")] - [SwaggerOperation("Update country")] + [SwaggerOperation("Update a country")] [SwaggerResponse( StatusCodes.Status200OK, "Request successful", typeof(CountryDto))] [SwaggerResponse( @@ -128,15 +134,20 @@ public class CountriesController : ControllerBase typeof(ProblemDetails))] public async Task Update( [FromRoute] Guid uuid, - [FromBody] UpdateCountryCommand command, + [FromBody] UpdateCountryViewModel viewModel, CancellationToken cancellationToken) { - command.Guid = uuid; - return await Mediator.Send(command, cancellationToken); + return await Mediator.Send( + new UpdateCountryCommand() + { + Guid = uuid, + Name = viewModel.Name + }, + cancellationToken); } [HttpDelete("{uuid:guid}")] - [SwaggerOperation("Delete country")] + [SwaggerOperation("Delete a country")] [SwaggerResponse(StatusCodes.Status204NoContent, "Request successful")] [SwaggerResponse( StatusCodes.Status400BadRequest, "Input data validation error", diff --git a/src/HttpApi/Controllers/RegionsController.cs b/src/HttpApi/Controllers/RegionsController.cs index 4b5a72d..478ec03 100644 --- a/src/HttpApi/Controllers/RegionsController.cs +++ b/src/HttpApi/Controllers/RegionsController.cs @@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; using cuqmbr.TravelGuide.Application.Common.Models; using cuqmbr.TravelGuide.Application.Common.ViewModels; +using cuqmbr.TravelGuide.Application.Regions.ViewModels; using cuqmbr.TravelGuide.Application.Regions; using cuqmbr.TravelGuide.Application.Regions.Commands.AddRegion; using cuqmbr.TravelGuide.Application.Regions.Queries.GetRegionsPage; @@ -16,7 +17,7 @@ namespace cuqmbr.TravelGuide.HttpApi.Controllers; public class RegionsController : ControllerBase { [HttpPost] - [SwaggerOperation("Create a region")] + [SwaggerOperation("Add a region")] [SwaggerResponse( StatusCodes.Status201Created, "Object successfuly created", typeof(RegionDto))] @@ -40,12 +41,17 @@ public class RegionsController : ControllerBase StatusCodes.Status500InternalServerError, "Internal server error", typeof(ProblemDetails))] public async Task> Add( - [FromBody] AddRegionCommand command, + [FromBody] AddRegionViewModel viewModel, CancellationToken cancellationToken) { return StatusCode( StatusCodes.Status201Created, - await Mediator.Send(command, cancellationToken)); + await Mediator.Send( + new AddRegionCommand() + { + Name = viewModel.Name + }, + cancellationToken)); } [HttpGet] @@ -79,13 +85,13 @@ public class RegionsController : ControllerBase PageSize = pageQuery.PageSize, Search = searchQuery.Search, Sort = sortQuery.Sort, - CountryUuid = filterQuery.CountryUuid + CountryGuid = filterQuery.CountryUuid }, cancellationToken); } [HttpGet("{uuid:guid}")] - [SwaggerOperation("Get region by uuid")] + [SwaggerOperation("Get a region by uuid")] [SwaggerResponse( StatusCodes.Status200OK, "Request successful", typeof(RegionDto))] [SwaggerResponse( @@ -107,12 +113,12 @@ public class RegionsController : ControllerBase [FromRoute] Guid uuid, CancellationToken cancellationToken) { - return await Mediator.Send(new GetRegionQuery() { Uuid = uuid }, + return await Mediator.Send(new GetRegionQuery() { Guid = uuid }, cancellationToken); } [HttpPut("{uuid:guid}")] - [SwaggerOperation("Update region")] + [SwaggerOperation("Update a region")] [SwaggerResponse( StatusCodes.Status200OK, "Request successful", typeof(RegionDto))] [SwaggerResponse( @@ -138,15 +144,21 @@ public class RegionsController : ControllerBase typeof(ProblemDetails))] public async Task Update( [FromRoute] Guid uuid, - [FromBody] UpdateRegionCommand command, + [FromBody] UpdateRegionViewModel viewModel, CancellationToken cancellationToken) { - command.Uuid = uuid; - return await Mediator.Send(command, cancellationToken); + return await Mediator.Send( + new UpdateRegionCommand() + { + Guid = uuid, + Name = viewModel.Name, + CountryGuid = viewModel.CountryUuid + }, + cancellationToken); } [HttpDelete("{uuid:guid}")] - [SwaggerOperation("Delete region")] + [SwaggerOperation("Delete a region")] [SwaggerResponse(StatusCodes.Status204NoContent, "Request successful")] [SwaggerResponse( StatusCodes.Status400BadRequest, "Input data validation error", @@ -168,7 +180,7 @@ public class RegionsController : ControllerBase CancellationToken cancellationToken) { await Mediator.Send( - new DeleteRegionCommand() { Uuid = uuid }, + new DeleteRegionCommand() { Guid = uuid }, cancellationToken); return StatusCode(StatusCodes.Status204NoContent); } diff --git a/src/HttpApi/appsettings.Development.json b/src/HttpApi/appsettings.Development.json index 4b2b05b..2bdff62 100644 --- a/src/HttpApi/appsettings.Development.json +++ b/src/HttpApi/appsettings.Development.json @@ -8,7 +8,7 @@ }, "Datastore": { "Type": "postgresql", - "ConnectionString": "Host=127.0.0.1:5432;Database=travel_guide;Username=postgres;Password=0000" + "ConnectionString": "Host=127.0.0.1:5432;Database=travel_guide;Username=postgres;Password=0000;Include Error Detail=true" }, "Localization": { "DefaultCultureName": "en-US", diff --git a/src/Identity/ConfigurationOptions.cs b/src/Identity/ConfigurationOptions.cs index bf10582..9bce7be 100644 --- a/src/Identity/ConfigurationOptions.cs +++ b/src/Identity/ConfigurationOptions.cs @@ -17,7 +17,7 @@ public sealed class Datastore public string PartitionName { get; set; } = "identity"; - public bool Initialize { get; set; } = true; + public bool Migrate { get; set; } = true; } public sealed class JsonWebToken diff --git a/src/Persistence/Configurations/BaseConfiguration.cs b/src/Persistence/Configurations/BaseConfiguration.cs deleted file mode 100644 index 4ed923a..0000000 --- a/src/Persistence/Configurations/BaseConfiguration.cs +++ /dev/null @@ -1,55 +0,0 @@ -using cuqmbr.TravelGuide.Domain.Entities; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace cuqmbr.TravelGuide.Persistence.InMemory.Configurations; - -public class BaseConfiguration : IEntityTypeConfiguration - where TEntity : EntityBase -{ - public virtual void Configure(EntityTypeBuilder builder) - { - builder - .HasKey(b => b.Id); - // .HasName($"pk_{builder.Metadata.GetTableName()}"); - - builder - .Property(b => b.Id) - .HasColumnName("id"); - // .HasColumnType("bigint") - // .UseSequence( - // $"{builder.Metadata.GetTableName()}_" + - // $"{builder.Property(b => b.Id).Metadata.GetColumnName()}_" + - // "sequence"); - // - // builder - // .HasIndex(b => b.Id) - // .HasDatabaseName( - // "ix_" + - // $"{builder.Metadata.GetTableName()}_" + - // $"{builder.Property(b => b.Id).Metadata.GetColumnName()}") - // .IsUnique(); - // - // - // builder - // .HasAlternateKey(b => b.Guid) - // .HasName( - // "altk_" + - // $"{builder.Metadata.GetTableName()}_" + - // $"{builder.Property(b => b.Guid).Metadata.GetColumnName()}"); - // - builder - .Property(b => b.Guid) - .HasColumnName("uuid") - // .HasColumnType("uuid") - .IsRequired(true); - // - // builder - // .HasIndex(b => b.Guid) - // .HasDatabaseName( - // "ix_" + - // $"{builder.Metadata.GetTableName()}_" + - // $"{builder.Property(b => b.Guid).Metadata.GetColumnName()}") - // .IsUnique(); - } -} diff --git a/src/Persistence/Configurations/CountryConfiguration.cs b/src/Persistence/Configurations/CountryConfiguration.cs deleted file mode 100644 index a335dfa..0000000 --- a/src/Persistence/Configurations/CountryConfiguration.cs +++ /dev/null @@ -1,31 +0,0 @@ -using cuqmbr.TravelGuide.Domain.Entities; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace cuqmbr.TravelGuide.Persistence.InMemory.Configurations; - -public class CountryConfiguration : BaseConfiguration -{ - public override void Configure(EntityTypeBuilder builder) - { - builder - .ToTable("countries"); - - base.Configure(builder); - - - builder - .Property(c => c.Name) - .HasColumnName("name") - // .HasColumnType("varchar(64)") - .IsRequired(true); - - // TODO: Remove comment - // builder - // .HasAlternateKey(c => c.Name) - // .HasName( - // "altk_" + - // $"{builder.Metadata.GetTableName()}_" + - // $"{builder.Property(c => c.Name).Metadata.GetColumnName()}"); - } -} diff --git a/src/Persistence/Configurations/RegionConfiguration.cs b/src/Persistence/Configurations/RegionConfiguration.cs deleted file mode 100644 index 87c4f8f..0000000 --- a/src/Persistence/Configurations/RegionConfiguration.cs +++ /dev/null @@ -1,40 +0,0 @@ -using cuqmbr.TravelGuide.Domain.Entities; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Metadata.Builders; - -namespace cuqmbr.TravelGuide.Persistence.InMemory.Configurations; - -public class RegionConfiguration : BaseConfiguration -{ - public override void Configure(EntityTypeBuilder builder) - { - builder - .ToTable("regions"); - - base.Configure(builder); - - - builder - .Property(r => r.Name) - .HasColumnName("name") - // .HasColumnType("varchar(64)") - .IsRequired(true); - - - builder - .Property(r => r.CountryId) - .HasColumnName("country_id") - // .HasColumnType("bigint") - .IsRequired(true); - - builder - .HasOne(r => r.Country) - .WithMany(c => c.Regions) - .HasForeignKey(r => r.CountryId) - .HasConstraintName( - "fk_" + - $"{builder.Metadata.GetTableName()}_" + - $"{builder.Property(r => r.CountryId).Metadata.GetColumnName()}") - .OnDelete(DeleteBehavior.Cascade); - } -} diff --git a/src/Persistence/InMemory/InMemoryDbContext.cs b/src/Persistence/InMemory/InMemoryDbContext.cs index 0c788c7..fa0cbef 100644 --- a/src/Persistence/InMemory/InMemoryDbContext.cs +++ b/src/Persistence/InMemory/InMemoryDbContext.cs @@ -14,6 +14,7 @@ public class InMemoryDbContext : DbContext public DbSet Countries { get => Set(); } public DbSet Regions { get => Set(); } public DbSet Cities { get => Set(); } + public DbSet
Addresses { get => Set
(); } protected override void OnModelCreating(ModelBuilder builder) { diff --git a/src/Persistence/InMemory/InMemoryUnitOfWork.cs b/src/Persistence/InMemory/InMemoryUnitOfWork.cs index 974c1df..0f10021 100644 --- a/src/Persistence/InMemory/InMemoryUnitOfWork.cs +++ b/src/Persistence/InMemory/InMemoryUnitOfWork.cs @@ -24,6 +24,8 @@ public sealed class InMemoryUnitOfWork : UnitOfWork public CityRepository CityRepository { get; init; } + public AddressRepository AddressRepository { get; init; } + public int Save() { return _dbContext.SaveChanges(); diff --git a/src/Persistence/InMemory/Repositories/InMemoryAddressRepository.cs b/src/Persistence/InMemory/Repositories/InMemoryAddressRepository.cs new file mode 100644 index 0000000..3b206f1 --- /dev/null +++ b/src/Persistence/InMemory/Repositories/InMemoryAddressRepository.cs @@ -0,0 +1,11 @@ +using cuqmbr.TravelGuide.Application.Common.Interfaces.Persistence.Repositories; +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Persistence.InMemory.Repositories; + +public sealed class InMemoryAddressRepository : + InMemoryBaseRepository
, AddressRepository +{ + public InMemoryAddressRepository(InMemoryDbContext dbContext) + : base(dbContext) { } +} diff --git a/src/Persistence/PostgreSql/Configurations/AddressConfiguration.cs b/src/Persistence/PostgreSql/Configurations/AddressConfiguration.cs new file mode 100644 index 0000000..7d7d53b --- /dev/null +++ b/src/Persistence/PostgreSql/Configurations/AddressConfiguration.cs @@ -0,0 +1,64 @@ +using cuqmbr.TravelGuide.Domain.Entities; +using cuqmbr.TravelGuide.Domain.Enums; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace cuqmbr.TravelGuide.Persistence.PostgreSql.Configurations; + +public class AddressConfiguration : BaseConfiguration
+{ + public override void Configure(EntityTypeBuilder
builder) + { + builder + .Property(a => a.VehicleType) + .HasColumnName("vehicle_type") + .HasColumnType("varchar(16)") + .IsRequired(true); + + builder + .ToTable( + "addresses", + b => b.HasCheckConstraint( + "ck_" + + $"{builder.Metadata.GetTableName()}_" + + $"{builder.Property(a => a.VehicleType) + .Metadata.GetColumnName()}", + $"{builder.Property(a => a.VehicleType) + .Metadata.GetColumnName()} IN ('{String + .Join("', '", VehicleType.Enumerations + .Values.Select(v => v.Name))}')")); + + base.Configure(builder); + + + builder + .Property(a => a.Name) + .HasColumnName("name") + .HasColumnType("varchar(128)") + .IsRequired(true); + + + builder + .Property(a => a.CityId) + .HasColumnName("city_id") + .HasColumnType("bigint") + .IsRequired(true); + + builder + .HasOne(a => a.City) + .WithMany(c => c.Addresses) + .HasForeignKey(a => a.CityId) + .HasConstraintName( + "fk_" + + $"{builder.Metadata.GetTableName()}_" + + $"{builder.Property(a => a.CityId).Metadata.GetColumnName()}") + .OnDelete(DeleteBehavior.Cascade); + + builder + .HasIndex(a => a.CityId) + .HasDatabaseName( + "ix_" + + $"{builder.Metadata.GetTableName()}_" + + $"{builder.Property(a => a.CityId).Metadata.GetColumnName()}"); + } +} diff --git a/src/Persistence/PostgreSql/Configurations/CityConfiguration.cs b/src/Persistence/PostgreSql/Configurations/CityConfiguration.cs index 589d652..ddafa7e 100644 --- a/src/Persistence/PostgreSql/Configurations/CityConfiguration.cs +++ b/src/Persistence/PostgreSql/Configurations/CityConfiguration.cs @@ -36,5 +36,12 @@ public class CityConfiguration : BaseConfiguration $"{builder.Metadata.GetTableName()}_" + $"{builder.Property(c => c.RegionId).Metadata.GetColumnName()}") .OnDelete(DeleteBehavior.Cascade); + + builder + .HasIndex(c => c.RegionId) + .HasDatabaseName( + "ix_" + + $"{builder.Metadata.GetTableName()}_" + + $"{builder.Property(c => c.RegionId).Metadata.GetColumnName()}"); } } diff --git a/src/Persistence/PostgreSql/Configurations/RegionConfiguration.cs b/src/Persistence/PostgreSql/Configurations/RegionConfiguration.cs index 1cd8573..b1005a5 100644 --- a/src/Persistence/PostgreSql/Configurations/RegionConfiguration.cs +++ b/src/Persistence/PostgreSql/Configurations/RegionConfiguration.cs @@ -36,5 +36,12 @@ public class RegionConfiguration : BaseConfiguration $"{builder.Metadata.GetTableName()}_" + $"{builder.Property(r => r.CountryId).Metadata.GetColumnName()}") .OnDelete(DeleteBehavior.Cascade); + + builder + .HasIndex(r => r.CountryId) + .HasDatabaseName( + "ix_" + + $"{builder.Metadata.GetTableName()}_" + + $"{builder.Property(r => r.CountryId).Metadata.GetColumnName()}"); } } diff --git a/src/Persistence/PostgreSql/Migrations/20250430113338_Countries_Regions_Cities.Designer.cs b/src/Persistence/PostgreSql/Migrations/20250430180231_Add_Countries_Regions_Cities_Addresses.Designer.cs similarity index 69% rename from src/Persistence/PostgreSql/Migrations/20250430113338_Countries_Regions_Cities.Designer.cs rename to src/Persistence/PostgreSql/Migrations/20250430180231_Add_Countries_Regions_Cities_Addresses.Designer.cs index d7efad4..92c0355 100644 --- a/src/Persistence/PostgreSql/Migrations/20250430113338_Countries_Regions_Cities.Designer.cs +++ b/src/Persistence/PostgreSql/Migrations/20250430180231_Add_Countries_Regions_Cities_Addresses.Designer.cs @@ -12,8 +12,8 @@ using cuqmbr.TravelGuide.Persistence.PostgreSql; namespace Persistence.PostgreSql.Migrations { [DbContext(typeof(PostgreSqlDbContext))] - [Migration("20250430113338_Countries_Regions_Cities")] - partial class Countries_Regions_Cities + [Migration("20250430180231_Add_Countries_Regions_Cities_Addresses")] + partial class Add_Countries_Regions_Cities_Addresses { /// protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -24,9 +24,10 @@ namespace Persistence.PostgreSql.Migrations .HasAnnotation("ProductVersion", "9.0.4") .HasAnnotation("Relational:MaxIdentifierLength", 63); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "vehicle_type", new[] { "bus", "train", "aircraft" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.HasSequence("addresses_id_sequence"); + modelBuilder.HasSequence("cities_id_sequence"); modelBuilder.HasSequence("countries_id_sequence"); @@ -37,25 +38,57 @@ namespace Persistence.PostgreSql.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("bigint"); + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.addresses_id_sequence')"); - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "addresses_id_sequence"); b.Property("CityId") - .HasColumnType("bigint"); + .HasColumnType("bigint") + .HasColumnName("city_id"); b.Property("Guid") - .HasColumnType("uuid"); + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); b.Property("Name") .IsRequired() - .HasColumnType("text"); + .HasColumnType("varchar(128)") + .HasColumnName("name"); - b.HasKey("Id"); + b.Property("VehicleType") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("vehicle_type"); - b.HasIndex("CityId"); + b.HasKey("Id") + .HasName("pk_addresses"); - b.ToTable("Address", "application"); + b.HasAlternateKey("Guid") + .HasName("altk_addresses_Guid"); + + b.HasIndex("CityId") + .HasDatabaseName("ix_addresses_city_id"); + + b.HasIndex("Guid") + .IsUnique() + .HasDatabaseName("ix_addresses_uuid"); + + b.HasIndex("Id") + .IsUnique() + .HasDatabaseName("ix_addresses_id"); + + b.ToTable("addresses", "application", t => + { + t.HasCheckConstraint("ck_addresses_vehicle_type", "vehicle_type IN ('bus', 'train', 'aircraft')"); + }); }); modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.City", b => @@ -95,7 +128,8 @@ namespace Persistence.PostgreSql.Migrations .IsUnique() .HasDatabaseName("ix_cities_id"); - b.HasIndex("RegionId"); + b.HasIndex("RegionId") + .HasDatabaseName("ix_cities_region_id"); b.ToTable("cities", "application"); }); @@ -165,7 +199,8 @@ namespace Persistence.PostgreSql.Migrations b.HasAlternateKey("Guid") .HasName("altk_regions_Guid"); - b.HasIndex("CountryId"); + b.HasIndex("CountryId") + .HasDatabaseName("ix_regions_country_id"); b.HasIndex("Guid") .IsUnique() @@ -178,62 +213,14 @@ namespace Persistence.PostgreSql.Migrations b.ToTable("regions", "application"); }); - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Route", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Guid") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Route", "application"); - }); - - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.RouteAddress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AddressId") - .HasColumnType("bigint"); - - b.Property("Guid") - .HasColumnType("uuid"); - - b.Property("Order") - .HasColumnType("smallint"); - - b.Property("RouteId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("AddressId"); - - b.HasIndex("RouteId"); - - b.ToTable("RouteAddress", "application"); - }); - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Address", b => { b.HasOne("cuqmbr.TravelGuide.Domain.Entities.City", "City") .WithMany("Addresses") .HasForeignKey("CityId") .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .IsRequired() + .HasConstraintName("fk_addresses_city_id"); b.Navigation("City"); }); @@ -262,30 +249,6 @@ namespace Persistence.PostgreSql.Migrations b.Navigation("Country"); }); - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.RouteAddress", b => - { - b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Address", "Address") - .WithMany("AddressRoutes") - .HasForeignKey("AddressId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Route", "Route") - .WithMany("RouteAddresses") - .HasForeignKey("RouteId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Address"); - - b.Navigation("Route"); - }); - - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Address", b => - { - b.Navigation("AddressRoutes"); - }); - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.City", b => { b.Navigation("Addresses"); @@ -300,11 +263,6 @@ namespace Persistence.PostgreSql.Migrations { b.Navigation("Cities"); }); - - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Route", b => - { - b.Navigation("RouteAddresses"); - }); #pragma warning restore 612, 618 } } diff --git a/src/Persistence/PostgreSql/Migrations/20250430113338_Countries_Regions_Cities.cs b/src/Persistence/PostgreSql/Migrations/20250430180231_Add_Countries_Regions_Cities_Addresses.cs similarity index 65% rename from src/Persistence/PostgreSql/Migrations/20250430113338_Countries_Regions_Cities.cs rename to src/Persistence/PostgreSql/Migrations/20250430180231_Add_Countries_Regions_Cities_Addresses.cs index 04b8098..bc5649f 100644 --- a/src/Persistence/PostgreSql/Migrations/20250430113338_Countries_Regions_Cities.cs +++ b/src/Persistence/PostgreSql/Migrations/20250430180231_Add_Countries_Regions_Cities_Addresses.cs @@ -1,13 +1,12 @@ using System; using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable namespace Persistence.PostgreSql.Migrations { /// - public partial class Countries_Regions_Cities : Migration + public partial class Add_Countries_Regions_Cities_Addresses : Migration { /// protected override void Up(MigrationBuilder migrationBuilder) @@ -15,8 +14,9 @@ namespace Persistence.PostgreSql.Migrations migrationBuilder.EnsureSchema( name: "application"); - migrationBuilder.AlterDatabase() - .Annotation("Npgsql:Enum:vehicle_type", "bus,train,aircraft"); + migrationBuilder.CreateSequence( + name: "addresses_id_sequence", + schema: "application"); migrationBuilder.CreateSequence( name: "cities_id_sequence", @@ -45,21 +45,6 @@ namespace Persistence.PostgreSql.Migrations table.UniqueConstraint("altk_countries_Guid", x => x.uuid); }); - migrationBuilder.CreateTable( - name: "Route", - schema: "application", - columns: table => new - { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - Guid = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Route", x => x.Id); - }); - migrationBuilder.CreateTable( name: "regions", schema: "application", @@ -107,64 +92,51 @@ namespace Persistence.PostgreSql.Migrations }); migrationBuilder.CreateTable( - name: "Address", + name: "addresses", schema: "application", columns: table => new { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Name = table.Column(type: "text", nullable: false), - CityId = table.Column(type: "bigint", nullable: false), - Guid = table.Column(type: "uuid", nullable: false) + id = table.Column(type: "bigint", nullable: false, defaultValueSql: "nextval('application.addresses_id_sequence')"), + name = table.Column(type: "varchar(128)", nullable: false), + Longitude = table.Column(type: "double precision", nullable: false), + Latitude = table.Column(type: "double precision", nullable: false), + vehicle_type = table.Column(type: "varchar(16)", nullable: false), + city_id = table.Column(type: "bigint", nullable: false), + uuid = table.Column(type: "uuid", nullable: false) }, constraints: table => { - table.PrimaryKey("PK_Address", x => x.Id); + table.PrimaryKey("pk_addresses", x => x.id); + table.UniqueConstraint("altk_addresses_Guid", x => x.uuid); + table.CheckConstraint("ck_addresses_vehicle_type", "vehicle_type IN ('bus', 'train', 'aircraft')"); table.ForeignKey( - name: "FK_Address_cities_CityId", - column: x => x.CityId, + name: "fk_addresses_city_id", + column: x => x.city_id, principalSchema: "application", principalTable: "cities", principalColumn: "id", onDelete: ReferentialAction.Cascade); }); - migrationBuilder.CreateTable( - name: "RouteAddress", + migrationBuilder.CreateIndex( + name: "ix_addresses_city_id", schema: "application", - columns: table => new - { - Id = table.Column(type: "bigint", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - Order = table.Column(type: "smallint", nullable: false), - AddressId = table.Column(type: "bigint", nullable: false), - RouteId = table.Column(type: "bigint", nullable: false), - Guid = table.Column(type: "uuid", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_RouteAddress", x => x.Id); - table.ForeignKey( - name: "FK_RouteAddress_Address_AddressId", - column: x => x.AddressId, - principalSchema: "application", - principalTable: "Address", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - table.ForeignKey( - name: "FK_RouteAddress_Route_RouteId", - column: x => x.RouteId, - principalSchema: "application", - principalTable: "Route", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); + table: "addresses", + column: "city_id"); migrationBuilder.CreateIndex( - name: "IX_Address_CityId", + name: "ix_addresses_id", schema: "application", - table: "Address", - column: "CityId"); + table: "addresses", + column: "id", + unique: true); + + migrationBuilder.CreateIndex( + name: "ix_addresses_uuid", + schema: "application", + table: "addresses", + column: "uuid", + unique: true); migrationBuilder.CreateIndex( name: "ix_cities_id", @@ -174,7 +146,7 @@ namespace Persistence.PostgreSql.Migrations unique: true); migrationBuilder.CreateIndex( - name: "IX_cities_region_id", + name: "ix_cities_region_id", schema: "application", table: "cities", column: "region_id"); @@ -201,7 +173,7 @@ namespace Persistence.PostgreSql.Migrations unique: true); migrationBuilder.CreateIndex( - name: "IX_regions_country_id", + name: "ix_regions_country_id", schema: "application", table: "regions", column: "country_id"); @@ -219,33 +191,13 @@ namespace Persistence.PostgreSql.Migrations table: "regions", column: "uuid", unique: true); - - migrationBuilder.CreateIndex( - name: "IX_RouteAddress_AddressId", - schema: "application", - table: "RouteAddress", - column: "AddressId"); - - migrationBuilder.CreateIndex( - name: "IX_RouteAddress_RouteId", - schema: "application", - table: "RouteAddress", - column: "RouteId"); } /// protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( - name: "RouteAddress", - schema: "application"); - - migrationBuilder.DropTable( - name: "Address", - schema: "application"); - - migrationBuilder.DropTable( - name: "Route", + name: "addresses", schema: "application"); migrationBuilder.DropTable( @@ -260,6 +212,10 @@ namespace Persistence.PostgreSql.Migrations name: "countries", schema: "application"); + migrationBuilder.DropSequence( + name: "addresses_id_sequence", + schema: "application"); + migrationBuilder.DropSequence( name: "cities_id_sequence", schema: "application"); diff --git a/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs b/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs index 1d282f6..d8602a8 100644 --- a/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs +++ b/src/Persistence/PostgreSql/Migrations/PostgreSqlDbContextModelSnapshot.cs @@ -21,9 +21,10 @@ namespace Persistence.PostgreSql.Migrations .HasAnnotation("ProductVersion", "9.0.4") .HasAnnotation("Relational:MaxIdentifierLength", 63); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "vehicle_type", new[] { "bus", "train", "aircraft" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + modelBuilder.HasSequence("addresses_id_sequence"); + modelBuilder.HasSequence("cities_id_sequence"); modelBuilder.HasSequence("countries_id_sequence"); @@ -34,25 +35,57 @@ namespace Persistence.PostgreSql.Migrations { b.Property("Id") .ValueGeneratedOnAdd() - .HasColumnType("bigint"); + .HasColumnType("bigint") + .HasColumnName("id") + .HasDefaultValueSql("nextval('application.addresses_id_sequence')"); - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + NpgsqlPropertyBuilderExtensions.UseSequence(b.Property("Id"), "addresses_id_sequence"); b.Property("CityId") - .HasColumnType("bigint"); + .HasColumnType("bigint") + .HasColumnName("city_id"); b.Property("Guid") - .HasColumnType("uuid"); + .HasColumnType("uuid") + .HasColumnName("uuid"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); b.Property("Name") .IsRequired() - .HasColumnType("text"); + .HasColumnType("varchar(128)") + .HasColumnName("name"); - b.HasKey("Id"); + b.Property("VehicleType") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("vehicle_type"); - b.HasIndex("CityId"); + b.HasKey("Id") + .HasName("pk_addresses"); - b.ToTable("Address", "application"); + b.HasAlternateKey("Guid") + .HasName("altk_addresses_Guid"); + + b.HasIndex("CityId") + .HasDatabaseName("ix_addresses_city_id"); + + b.HasIndex("Guid") + .IsUnique() + .HasDatabaseName("ix_addresses_uuid"); + + b.HasIndex("Id") + .IsUnique() + .HasDatabaseName("ix_addresses_id"); + + b.ToTable("addresses", "application", t => + { + t.HasCheckConstraint("ck_addresses_vehicle_type", "vehicle_type IN ('bus', 'train', 'aircraft')"); + }); }); modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.City", b => @@ -92,7 +125,8 @@ namespace Persistence.PostgreSql.Migrations .IsUnique() .HasDatabaseName("ix_cities_id"); - b.HasIndex("RegionId"); + b.HasIndex("RegionId") + .HasDatabaseName("ix_cities_region_id"); b.ToTable("cities", "application"); }); @@ -162,7 +196,8 @@ namespace Persistence.PostgreSql.Migrations b.HasAlternateKey("Guid") .HasName("altk_regions_Guid"); - b.HasIndex("CountryId"); + b.HasIndex("CountryId") + .HasDatabaseName("ix_regions_country_id"); b.HasIndex("Guid") .IsUnique() @@ -175,62 +210,14 @@ namespace Persistence.PostgreSql.Migrations b.ToTable("regions", "application"); }); - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Route", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Guid") - .HasColumnType("uuid"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Route", "application"); - }); - - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.RouteAddress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AddressId") - .HasColumnType("bigint"); - - b.Property("Guid") - .HasColumnType("uuid"); - - b.Property("Order") - .HasColumnType("smallint"); - - b.Property("RouteId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("AddressId"); - - b.HasIndex("RouteId"); - - b.ToTable("RouteAddress", "application"); - }); - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Address", b => { b.HasOne("cuqmbr.TravelGuide.Domain.Entities.City", "City") .WithMany("Addresses") .HasForeignKey("CityId") .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); + .IsRequired() + .HasConstraintName("fk_addresses_city_id"); b.Navigation("City"); }); @@ -259,30 +246,6 @@ namespace Persistence.PostgreSql.Migrations b.Navigation("Country"); }); - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.RouteAddress", b => - { - b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Address", "Address") - .WithMany("AddressRoutes") - .HasForeignKey("AddressId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("cuqmbr.TravelGuide.Domain.Entities.Route", "Route") - .WithMany("RouteAddresses") - .HasForeignKey("RouteId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Address"); - - b.Navigation("Route"); - }); - - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Address", b => - { - b.Navigation("AddressRoutes"); - }); - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.City", b => { b.Navigation("Addresses"); @@ -297,11 +260,6 @@ namespace Persistence.PostgreSql.Migrations { b.Navigation("Cities"); }); - - modelBuilder.Entity("cuqmbr.TravelGuide.Domain.Entities.Route", b => - { - b.Navigation("RouteAddresses"); - }); #pragma warning restore 612, 618 } } diff --git a/src/Persistence/PostgreSql/PostgreSqlDbContext.cs b/src/Persistence/PostgreSql/PostgreSqlDbContext.cs index fff9242..009b9a6 100644 --- a/src/Persistence/PostgreSql/PostgreSqlDbContext.cs +++ b/src/Persistence/PostgreSql/PostgreSqlDbContext.cs @@ -24,15 +24,12 @@ public class PostgreSqlDbContext : DbContext builder.HasDefaultSchema(DefaultSchema); - builder.HasPostgresEnum( - "vehicle_type", - VehicleType.Enumerations.Select(e => e.Value.Name).ToArray()); - builder .ApplyConfigurationsFromAssembly( Assembly.GetExecutingAssembly(), t => t.Namespace == "cuqmbr.TravelGuide.Persistence.PostgreSql.Configurations"); + } protected override void ConfigureConventions( diff --git a/src/Persistence/PostgreSql/PostgreSqlUnitOfWork.cs b/src/Persistence/PostgreSql/PostgreSqlUnitOfWork.cs index ae0977a..bd97758 100644 --- a/src/Persistence/PostgreSql/PostgreSqlUnitOfWork.cs +++ b/src/Persistence/PostgreSql/PostgreSqlUnitOfWork.cs @@ -16,6 +16,7 @@ public sealed class PostgreSqlUnitOfWork : UnitOfWork CountryRepository = new PostgreSqlCountryRepository(_dbContext); RegionRepository = new PostgreSqlRegionRepository(_dbContext); CityRepository = new PostgreSqlCityRepository(_dbContext); + AddressRepository = new PostgreSqlAddressRepository(_dbContext); } public CountryRepository CountryRepository { get; init; } @@ -24,6 +25,8 @@ public sealed class PostgreSqlUnitOfWork : UnitOfWork public CityRepository CityRepository { get; init; } + public AddressRepository AddressRepository { get; init; } + public int Save() { return _dbContext.SaveChanges(); diff --git a/src/Persistence/PostgreSql/Repositories/PostgreSqlAddressRepository.cs b/src/Persistence/PostgreSql/Repositories/PostgreSqlAddressRepository.cs new file mode 100644 index 0000000..58915b0 --- /dev/null +++ b/src/Persistence/PostgreSql/Repositories/PostgreSqlAddressRepository.cs @@ -0,0 +1,11 @@ +using cuqmbr.TravelGuide.Application.Common.Interfaces.Persistence.Repositories; +using cuqmbr.TravelGuide.Domain.Entities; + +namespace cuqmbr.TravelGuide.Persistence.PostgreSql.Repositories; + +public sealed class PostgreSqlAddressRepository : + PostgreSqlBaseRepository
, AddressRepository +{ + public PostgreSqlAddressRepository(PostgreSqlDbContext dbContext) + : base(dbContext) { } +} diff --git a/tst/Application.IntegrationTests/CitiesTests.cs b/tst/Application.IntegrationTests/CitiesTests.cs index 3554879..4a4c68e 100644 --- a/tst/Application.IntegrationTests/CitiesTests.cs +++ b/tst/Application.IntegrationTests/CitiesTests.cs @@ -35,7 +35,7 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); string cityName = "City Name"; @@ -44,13 +44,13 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName, - RegionUuid = addRegionResult.Uuid + RegionGuid = addRegionResult.Uuid }, TestContext.Current.CancellationToken); var getCityResult = await mediator.Send( new GetCityQuery() { - Uuid = addCityResult.Uuid, + Guid = addCityResult.Uuid, }, TestContext.Current.CancellationToken); Assert.NotNull(getCityResult); @@ -89,7 +89,7 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); string cityName = "City Name"; @@ -98,14 +98,14 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName, - RegionUuid = addRegionResult.Uuid + RegionGuid = addRegionResult.Uuid }, TestContext.Current.CancellationToken); await Assert.ThrowsAsync(() => mediator.Send(new AddCityCommand() { Name = cityName, - RegionUuid = addRegionResult.Uuid + RegionGuid = addRegionResult.Uuid }, TestContext.Current.CancellationToken)); } @@ -132,14 +132,14 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); string cityName = "City Name"; @@ -148,20 +148,20 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName, - RegionUuid = addRegionResult1.Uuid + RegionGuid = addRegionResult1.Uuid }, TestContext.Current.CancellationToken); var addCityResult2 = await mediator.Send( new AddCityCommand() { Name = cityName, - RegionUuid = addRegionResult2.Uuid + RegionGuid = addRegionResult2.Uuid }, TestContext.Current.CancellationToken); var getCityResult1 = await mediator.Send( new GetCityQuery() { - Uuid = addCityResult1.Uuid, + Guid = addCityResult1.Uuid, }, TestContext.Current.CancellationToken); Assert.NotNull(getCityResult1); @@ -180,7 +180,7 @@ public class CitiesTests : TestBase var getCityResult2 = await mediator.Send( new GetCityQuery() { - Uuid = addCityResult2.Uuid, + Guid = addCityResult2.Uuid, }, TestContext.Current.CancellationToken); Assert.NotNull(getCityResult2); @@ -209,7 +209,7 @@ public class CitiesTests : TestBase mediator.Send(new AddCityCommand() { Name = "Name", - RegionUuid = Guid.NewGuid() + RegionGuid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -246,7 +246,7 @@ public class CitiesTests : TestBase mediator.Send(new AddCityCommand() { Name = "Name", - RegionUuid = + RegionGuid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty }, TestContext.Current.CancellationToken)); } @@ -303,7 +303,7 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); string cityName = "City Name"; @@ -312,7 +312,7 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName, - RegionUuid = addRegionResult.Uuid + RegionGuid = addRegionResult.Uuid }, TestContext.Current.CancellationToken); string newName = "Different Name"; @@ -320,9 +320,9 @@ public class CitiesTests : TestBase var updateCityResult = await mediator.Send( new UpdateCityCommand() { - Uuid = addCityResult.Uuid, + Guid = addCityResult.Uuid, Name = newName, - RegionUuid = addRegionResult.Uuid + RegionGuid = addRegionResult.Uuid }, TestContext.Current.CancellationToken); Assert.NotNull(updateCityResult); @@ -355,7 +355,7 @@ public class CitiesTests : TestBase mediator.Send(new UpdateCityCommand() { Name = name, - RegionUuid = Guid.NewGuid() + RegionGuid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -373,9 +373,9 @@ public class CitiesTests : TestBase await Assert.ThrowsAsync(() => mediator.Send(new UpdateCityCommand() { - Uuid = Guid.NewGuid(), + Guid = Guid.NewGuid(), Name = "Name", - RegionUuid = + RegionGuid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty }, TestContext.Current.CancellationToken)); } @@ -394,9 +394,9 @@ public class CitiesTests : TestBase await Assert.ThrowsAsync(() => mediator.Send(new UpdateCityCommand() { - Uuid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty, + Guid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty, Name = "Name", - RegionUuid = Guid.NewGuid() + RegionGuid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -418,15 +418,15 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = "Name", - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); await Assert.ThrowsAsync(() => mediator.Send(new UpdateCityCommand() { - Uuid = Guid.NewGuid(), + Guid = Guid.NewGuid(), Name = "Different Name", - RegionUuid = addRegionResult.Uuid + RegionGuid = addRegionResult.Uuid }, TestContext.Current.CancellationToken)); } @@ -448,22 +448,22 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = "Name", - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); var addCityResult = await mediator.Send( new AddCityCommand() { Name = "Name", - RegionUuid = addRegionResult.Uuid + RegionGuid = addRegionResult.Uuid }, TestContext.Current.CancellationToken); await Assert.ThrowsAsync(() => mediator.Send(new UpdateCityCommand() { - Uuid = addCityResult.Uuid, + Guid = addCityResult.Uuid, Name = "Different Name", - RegionUuid = Guid.NewGuid() + RegionGuid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -515,26 +515,26 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = "Name", - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); var addCityResult = await mediator.Send( new AddCityCommand() { Name = "Name", - RegionUuid = addRegionResult.Uuid + RegionGuid = addRegionResult.Uuid }, TestContext.Current.CancellationToken); await mediator.Send( new DeleteCityCommand() { - Uuid = addCityResult.Uuid, + Guid = addCityResult.Uuid, }, TestContext.Current.CancellationToken); await Assert.ThrowsAsync(() => mediator.Send(new GetCityQuery() { - Uuid = addCityResult.Uuid, + Guid = addCityResult.Uuid, }, TestContext.Current.CancellationToken)); } @@ -552,7 +552,7 @@ public class CitiesTests : TestBase await Assert.ThrowsAsync(() => mediator.Send(new DeleteCityCommand() { - Uuid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty + Guid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty }, TestContext.Current.CancellationToken)); } @@ -567,7 +567,7 @@ public class CitiesTests : TestBase await Assert.ThrowsAsync(() => mediator.Send(new DeleteCityCommand() { - Uuid = Guid.NewGuid() + Guid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -623,7 +623,7 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); string cityName = "Name"; @@ -632,13 +632,13 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName, - RegionUuid = addRegionResult.Uuid + RegionGuid = addRegionResult.Uuid }, TestContext.Current.CancellationToken); var getCityResult = await mediator.Send( new GetCityQuery() { - Uuid = addCityResult.Uuid, + Guid = addCityResult.Uuid, }, TestContext.Current.CancellationToken); Assert.NotNull(getCityResult); @@ -670,7 +670,7 @@ public class CitiesTests : TestBase mediator.Send( new GetCityQuery() { - Uuid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty + Guid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty }, TestContext.Current.CancellationToken)); } @@ -686,7 +686,7 @@ public class CitiesTests : TestBase mediator.Send( new GetCityQuery() { - Uuid = Guid.NewGuid() + Guid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -743,14 +743,14 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); string cityName1 = "City Name 1"; @@ -760,14 +760,14 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName1, - RegionUuid = addRegionResult1.Uuid + RegionGuid = addRegionResult1.Uuid }, TestContext.Current.CancellationToken); var addCityResult2 = await mediator.Send( new AddCityCommand() { Name = cityName2, - RegionUuid = addRegionResult2.Uuid + RegionGuid = addRegionResult2.Uuid }, TestContext.Current.CancellationToken); var getCitiesResult = await mediator.Send( @@ -928,14 +928,14 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); string cityName1 = "City Name 1"; @@ -945,14 +945,14 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName1, - RegionUuid = addRegionResult1.Uuid + RegionGuid = addRegionResult1.Uuid }, TestContext.Current.CancellationToken); var addCityResult2 = await mediator.Send( new AddCityCommand() { Name = cityName2, - RegionUuid = addRegionResult2.Uuid + RegionGuid = addRegionResult2.Uuid }, TestContext.Current.CancellationToken); var getCitiesResult = await mediator.Send( @@ -1022,14 +1022,14 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); string cityName1 = "City Name 1"; @@ -1039,14 +1039,14 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName1, - RegionUuid = addRegionResult1.Uuid + RegionGuid = addRegionResult1.Uuid }, TestContext.Current.CancellationToken); var addCityResult2 = await mediator.Send( new AddCityCommand() { Name = cityName2, - RegionUuid = addRegionResult2.Uuid + RegionGuid = addRegionResult2.Uuid }, TestContext.Current.CancellationToken); var getCitiesResult = await mediator.Send( @@ -1116,14 +1116,14 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); string cityName1 = "City Name 1"; @@ -1133,14 +1133,14 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName1, - RegionUuid = addRegionResult1.Uuid + RegionGuid = addRegionResult1.Uuid }, TestContext.Current.CancellationToken); var addCityResult2 = await mediator.Send( new AddCityCommand() { Name = cityName2, - RegionUuid = addRegionResult2.Uuid + RegionGuid = addRegionResult2.Uuid }, TestContext.Current.CancellationToken); var getCitiesResult = await mediator.Send( @@ -1210,14 +1210,14 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); string cityName1 = "City Name 1"; @@ -1227,14 +1227,14 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName1, - RegionUuid = addRegionResult1.Uuid + RegionGuid = addRegionResult1.Uuid }, TestContext.Current.CancellationToken); var addCityResult2 = await mediator.Send( new AddCityCommand() { Name = cityName2, - RegionUuid = addRegionResult2.Uuid + RegionGuid = addRegionResult2.Uuid }, TestContext.Current.CancellationToken); var getCitiesResult = await mediator.Send( @@ -1324,14 +1324,14 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); string cityName1 = "City Name 1"; @@ -1341,14 +1341,14 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName1, - RegionUuid = addRegionResult1.Uuid + RegionGuid = addRegionResult1.Uuid }, TestContext.Current.CancellationToken); var addCityResult2 = await mediator.Send( new AddCityCommand() { Name = cityName2, - RegionUuid = addRegionResult2.Uuid + RegionGuid = addRegionResult2.Uuid }, TestContext.Current.CancellationToken); var getCitiesResult = await mediator.Send( @@ -1356,7 +1356,7 @@ public class CitiesTests : TestBase { PageNumber = 1, PageSize = 10, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); Assert.NotNull(getCitiesResult); @@ -1419,14 +1419,14 @@ public class CitiesTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); string cityName1 = "City Name 1"; @@ -1436,14 +1436,14 @@ public class CitiesTests : TestBase new AddCityCommand() { Name = cityName1, - RegionUuid = addRegionResult1.Uuid + RegionGuid = addRegionResult1.Uuid }, TestContext.Current.CancellationToken); var addCityResult2 = await mediator.Send( new AddCityCommand() { Name = cityName2, - RegionUuid = addRegionResult2.Uuid + RegionGuid = addRegionResult2.Uuid }, TestContext.Current.CancellationToken); var getCitiesResult = await mediator.Send( @@ -1451,7 +1451,7 @@ public class CitiesTests : TestBase { PageNumber = 1, PageSize = 10, - RegionUuid = addRegionResult2.Uuid + RegionGuid = addRegionResult2.Uuid }, TestContext.Current.CancellationToken); Assert.NotNull(getCitiesResult); diff --git a/tst/Application.IntegrationTests/RegionsTests.cs b/tst/Application.IntegrationTests/RegionsTests.cs index 60b7f10..78e0991 100644 --- a/tst/Application.IntegrationTests/RegionsTests.cs +++ b/tst/Application.IntegrationTests/RegionsTests.cs @@ -34,13 +34,13 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = regionName, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); var getRegionResult = await mediator.Send( new GetRegionQuery() { - Uuid = addRegionResult.Uuid, + Guid = addRegionResult.Uuid, }, TestContext.Current.CancellationToken); Assert.NotNull(getRegionResult); @@ -73,14 +73,14 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = regionName, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); await Assert.ThrowsAsync(() => mediator.Send(new AddRegionCommand() { Name = regionName, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken)); } @@ -114,20 +114,20 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = regionName, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); var getRegionResult1 = await mediator.Send( new GetRegionQuery() { - Uuid = addRegionResult1.Uuid, + Guid = addRegionResult1.Uuid, }, TestContext.Current.CancellationToken); Assert.NotNull(getRegionResult1); @@ -138,7 +138,7 @@ public class RegionsTests : TestBase var getRegionResult2 = await mediator.Send( new GetRegionQuery() { - Uuid = addRegionResult2.Uuid, + Guid = addRegionResult2.Uuid, }, TestContext.Current.CancellationToken); Assert.NotNull(getRegionResult2); @@ -161,7 +161,7 @@ public class RegionsTests : TestBase mediator.Send(new AddRegionCommand() { Name = "Name", - CountryUuid = Guid.NewGuid() + CountryGuid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -198,7 +198,7 @@ public class RegionsTests : TestBase mediator.Send(new AddRegionCommand() { Name = "Name", - CountryUuid = + CountryGuid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty }, TestContext.Current.CancellationToken)); } @@ -255,7 +255,7 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = regionName, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); string newName = "Different Name"; @@ -263,15 +263,15 @@ public class RegionsTests : TestBase var updateRegionResult = await mediator.Send( new UpdateRegionCommand() { - Uuid = addRegionResult.Uuid, + Guid = addRegionResult.Uuid, Name = newName, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); var getRegionResult = await mediator.Send( new GetRegionQuery() { - Uuid = addRegionResult.Uuid, + Guid = addRegionResult.Uuid, }, TestContext.Current.CancellationToken); Assert.NotNull(getRegionResult); @@ -297,7 +297,7 @@ public class RegionsTests : TestBase mediator.Send(new UpdateRegionCommand() { Name = name, - CountryUuid = Guid.NewGuid() + CountryGuid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -315,9 +315,9 @@ public class RegionsTests : TestBase await Assert.ThrowsAsync(() => mediator.Send(new UpdateRegionCommand() { - Uuid = Guid.NewGuid(), + Guid = Guid.NewGuid(), Name = "Name", - CountryUuid = + CountryGuid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty }, TestContext.Current.CancellationToken)); } @@ -336,9 +336,9 @@ public class RegionsTests : TestBase await Assert.ThrowsAsync(() => mediator.Send(new UpdateRegionCommand() { - Uuid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty, + Guid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty, Name = "Name", - CountryUuid = Guid.NewGuid() + CountryGuid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -359,9 +359,9 @@ public class RegionsTests : TestBase await Assert.ThrowsAsync(() => mediator.Send(new UpdateRegionCommand() { - Uuid = Guid.NewGuid(), + Guid = Guid.NewGuid(), Name = "Different Name", - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken)); } @@ -383,15 +383,15 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = "Name", - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); await Assert.ThrowsAsync(() => mediator.Send(new UpdateRegionCommand() { - Uuid = addCountryResult.Uuid, + Guid = addCountryResult.Uuid, Name = "Different Name", - CountryUuid = Guid.NewGuid() + CountryGuid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -443,19 +443,19 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = "Name", - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); await mediator.Send( new DeleteRegionCommand() { - Uuid = addRegionResult.Uuid, + Guid = addRegionResult.Uuid, }, TestContext.Current.CancellationToken); await Assert.ThrowsAsync(() => mediator.Send(new GetRegionQuery() { - Uuid = addRegionResult.Uuid, + Guid = addRegionResult.Uuid, }, TestContext.Current.CancellationToken)); } @@ -473,7 +473,7 @@ public class RegionsTests : TestBase await Assert.ThrowsAsync(() => mediator.Send(new DeleteRegionCommand() { - Uuid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty + Guid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty }, TestContext.Current.CancellationToken)); } @@ -488,7 +488,7 @@ public class RegionsTests : TestBase await Assert.ThrowsAsync(() => mediator.Send(new DeleteRegionCommand() { - Uuid = Guid.NewGuid() + Guid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -544,13 +544,13 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = regionName, - CountryUuid = addCountryResult.Uuid + CountryGuid = addCountryResult.Uuid }, TestContext.Current.CancellationToken); var getRegionResult = await mediator.Send( new GetRegionQuery() { - Uuid = addRegionResult.Uuid, + Guid = addRegionResult.Uuid, }, TestContext.Current.CancellationToken); Assert.NotNull(getRegionResult); @@ -577,7 +577,7 @@ public class RegionsTests : TestBase mediator.Send( new GetRegionQuery() { - Uuid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty + Guid = Guid.TryParse(uuid, out var guid) ? guid : Guid.Empty }, TestContext.Current.CancellationToken)); } @@ -593,7 +593,7 @@ public class RegionsTests : TestBase mediator.Send( new GetRegionQuery() { - Uuid = Guid.NewGuid() + Guid = Guid.NewGuid() }, TestContext.Current.CancellationToken)); } @@ -657,14 +657,14 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); var getRegionsResult = await mediator.Send( @@ -782,14 +782,14 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); var getRegionsResult = await mediator.Send( @@ -850,14 +850,14 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); var getRegionsResult = await mediator.Send( @@ -918,14 +918,14 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); var getRegionsResult = await mediator.Send( @@ -987,21 +987,21 @@ public class RegionsTests : TestBase new AddRegionCommand() { Name = regionName1, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); var addRegionResult2 = await mediator.Send( new AddRegionCommand() { Name = regionName2, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); var addRegionResult3 = await mediator.Send( new AddRegionCommand() { Name = regionName3, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); var getRegionsResult = await mediator.Send( @@ -1009,7 +1009,7 @@ public class RegionsTests : TestBase { PageNumber = 1, PageSize = 10, - CountryUuid = addCountryResult1.Uuid + CountryGuid = addCountryResult1.Uuid }, TestContext.Current.CancellationToken); Assert.NotNull(getRegionsResult); @@ -1036,7 +1036,7 @@ public class RegionsTests : TestBase { PageNumber = 1, PageSize = 10, - CountryUuid = addCountryResult2.Uuid + CountryGuid = addCountryResult2.Uuid }, TestContext.Current.CancellationToken); Assert.NotNull(getRegionsResult);