diff --git a/AutobusApi.Api/Controllers/AddressController.cs b/AutobusApi.Api/Controllers/AddressController.cs new file mode 100644 index 0000000..0bbeb11 --- /dev/null +++ b/AutobusApi.Api/Controllers/AddressController.cs @@ -0,0 +1,46 @@ +using AutobusApi.Application.Common.Models; +using AutobusApi.Application.Addresses.Commands.CreateAddress; +using AutobusApi.Application.Addresses.Commands.DeleteAddress; +using AutobusApi.Application.Addresses.Commands.UpdateAddress; +using AutobusApi.Application.Addresses.Queries; +using AutobusApi.Application.Addresses.Queries.GetAddressesWithPagination; +using AutobusApi.Application.Addresses.Queries.GetAddress; +using Microsoft.AspNetCore.Mvc; + +namespace AutobusApi.Api.Controllers; + +[Route("addresses")] +public class AddressController : BaseController +{ + [HttpPost] + public async Task Create([FromBody] CreateAddressCommand command, CancellationToken cancellationToken) + { + return await Mediator.Send(command, cancellationToken); + } + + [HttpGet] + public async Task> GetPage([FromQuery] GetAddressesWithPaginationQuery query, CancellationToken cancellationToken) + { + return await Mediator.Send(query, cancellationToken); + } + + [HttpGet("{id}")] + public async Task Get(int id, /* [FromQuery] GetAddressQuery query, */ CancellationToken cancellationToken) + { + var query = new GetAddressQuery() { Id = id }; + return await Mediator.Send(query, cancellationToken); + } + + [HttpPut] + public async Task Update([FromBody] UpdateAddressCommand command, CancellationToken cancellationToken) + { + await Mediator.Send(command, cancellationToken); + } + + [HttpDelete("{id}")] + public async Task Delete(int id, /* [FromBody] DeleteAddressCommand command, */ CancellationToken cancellationToken) + { + var command = new DeleteAddressCommand() { Id = id }; + await Mediator.Send(command, cancellationToken); + } +} diff --git a/AutobusApi.Api/Controllers/BaseController.cs b/AutobusApi.Api/Controllers/BaseController.cs index 39137b3..4528bbc 100644 --- a/AutobusApi.Api/Controllers/BaseController.cs +++ b/AutobusApi.Api/Controllers/BaseController.cs @@ -4,7 +4,6 @@ using Microsoft.AspNetCore.Mvc; namespace AutobusApi.Api.Controllers; [ApiController] -[Route("[controller]")] public class BaseController : ControllerBase { private IMediator _mediator; diff --git a/AutobusApi.Api/Controllers/BusController.cs b/AutobusApi.Api/Controllers/BusController.cs new file mode 100644 index 0000000..970f33d --- /dev/null +++ b/AutobusApi.Api/Controllers/BusController.cs @@ -0,0 +1,46 @@ +using AutobusApi.Application.Common.Models; +using AutobusApi.Application.Buses.Commands.CreateBus; +using AutobusApi.Application.Buses.Commands.DeleteBus; +using AutobusApi.Application.Buses.Commands.UpdateBus; +using AutobusApi.Application.Buses.Queries; +using AutobusApi.Application.Buses.Queries.GetBusesWithPagination; +using AutobusApi.Application.Buses.Queries.GetBus; +using Microsoft.AspNetCore.Mvc; + +namespace AutobusApi.Api.Controllers; + +[Route("vehicle/buses")] +public class BusController : BaseController +{ + [HttpPost] + public async Task Create([FromBody] CreateBusCommand command, CancellationToken cancellationToken) + { + return await Mediator.Send(command, cancellationToken); + } + + [HttpGet] + public async Task> GetPage([FromQuery] GetBusesWithPaginationQuery query, CancellationToken cancellationToken) + { + return await Mediator.Send(query, cancellationToken); + } + + [HttpGet("{id}")] + public async Task Get(int id, /* [FromQuery] GetBusQuery query, */ CancellationToken cancellationToken) + { + var query = new GetBusQuery() { Id = id }; + return await Mediator.Send(query, cancellationToken); + } + + [HttpPut] + public async Task Update([FromBody] UpdateBusCommand command, CancellationToken cancellationToken) + { + await Mediator.Send(command, cancellationToken); + } + + [HttpDelete("{id}")] + public async Task Delete(int id, /* [FromBody] DeleteBusCommand command, */ CancellationToken cancellationToken) + { + var command = new DeleteBusCommand() { Id = id }; + await Mediator.Send(command, cancellationToken); + } +} diff --git a/AutobusApi.Api/Controllers/CityController.cs b/AutobusApi.Api/Controllers/CityController.cs new file mode 100644 index 0000000..70aa734 --- /dev/null +++ b/AutobusApi.Api/Controllers/CityController.cs @@ -0,0 +1,46 @@ +using AutobusApi.Application.Common.Models; +using AutobusApi.Application.Cities.Commands.CreateCity; +using AutobusApi.Application.Cities.Commands.DeleteCity; +using AutobusApi.Application.Cities.Commands.UpdateCity; +using AutobusApi.Application.Cities.Queries; +using AutobusApi.Application.Cities.Queries.GetCitiesWithPagination; +using AutobusApi.Application.Cities.Queries.GetCity; +using Microsoft.AspNetCore.Mvc; + +namespace AutobusApi.Api.Controllers; + +[Route("cities")] +public class CityController : BaseController +{ + [HttpPost] + public async Task Create([FromBody] CreateCityCommand command, CancellationToken cancellationToken) + { + return await Mediator.Send(command, cancellationToken); + } + + [HttpGet] + public async Task> GetPage([FromQuery] GetCitiesWithPaginationQuery query, CancellationToken cancellationToken) + { + return await Mediator.Send(query, cancellationToken); + } + + [HttpGet("{id}")] + public async Task Get(int id, /* [FromQuery] GetCityQuery query, */ CancellationToken cancellationToken) + { + var query = new GetCityQuery() { Id = id }; + return await Mediator.Send(query, cancellationToken); + } + + [HttpPut] + public async Task Update([FromBody] UpdateCityCommand command, CancellationToken cancellationToken) + { + await Mediator.Send(command, cancellationToken); + } + + [HttpDelete("{id}")] + public async Task Delete(int id, /* [FromBody] DeleteCityCommand command, */ CancellationToken cancellationToken) + { + var command = new DeleteCityCommand() { Id = id }; + await Mediator.Send(command, cancellationToken); + } +} diff --git a/AutobusApi.Api/Controllers/CompanyController.cs b/AutobusApi.Api/Controllers/CompanyController.cs new file mode 100644 index 0000000..3c3b4df --- /dev/null +++ b/AutobusApi.Api/Controllers/CompanyController.cs @@ -0,0 +1,46 @@ +using AutobusApi.Application.Common.Models; +using AutobusApi.Application.Companies.Commands.CreateCompany; +using AutobusApi.Application.Companies.Commands.DeleteCompany; +using AutobusApi.Application.Companies.Commands.UpdateCompany; +using AutobusApi.Application.Companies.Queries; +using AutobusApi.Application.Companies.Queries.GetCompaniesWithPagination; +using AutobusApi.Application.Companies.Queries.GetCompany; +using Microsoft.AspNetCore.Mvc; + +namespace AutobusApi.Api.Controllers; + +[Route("companies")] +public class CompanyController : BaseController +{ + [HttpPost] + public async Task Create([FromBody] CreateCompanyCommand command, CancellationToken cancellationToken) + { + return await Mediator.Send(command, cancellationToken); + } + + [HttpGet] + public async Task> GetPage([FromQuery] GetCompaniesWithPaginationQuery query, CancellationToken cancellationToken) + { + return await Mediator.Send(query, cancellationToken); + } + + [HttpGet("{id}")] + public async Task Get(int id, /* [FromQuery] GetCompanyQuery query, */ CancellationToken cancellationToken) + { + var query = new GetCompanyQuery() { Id = id }; + return await Mediator.Send(query, cancellationToken); + } + + [HttpPut] + public async Task Update([FromBody] UpdateCompanyCommand command, CancellationToken cancellationToken) + { + await Mediator.Send(command, cancellationToken); + } + + [HttpDelete("{id}")] + public async Task Delete(int id, /* [FromBody] DeleteCompanyCommand command, */ CancellationToken cancellationToken) + { + var command = new DeleteCompanyCommand() { Id = id }; + await Mediator.Send(command, cancellationToken); + } +} diff --git a/AutobusApi.Api/Controllers/CountryController.cs b/AutobusApi.Api/Controllers/CountryController.cs new file mode 100644 index 0000000..46ac0a1 --- /dev/null +++ b/AutobusApi.Api/Controllers/CountryController.cs @@ -0,0 +1,46 @@ +using AutobusApi.Application.Common.Models; +using AutobusApi.Application.Countries.Commands.CreateCountry; +using AutobusApi.Application.Countries.Commands.DeleteCountry; +using AutobusApi.Application.Countries.Commands.UpdateCountry; +using AutobusApi.Application.Countries.Queries; +using AutobusApi.Application.Countries.Queries.GetCountriesWithPagination; +using AutobusApi.Application.Countries.Queries.GetCountry; +using Microsoft.AspNetCore.Mvc; + +namespace AutobusApi.Api.Controllers; + +[Route("countries")] +public class CountryController : BaseController +{ + [HttpPost] + public async Task Create([FromBody] CreateCountryCommand command, CancellationToken cancellationToken) + { + return await Mediator.Send(command, cancellationToken); + } + + [HttpGet] + public async Task> GetPage([FromQuery] GetCountriesWithPaginationQuery query, CancellationToken cancellationToken) + { + return await Mediator.Send(query, cancellationToken); + } + + [HttpGet("{id}")] + public async Task Get(int id, /* [FromQuery] GetCountryQuery query, */ CancellationToken cancellationToken) + { + var query = new GetCountryQuery() { Id = id }; + return await Mediator.Send(query, cancellationToken); + } + + [HttpPut] + public async Task Update([FromBody] UpdateCountryCommand command, CancellationToken cancellationToken) + { + await Mediator.Send(command, cancellationToken); + } + + [HttpDelete("{id}")] + public async Task Delete(int id, /* [FromBody] DeleteCountryCommand command, */ CancellationToken cancellationToken) + { + var command = new DeleteCountryCommand() { Id = id }; + await Mediator.Send(command, cancellationToken); + } +} diff --git a/AutobusApi.Api/Controllers/EmployeeController.cs b/AutobusApi.Api/Controllers/EmployeeController.cs new file mode 100644 index 0000000..5f4ea5c --- /dev/null +++ b/AutobusApi.Api/Controllers/EmployeeController.cs @@ -0,0 +1,46 @@ +using AutobusApi.Application.Common.Models; +using AutobusApi.Application.Employees.Commands.CreateEmployee; +using AutobusApi.Application.Employees.Commands.DeleteEmployee; +using AutobusApi.Application.Employees.Commands.UpdateEmployee; +using AutobusApi.Application.Employees.Queries; +using AutobusApi.Application.Employees.Queries.GetEmployeesWithPagination; +using AutobusApi.Application.Employees.Queries.GetEmployee; +using Microsoft.AspNetCore.Mvc; + +namespace AutobusApi.Api.Controllers; + +[Route("employees")] +public class EmployeeController : BaseController +{ + [HttpPost] + public async Task Create([FromBody] CreateEmployeeCommand command, CancellationToken cancellationToken) + { + return await Mediator.Send(command, cancellationToken); + } + + [HttpGet] + public async Task> GetPage([FromQuery] GetEmployeesWithPaginationQuery query, CancellationToken cancellationToken) + { + return await Mediator.Send(query, cancellationToken); + } + + [HttpGet("{id}")] + public async Task Get(int id, /* [FromQuery] GetEmployeeQuery query, */ CancellationToken cancellationToken) + { + var query = new GetEmployeeQuery() { Id = id }; + return await Mediator.Send(query, cancellationToken); + } + + [HttpPut] + public async Task Update([FromBody] UpdateEmployeeCommand command, CancellationToken cancellationToken) + { + await Mediator.Send(command, cancellationToken); + } + + [HttpDelete("{id}")] + public async Task Delete(int id, /* [FromBody] DeleteEmployeeCommand command, */ CancellationToken cancellationToken) + { + var command = new DeleteEmployeeCommand() { Id = id }; + await Mediator.Send(command, cancellationToken); + } +} diff --git a/AutobusApi.Api/Controllers/IdentityController.cs b/AutobusApi.Api/Controllers/IdentityController.cs index efb2864..235f2c1 100644 --- a/AutobusApi.Api/Controllers/IdentityController.cs +++ b/AutobusApi.Api/Controllers/IdentityController.cs @@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Mvc; namespace AutobusApi.Api.Controllers; +[Route("identity")] public class IdentityController : BaseController { [HttpPost("register")] diff --git a/AutobusApi.Api/Controllers/RegionController.cs b/AutobusApi.Api/Controllers/RegionController.cs new file mode 100644 index 0000000..201d7a6 --- /dev/null +++ b/AutobusApi.Api/Controllers/RegionController.cs @@ -0,0 +1,46 @@ +using AutobusApi.Application.Common.Models; +using AutobusApi.Application.Regions.Commands.CreateRegion; +using AutobusApi.Application.Regions.Commands.DeleteRegion; +using AutobusApi.Application.Regions.Commands.UpdateRegion; +using AutobusApi.Application.Regions.Queries; +using AutobusApi.Application.Regions.Queries.GetRegionsWithPagination; +using AutobusApi.Application.Regions.Queries.GetRegion; +using Microsoft.AspNetCore.Mvc; + +namespace AutobusApi.Api.Controllers; + +[Route("regions")] +public class RegionController : BaseController +{ + [HttpPost] + public async Task Create([FromBody] CreateRegionCommand command, CancellationToken cancellationToken) + { + return await Mediator.Send(command, cancellationToken); + } + + [HttpGet] + public async Task> GetPage([FromQuery] GetRegionsWithPaginationQuery query, CancellationToken cancellationToken) + { + return await Mediator.Send(query, cancellationToken); + } + + [HttpGet("{id}")] + public async Task Get(int id, /* [FromQuery] GetRegionQuery query, */ CancellationToken cancellationToken) + { + var query = new GetRegionQuery() { Id = id }; + return await Mediator.Send(query, cancellationToken); + } + + [HttpPut] + public async Task Update([FromBody] UpdateRegionCommand command, CancellationToken cancellationToken) + { + await Mediator.Send(command, cancellationToken); + } + + [HttpDelete("{id}")] + public async Task Delete(int id, /* [FromBody] DeleteRegionCommand command, */ CancellationToken cancellationToken) + { + var command = new DeleteRegionCommand() { Id = id }; + await Mediator.Send(command, cancellationToken); + } +} diff --git a/AutobusApi.Api/Controllers/RouteController.cs b/AutobusApi.Api/Controllers/RouteController.cs new file mode 100644 index 0000000..f8583de --- /dev/null +++ b/AutobusApi.Api/Controllers/RouteController.cs @@ -0,0 +1,46 @@ +using AutobusApi.Application.Common.Models; +using AutobusApi.Application.Routes.Commands.CreateRoute; +using AutobusApi.Application.Routes.Commands.DeleteRoute; +using AutobusApi.Application.Routes.Commands.UpdateRoute; +using AutobusApi.Application.Routes.Queries; +using AutobusApi.Application.Routes.Queries.GetRoutesWithPagination; +using AutobusApi.Application.Routes.Queries.GetRoute; +using Microsoft.AspNetCore.Mvc; + +namespace AutobusApi.Api.Controllers; + +[Route("routes")] +public class RouteController : BaseController +{ + [HttpPost] + public async Task Create([FromBody] CreateRouteCommand command, CancellationToken cancellationToken) + { + return await Mediator.Send(command, cancellationToken); + } + + [HttpGet] + public async Task> GetPage([FromQuery] GetRoutesWithPaginationQuery query, CancellationToken cancellationToken) + { + return await Mediator.Send(query, cancellationToken); + } + + [HttpGet("{id}")] + public async Task Get(int id, /* [FromQuery] GetRouteQuery query, */ CancellationToken cancellationToken) + { + var query = new GetRouteQuery() { Id = id }; + return await Mediator.Send(query, cancellationToken); + } + + [HttpPut] + public async Task Update([FromBody] UpdateRouteCommand command, CancellationToken cancellationToken) + { + await Mediator.Send(command, cancellationToken); + } + + [HttpDelete("{id}")] + public async Task Delete(int id, /* [FromBody] DeleteRouteCommand command, */ CancellationToken cancellationToken) + { + var command = new DeleteRouteCommand() { Id = id }; + await Mediator.Send(command, cancellationToken); + } +} diff --git a/AutobusApi.Api/Controllers/TicketGroupController.cs b/AutobusApi.Api/Controllers/TicketGroupController.cs new file mode 100644 index 0000000..af968cf --- /dev/null +++ b/AutobusApi.Api/Controllers/TicketGroupController.cs @@ -0,0 +1,46 @@ +using AutobusApi.Application.Common.Models; +using AutobusApi.Application.TicketGroups.Commands.CreateTicketGroup; +using AutobusApi.Application.TicketGroups.Commands.DeleteTicketGroup; +using AutobusApi.Application.TicketGroups.Commands.UpdateTicketGroup; +using AutobusApi.Application.TicketGroups.Queries; +using AutobusApi.Application.TicketGroups.Queries.GetTicketGroupsWithPagination; +using AutobusApi.Application.TicketGroups.Queries.GetTicketGroup; +using Microsoft.AspNetCore.Mvc; + +namespace AutobusApi.Api.Controllers; + +[Route("ticketgroups")] +public class TicketGroupController : BaseController +{ + [HttpPost] + public async Task Create([FromBody] CreateTicketGroupCommand command, CancellationToken cancellationToken) + { + return await Mediator.Send(command, cancellationToken); + } + + [HttpGet] + public async Task> GetPage([FromQuery] GetTicketGroupsWithPaginationQuery query, CancellationToken cancellationToken) + { + return await Mediator.Send(query, cancellationToken); + } + + [HttpGet("{id}")] + public async Task Get(int id, /* [FromQuery] GetTicketGroupQuery query, */ CancellationToken cancellationToken) + { + var query = new GetTicketGroupQuery() { Id = id }; + return await Mediator.Send(query, cancellationToken); + } + + [HttpPut] + public async Task Update([FromBody] UpdateTicketGroupCommand command, CancellationToken cancellationToken) + { + await Mediator.Send(command, cancellationToken); + } + + [HttpDelete("{id}")] + public async Task Delete(int id, /* [FromBody] DeleteTicketGroupCommand command, */ CancellationToken cancellationToken) + { + var command = new DeleteTicketGroupCommand() { Id = id }; + await Mediator.Send(command, cancellationToken); + } +} diff --git a/AutobusApi.Api/Controllers/VehicleEnrollmentController.cs b/AutobusApi.Api/Controllers/VehicleEnrollmentController.cs new file mode 100644 index 0000000..c651e9c --- /dev/null +++ b/AutobusApi.Api/Controllers/VehicleEnrollmentController.cs @@ -0,0 +1,46 @@ +using AutobusApi.Application.Common.Models; +using AutobusApi.Application.VehicleEnrollments.Commands.CreateVehicleEnrollment; +using AutobusApi.Application.VehicleEnrollments.Commands.DeleteVehicleEnrollment; +using AutobusApi.Application.VehicleEnrollments.Commands.UpdateVehicleEnrollment; +using AutobusApi.Application.VehicleEnrollments.Queries; +using AutobusApi.Application.VehicleEnrollments.Queries.GetVehicleEnrollmentsWithPagination; +using AutobusApi.Application.VehicleEnrollments.Queries.GetVehicleEnrollment; +using Microsoft.AspNetCore.Mvc; + +namespace AutobusApi.Api.Controllers; + +[Route("vehicleenrollments")] +public class VehicleEnrollmentController : BaseController +{ + [HttpPost] + public async Task Create([FromBody] CreateVehicleEnrollmentCommand command, CancellationToken cancellationToken) + { + return await Mediator.Send(command, cancellationToken); + } + + [HttpGet] + public async Task> GetPage([FromQuery] GetVehicleEnrollmentsWithPaginationQuery query, CancellationToken cancellationToken) + { + return await Mediator.Send(query, cancellationToken); + } + + [HttpGet("{id}")] + public async Task Get(int id, /* [FromQuery] GetVehicleEnrollmentQuery query, */ CancellationToken cancellationToken) + { + var query = new GetVehicleEnrollmentQuery() { Id = id }; + return await Mediator.Send(query, cancellationToken); + } + + [HttpPut] + public async Task Update([FromBody] UpdateVehicleEnrollmentCommand command, CancellationToken cancellationToken) + { + await Mediator.Send(command, cancellationToken); + } + + [HttpDelete("{id}")] + public async Task Delete(int id, /* [FromBody] DeleteVehicleEnrollmentCommand command, */ CancellationToken cancellationToken) + { + var command = new DeleteVehicleEnrollmentCommand() { Id = id }; + await Mediator.Send(command, cancellationToken); + } +} diff --git a/AutobusApi.Api/Middlewares/GlobalExceptionHandlerMiddleware.cs b/AutobusApi.Api/Middlewares/GlobalExceptionHandlerMiddleware.cs index 9e67ff6..9fb9d22 100644 --- a/AutobusApi.Api/Middlewares/GlobalExceptionHandlerMiddleware.cs +++ b/AutobusApi.Api/Middlewares/GlobalExceptionHandlerMiddleware.cs @@ -124,6 +124,7 @@ public class GlobalExceptionHandlerMiddleware : IMiddleware Detail = "Report this error to service's support team.", }); + await Console.Error.WriteLineAsync(exception.Message); await Console.Error.WriteLineAsync(exception.StackTrace); } } diff --git a/AutobusApi.Api/appsettings.Development.json b/AutobusApi.Api/appsettings.Development.json index 7075fba..ddfb952 100644 --- a/AutobusApi.Api/appsettings.Development.json +++ b/AutobusApi.Api/appsettings.Development.json @@ -1,6 +1,6 @@ { "ConnectionStrings": { - "DefaultConnection": "Host=10.0.0.20:5432;Database=autobus;Username=postgres;Password=12345678" + "DefaultConnection": "Host=10.0.1.20:5432;Database=autobus;Username=postgres;Password=12345678" }, "Jwt": { "Issuer": "", diff --git a/AutobusApi.Application/Addresses/Commands/CreateAddress/CreateAddressCommand.cs b/AutobusApi.Application/Addresses/Commands/CreateAddress/CreateAddressCommand.cs new file mode 100644 index 0000000..9172ed1 --- /dev/null +++ b/AutobusApi.Application/Addresses/Commands/CreateAddress/CreateAddressCommand.cs @@ -0,0 +1,16 @@ +using MediatR; + +namespace AutobusApi.Application.Addresses.Commands.CreateAddress; + +public record CreateAddressCommand : IRequest +{ + public string Name { get; set; } = null!; + + public int CityId { get; set; } + + public double Latitude { get; set; } + + public double Longitude { get; set; } + + public string VehicleType { get; set; } = null!; +} diff --git a/AutobusApi.Application/Addresses/Commands/CreateAddress/CreateAddressCommandHandler.cs b/AutobusApi.Application/Addresses/Commands/CreateAddress/CreateAddressCommandHandler.cs new file mode 100644 index 0000000..c909910 --- /dev/null +++ b/AutobusApi.Application/Addresses/Commands/CreateAddress/CreateAddressCommandHandler.cs @@ -0,0 +1,36 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using AutobusApi.Domain.Enums; +using MediatR; + +namespace AutobusApi.Application.Addresses.Commands.CreateAddress; + +public class CreateAddressCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public CreateAddressCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + CreateAddressCommand request, + CancellationToken cancellationToken) + { + var address = new Address(); + + address.Name = request.Name; + address.CityId = request.CityId; + // TODO: Fix abstraction problems + // address.Location.Latitude = request.Latitude; + // address.Location.Longitude = request.Longitude; + address.VehicleType = Enum.Parse(request.VehicleType); + + _dbContext.Addresses.Add(address); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return address.Id; + } +} diff --git a/AutobusApi.Application/Addresses/Commands/CreateAddress/CreateAddressCommandValidator.cs b/AutobusApi.Application/Addresses/Commands/CreateAddress/CreateAddressCommandValidator.cs new file mode 100644 index 0000000..1fd3e15 --- /dev/null +++ b/AutobusApi.Application/Addresses/Commands/CreateAddress/CreateAddressCommandValidator.cs @@ -0,0 +1,20 @@ +using AutobusApi.Domain.Enums; +using FluentValidation; + +namespace AutobusApi.Application.Addresses.Commands.CreateAddress; + +public class CreateAddressCommandValidator : AbstractValidator +{ + public CreateAddressCommandValidator() + { + RuleFor(v => v.Name).MinimumLength(2).MaximumLength(64); + + RuleFor(v => v.CityId).GreaterThan(0); + + RuleFor(v => v.Latitude).GreaterThanOrEqualTo(-180).LessThanOrEqualTo(180); + + RuleFor(v => v.Longitude).GreaterThanOrEqualTo(-180).LessThanOrEqualTo(180); + + RuleFor(v => v.VehicleType).Must(value => Enum.TryParse(value, true, out _)); + } +} diff --git a/AutobusApi.Application/Addresses/Commands/DeleteAddress/DeleteAddressCommand.cs b/AutobusApi.Application/Addresses/Commands/DeleteAddress/DeleteAddressCommand.cs new file mode 100644 index 0000000..3a26650 --- /dev/null +++ b/AutobusApi.Application/Addresses/Commands/DeleteAddress/DeleteAddressCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Addresses.Commands.DeleteAddress; + +public record DeleteAddressCommand : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandHandler.cs b/AutobusApi.Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandHandler.cs new file mode 100644 index 0000000..aee4d25 --- /dev/null +++ b/AutobusApi.Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Addresses.Commands.DeleteAddress; + +public class DeleteAddressCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public DeleteAddressCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + DeleteAddressCommand request, + CancellationToken cancellationToken) + { + var address = await _dbContext.Addresses + .SingleOrDefaultAsync(c => c.Id == request.Id, cancellationToken); + + if (address == null) + { + throw new NotFoundException(); + } + + _dbContext.Addresses.Remove(address); + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandValidator.cs b/AutobusApi.Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandValidator.cs new file mode 100644 index 0000000..0b86a2a --- /dev/null +++ b/AutobusApi.Application/Addresses/Commands/DeleteAddress/DeleteAddressCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Addresses.Commands.DeleteAddress; + +public class DeleteAddressCommandValidator : AbstractValidator +{ + public DeleteAddressCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Addresses/Commands/UpdateAddress/UpdateAddressCommand.cs b/AutobusApi.Application/Addresses/Commands/UpdateAddress/UpdateAddressCommand.cs new file mode 100644 index 0000000..7df4774 --- /dev/null +++ b/AutobusApi.Application/Addresses/Commands/UpdateAddress/UpdateAddressCommand.cs @@ -0,0 +1,18 @@ +using MediatR; + +namespace AutobusApi.Application.Addresses.Commands.UpdateAddress; + +public record UpdateAddressCommand : IRequest +{ + public int Id { get; set; } + + public string Name { get; set; } = null!; + + public int CityId { get; set; } + + public double Latitude { get; set; } + + public double Longitude { get; set; } + + public string VehicleType { get; set; } = null!; +} diff --git a/AutobusApi.Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandHandler.cs b/AutobusApi.Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandHandler.cs new file mode 100644 index 0000000..b9d8b76 --- /dev/null +++ b/AutobusApi.Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandHandler.cs @@ -0,0 +1,38 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Enums; +using MediatR; + +namespace AutobusApi.Application.Addresses.Commands.UpdateAddress; + +public class UpdateAddressCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public UpdateAddressCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + UpdateAddressCommand request, + CancellationToken cancellationToken) + { + var address = await _dbContext.Addresses + .FindAsync(new object[] { request.Id }, cancellationToken); + + if (address == null) + { + throw new NotFoundException(); + } + + address.Name = request.Name; + address.CityId = request.CityId; + // TODO: Fix abstraction problems + // address.Location.Latitude = request.Latitude; + // address.Location.Longitude = request.Longitude; + address.VehicleType = Enum.Parse(request.VehicleType); + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandValidator.cs b/AutobusApi.Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandValidator.cs new file mode 100644 index 0000000..b71f39c --- /dev/null +++ b/AutobusApi.Application/Addresses/Commands/UpdateAddress/UpdateAddressCommandValidator.cs @@ -0,0 +1,22 @@ +using AutobusApi.Domain.Enums; +using FluentValidation; + +namespace AutobusApi.Application.Addresses.Commands.UpdateAddress; + +public class UpdateAddressCommandValidator : AbstractValidator +{ + public UpdateAddressCommandValidator() + { + RuleFor(v => v.Name).MinimumLength(2).MaximumLength(64); + + RuleFor(v => v.Id).GreaterThan(0); + + RuleFor(v => v.CityId).GreaterThan(0); + + RuleFor(v => v.Latitude).GreaterThanOrEqualTo(-180).LessThanOrEqualTo(180); + + RuleFor(v => v.Longitude).GreaterThanOrEqualTo(-180).LessThanOrEqualTo(180); + + RuleFor(v => v.VehicleType).Must(value => Enum.TryParse(value, true, out _)); + } +} diff --git a/AutobusApi.Application/Addresses/Queries/AddressDto.cs b/AutobusApi.Application/Addresses/Queries/AddressDto.cs new file mode 100644 index 0000000..2e345ca --- /dev/null +++ b/AutobusApi.Application/Addresses/Queries/AddressDto.cs @@ -0,0 +1,44 @@ +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Domain.Entities; +using AutoMapper; + +namespace AutobusApi.Application.Addresses.Queries; + +public class AddressDto : IMapFrom
+{ + public int Id { get; set; } + + public string Name { get; set; } = null!; + + public double Latitude { get; set; } + + public double Longitude { get; set; } + + public string VehicleType { get; set; } = null!; + + public int CityId { get; set; } + + public string CityName { get; set; } = null!; + + public int RegionId { get; set; } + + public string RegionName { get; set; } = null!; + + public int CountryId { get; set; } + + public string CountryName { get; set; } = null!; + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember(d => d.CityId, opt => opt.MapFrom(s => s.CityId)) + .ForMember(d => d.CityName, opt => opt.MapFrom(s => s.City.Name)) + .ForMember(d => d.RegionId, opt => opt.MapFrom(s => s.City.RegionId)) + .ForMember(d => d.RegionName, opt => opt.MapFrom(s => s.City.Region.Name)) + .ForMember(d => d.CountryId, opt => opt.MapFrom(s => s.City.Region.CountryId)) + .ForMember(d => d.CountryName, opt => opt.MapFrom(s => s.City.Region.Country.Name)) + .ForMember(d => d.Latitude, opt => opt.MapFrom(s => s.Location.Latitude)) + .ForMember(d => d.Longitude, opt => opt.MapFrom(s => s.Location.Longitude)) + .ForMember(d => d.VehicleType, opt => opt.MapFrom(s => s.VehicleType.ToString())); + } +} diff --git a/AutobusApi.Application/Addresses/Queries/GetAddress/GetAddressQuery.cs b/AutobusApi.Application/Addresses/Queries/GetAddress/GetAddressQuery.cs new file mode 100644 index 0000000..bfaf0ca --- /dev/null +++ b/AutobusApi.Application/Addresses/Queries/GetAddress/GetAddressQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Addresses.Queries.GetAddress; + +public record GetAddressQuery : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Addresses/Queries/GetAddress/GetAddressQueryHandler.cs b/AutobusApi.Application/Addresses/Queries/GetAddress/GetAddressQueryHandler.cs new file mode 100644 index 0000000..64193b9 --- /dev/null +++ b/AutobusApi.Application/Addresses/Queries/GetAddress/GetAddressQueryHandler.cs @@ -0,0 +1,36 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutoMapper; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Addresses.Queries.GetAddress; + +public class GetAddressQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetAddressQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task Handle( + GetAddressQuery request, + CancellationToken cancellationToken) + { + var address = await _dbContext.Addresses + .SingleOrDefaultAsync(c => c.Id == request.Id); + + if (address == null) + { + throw new NotFoundException(); + } + + return _mapper.Map(address); + } +} diff --git a/AutobusApi.Application/Addresses/Queries/GetAddress/GetAddressQueryValidator.cs b/AutobusApi.Application/Addresses/Queries/GetAddress/GetAddressQueryValidator.cs new file mode 100644 index 0000000..cc942da --- /dev/null +++ b/AutobusApi.Application/Addresses/Queries/GetAddress/GetAddressQueryValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Addresses.Queries.GetAddress; + +public class GetAddressQueryValidator : AbstractValidator +{ + public GetAddressQueryValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Addresses/Queries/GetAddressesWithPagination/GetAddressesWithPaginationQuery.cs b/AutobusApi.Application/Addresses/Queries/GetAddressesWithPagination/GetAddressesWithPaginationQuery.cs new file mode 100644 index 0000000..63a7a1b --- /dev/null +++ b/AutobusApi.Application/Addresses/Queries/GetAddressesWithPagination/GetAddressesWithPaginationQuery.cs @@ -0,0 +1,13 @@ +using AutobusApi.Application.Common.Models; +using MediatR; + +namespace AutobusApi.Application.Addresses.Queries.GetAddressesWithPagination; + +public record GetAddressesWithPaginationQuery : IRequest> +{ + public string Sort { get; set; } = ""; + + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; +} diff --git a/AutobusApi.Application/Addresses/Queries/GetAddressesWithPagination/GetAddressesWithPaginationQueryHandler.cs b/AutobusApi.Application/Addresses/Queries/GetAddressesWithPagination/GetAddressesWithPaginationQueryHandler.cs new file mode 100644 index 0000000..22338f3 --- /dev/null +++ b/AutobusApi.Application/Addresses/Queries/GetAddressesWithPagination/GetAddressesWithPaginationQueryHandler.cs @@ -0,0 +1,32 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Application.Common.Models; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; + +namespace AutobusApi.Application.Addresses.Queries.GetAddressesWithPagination; + +public class GetAddressesWithPaginationQueryHandler : IRequestHandler> +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetAddressesWithPaginationQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task> Handle( + GetAddressesWithPaginationQuery request, + CancellationToken cancellationToken) + { + return await _dbContext.Addresses + .ProjectTo(_mapper.ConfigurationProvider) + .ApplySort(request.Sort) + .PaginatedListAsync(request.PageNumber, request.PageSize); + } +} diff --git a/AutobusApi.Application/Addresses/Queries/GetAddressesWithPagination/GetAddressesWithPaginationQueryValidator.cs b/AutobusApi.Application/Addresses/Queries/GetAddressesWithPagination/GetAddressesWithPaginationQueryValidator.cs new file mode 100644 index 0000000..a371312 --- /dev/null +++ b/AutobusApi.Application/Addresses/Queries/GetAddressesWithPagination/GetAddressesWithPaginationQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.Addresses.Queries.GetAddressesWithPagination; + +public class GetAddressesWithPaginationQueryValidator : AbstractValidator +{ + public GetAddressesWithPaginationQueryValidator() + { + RuleFor(v => v.PageNumber).GreaterThanOrEqualTo(1); + + RuleFor(v => v.PageSize).GreaterThanOrEqualTo(1).LessThanOrEqualTo(50); + } +} diff --git a/AutobusApi.Application/AutobusApi.Application.csproj b/AutobusApi.Application/AutobusApi.Application.csproj index d544c6b..79ffc69 100644 --- a/AutobusApi.Application/AutobusApi.Application.csproj +++ b/AutobusApi.Application/AutobusApi.Application.csproj @@ -18,6 +18,7 @@ 7.0.14 + diff --git a/AutobusApi.Application/Buses/Commands/CreateBus/CreateBusCommand.cs b/AutobusApi.Application/Buses/Commands/CreateBus/CreateBusCommand.cs new file mode 100644 index 0000000..41448ef --- /dev/null +++ b/AutobusApi.Application/Buses/Commands/CreateBus/CreateBusCommand.cs @@ -0,0 +1,24 @@ +using MediatR; + +namespace AutobusApi.Application.Buses.Commands.CreateBus; + +public record CreateBusCommand : IRequest +{ + public int CompanyId { get; set; } + + public string Number { get; set; } = null!; + + public string Model { get; set; } = null!; + + public int Capacity { get; set; } + + public bool HasClimateControl { get; set; } + + public bool HasWiFi { get; set; } + + public bool HasMultimedia { get; set; } + + public bool HasWC { get; set; } + + public bool HasOutlets { get; set; } +} diff --git a/AutobusApi.Application/Buses/Commands/CreateBus/CreateBusCommandHandler.cs b/AutobusApi.Application/Buses/Commands/CreateBus/CreateBusCommandHandler.cs new file mode 100644 index 0000000..aab36d8 --- /dev/null +++ b/AutobusApi.Application/Buses/Commands/CreateBus/CreateBusCommandHandler.cs @@ -0,0 +1,38 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using MediatR; + +namespace AutobusApi.Application.Buses.Commands.CreateBus; + +public class CreateBusCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public CreateBusCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + CreateBusCommand request, + CancellationToken cancellationToken) + { + var bus = new Bus(); + + bus.CompanyId = request.CompanyId; + bus.Number = request.Number; + bus.Model = request.Model; + bus.Capacity = request.Capacity; + bus.HasClimateControl = request.HasClimateControl; + bus.HasWiFi = request.HasWiFi; + bus.HasMultimedia = request.HasMultimedia; + bus.HasWC = request.HasWC; + bus.HasOutlets = request.HasOutlets; + + _dbContext.Vehicles.Add(bus); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return bus.Id; + } +} diff --git a/AutobusApi.Application/Buses/Commands/CreateBus/CreateBusCommandValidator.cs b/AutobusApi.Application/Buses/Commands/CreateBus/CreateBusCommandValidator.cs new file mode 100644 index 0000000..f1fa161 --- /dev/null +++ b/AutobusApi.Application/Buses/Commands/CreateBus/CreateBusCommandValidator.cs @@ -0,0 +1,15 @@ +using FluentValidation; + +namespace AutobusApi.Application.Buses.Commands.CreateBus; + +public class CreateBusCommandValidator : AbstractValidator +{ + public CreateBusCommandValidator() + { + RuleFor(v => v.Number).MinimumLength(8).MaximumLength(8); + + RuleFor(v => v.Model).MinimumLength(2).MaximumLength(64); + + RuleFor(v => v.Capacity).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Buses/Commands/DeleteBus/DeleteBusCommand.cs b/AutobusApi.Application/Buses/Commands/DeleteBus/DeleteBusCommand.cs new file mode 100644 index 0000000..51ac7de --- /dev/null +++ b/AutobusApi.Application/Buses/Commands/DeleteBus/DeleteBusCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Buses.Commands.DeleteBus; + +public record DeleteBusCommand : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Buses/Commands/DeleteBus/DeleteBusCommandHandler.cs b/AutobusApi.Application/Buses/Commands/DeleteBus/DeleteBusCommandHandler.cs new file mode 100644 index 0000000..58a1180 --- /dev/null +++ b/AutobusApi.Application/Buses/Commands/DeleteBus/DeleteBusCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Buses.Commands.DeleteBus; + +public class DeleteBusCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public DeleteBusCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + DeleteBusCommand request, + CancellationToken cancellationToken) + { + var bus = await _dbContext.Buses + .SingleOrDefaultAsync(c => c.Id == request.Id, cancellationToken); + + if (bus == null) + { + throw new NotFoundException(); + } + + _dbContext.Buses.Remove(bus); + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Buses/Commands/DeleteBus/DeleteBusCommandValidator.cs b/AutobusApi.Application/Buses/Commands/DeleteBus/DeleteBusCommandValidator.cs new file mode 100644 index 0000000..6cfaad3 --- /dev/null +++ b/AutobusApi.Application/Buses/Commands/DeleteBus/DeleteBusCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Buses.Commands.DeleteBus; + +public class DeleteBusCommandValidator : AbstractValidator +{ + public DeleteBusCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Buses/Commands/UpdateBus/UpdateBusCommand.cs b/AutobusApi.Application/Buses/Commands/UpdateBus/UpdateBusCommand.cs new file mode 100644 index 0000000..18cc24a --- /dev/null +++ b/AutobusApi.Application/Buses/Commands/UpdateBus/UpdateBusCommand.cs @@ -0,0 +1,26 @@ +using MediatR; + +namespace AutobusApi.Application.Buses.Commands.UpdateBus; + +public record UpdateBusCommand : IRequest +{ + public int Id { get; set; } + + public int CompanyId { get; set; } + + public string Number { get; set; } = null!; + + public string Model { get; set; } = null!; + + public int Capacity { get; set; } + + public bool HasClimateControl { get; set; } + + public bool HasWiFi { get; set; } + + public bool HasMultimedia { get; set; } + + public bool HasWC { get; set; } + + public bool HasOutlets { get; set; } +} diff --git a/AutobusApi.Application/Buses/Commands/UpdateBus/UpdateBusCommandHandler.cs b/AutobusApi.Application/Buses/Commands/UpdateBus/UpdateBusCommandHandler.cs new file mode 100644 index 0000000..ec60b3e --- /dev/null +++ b/AutobusApi.Application/Buses/Commands/UpdateBus/UpdateBusCommandHandler.cs @@ -0,0 +1,40 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; + +namespace AutobusApi.Application.Buses.Commands.UpdateBus; + +public class UpdateBusCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public UpdateBusCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + UpdateBusCommand request, + CancellationToken cancellationToken) + { + var bus = await _dbContext.Buses + .FindAsync(new object[] { request.Id }, cancellationToken); + + if (bus == null) + { + throw new NotFoundException(); + } + + bus.CompanyId = request.CompanyId; + bus.Number = request.Number; + bus.Model = request.Model; + bus.Capacity = request.Capacity; + bus.HasClimateControl = request.HasClimateControl; + bus.HasWiFi = request.HasWiFi; + bus.HasMultimedia = request.HasMultimedia; + bus.HasWC = request.HasWC; + bus.HasOutlets = request.HasOutlets; + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Buses/Commands/UpdateBus/UpdateBusCommandValidator.cs b/AutobusApi.Application/Buses/Commands/UpdateBus/UpdateBusCommandValidator.cs new file mode 100644 index 0000000..989cfdd --- /dev/null +++ b/AutobusApi.Application/Buses/Commands/UpdateBus/UpdateBusCommandValidator.cs @@ -0,0 +1,19 @@ +using FluentValidation; + +namespace AutobusApi.Application.Buses.Commands.UpdateBus; + +public class UpdateBusCommandValidator : AbstractValidator +{ + public UpdateBusCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + + RuleFor(v => v.CompanyId).GreaterThan(0); + + RuleFor(v => v.Number).MinimumLength(8).MaximumLength(8); + + RuleFor(v => v.Model).MinimumLength(2).MaximumLength(64); + + RuleFor(v => v.Capacity).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Buses/Queries/BusDto.cs b/AutobusApi.Application/Buses/Queries/BusDto.cs new file mode 100644 index 0000000..2228b5f --- /dev/null +++ b/AutobusApi.Application/Buses/Queries/BusDto.cs @@ -0,0 +1,27 @@ +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Domain.Entities; + +namespace AutobusApi.Application.Buses.Queries; + +public class BusDto : IMapFrom +{ + public int Id { get; set; } + + public int CompanyId { get; set; } + + public string Number { get; set; } = null!; + + public string Model { get; set; } = null!; + + public int Capacity { get; set; } + + public bool HasClimateControl { get; set; } + + public bool HasWiFi { get; set; } + + public bool HasMultimedia { get; set; } + + public bool HasWC { get; set; } + + public bool HasOutlets { get; set; } +} diff --git a/AutobusApi.Application/Buses/Queries/GetBus/GetBusQuery.cs b/AutobusApi.Application/Buses/Queries/GetBus/GetBusQuery.cs new file mode 100644 index 0000000..3373865 --- /dev/null +++ b/AutobusApi.Application/Buses/Queries/GetBus/GetBusQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Buses.Queries.GetBus; + +public record GetBusQuery : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Buses/Queries/GetBus/GetBusQueryHandler.cs b/AutobusApi.Application/Buses/Queries/GetBus/GetBusQueryHandler.cs new file mode 100644 index 0000000..8fdf17a --- /dev/null +++ b/AutobusApi.Application/Buses/Queries/GetBus/GetBusQueryHandler.cs @@ -0,0 +1,38 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Buses.Queries.GetBus; + +public class GetBusQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetBusQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task Handle( + GetBusQuery request, + CancellationToken cancellationToken) + { + var bus = await _dbContext.Buses + .ProjectTo(_mapper.ConfigurationProvider) + .SingleOrDefaultAsync(c => c.Id == request.Id); + + if (bus == null) + { + throw new NotFoundException(); + } + + return bus; + } +} diff --git a/AutobusApi.Application/Buses/Queries/GetBus/GetBusQueryValidator.cs b/AutobusApi.Application/Buses/Queries/GetBus/GetBusQueryValidator.cs new file mode 100644 index 0000000..c7fc7ff --- /dev/null +++ b/AutobusApi.Application/Buses/Queries/GetBus/GetBusQueryValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Buses.Queries.GetBus; + +public class GetBusQueryValidator : AbstractValidator +{ + public GetBusQueryValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Buses/Queries/GetBusesWithPagination/GetBusesWithPaginationQuery.cs b/AutobusApi.Application/Buses/Queries/GetBusesWithPagination/GetBusesWithPaginationQuery.cs new file mode 100644 index 0000000..0ddafe1 --- /dev/null +++ b/AutobusApi.Application/Buses/Queries/GetBusesWithPagination/GetBusesWithPaginationQuery.cs @@ -0,0 +1,13 @@ +using AutobusApi.Application.Common.Models; +using MediatR; + +namespace AutobusApi.Application.Buses.Queries.GetBusesWithPagination; + +public record GetBusesWithPaginationQuery : IRequest> +{ + public string Sort { get; set; } = ""; + + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; +} diff --git a/AutobusApi.Application/Buses/Queries/GetBusesWithPagination/GetBusesWithPaginationQueryHandler.cs b/AutobusApi.Application/Buses/Queries/GetBusesWithPagination/GetBusesWithPaginationQueryHandler.cs new file mode 100644 index 0000000..377fe36 --- /dev/null +++ b/AutobusApi.Application/Buses/Queries/GetBusesWithPagination/GetBusesWithPaginationQueryHandler.cs @@ -0,0 +1,32 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Application.Common.Models; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; + +namespace AutobusApi.Application.Buses.Queries.GetBusesWithPagination; + +public class GetBusesWithPaginationQueryHandler : IRequestHandler> +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetBusesWithPaginationQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task> Handle( + GetBusesWithPaginationQuery request, + CancellationToken cancellationToken) + { + return await _dbContext.Buses + .ProjectTo(_mapper.ConfigurationProvider) + .ApplySort(request.Sort) + .PaginatedListAsync(request.PageNumber, request.PageSize); + } +} diff --git a/AutobusApi.Application/Buses/Queries/GetBusesWithPagination/GetBusesWithPaginationQueryValidator.cs b/AutobusApi.Application/Buses/Queries/GetBusesWithPagination/GetBusesWithPaginationQueryValidator.cs new file mode 100644 index 0000000..9ed2094 --- /dev/null +++ b/AutobusApi.Application/Buses/Queries/GetBusesWithPagination/GetBusesWithPaginationQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.Buses.Queries.GetBusesWithPagination; + +public class GetBusesWithPaginationQueryValidator : AbstractValidator +{ + public GetBusesWithPaginationQueryValidator() + { + RuleFor(v => v.PageNumber).GreaterThanOrEqualTo(1); + + RuleFor(v => v.PageSize).GreaterThanOrEqualTo(1).LessThanOrEqualTo(50); + } +} diff --git a/AutobusApi.Application/Cities/Commands/CreateCity/CreateCityCommand.cs b/AutobusApi.Application/Cities/Commands/CreateCity/CreateCityCommand.cs new file mode 100644 index 0000000..a7e48e9 --- /dev/null +++ b/AutobusApi.Application/Cities/Commands/CreateCity/CreateCityCommand.cs @@ -0,0 +1,10 @@ +using MediatR; + +namespace AutobusApi.Application.Cities.Commands.CreateCity; + +public record CreateCityCommand : IRequest +{ + public string Name { get; set; } = null!; + + public int RegionId { get; set; } +} diff --git a/AutobusApi.Application/Cities/Commands/CreateCity/CreateCityCommandHandler.cs b/AutobusApi.Application/Cities/Commands/CreateCity/CreateCityCommandHandler.cs new file mode 100644 index 0000000..d07b20c --- /dev/null +++ b/AutobusApi.Application/Cities/Commands/CreateCity/CreateCityCommandHandler.cs @@ -0,0 +1,31 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using MediatR; + +namespace AutobusApi.Application.Cities.Commands.CreateCity; + +public class CreateCityCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public CreateCityCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + CreateCityCommand request, + CancellationToken cancellationToken) + { + var city = new City(); + + city.Name = request.Name; + city.RegionId = request.RegionId; + + _dbContext.Cities.Add(city); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return city.Id; + } +} diff --git a/AutobusApi.Application/Cities/Commands/CreateCity/CreateCityCommandValidator.cs b/AutobusApi.Application/Cities/Commands/CreateCity/CreateCityCommandValidator.cs new file mode 100644 index 0000000..ac67c9c --- /dev/null +++ b/AutobusApi.Application/Cities/Commands/CreateCity/CreateCityCommandValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.Cities.Commands.CreateCity; + +public class CreateCityCommandValidator : AbstractValidator +{ + public CreateCityCommandValidator() + { + RuleFor(v => v.Name).MinimumLength(2).MaximumLength(64); + + RuleFor(v => v.RegionId).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Cities/Commands/DeleteCity/DeleteCityCommand.cs b/AutobusApi.Application/Cities/Commands/DeleteCity/DeleteCityCommand.cs new file mode 100644 index 0000000..818e7e7 --- /dev/null +++ b/AutobusApi.Application/Cities/Commands/DeleteCity/DeleteCityCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Cities.Commands.DeleteCity; + +public record DeleteCityCommand : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Cities/Commands/DeleteCity/DeleteCityCommandHandler.cs b/AutobusApi.Application/Cities/Commands/DeleteCity/DeleteCityCommandHandler.cs new file mode 100644 index 0000000..54658d1 --- /dev/null +++ b/AutobusApi.Application/Cities/Commands/DeleteCity/DeleteCityCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Cities.Commands.DeleteCity; + +public class DeleteCityCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public DeleteCityCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + DeleteCityCommand request, + CancellationToken cancellationToken) + { + var city = await _dbContext.Cities + .SingleOrDefaultAsync(c => c.Id == request.Id, cancellationToken); + + if (city == null) + { + throw new NotFoundException(); + } + + _dbContext.Cities.Remove(city); + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Cities/Commands/DeleteCity/DeleteCityCommandValidator.cs b/AutobusApi.Application/Cities/Commands/DeleteCity/DeleteCityCommandValidator.cs new file mode 100644 index 0000000..9f4a58e --- /dev/null +++ b/AutobusApi.Application/Cities/Commands/DeleteCity/DeleteCityCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Cities.Commands.DeleteCity; + +public class DeleteCityCommandValidator : AbstractValidator +{ + public DeleteCityCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Cities/Commands/UpdateCity/UpdateCityCommand.cs b/AutobusApi.Application/Cities/Commands/UpdateCity/UpdateCityCommand.cs new file mode 100644 index 0000000..ecb8461 --- /dev/null +++ b/AutobusApi.Application/Cities/Commands/UpdateCity/UpdateCityCommand.cs @@ -0,0 +1,12 @@ +using MediatR; + +namespace AutobusApi.Application.Cities.Commands.UpdateCity; + +public record UpdateCityCommand : IRequest +{ + public int Id { get; set; } + + public string Name { get; set; } = null!; + + public int RegionId { get; set; } +} diff --git a/AutobusApi.Application/Cities/Commands/UpdateCity/UpdateCityCommandHandler.cs b/AutobusApi.Application/Cities/Commands/UpdateCity/UpdateCityCommandHandler.cs new file mode 100644 index 0000000..23716c2 --- /dev/null +++ b/AutobusApi.Application/Cities/Commands/UpdateCity/UpdateCityCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; + +namespace AutobusApi.Application.Cities.Commands.UpdateCity; + +public class UpdateCityCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public UpdateCityCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + UpdateCityCommand request, + CancellationToken cancellationToken) + { + var city = await _dbContext.Cities + .FindAsync(new object[] { request.Id }, cancellationToken); + + if (city == null) + { + throw new NotFoundException(); + } + + city.Name = request.Name; + city.RegionId = request.RegionId; + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Cities/Commands/UpdateCity/UpdateCityCommandValidator.cs b/AutobusApi.Application/Cities/Commands/UpdateCity/UpdateCityCommandValidator.cs new file mode 100644 index 0000000..9981a10 --- /dev/null +++ b/AutobusApi.Application/Cities/Commands/UpdateCity/UpdateCityCommandValidator.cs @@ -0,0 +1,15 @@ +using FluentValidation; + +namespace AutobusApi.Application.Cities.Commands.UpdateCity; + +public class UpdateCityCommandValidator : AbstractValidator +{ + public UpdateCityCommandValidator() + { + RuleFor(v => v.Name).MinimumLength(2).MaximumLength(64); + + RuleFor(v => v.Id).GreaterThan(0); + + RuleFor(v => v.RegionId).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Cities/Queries/CityDto.cs b/AutobusApi.Application/Cities/Queries/CityDto.cs new file mode 100644 index 0000000..2be8c99 --- /dev/null +++ b/AutobusApi.Application/Cities/Queries/CityDto.cs @@ -0,0 +1,27 @@ +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Domain.Entities; +using AutoMapper; + +namespace AutobusApi.Application.Cities.Queries; + +public class CityDto : IMapFrom +{ + public int Id { get; set; } + + public string Name { get; set; } = null!; + + public int CountryId { get; set; } + + public string CountryName { get; set; } = null!; + + public int RegionId { get; set; } + + public string RegionName { get; set; } = null!; + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember(d => d.CountryId, opt => opt.MapFrom(s => s.Region.Country.Id)) + .ForMember(d => d.CountryName, opt => opt.MapFrom(s => s.Region.Country.Name)); + } +} diff --git a/AutobusApi.Application/Cities/Queries/GetCitiesWithPagination/GetCitiesWithPaginationQuery.cs b/AutobusApi.Application/Cities/Queries/GetCitiesWithPagination/GetCitiesWithPaginationQuery.cs new file mode 100644 index 0000000..afa283c --- /dev/null +++ b/AutobusApi.Application/Cities/Queries/GetCitiesWithPagination/GetCitiesWithPaginationQuery.cs @@ -0,0 +1,13 @@ +using AutobusApi.Application.Common.Models; +using MediatR; + +namespace AutobusApi.Application.Cities.Queries.GetCitiesWithPagination; + +public record GetCitiesWithPaginationQuery : IRequest> +{ + public string Sort { get; set; } = ""; + + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; +} diff --git a/AutobusApi.Application/Cities/Queries/GetCitiesWithPagination/GetCitiesWithPaginationQueryHandler.cs b/AutobusApi.Application/Cities/Queries/GetCitiesWithPagination/GetCitiesWithPaginationQueryHandler.cs new file mode 100644 index 0000000..47a0df0 --- /dev/null +++ b/AutobusApi.Application/Cities/Queries/GetCitiesWithPagination/GetCitiesWithPaginationQueryHandler.cs @@ -0,0 +1,32 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Application.Common.Models; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; + +namespace AutobusApi.Application.Cities.Queries.GetCitiesWithPagination; + +public class GetCitiesWithPaginationQueryHandler : IRequestHandler> +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetCitiesWithPaginationQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task> Handle( + GetCitiesWithPaginationQuery request, + CancellationToken cancellationToken) + { + return await _dbContext.Cities + .ProjectTo(_mapper.ConfigurationProvider) + .ApplySort(request.Sort) + .PaginatedListAsync(request.PageNumber, request.PageSize); + } +} diff --git a/AutobusApi.Application/Cities/Queries/GetCitiesWithPagination/GetCitiesWithPaginationQueryValidator.cs b/AutobusApi.Application/Cities/Queries/GetCitiesWithPagination/GetCitiesWithPaginationQueryValidator.cs new file mode 100644 index 0000000..8cb79e1 --- /dev/null +++ b/AutobusApi.Application/Cities/Queries/GetCitiesWithPagination/GetCitiesWithPaginationQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.Cities.Queries.GetCitiesWithPagination; + +public class GetCitiesWithPaginationQueryValidator : AbstractValidator +{ + public GetCitiesWithPaginationQueryValidator() + { + RuleFor(v => v.PageNumber).GreaterThanOrEqualTo(1); + + RuleFor(v => v.PageSize).GreaterThanOrEqualTo(1).LessThanOrEqualTo(50); + } +} diff --git a/AutobusApi.Application/Cities/Queries/GetCity/GetCityQuery.cs b/AutobusApi.Application/Cities/Queries/GetCity/GetCityQuery.cs new file mode 100644 index 0000000..072e7e0 --- /dev/null +++ b/AutobusApi.Application/Cities/Queries/GetCity/GetCityQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Cities.Queries.GetCity; + +public record GetCityQuery : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Cities/Queries/GetCity/GetCityQueryHandler.cs b/AutobusApi.Application/Cities/Queries/GetCity/GetCityQueryHandler.cs new file mode 100644 index 0000000..492b626 --- /dev/null +++ b/AutobusApi.Application/Cities/Queries/GetCity/GetCityQueryHandler.cs @@ -0,0 +1,36 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutoMapper; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Cities.Queries.GetCity; + +public class GetCityQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetCityQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task Handle( + GetCityQuery request, + CancellationToken cancellationToken) + { + var city = await _dbContext.Cities + .SingleOrDefaultAsync(c => c.Id == request.Id); + + if (city == null) + { + throw new NotFoundException(); + } + + return _mapper.Map(city); + } +} diff --git a/AutobusApi.Application/Cities/Queries/GetCity/GetCityQueryValidator.cs b/AutobusApi.Application/Cities/Queries/GetCity/GetCityQueryValidator.cs new file mode 100644 index 0000000..1442d69 --- /dev/null +++ b/AutobusApi.Application/Cities/Queries/GetCity/GetCityQueryValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Cities.Queries.GetCity; + +public class GetCityQueryValidator : AbstractValidator +{ + public GetCityQueryValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Common/Exceptions/NotFoundException.cs b/AutobusApi.Application/Common/Exceptions/NotFoundException.cs new file mode 100644 index 0000000..56731b0 --- /dev/null +++ b/AutobusApi.Application/Common/Exceptions/NotFoundException.cs @@ -0,0 +1,3 @@ +namespace AutobusApi.Application.Common.Exceptions; + +public class NotFoundException : Exception { } diff --git a/AutobusApi.Application/Common/Interfaces/IIdentityService.cs b/AutobusApi.Application/Common/Interfaces/IIdentityService.cs index 675f88c..bb08611 100644 --- a/AutobusApi.Application/Common/Interfaces/IIdentityService.cs +++ b/AutobusApi.Application/Common/Interfaces/IIdentityService.cs @@ -4,7 +4,7 @@ namespace AutobusApi.Application.Common.Interfaces; public interface IIdentityService { - Task RegisterAsync(string email, string password, CancellationToken cancellationToken); + Task RegisterAsync(string email, string password, CancellationToken cancellationToken); Task LoginAsync(string email, string password, CancellationToken cancellationToken); diff --git a/AutobusApi.Application/Common/Mappings/MappingExtensions.cs b/AutobusApi.Application/Common/Mappings/MappingExtensions.cs new file mode 100644 index 0000000..56797fc --- /dev/null +++ b/AutobusApi.Application/Common/Mappings/MappingExtensions.cs @@ -0,0 +1,156 @@ +using System.Reflection; +using System.Linq.Dynamic.Core; +using System.Text; +using AutobusApi.Application.Common.Models; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using Microsoft.EntityFrameworkCore; +using System.Dynamic; + +namespace AutobusApi.Application.Common.Mappings; + +public static class MappingExtensions +{ + public static Task> PaginatedListAsync( + this IQueryable queryable, + int pageNumber, + int pageSize) + where T : class + { + return PaginatedList.CreateAsync(queryable.AsNoTracking(), pageNumber, pageSize); + } + + public static Task> ProjectToListAsync( + this IQueryable queryable, + IConfigurationProvider configuration) + where T : class + { + return queryable.ProjectTo(configuration).AsNoTracking().ToListAsync(); + } + + public static IQueryable ApplySort( + this IQueryable entities, + string? orderByQueryString) + { + if (!entities.Any() || String.IsNullOrWhiteSpace(orderByQueryString)) + { + return entities; + } + + var orderParams = orderByQueryString.Trim().Split(","); + var propertyInfos = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); + var orderQueryBuilder = new StringBuilder(); + + foreach (var param in orderParams) + { + if (string.IsNullOrWhiteSpace(param)) + { + continue; + } + + var propertyFromQueryName = param[0] == '-' || param[0] == '+' ? param.Substring(1) : param; + var objectProperty = propertyInfos.FirstOrDefault(pi => + pi.Name.Equals(propertyFromQueryName, StringComparison.InvariantCultureIgnoreCase)); + + if (objectProperty == null) + { + continue; + } + + var sortingOrder = param[0] == '-' ? "descending" : "ascending"; + + orderQueryBuilder.Append($"{objectProperty.Name} {sortingOrder}, "); + } + + var orderQuery = orderQueryBuilder.ToString().TrimEnd(',', ' '); + + return entities.OrderBy(orderQuery); + } + + + + + + + public static IQueryable ShapeData( + this IQueryable entities, + string? fieldsString) + { + var allProperties = GetAllProperties(); + var requiredProperties = GetRequiredProperties(fieldsString, allProperties); + return FetchData(entities, requiredProperties); + } + + public static ExpandoObject ShapeData( + this T entity, + string? fieldsString) + { + var allProperties = GetAllProperties(); + var requiredProperties = GetRequiredProperties(fieldsString, allProperties); + return FetchDataForEntity(entity, requiredProperties); + } + + private static IEnumerable GetAllProperties() + { + return typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance); + } + + private static IEnumerable GetRequiredProperties( + string? fieldsString, + IEnumerable properties) + { + var requiredProperties = new List(); + + if (!string.IsNullOrWhiteSpace(fieldsString)) + { + var fields = fieldsString.Split(',', StringSplitOptions.RemoveEmptyEntries); + + foreach (var field in fields) + { + var property = properties.FirstOrDefault(pi => pi.Name.Equals(field.Trim(), StringComparison.InvariantCultureIgnoreCase)); + + if (property == null) + continue; + + requiredProperties.Add(property); + } + } + else + { + requiredProperties = properties.ToList(); + } + + return requiredProperties; + } + + private static IQueryable FetchData( + IQueryable entities, + IEnumerable requiredProperties) + { + var shapedData = new List(); + + foreach (var entity in entities) + { + var shapedObject = FetchDataForEntity(entity, requiredProperties); + shapedData.Add(shapedObject); + } + + return shapedData.AsQueryable(); + } + + private static ExpandoObject FetchDataForEntity( + T entity, + IEnumerable requiredProperties) + { + var shapedObject = new ExpandoObject(); + + foreach (var property in requiredProperties) + { + var objectPropertyValue = property.GetValue(entity); + shapedObject.TryAdd(property.Name, objectPropertyValue); + } + + return shapedObject; + } + +} diff --git a/AutobusApi.Application/Common/Models/PaginatedList.cs b/AutobusApi.Application/Common/Models/PaginatedList.cs new file mode 100644 index 0000000..435ee49 --- /dev/null +++ b/AutobusApi.Application/Common/Models/PaginatedList.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Common.Models; + +public class PaginatedList +{ + public IReadOnlyCollection Items { get; } + public int PageNumber { get; } + public int TotalPages { get; } + public int TotalCount { get; } + + public PaginatedList(IReadOnlyCollection items, int count, int pageNumber, int pageSize) + { + PageNumber = pageNumber; + TotalPages = (int)Math.Ceiling(count / (double)pageSize); + TotalCount = count; + Items = items; + } + + public bool HasPreviousPage => PageNumber > 1; + + public bool HasNextPage => PageNumber < TotalPages; + + public static async Task> CreateAsync(IQueryable source, int pageNumber, int pageSize) + { + var count = await source.CountAsync(); + var items = await source.Skip((pageNumber - 1) * pageSize).Take(pageSize).ToListAsync(); + + return new PaginatedList(items, count, pageNumber, pageSize); + } +} diff --git a/AutobusApi.Application/Companies/Commands/CreateCompany/CreateCompanyCommand.cs b/AutobusApi.Application/Companies/Commands/CreateCompany/CreateCompanyCommand.cs new file mode 100644 index 0000000..be44f03 --- /dev/null +++ b/AutobusApi.Application/Companies/Commands/CreateCompany/CreateCompanyCommand.cs @@ -0,0 +1,32 @@ +using MediatR; + +namespace AutobusApi.Application.Companies.Commands.CreateCompany; + +public record CreateCompanyCommand : IRequest +{ + public string Name { get; set; } = null!; + + public string LegalAddress { get; set; } = null!; + + public string ContactEmail { get; set; } = null!; + + public string ContactPhoneNumber { get; set; } = null!; + + public string OwnerEmail { get; set; } = null!; + + public string OwnerPassword { get; set; } = null!; + + public string OwnerFirstName { get; set; } = null!; + + public string OwnerLastName { get; set; } = null!; + + public string OwnerPatronymic { get; set; } = null!; + + public string OwnerSex { get; set; } = null!; + + public DateOnly OwnerBirthDate { get; set; } + + public string OwnerDocumentType { get; set; } = null!; + + public string OwnerDocumentInformation { get; set; } = null!; +} diff --git a/AutobusApi.Application/Companies/Commands/CreateCompany/CreateCompanyCommandHandler.cs b/AutobusApi.Application/Companies/Commands/CreateCompany/CreateCompanyCommandHandler.cs new file mode 100644 index 0000000..ddf3264 --- /dev/null +++ b/AutobusApi.Application/Companies/Commands/CreateCompany/CreateCompanyCommandHandler.cs @@ -0,0 +1,61 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using AutobusApi.Domain.Enums; +using MediatR; + +namespace AutobusApi.Application.Companies.Commands.CreateCompany; + +public class CreateCompanyCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IIdentityService _identityService; + + public CreateCompanyCommandHandler( + IApplicationDbContext dbContext, + IIdentityService identityService) + { + _dbContext = dbContext; + _identityService = identityService; + } + + public async Task Handle( + CreateCompanyCommand request, + CancellationToken cancellationToken) + { + var company = new Company(); + + company.Name = request.Name; + company.LegalAddress = request.LegalAddress; + company.ContactPhoneNumber = request.ContactPhoneNumber; + company.ContactEmail = request.ContactEmail; + + var userId = await _identityService.RegisterAsync(request.OwnerEmail, request.OwnerPassword, cancellationToken); + + company.Employees = new List() + { + new Employee() + { + IdentityId = userId, + FirstName = request.OwnerFirstName, + LastName = request.OwnerLastName, + Patronymic = request.OwnerPatronymic, + Sex = Enum.Parse(request.OwnerSex), + BirthDate = request.OwnerBirthDate, + Documents = new List() + { + new EmployeeDocument() + { + Type = Enum.Parse(request.OwnerDocumentType), + Information = request.OwnerDocumentInformation + } + } + } + }; + + _dbContext.Companies.Add(company); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return company.Id; + } +} diff --git a/AutobusApi.Application/Companies/Commands/CreateCompany/CreateCompanyCommandValidator.cs b/AutobusApi.Application/Companies/Commands/CreateCompany/CreateCompanyCommandValidator.cs new file mode 100644 index 0000000..2fbea28 --- /dev/null +++ b/AutobusApi.Application/Companies/Commands/CreateCompany/CreateCompanyCommandValidator.cs @@ -0,0 +1,47 @@ +using AutobusApi.Domain.Enums; +using FluentValidation; + +namespace AutobusApi.Application.Companies.Commands.CreateCompany; + +public class CreateCompanyCommandValidator : AbstractValidator +{ + public CreateCompanyCommandValidator() + { + RuleFor(v => v.Name).MinimumLength(2).MaximumLength(64); + + RuleFor(v => v.LegalAddress).MinimumLength(2).MaximumLength(256); + + RuleFor(v => v.ContactPhoneNumber) + .NotEmpty().WithMessage("Phone number is required.") + .Matches(@"^\s*(?:\+?(\d{1,3}))?([-. (]*(\d{3})[-. )]*)?((\d{3})[-. ]*(\d{2,4})(?:[-.x ]*(\d+))?)\s*$").WithMessage("Phone number is invalid."); + + RuleFor(v => v.ContactEmail) + .NotEmpty().WithMessage("Email address is required.") + .Matches(@"\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b").WithMessage("Email address is invalid."); + + RuleFor(v => v.OwnerEmail) + .NotEmpty().WithMessage("Email address is required.") + .Matches(@"\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b").WithMessage("Email address is invalid."); + + RuleFor(v => v.OwnerPassword) + .NotEmpty().WithMessage("Password is required.") + .MinimumLength(8).WithMessage("Password must be at least 8 characters long.") + .MaximumLength(64).WithMessage("Password must be at most 64 characters long.") + .Matches(@"(?=.*[A-Z]).*").WithMessage("Password must contain at least one uppercase letter.") + .Matches(@"(?=.*[a-z]).*").WithMessage("Password must contain at least one lowercase letter.") + .Matches(@"(?=.*[\d]).*").WithMessage("Password must contain at least one digit.") + .Matches(@"(?=.*[!@#$%^&*()]).*").WithMessage("Password must contain at least one of the following special charactters: !@#$%^&*()."); + + RuleFor(v => v.OwnerFirstName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.OwnerLastName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.OwnerPatronymic).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.OwnerSex).Must(value => Enum.TryParse(value, true, out _)); + + RuleFor(v => v.OwnerDocumentType).Must(value => Enum.TryParse(value, true, out _)); + + RuleFor(v => v.OwnerDocumentInformation).MinimumLength(2).MaximumLength(256); + } +} diff --git a/AutobusApi.Application/Companies/Commands/DeleteCompany/DeleteCompanyCommand.cs b/AutobusApi.Application/Companies/Commands/DeleteCompany/DeleteCompanyCommand.cs new file mode 100644 index 0000000..2a99c49 --- /dev/null +++ b/AutobusApi.Application/Companies/Commands/DeleteCompany/DeleteCompanyCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Companies.Commands.DeleteCompany; + +public record DeleteCompanyCommand : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Companies/Commands/DeleteCompany/DeleteCompanyCommandHandler.cs b/AutobusApi.Application/Companies/Commands/DeleteCompany/DeleteCompanyCommandHandler.cs new file mode 100644 index 0000000..a844f3a --- /dev/null +++ b/AutobusApi.Application/Companies/Commands/DeleteCompany/DeleteCompanyCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Companies.Commands.DeleteCompany; + +public class DeleteCompanyCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public DeleteCompanyCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + DeleteCompanyCommand request, + CancellationToken cancellationToken) + { + var company = await _dbContext.Companies + .SingleOrDefaultAsync(c => c.Id == request.Id, cancellationToken); + + if (company == null) + { + throw new NotFoundException(); + } + + _dbContext.Companies.Remove(company); + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Companies/Commands/DeleteCompany/DeleteCompanyCommandValidator.cs b/AutobusApi.Application/Companies/Commands/DeleteCompany/DeleteCompanyCommandValidator.cs new file mode 100644 index 0000000..3b7884d --- /dev/null +++ b/AutobusApi.Application/Companies/Commands/DeleteCompany/DeleteCompanyCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Companies.Commands.DeleteCompany; + +public class DeleteCompanyCommandValidator : AbstractValidator +{ + public DeleteCompanyCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Companies/Commands/UpdateCompany/UpdateCompanyCommand.cs b/AutobusApi.Application/Companies/Commands/UpdateCompany/UpdateCompanyCommand.cs new file mode 100644 index 0000000..5bbb6e7 --- /dev/null +++ b/AutobusApi.Application/Companies/Commands/UpdateCompany/UpdateCompanyCommand.cs @@ -0,0 +1,16 @@ +using MediatR; + +namespace AutobusApi.Application.Companies.Commands.UpdateCompany; + +public record UpdateCompanyCommand : IRequest +{ + public int Id { get; set; } + + public string Name { get; set; } = null!; + + public string LegalAddress { get; set; } = null!; + + public string ContactEmail { get; set; } = null!; + + public string ContactPhoneNumber { get; set; } = null!; +} diff --git a/AutobusApi.Application/Companies/Commands/UpdateCompany/UpdateCompanyCommandHandler.cs b/AutobusApi.Application/Companies/Commands/UpdateCompany/UpdateCompanyCommandHandler.cs new file mode 100644 index 0000000..4293baa --- /dev/null +++ b/AutobusApi.Application/Companies/Commands/UpdateCompany/UpdateCompanyCommandHandler.cs @@ -0,0 +1,35 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; + +namespace AutobusApi.Application.Companies.Commands.UpdateCompany; + +public class UpdateCompanyCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public UpdateCompanyCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + UpdateCompanyCommand request, + CancellationToken cancellationToken) + { + var company = await _dbContext.Companies + .FindAsync(new object[] { request.Id }, cancellationToken); + + if (company == null) + { + throw new NotFoundException(); + } + + company.Name = request.Name; + company.LegalAddress = request.LegalAddress; + company.ContactEmail = request.ContactEmail; + company.ContactPhoneNumber = request.ContactPhoneNumber; + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Companies/Commands/UpdateCompany/UpdateCompanyCommandValidator.cs b/AutobusApi.Application/Companies/Commands/UpdateCompany/UpdateCompanyCommandValidator.cs new file mode 100644 index 0000000..15665c8 --- /dev/null +++ b/AutobusApi.Application/Companies/Commands/UpdateCompany/UpdateCompanyCommandValidator.cs @@ -0,0 +1,23 @@ +using FluentValidation; + +namespace AutobusApi.Application.Companies.Commands.UpdateCompany; + +public class UpdateCompanyCommandValidator : AbstractValidator +{ + public UpdateCompanyCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + + RuleFor(v => v.Name).MinimumLength(2).MaximumLength(64); + + RuleFor(v => v.LegalAddress).MinimumLength(2).MaximumLength(256); + + RuleFor(v => v.ContactPhoneNumber) + .NotEmpty().WithMessage("Phone number is required.") + .Matches(@"^\s*(?:\+?(\d{1,3}))?([-. (]*(\d{3})[-. )]*)?((\d{3})[-. ]*(\d{2,4})(?:[-.x ]*(\d+))?)\s*$").WithMessage("Phone number is invalid."); + + RuleFor(v => v.ContactEmail) + .NotEmpty().WithMessage("Email address is required.") + .Matches(@"\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b").WithMessage("Email address is invalid."); + } +} diff --git a/AutobusApi.Application/Companies/Queries/CompanyDto.cs b/AutobusApi.Application/Companies/Queries/CompanyDto.cs new file mode 100644 index 0000000..b972db9 --- /dev/null +++ b/AutobusApi.Application/Companies/Queries/CompanyDto.cs @@ -0,0 +1,17 @@ +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Domain.Entities; + +namespace AutobusApi.Application.Companies.Queries; + +public class CompanyDto : IMapFrom +{ + public int Id { get; set; } + + public string Name { get; set; } = null!; + + public string LegalAddress { get; set; } = null!; + + public string ContactEmail { get; set; } = null!; + + public string ContactPhoneNumber { get; set; } = null!; +} diff --git a/AutobusApi.Application/Companies/Queries/GetCompaniesWithPagination/GetCompaniesWithPaginationQuery.cs b/AutobusApi.Application/Companies/Queries/GetCompaniesWithPagination/GetCompaniesWithPaginationQuery.cs new file mode 100644 index 0000000..c370af3 --- /dev/null +++ b/AutobusApi.Application/Companies/Queries/GetCompaniesWithPagination/GetCompaniesWithPaginationQuery.cs @@ -0,0 +1,13 @@ +using AutobusApi.Application.Common.Models; +using MediatR; + +namespace AutobusApi.Application.Companies.Queries.GetCompaniesWithPagination; + +public record GetCompaniesWithPaginationQuery : IRequest> +{ + public string Sort { get; set; } = ""; + + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; +} diff --git a/AutobusApi.Application/Companies/Queries/GetCompaniesWithPagination/GetCompaniesWithPaginationQueryHandler.cs b/AutobusApi.Application/Companies/Queries/GetCompaniesWithPagination/GetCompaniesWithPaginationQueryHandler.cs new file mode 100644 index 0000000..6464163 --- /dev/null +++ b/AutobusApi.Application/Companies/Queries/GetCompaniesWithPagination/GetCompaniesWithPaginationQueryHandler.cs @@ -0,0 +1,32 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Application.Common.Models; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; + +namespace AutobusApi.Application.Companies.Queries.GetCompaniesWithPagination; + +public class GetCompaniesWithPaginationQueryHandler : IRequestHandler> +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetCompaniesWithPaginationQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task> Handle( + GetCompaniesWithPaginationQuery request, + CancellationToken cancellationToken) + { + return await _dbContext.Companies + .ProjectTo(_mapper.ConfigurationProvider) + .ApplySort(request.Sort) + .PaginatedListAsync(request.PageNumber, request.PageSize); + } +} diff --git a/AutobusApi.Application/Companies/Queries/GetCompaniesWithPagination/GetCompaniesWithPaginationQueryValidator.cs b/AutobusApi.Application/Companies/Queries/GetCompaniesWithPagination/GetCompaniesWithPaginationQueryValidator.cs new file mode 100644 index 0000000..a2be5ea --- /dev/null +++ b/AutobusApi.Application/Companies/Queries/GetCompaniesWithPagination/GetCompaniesWithPaginationQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.Companies.Queries.GetCompaniesWithPagination; + +public class GetCompaniesWithPaginationQueryValidator : AbstractValidator +{ + public GetCompaniesWithPaginationQueryValidator() + { + RuleFor(v => v.PageNumber).GreaterThanOrEqualTo(1); + + RuleFor(v => v.PageSize).GreaterThanOrEqualTo(1).LessThanOrEqualTo(50); + } +} diff --git a/AutobusApi.Application/Companies/Queries/GetCompany/GetCompanyQuery.cs b/AutobusApi.Application/Companies/Queries/GetCompany/GetCompanyQuery.cs new file mode 100644 index 0000000..199fb12 --- /dev/null +++ b/AutobusApi.Application/Companies/Queries/GetCompany/GetCompanyQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Companies.Queries.GetCompany; + +public record GetCompanyQuery : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Companies/Queries/GetCompany/GetCompanyQueryHandler.cs b/AutobusApi.Application/Companies/Queries/GetCompany/GetCompanyQueryHandler.cs new file mode 100644 index 0000000..5a09ad7 --- /dev/null +++ b/AutobusApi.Application/Companies/Queries/GetCompany/GetCompanyQueryHandler.cs @@ -0,0 +1,38 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Companies.Queries.GetCompany; + +public class GetCompanyQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetCompanyQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task Handle( + GetCompanyQuery request, + CancellationToken cancellationToken) + { + var company = await _dbContext.Companies + .ProjectTo(_mapper.ConfigurationProvider) + .SingleOrDefaultAsync(c => c.Id == request.Id); + + if (company == null) + { + throw new NotFoundException(); + } + + return company; + } +} diff --git a/AutobusApi.Application/Companies/Queries/GetCompany/GetCompanyQueryValidator.cs b/AutobusApi.Application/Companies/Queries/GetCompany/GetCompanyQueryValidator.cs new file mode 100644 index 0000000..7298687 --- /dev/null +++ b/AutobusApi.Application/Companies/Queries/GetCompany/GetCompanyQueryValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Companies.Queries.GetCompany; + +public class GetCompanyQueryValidator : AbstractValidator +{ + public GetCompanyQueryValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Countries/Commands/CreateCountry/CreateCountryCommand.cs b/AutobusApi.Application/Countries/Commands/CreateCountry/CreateCountryCommand.cs new file mode 100644 index 0000000..c251d7b --- /dev/null +++ b/AutobusApi.Application/Countries/Commands/CreateCountry/CreateCountryCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Countries.Commands.CreateCountry; + +public record CreateCountryCommand : IRequest +{ + public string Name { get; set; } = null!; +} diff --git a/AutobusApi.Application/Countries/Commands/CreateCountry/CreateCountryCommandHandler.cs b/AutobusApi.Application/Countries/Commands/CreateCountry/CreateCountryCommandHandler.cs new file mode 100644 index 0000000..ec700be --- /dev/null +++ b/AutobusApi.Application/Countries/Commands/CreateCountry/CreateCountryCommandHandler.cs @@ -0,0 +1,30 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using MediatR; + +namespace AutobusApi.Application.Countries.Commands.CreateCountry; + +public class CreateCountryCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public CreateCountryCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + CreateCountryCommand request, + CancellationToken cancellationToken) + { + var country = new Country(); + + country.Name = request.Name; + + _dbContext.Countries.Add(country); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return country.Id; + } +} diff --git a/AutobusApi.Application/Countries/Commands/CreateCountry/CreateCountryCommandValidator.cs b/AutobusApi.Application/Countries/Commands/CreateCountry/CreateCountryCommandValidator.cs new file mode 100644 index 0000000..d2eaba1 --- /dev/null +++ b/AutobusApi.Application/Countries/Commands/CreateCountry/CreateCountryCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Countries.Commands.CreateCountry; + +public class CreateCountryCommandValidator : AbstractValidator +{ + public CreateCountryCommandValidator() + { + RuleFor(v => v.Name).MinimumLength(2).MaximumLength(64); + } +} diff --git a/AutobusApi.Application/Countries/Commands/DeleteCountry/DeleteCountryCommand.cs b/AutobusApi.Application/Countries/Commands/DeleteCountry/DeleteCountryCommand.cs new file mode 100644 index 0000000..4ac5030 --- /dev/null +++ b/AutobusApi.Application/Countries/Commands/DeleteCountry/DeleteCountryCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Countries.Commands.DeleteCountry; + +public record DeleteCountryCommand : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Countries/Commands/DeleteCountry/DeleteCountryCommandHandler.cs b/AutobusApi.Application/Countries/Commands/DeleteCountry/DeleteCountryCommandHandler.cs new file mode 100644 index 0000000..dacb0f4 --- /dev/null +++ b/AutobusApi.Application/Countries/Commands/DeleteCountry/DeleteCountryCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Countries.Commands.DeleteCountry; + +public class DeleteCountryCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public DeleteCountryCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + DeleteCountryCommand request, + CancellationToken cancellationToken) + { + var country = await _dbContext.Countries + .SingleOrDefaultAsync(c => c.Id == request.Id, cancellationToken); + + if (country == null) + { + throw new NotFoundException(); + } + + _dbContext.Countries.Remove(country); + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Countries/Commands/DeleteCountry/DeleteCountryCommandValidator.cs b/AutobusApi.Application/Countries/Commands/DeleteCountry/DeleteCountryCommandValidator.cs new file mode 100644 index 0000000..d67fe46 --- /dev/null +++ b/AutobusApi.Application/Countries/Commands/DeleteCountry/DeleteCountryCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Countries.Commands.DeleteCountry; + +public class DeleteCountryCommandValidator : AbstractValidator +{ + public DeleteCountryCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Countries/Commands/UpdateCountry/UpdateCountryCommand.cs b/AutobusApi.Application/Countries/Commands/UpdateCountry/UpdateCountryCommand.cs new file mode 100644 index 0000000..5e5233c --- /dev/null +++ b/AutobusApi.Application/Countries/Commands/UpdateCountry/UpdateCountryCommand.cs @@ -0,0 +1,10 @@ +using MediatR; + +namespace AutobusApi.Application.Countries.Commands.UpdateCountry; + +public record UpdateCountryCommand : IRequest +{ + public int Id { get; set; } + + public string Name { get; set; } = null!; +} diff --git a/AutobusApi.Application/Countries/Commands/UpdateCountry/UpdateCountryCommandHandler.cs b/AutobusApi.Application/Countries/Commands/UpdateCountry/UpdateCountryCommandHandler.cs new file mode 100644 index 0000000..35bcd4f --- /dev/null +++ b/AutobusApi.Application/Countries/Commands/UpdateCountry/UpdateCountryCommandHandler.cs @@ -0,0 +1,32 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; + +namespace AutobusApi.Application.Countries.Commands.UpdateCountry; + +public class UpdateCountryCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public UpdateCountryCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + UpdateCountryCommand request, + CancellationToken cancellationToken) + { + var country = await _dbContext.Countries + .FindAsync(new object[] { request.Id }, cancellationToken); + + if (country == null) + { + throw new NotFoundException(); + } + + country.Name = request.Name; + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Countries/Commands/UpdateCountry/UpdateCountryCommandValidator.cs b/AutobusApi.Application/Countries/Commands/UpdateCountry/UpdateCountryCommandValidator.cs new file mode 100644 index 0000000..04ad078 --- /dev/null +++ b/AutobusApi.Application/Countries/Commands/UpdateCountry/UpdateCountryCommandValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.Countries.Commands.UpdateCountry; + +public class UpdateCountryCommandValidator : AbstractValidator +{ + public UpdateCountryCommandValidator() + { + RuleFor(v => v.Name).MinimumLength(2).MaximumLength(64); + + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Countries/Queries/CountryDto.cs b/AutobusApi.Application/Countries/Queries/CountryDto.cs new file mode 100644 index 0000000..2027a47 --- /dev/null +++ b/AutobusApi.Application/Countries/Queries/CountryDto.cs @@ -0,0 +1,11 @@ +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Domain.Entities; + +namespace AutobusApi.Application.Countries.Queries; + +public class CountryDto : IMapFrom +{ + public int Id { get; set; } + + public string Name { get; set; } = null!; +} diff --git a/AutobusApi.Application/Countries/Queries/GetCountriesWithPagination/GetCountriesWithPaginationQuery.cs b/AutobusApi.Application/Countries/Queries/GetCountriesWithPagination/GetCountriesWithPaginationQuery.cs new file mode 100644 index 0000000..d7a8923 --- /dev/null +++ b/AutobusApi.Application/Countries/Queries/GetCountriesWithPagination/GetCountriesWithPaginationQuery.cs @@ -0,0 +1,13 @@ +using AutobusApi.Application.Common.Models; +using MediatR; + +namespace AutobusApi.Application.Countries.Queries.GetCountriesWithPagination; + +public record GetCountriesWithPaginationQuery : IRequest> +{ + public string Sort { get; set; } = ""; + + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; +} diff --git a/AutobusApi.Application/Countries/Queries/GetCountriesWithPagination/GetCountriesWithPaginationQueryHandler.cs b/AutobusApi.Application/Countries/Queries/GetCountriesWithPagination/GetCountriesWithPaginationQueryHandler.cs new file mode 100644 index 0000000..659ff51 --- /dev/null +++ b/AutobusApi.Application/Countries/Queries/GetCountriesWithPagination/GetCountriesWithPaginationQueryHandler.cs @@ -0,0 +1,32 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Application.Common.Models; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; + +namespace AutobusApi.Application.Countries.Queries.GetCountriesWithPagination; + +public class GetCountriesWithPaginationQueryHandler : IRequestHandler> +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetCountriesWithPaginationQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task> Handle( + GetCountriesWithPaginationQuery request, + CancellationToken cancellationToken) + { + return await _dbContext.Countries + .ProjectTo(_mapper.ConfigurationProvider) + .ApplySort(request.Sort) + .PaginatedListAsync(request.PageNumber, request.PageSize); + } +} diff --git a/AutobusApi.Application/Countries/Queries/GetCountriesWithPagination/GetCountriesWithPaginationQueryValidator.cs b/AutobusApi.Application/Countries/Queries/GetCountriesWithPagination/GetCountriesWithPaginationQueryValidator.cs new file mode 100644 index 0000000..ed96df8 --- /dev/null +++ b/AutobusApi.Application/Countries/Queries/GetCountriesWithPagination/GetCountriesWithPaginationQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.Countries.Queries.GetCountriesWithPagination; + +public class GetCountriesWithPaginationQueryValidator : AbstractValidator +{ + public GetCountriesWithPaginationQueryValidator() + { + RuleFor(v => v.PageNumber).GreaterThanOrEqualTo(1); + + RuleFor(v => v.PageSize).GreaterThanOrEqualTo(1).LessThanOrEqualTo(50); + } +} diff --git a/AutobusApi.Application/Countries/Queries/GetCountry/GetCountryQuery.cs b/AutobusApi.Application/Countries/Queries/GetCountry/GetCountryQuery.cs new file mode 100644 index 0000000..fe1aae8 --- /dev/null +++ b/AutobusApi.Application/Countries/Queries/GetCountry/GetCountryQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Countries.Queries.GetCountry; + +public record GetCountryQuery : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Countries/Queries/GetCountry/GetCountryQueryHandler.cs b/AutobusApi.Application/Countries/Queries/GetCountry/GetCountryQueryHandler.cs new file mode 100644 index 0000000..b69cb76 --- /dev/null +++ b/AutobusApi.Application/Countries/Queries/GetCountry/GetCountryQueryHandler.cs @@ -0,0 +1,38 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Countries.Queries.GetCountry; + +public class GetCountryQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetCountryQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task Handle( + GetCountryQuery request, + CancellationToken cancellationToken) + { + var country = await _dbContext.Countries + .ProjectTo(_mapper.ConfigurationProvider) + .SingleOrDefaultAsync(c => c.Id == request.Id); + + if (country == null) + { + throw new NotFoundException(); + } + + return country; + } +} diff --git a/AutobusApi.Application/Countries/Queries/GetCountry/GetCountryQueryValidator.cs b/AutobusApi.Application/Countries/Queries/GetCountry/GetCountryQueryValidator.cs new file mode 100644 index 0000000..2c84850 --- /dev/null +++ b/AutobusApi.Application/Countries/Queries/GetCountry/GetCountryQueryValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Countries.Queries.GetCountry; + +public class GetCountryQueryValidator : AbstractValidator +{ + public GetCountryQueryValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Employees/Commands/CreateEmployee/CreateEmployeeCommand.cs b/AutobusApi.Application/Employees/Commands/CreateEmployee/CreateEmployeeCommand.cs new file mode 100644 index 0000000..885c537 --- /dev/null +++ b/AutobusApi.Application/Employees/Commands/CreateEmployee/CreateEmployeeCommand.cs @@ -0,0 +1,31 @@ +using MediatR; + +namespace AutobusApi.Application.Employees.Commands.CreateEmployee; + +public record CreateEmployeeCommand : IRequest +{ + public int CompanyId { get; set; } + + public string Email { get; set; } = null!; + + public string Password { get; set; } = null!; + + public string FirstName { get; set; } = null!; + + public string LastName { get; set; } = null!; + + public string Patronymic { get; set; } = null!; + + public string Sex { get; set; } = null!; + + public DateOnly BirthDate { get; set; } + + public List Documents { get; set; } = null!; +} + +public record CreateEmployeeDocumentCommand +{ + public string Type { get; set; } = null!; + + public string Information { get; set; } = null!; +} diff --git a/AutobusApi.Application/Employees/Commands/CreateEmployee/CreateEmployeeCommandHandler.cs b/AutobusApi.Application/Employees/Commands/CreateEmployee/CreateEmployeeCommandHandler.cs new file mode 100644 index 0000000..8afbce3 --- /dev/null +++ b/AutobusApi.Application/Employees/Commands/CreateEmployee/CreateEmployeeCommandHandler.cs @@ -0,0 +1,55 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using AutobusApi.Domain.Enums; +using MediatR; + +namespace AutobusApi.Application.Employees.Commands.CreateEmployee; + +public class CreateEmployeeCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IIdentityService _identityService; + + public CreateEmployeeCommandHandler( + IApplicationDbContext dbContext, + IIdentityService identityService) + { + _dbContext = dbContext; + _identityService = identityService; + } + + public async Task Handle( + CreateEmployeeCommand request, + CancellationToken cancellationToken) + { + var employee = new Employee(); + + employee.EmployerCompanyId = request.CompanyId; + employee.FirstName = request.FirstName; + employee.LastName = request.LastName; + employee.Patronymic = request.Patronymic; + employee.Sex = Enum.Parse(request.Sex); + employee.BirthDate = request.BirthDate; + + var userId = await _identityService.RegisterAsync(request.Email, request.Password, cancellationToken); + + employee.IdentityId = userId; + + employee.Documents = new List(); + + foreach (var document in request.Documents) + { + employee.Documents.Add(new EmployeeDocument() + { + Type = Enum.Parse(document.Type), + Information = document.Information + }); + } + + _dbContext.Employees.Add(employee); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return employee.Id; + } +} diff --git a/AutobusApi.Application/Employees/Commands/CreateEmployee/CreateEmployeeCommandValidator.cs b/AutobusApi.Application/Employees/Commands/CreateEmployee/CreateEmployeeCommandValidator.cs new file mode 100644 index 0000000..d5a48dd --- /dev/null +++ b/AutobusApi.Application/Employees/Commands/CreateEmployee/CreateEmployeeCommandValidator.cs @@ -0,0 +1,38 @@ +using AutobusApi.Domain.Enums; +using FluentValidation; + +namespace AutobusApi.Application.Employees.Commands.CreateEmployee; + +public class CreateEmployeeCommandValidator : AbstractValidator +{ + public CreateEmployeeCommandValidator() + { + RuleFor(v => v.Email) + .NotEmpty().WithMessage("Email address is required.") + .Matches(@"\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b").WithMessage("Email address is invalid."); + + RuleFor(v => v.Password) + .NotEmpty().WithMessage("Password is required.") + .MinimumLength(8).WithMessage("Password must be at least 8 characters long.") + .MaximumLength(64).WithMessage("Password must be at most 64 characters long.") + .Matches(@"(?=.*[A-Z]).*").WithMessage("Password must contain at least one uppercase letter.") + .Matches(@"(?=.*[a-z]).*").WithMessage("Password must contain at least one lowercase letter.") + .Matches(@"(?=.*[\d]).*").WithMessage("Password must contain at least one digit.") + .Matches(@"(?=.*[!@#$%^&*()]).*").WithMessage("Password must contain at least one of the following special charactters: !@#$%^&*()."); + + RuleFor(v => v.FirstName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.LastName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.Patronymic).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.Sex).Must(value => Enum.TryParse(value, true, out _)); + + RuleForEach(v => v.Documents).ChildRules(document => + { + document.RuleFor(v => v.Type).Must(value => Enum.TryParse(value, true, out _)); + + document.RuleFor(v => v.Information).MinimumLength(2).MaximumLength(256); + }); + } +} diff --git a/AutobusApi.Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommand.cs b/AutobusApi.Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommand.cs new file mode 100644 index 0000000..f5567b6 --- /dev/null +++ b/AutobusApi.Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Employees.Commands.DeleteEmployee; + +public record DeleteEmployeeCommand : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommandHandler.cs b/AutobusApi.Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommandHandler.cs new file mode 100644 index 0000000..269e77b --- /dev/null +++ b/AutobusApi.Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Employees.Commands.DeleteEmployee; + +public class DeleteEmployeeCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public DeleteEmployeeCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + DeleteEmployeeCommand request, + CancellationToken cancellationToken) + { + var employee = await _dbContext.Employees + .SingleOrDefaultAsync(c => c.Id == request.Id, cancellationToken); + + if (employee == null) + { + throw new NotFoundException(); + } + + _dbContext.Employees.Remove(employee); + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommandValidator.cs b/AutobusApi.Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommandValidator.cs new file mode 100644 index 0000000..cb98268 --- /dev/null +++ b/AutobusApi.Application/Employees/Commands/DeleteEmployee/DeleteEmployeeCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Employees.Commands.DeleteEmployee; + +public class DeleteEmployeeCommandValidator : AbstractValidator +{ + public DeleteEmployeeCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Employees/Commands/UpdateEmployee/UpdateEmployeeCommand.cs b/AutobusApi.Application/Employees/Commands/UpdateEmployee/UpdateEmployeeCommand.cs new file mode 100644 index 0000000..d83d0c5 --- /dev/null +++ b/AutobusApi.Application/Employees/Commands/UpdateEmployee/UpdateEmployeeCommand.cs @@ -0,0 +1,29 @@ +using MediatR; + +namespace AutobusApi.Application.Employees.Commands.UpdateEmployee; + +public record UpdateEmployeeCommand : IRequest +{ + public int Id { get; set; } + + public string FirstName { get; set; } = null!; + + public string LastName { get; set; } = null!; + + public string Patronymic { get; set; } = null!; + + public string Sex { get; set; } = null!; + + public DateOnly BirthDate { get; set; } + + public List Documents { get; set; } = null!; +} + +public record UpdateEmployeeDocumentCommand +{ + public int? Id { get; set; } + + public string Type { get; set; } = null!; + + public string Information { get; set; } = null!; +} diff --git a/AutobusApi.Application/Employees/Commands/UpdateEmployee/UpdateEmployeeCommandHandler.cs b/AutobusApi.Application/Employees/Commands/UpdateEmployee/UpdateEmployeeCommandHandler.cs new file mode 100644 index 0000000..431cd89 --- /dev/null +++ b/AutobusApi.Application/Employees/Commands/UpdateEmployee/UpdateEmployeeCommandHandler.cs @@ -0,0 +1,114 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using AutobusApi.Domain.Enums; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Employees.Commands.UpdateEmployee; + +public class UpdateEmployeeCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public UpdateEmployeeCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + UpdateEmployeeCommand request, + CancellationToken cancellationToken) + { + var employee = await _dbContext.Employees + .Include(e => e.Documents) + .SingleAsync(e => e.Id == request.Id, cancellationToken); + + if (employee == null) + { + throw new NotFoundException(); + } + + employee.FirstName = request.FirstName; + employee.LastName = request.LastName; + employee.Patronymic = request.Patronymic; + employee.Sex = Enum.Parse(request.Sex); + employee.BirthDate = request.BirthDate; + + var compaper = new EmployeeDocumentEqualityComparer(); + + var routeAddressesToBeRemoved = employee.Documents + .ExceptBy(request.Documents.Select( + d => new EmployeeDocument + { + Id = d.Id ?? 0, + Information = d.Information, + Type = Enum.Parse(d.Type) + }), + d => new EmployeeDocument + { + Id = d.Id, + Information = d.Information, + Type = d.Type + }, + compaper + ).ToList(); + + var remainingEmployeeDocumentes = employee.Documents + .ExceptBy(routeAddressesToBeRemoved.Select( + dtbr => new EmployeeDocument + { + Id = dtbr.Id, + Information = dtbr.Information, + Type = dtbr.Type + }), + d => new EmployeeDocument + { + Id = d.Id, + Information = d.Information, + Type = d.Type + }, + compaper + ).ToList(); + + var newEmployeeDocumentes = remainingEmployeeDocumentes + .UnionBy(request.Documents.Select( + d => new EmployeeDocument + { + Id = d.Id ?? 0, + Information = d.Information, + Type = Enum.Parse(d.Type) + }), + rd => new EmployeeDocument + { + Id = rd.Id, + Information = rd.Information, + Type = rd.Type + }, + compaper + ).ToList(); + + employee.Documents = newEmployeeDocumentes.ToList(); + + await _dbContext.SaveChangesAsync(cancellationToken); + } + + private class EmployeeDocumentEqualityComparer : IEqualityComparer + { + public bool Equals(EmployeeDocument? x, EmployeeDocument? y) + { + return + x?.Id == y?.Id && + x?.Type == y?.Type && + x?.Information == y?.Information; + } + + public int GetHashCode(EmployeeDocument obj) + { + return + obj.Id.GetHashCode() + + obj.Information.GetHashCode() + + obj.Type.GetHashCode(); + } + } +} diff --git a/AutobusApi.Application/Employees/Commands/UpdateEmployee/UpdateEmployeeCommandValidator.cs b/AutobusApi.Application/Employees/Commands/UpdateEmployee/UpdateEmployeeCommandValidator.cs new file mode 100644 index 0000000..30aeef2 --- /dev/null +++ b/AutobusApi.Application/Employees/Commands/UpdateEmployee/UpdateEmployeeCommandValidator.cs @@ -0,0 +1,27 @@ +using AutobusApi.Domain.Enums; +using FluentValidation; + +namespace AutobusApi.Application.Employees.Commands.UpdateEmployee; + +public class UpdateEmployeeCommandValidator : AbstractValidator +{ + public UpdateEmployeeCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + + RuleFor(v => v.FirstName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.LastName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.Patronymic).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.Sex).Must(value => Enum.TryParse(value, true, out _)); + + RuleForEach(v => v.Documents).ChildRules(document => + { + document.RuleFor(v => v.Type).Must(value => Enum.TryParse(value, true, out _)); + + document.RuleFor(v => v.Information).MinimumLength(2).MaximumLength(256); + }); + } +} diff --git a/AutobusApi.Application/Employees/Queries/EmployeeDto.cs b/AutobusApi.Application/Employees/Queries/EmployeeDto.cs new file mode 100644 index 0000000..08ff821 --- /dev/null +++ b/AutobusApi.Application/Employees/Queries/EmployeeDto.cs @@ -0,0 +1,30 @@ +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Domain.Entities; + +namespace AutobusApi.Application.Employees.Queries; + +public class EmployeeDto : IMapFrom +{ + public int Id { get; set; } + + public string FirstName { get; set; } = null!; + + public string LastName { get; set; } = null!; + + public string Patronymic { get; set; } = null!; + + public string Sex { get; set; } = null!; + + public DateOnly BirthDate { get; set; } + + public List Documents { get; set; } = null!; +} + +public class EmployeeDocumentDto : IMapFrom +{ + public int Id { get; set; } + + public string Type { get; set; } = null!; + + public string Information { get; set; } = null!; +} diff --git a/AutobusApi.Application/Employees/Queries/GetEmployee/GetEmployeeQuery.cs b/AutobusApi.Application/Employees/Queries/GetEmployee/GetEmployeeQuery.cs new file mode 100644 index 0000000..a469b51 --- /dev/null +++ b/AutobusApi.Application/Employees/Queries/GetEmployee/GetEmployeeQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Employees.Queries.GetEmployee; + +public record GetEmployeeQuery : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Employees/Queries/GetEmployee/GetEmployeeQueryHandler.cs b/AutobusApi.Application/Employees/Queries/GetEmployee/GetEmployeeQueryHandler.cs new file mode 100644 index 0000000..e3454bb --- /dev/null +++ b/AutobusApi.Application/Employees/Queries/GetEmployee/GetEmployeeQueryHandler.cs @@ -0,0 +1,38 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Employees.Queries.GetEmployee; + +public class GetEmployeeQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetEmployeeQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task Handle( + GetEmployeeQuery request, + CancellationToken cancellationToken) + { + var employee = await _dbContext.Employees + .ProjectTo(_mapper.ConfigurationProvider) + .SingleOrDefaultAsync(c => c.Id == request.Id); + + if (employee == null) + { + throw new NotFoundException(); + } + + return employee; + } +} diff --git a/AutobusApi.Application/Employees/Queries/GetEmployee/GetEmployeeQueryValidator.cs b/AutobusApi.Application/Employees/Queries/GetEmployee/GetEmployeeQueryValidator.cs new file mode 100644 index 0000000..04dad78 --- /dev/null +++ b/AutobusApi.Application/Employees/Queries/GetEmployee/GetEmployeeQueryValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Employees.Queries.GetEmployee; + +public class GetEmployeeQueryValidator : AbstractValidator +{ + public GetEmployeeQueryValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Employees/Queries/GetEmployeesWithPagination/GetEmployeesWithPaginationQuery.cs b/AutobusApi.Application/Employees/Queries/GetEmployeesWithPagination/GetEmployeesWithPaginationQuery.cs new file mode 100644 index 0000000..93ab915 --- /dev/null +++ b/AutobusApi.Application/Employees/Queries/GetEmployeesWithPagination/GetEmployeesWithPaginationQuery.cs @@ -0,0 +1,13 @@ +using AutobusApi.Application.Common.Models; +using MediatR; + +namespace AutobusApi.Application.Employees.Queries.GetEmployeesWithPagination; + +public record GetEmployeesWithPaginationQuery : IRequest> +{ + public string Sort { get; set; } = ""; + + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; +} diff --git a/AutobusApi.Application/Employees/Queries/GetEmployeesWithPagination/GetEmployeesWithPaginationQueryHandler.cs b/AutobusApi.Application/Employees/Queries/GetEmployeesWithPagination/GetEmployeesWithPaginationQueryHandler.cs new file mode 100644 index 0000000..078f56e --- /dev/null +++ b/AutobusApi.Application/Employees/Queries/GetEmployeesWithPagination/GetEmployeesWithPaginationQueryHandler.cs @@ -0,0 +1,32 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Application.Common.Models; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; + +namespace AutobusApi.Application.Employees.Queries.GetEmployeesWithPagination; + +public class GetEmployeesWithPaginationQueryHandler : IRequestHandler> +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetEmployeesWithPaginationQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task> Handle( + GetEmployeesWithPaginationQuery request, + CancellationToken cancellationToken) + { + return await _dbContext.Employees + .ProjectTo(_mapper.ConfigurationProvider) + .ApplySort(request.Sort) + .PaginatedListAsync(request.PageNumber, request.PageSize); + } +} diff --git a/AutobusApi.Application/Employees/Queries/GetEmployeesWithPagination/GetEmployeesWithPaginationQueryValidator.cs b/AutobusApi.Application/Employees/Queries/GetEmployeesWithPagination/GetEmployeesWithPaginationQueryValidator.cs new file mode 100644 index 0000000..8fd86fe --- /dev/null +++ b/AutobusApi.Application/Employees/Queries/GetEmployeesWithPagination/GetEmployeesWithPaginationQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.Employees.Queries.GetEmployeesWithPagination; + +public class GetEmployeesWithPaginationQueryValidator : AbstractValidator +{ + public GetEmployeesWithPaginationQueryValidator() + { + RuleFor(v => v.PageNumber).GreaterThanOrEqualTo(1); + + RuleFor(v => v.PageSize).GreaterThanOrEqualTo(1).LessThanOrEqualTo(50); + } +} diff --git a/AutobusApi.Application/Identity/Commands/Register/RegisterCommandValidator.cs b/AutobusApi.Application/Identity/Commands/Register/RegisterCommandValidator.cs index ec3171a..9ff7df0 100644 --- a/AutobusApi.Application/Identity/Commands/Register/RegisterCommandValidator.cs +++ b/AutobusApi.Application/Identity/Commands/Register/RegisterCommandValidator.cs @@ -6,6 +6,7 @@ public class RegisterCommandValidator : AbstractValidator { public RegisterCommandValidator() { + // https://regexr.com/2ri2c RuleFor(v => v.Email) .NotEmpty().WithMessage("Email address is required.") .Matches(@"\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b").WithMessage("Email address is invalid."); diff --git a/AutobusApi.Application/Regions/Commands/CreateRegion/CreateRegionCommand.cs b/AutobusApi.Application/Regions/Commands/CreateRegion/CreateRegionCommand.cs new file mode 100644 index 0000000..b2f28fd --- /dev/null +++ b/AutobusApi.Application/Regions/Commands/CreateRegion/CreateRegionCommand.cs @@ -0,0 +1,10 @@ +using MediatR; + +namespace AutobusApi.Application.Regions.Commands.CreateRegion; + +public record CreateRegionCommand : IRequest +{ + public string Name { get; set; } = null!; + + public int CountryId { get; set; } +} diff --git a/AutobusApi.Application/Regions/Commands/CreateRegion/CreateRegionCommandHandler.cs b/AutobusApi.Application/Regions/Commands/CreateRegion/CreateRegionCommandHandler.cs new file mode 100644 index 0000000..8c09d1f --- /dev/null +++ b/AutobusApi.Application/Regions/Commands/CreateRegion/CreateRegionCommandHandler.cs @@ -0,0 +1,31 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using MediatR; + +namespace AutobusApi.Application.Regions.Commands.CreateRegion; + +public class CreateRegionCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public CreateRegionCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + CreateRegionCommand request, + CancellationToken cancellationToken) + { + var region = new Region(); + + region.Name = request.Name; + region.CountryId = request.CountryId; + + _dbContext.Regions.Add(region); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return region.Id; + } +} diff --git a/AutobusApi.Application/Regions/Commands/CreateRegion/CreateRegionCommandValidator.cs b/AutobusApi.Application/Regions/Commands/CreateRegion/CreateRegionCommandValidator.cs new file mode 100644 index 0000000..c713f13 --- /dev/null +++ b/AutobusApi.Application/Regions/Commands/CreateRegion/CreateRegionCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Regions.Commands.CreateRegion; + +public class CreateRegionCommandValidator : AbstractValidator +{ + public CreateRegionCommandValidator() + { + RuleFor(v => v.Name).MinimumLength(2).MaximumLength(64); + } +} diff --git a/AutobusApi.Application/Regions/Commands/DeleteRegion/DeleteRegionCommand.cs b/AutobusApi.Application/Regions/Commands/DeleteRegion/DeleteRegionCommand.cs new file mode 100644 index 0000000..c68b05e --- /dev/null +++ b/AutobusApi.Application/Regions/Commands/DeleteRegion/DeleteRegionCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Regions.Commands.DeleteRegion; + +public record DeleteRegionCommand : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Regions/Commands/DeleteRegion/DeleteRegionCommandHandler.cs b/AutobusApi.Application/Regions/Commands/DeleteRegion/DeleteRegionCommandHandler.cs new file mode 100644 index 0000000..3af22c2 --- /dev/null +++ b/AutobusApi.Application/Regions/Commands/DeleteRegion/DeleteRegionCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Regions.Commands.DeleteRegion; + +public class DeleteRegionCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public DeleteRegionCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + DeleteRegionCommand request, + CancellationToken cancellationToken) + { + var region = await _dbContext.Regions + .SingleOrDefaultAsync(c => c.Id == request.Id, cancellationToken); + + if (region == null) + { + throw new NotFoundException(); + } + + _dbContext.Regions.Remove(region); + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Regions/Commands/DeleteRegion/DeleteRegionCommandValidator.cs b/AutobusApi.Application/Regions/Commands/DeleteRegion/DeleteRegionCommandValidator.cs new file mode 100644 index 0000000..ab094e4 --- /dev/null +++ b/AutobusApi.Application/Regions/Commands/DeleteRegion/DeleteRegionCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Regions.Commands.DeleteRegion; + +public class DeleteRegionCommandValidator : AbstractValidator +{ + public DeleteRegionCommandValidator() + { + RuleFor(v => v.Id).NotNull(); + } +} diff --git a/AutobusApi.Application/Regions/Commands/UpdateRegion/UpdateRegionCommand.cs b/AutobusApi.Application/Regions/Commands/UpdateRegion/UpdateRegionCommand.cs new file mode 100644 index 0000000..aa4454a --- /dev/null +++ b/AutobusApi.Application/Regions/Commands/UpdateRegion/UpdateRegionCommand.cs @@ -0,0 +1,12 @@ +using MediatR; + +namespace AutobusApi.Application.Regions.Commands.UpdateRegion; + +public record UpdateRegionCommand : IRequest +{ + public int Id { get; set; } + + public string Name { get; set; } = null!; + + public int CountryId { get; set; } +} diff --git a/AutobusApi.Application/Regions/Commands/UpdateRegion/UpdateRegionCommandHandler.cs b/AutobusApi.Application/Regions/Commands/UpdateRegion/UpdateRegionCommandHandler.cs new file mode 100644 index 0000000..6ab9ad3 --- /dev/null +++ b/AutobusApi.Application/Regions/Commands/UpdateRegion/UpdateRegionCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; + +namespace AutobusApi.Application.Regions.Commands.UpdateRegion; + +public class UpdateRegionCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public UpdateRegionCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + UpdateRegionCommand request, + CancellationToken cancellationToken) + { + var region = await _dbContext.Regions + .FindAsync(new object[] { request.Id }, cancellationToken); + + if (region == null) + { + throw new NotFoundException(); + } + + region.Name = request.Name; + region.CountryId = request.CountryId; + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Regions/Commands/UpdateRegion/UpdateRegionCommandValidator.cs b/AutobusApi.Application/Regions/Commands/UpdateRegion/UpdateRegionCommandValidator.cs new file mode 100644 index 0000000..d1738c7 --- /dev/null +++ b/AutobusApi.Application/Regions/Commands/UpdateRegion/UpdateRegionCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Regions.Commands.UpdateRegion; + +public class UpdateRegionCommandValidator : AbstractValidator +{ + public UpdateRegionCommandValidator() + { + RuleFor(v => v.Name).MinimumLength(2).MaximumLength(64); + } +} diff --git a/AutobusApi.Application/Regions/Queries/GetRegion/GetRegionQuery.cs b/AutobusApi.Application/Regions/Queries/GetRegion/GetRegionQuery.cs new file mode 100644 index 0000000..8fe2681 --- /dev/null +++ b/AutobusApi.Application/Regions/Queries/GetRegion/GetRegionQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Regions.Queries.GetRegion; + +public record GetRegionQuery : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Regions/Queries/GetRegion/GetRegionQueryHandler.cs b/AutobusApi.Application/Regions/Queries/GetRegion/GetRegionQueryHandler.cs new file mode 100644 index 0000000..7b6d611 --- /dev/null +++ b/AutobusApi.Application/Regions/Queries/GetRegion/GetRegionQueryHandler.cs @@ -0,0 +1,36 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutoMapper; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Regions.Queries.GetRegion; + +public class GetRegionQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetRegionQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task Handle( + GetRegionQuery request, + CancellationToken cancellationToken) + { + var region = await _dbContext.Regions + .SingleOrDefaultAsync(c => c.Id == request.Id); + + if (region == null) + { + throw new NotFoundException(); + } + + return _mapper.Map(region); + } +} diff --git a/AutobusApi.Application/Regions/Queries/GetRegion/GetRegionQueryValidator.cs b/AutobusApi.Application/Regions/Queries/GetRegion/GetRegionQueryValidator.cs new file mode 100644 index 0000000..15e8ed0 --- /dev/null +++ b/AutobusApi.Application/Regions/Queries/GetRegion/GetRegionQueryValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Regions.Queries.GetRegion; + +public class GetRegionQueryValidator : AbstractValidator +{ + public GetRegionQueryValidator() + { + RuleFor(v => v.Id).NotNull(); + } +} diff --git a/AutobusApi.Application/Regions/Queries/GetRegionsWithPagination/GetRegionsWithPaginationQuery.cs b/AutobusApi.Application/Regions/Queries/GetRegionsWithPagination/GetRegionsWithPaginationQuery.cs new file mode 100644 index 0000000..6ad93fa --- /dev/null +++ b/AutobusApi.Application/Regions/Queries/GetRegionsWithPagination/GetRegionsWithPaginationQuery.cs @@ -0,0 +1,13 @@ +using AutobusApi.Application.Common.Models; +using MediatR; + +namespace AutobusApi.Application.Regions.Queries.GetRegionsWithPagination; + +public record GetRegionsWithPaginationQuery : IRequest> +{ + public string Sort { get; set; } = ""; + + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; +} diff --git a/AutobusApi.Application/Regions/Queries/GetRegionsWithPagination/GetRegionsWithPaginationQueryHandler.cs b/AutobusApi.Application/Regions/Queries/GetRegionsWithPagination/GetRegionsWithPaginationQueryHandler.cs new file mode 100644 index 0000000..fe78c37 --- /dev/null +++ b/AutobusApi.Application/Regions/Queries/GetRegionsWithPagination/GetRegionsWithPaginationQueryHandler.cs @@ -0,0 +1,32 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Application.Common.Models; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; + +namespace AutobusApi.Application.Regions.Queries.GetRegionsWithPagination; + +public class GetRegionsWithPaginationQueryHandler : IRequestHandler> +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetRegionsWithPaginationQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task> Handle( + GetRegionsWithPaginationQuery request, + CancellationToken cancellationToken) + { + return await _dbContext.Regions + .ProjectTo(_mapper.ConfigurationProvider) + .ApplySort(request.Sort) + .PaginatedListAsync(request.PageNumber, request.PageSize); + } +} diff --git a/AutobusApi.Application/Regions/Queries/GetRegionsWithPagination/GetRegionsWithPaginationQueryValidator.cs b/AutobusApi.Application/Regions/Queries/GetRegionsWithPagination/GetRegionsWithPaginationQueryValidator.cs new file mode 100644 index 0000000..fe9146b --- /dev/null +++ b/AutobusApi.Application/Regions/Queries/GetRegionsWithPagination/GetRegionsWithPaginationQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.Regions.Queries.GetRegionsWithPagination; + +public class GetRegionsWithPaginationQueryValidator : AbstractValidator +{ + public GetRegionsWithPaginationQueryValidator() + { + RuleFor(v => v.PageNumber).GreaterThanOrEqualTo(1); + + RuleFor(v => v.PageSize).GreaterThanOrEqualTo(1).LessThanOrEqualTo(50); + } +} diff --git a/AutobusApi.Application/Regions/Queries/RegionDto.cs b/AutobusApi.Application/Regions/Queries/RegionDto.cs new file mode 100644 index 0000000..bf81195 --- /dev/null +++ b/AutobusApi.Application/Regions/Queries/RegionDto.cs @@ -0,0 +1,15 @@ +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Domain.Entities; + +namespace AutobusApi.Application.Regions.Queries; + +public class RegionDto : IMapFrom +{ + public int Id { get; set; } + + public string Name { get; set; } = null!; + + public int CountryId { get; set; } + + public string CountryName { get; set; } = null!; +} diff --git a/AutobusApi.Application/Routes/Commands/CreateRoute/CreateRouteCommand.cs b/AutobusApi.Application/Routes/Commands/CreateRoute/CreateRouteCommand.cs new file mode 100644 index 0000000..b20df4b --- /dev/null +++ b/AutobusApi.Application/Routes/Commands/CreateRoute/CreateRouteCommand.cs @@ -0,0 +1,15 @@ +using MediatR; + +namespace AutobusApi.Application.Routes.Commands.CreateRoute; + +public record CreateRouteCommand : IRequest +{ + public List Addresses { get; set; } = null!; +} + +public record CreateRouteAddressCommand +{ + public int Id { get; set; } + + public int Order { get; set; } +} diff --git a/AutobusApi.Application/Routes/Commands/CreateRoute/CreateRouteCommandHandler.cs b/AutobusApi.Application/Routes/Commands/CreateRoute/CreateRouteCommandHandler.cs new file mode 100644 index 0000000..f85a6ad --- /dev/null +++ b/AutobusApi.Application/Routes/Commands/CreateRoute/CreateRouteCommandHandler.cs @@ -0,0 +1,35 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using MediatR; + +namespace AutobusApi.Application.Routes.Commands.CreateRoute; + +public class CreateRouteCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public CreateRouteCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + CreateRouteCommand request, + CancellationToken cancellationToken) + { + var route = new Route(); + + route.RouteAddresses = request.Addresses.Select(a => + new RouteAddress + { + Order = a.Order, + AddressId = a.Id + }).ToList(); + + _dbContext.Routes.Add(route); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return route.Id; + } +} diff --git a/AutobusApi.Application/Routes/Commands/CreateRoute/CreateRouteCommandValidator.cs b/AutobusApi.Application/Routes/Commands/CreateRoute/CreateRouteCommandValidator.cs new file mode 100644 index 0000000..7991a5f --- /dev/null +++ b/AutobusApi.Application/Routes/Commands/CreateRoute/CreateRouteCommandValidator.cs @@ -0,0 +1,16 @@ +using FluentValidation; + +namespace AutobusApi.Application.Routes.Commands.CreateRoute; + +public class CreateRouteCommandValidator : AbstractValidator +{ + public CreateRouteCommandValidator() + { + RuleFor(v => v.Addresses).Must(v => v.Count >= 2); + + RuleForEach(v => v.Addresses).ChildRules(address => + { + address.RuleFor(v => v.Id).GreaterThan(0); + }); + } +} diff --git a/AutobusApi.Application/Routes/Commands/DeleteRoute/DeleteRouteCommand.cs b/AutobusApi.Application/Routes/Commands/DeleteRoute/DeleteRouteCommand.cs new file mode 100644 index 0000000..c0223ee --- /dev/null +++ b/AutobusApi.Application/Routes/Commands/DeleteRoute/DeleteRouteCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Routes.Commands.DeleteRoute; + +public record DeleteRouteCommand : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Routes/Commands/DeleteRoute/DeleteRouteCommandHandler.cs b/AutobusApi.Application/Routes/Commands/DeleteRoute/DeleteRouteCommandHandler.cs new file mode 100644 index 0000000..848bc77 --- /dev/null +++ b/AutobusApi.Application/Routes/Commands/DeleteRoute/DeleteRouteCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Routes.Commands.DeleteRoute; + +public class DeleteRouteCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public DeleteRouteCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + DeleteRouteCommand request, + CancellationToken cancellationToken) + { + var route = await _dbContext.Routes + .SingleOrDefaultAsync(c => c.Id == request.Id, cancellationToken); + + if (route == null) + { + throw new NotFoundException(); + } + + _dbContext.Routes.Remove(route); + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/Routes/Commands/DeleteRoute/DeleteRouteCommandValidator.cs b/AutobusApi.Application/Routes/Commands/DeleteRoute/DeleteRouteCommandValidator.cs new file mode 100644 index 0000000..8115c28 --- /dev/null +++ b/AutobusApi.Application/Routes/Commands/DeleteRoute/DeleteRouteCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Routes.Commands.DeleteRoute; + +public class DeleteRouteCommandValidator : AbstractValidator +{ + public DeleteRouteCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Routes/Commands/UpdateRoute/UpdateRouteCommand.cs b/AutobusApi.Application/Routes/Commands/UpdateRoute/UpdateRouteCommand.cs new file mode 100644 index 0000000..519974f --- /dev/null +++ b/AutobusApi.Application/Routes/Commands/UpdateRoute/UpdateRouteCommand.cs @@ -0,0 +1,17 @@ +using MediatR; + +namespace AutobusApi.Application.Routes.Commands.UpdateRoute; + +public record UpdateRouteCommand : IRequest +{ + public int Id { get; set; } + + public List Addresses { get; set; } = null!; +} + +public record UpdateRouteAddressAddressCommand +{ + public int Id { get; set; } + + public int Order { get; set; } +} diff --git a/AutobusApi.Application/Routes/Commands/UpdateRoute/UpdateRouteCommandHandler.cs b/AutobusApi.Application/Routes/Commands/UpdateRoute/UpdateRouteCommandHandler.cs new file mode 100644 index 0000000..656eba0 --- /dev/null +++ b/AutobusApi.Application/Routes/Commands/UpdateRoute/UpdateRouteCommandHandler.cs @@ -0,0 +1,96 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Routes.Commands.UpdateRoute; + +public class UpdateRouteCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public UpdateRouteCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + UpdateRouteCommand request, + CancellationToken cancellationToken) + { + var route = await _dbContext.Routes + .Include(r => r.RouteAddresses) + .SingleAsync(r => r.Id == request.Id, cancellationToken); + + if (route == null) + { + throw new NotFoundException(); + } + + var compaper = new RouteAddressEqualityComparer(); + + var routeAddressesToBeRemoved = route.RouteAddresses + .ExceptBy(request.Addresses.Select( + a => new RouteAddress + { + AddressId = a.Id, + Order = a.Order + }), + ra => new RouteAddress + { + AddressId = ra.AddressId, + Order = ra.Order + }, + compaper + ).ToList(); + + var remainingRouteAddresses = route.RouteAddresses + .ExceptBy(routeAddressesToBeRemoved.Select( + ratbr => new RouteAddress + { + AddressId = ratbr.AddressId, + Order = ratbr.Order + }), + ra => new RouteAddress + { + AddressId = ra.AddressId, + Order = ra.Order + }, + compaper + ).ToList(); + + var newRouteAddresses = remainingRouteAddresses + .UnionBy(request.Addresses.Select( + a => new RouteAddress + { + AddressId = a.Id, + Order = a.Order + }), + rra => new RouteAddress + { + AddressId = rra.AddressId, + Order = rra.Order + }, + compaper + ).ToList(); + + route.RouteAddresses = newRouteAddresses.ToList(); + + await _dbContext.SaveChangesAsync(cancellationToken); + } + + private class RouteAddressEqualityComparer : IEqualityComparer + { + public bool Equals(RouteAddress? x, RouteAddress? y) + { + return x?.AddressId == y?.AddressId && x?.Order == y?.Order; + } + + public int GetHashCode(RouteAddress obj) + { + return obj.AddressId.GetHashCode() + obj.Order.GetHashCode(); + } + } +} + diff --git a/AutobusApi.Application/Routes/Commands/UpdateRoute/UpdateRouteCommandValidator.cs b/AutobusApi.Application/Routes/Commands/UpdateRoute/UpdateRouteCommandValidator.cs new file mode 100644 index 0000000..06e5d89 --- /dev/null +++ b/AutobusApi.Application/Routes/Commands/UpdateRoute/UpdateRouteCommandValidator.cs @@ -0,0 +1,17 @@ +using FluentValidation; + +namespace AutobusApi.Application.Routes.Commands.UpdateRoute; + +public class UpdateRouteCommandValidator : AbstractValidator +{ + public UpdateRouteCommandValidator() + { + RuleFor(v => v.Addresses).Must(v => v.Count >= 2); + + RuleForEach(v => v.Addresses).ChildRules(address => + { + address.RuleFor(v => v.Id).GreaterThan(0); + address.RuleFor(v => v.Order).GreaterThan(0); + }); + } +} diff --git a/AutobusApi.Application/Routes/Queries/GetRoute/GetRouteQuery.cs b/AutobusApi.Application/Routes/Queries/GetRoute/GetRouteQuery.cs new file mode 100644 index 0000000..cf5e8ed --- /dev/null +++ b/AutobusApi.Application/Routes/Queries/GetRoute/GetRouteQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.Routes.Queries.GetRoute; + +public record GetRouteQuery : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/Routes/Queries/GetRoute/GetRouteQueryHandler.cs b/AutobusApi.Application/Routes/Queries/GetRoute/GetRouteQueryHandler.cs new file mode 100644 index 0000000..f63ab8d --- /dev/null +++ b/AutobusApi.Application/Routes/Queries/GetRoute/GetRouteQueryHandler.cs @@ -0,0 +1,38 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.Routes.Queries.GetRoute; + +public class GetRouteQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetRouteQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task Handle( + GetRouteQuery request, + CancellationToken cancellationToken) + { + var route = await _dbContext.Routes + .ProjectTo(_mapper.ConfigurationProvider) + .SingleOrDefaultAsync(c => c.Id == request.Id); + + if (route == null) + { + throw new NotFoundException(); + } + + return route; + } +} diff --git a/AutobusApi.Application/Routes/Queries/GetRoute/GetRouteQueryValidator.cs b/AutobusApi.Application/Routes/Queries/GetRoute/GetRouteQueryValidator.cs new file mode 100644 index 0000000..8b7646a --- /dev/null +++ b/AutobusApi.Application/Routes/Queries/GetRoute/GetRouteQueryValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.Routes.Queries.GetRoute; + +public class GetRouteQueryValidator : AbstractValidator +{ + public GetRouteQueryValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/Routes/Queries/GetRoutesWithPagination/GetRoutesWithPaginationQuery.cs b/AutobusApi.Application/Routes/Queries/GetRoutesWithPagination/GetRoutesWithPaginationQuery.cs new file mode 100644 index 0000000..5e3c69b --- /dev/null +++ b/AutobusApi.Application/Routes/Queries/GetRoutesWithPagination/GetRoutesWithPaginationQuery.cs @@ -0,0 +1,13 @@ +using AutobusApi.Application.Common.Models; +using MediatR; + +namespace AutobusApi.Application.Routes.Queries.GetRoutesWithPagination; + +public record GetRoutesWithPaginationQuery : IRequest> +{ + public string Sort { get; set; } = ""; + + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; +} diff --git a/AutobusApi.Application/Routes/Queries/GetRoutesWithPagination/GetRoutesWithPaginationQueryHandler.cs b/AutobusApi.Application/Routes/Queries/GetRoutesWithPagination/GetRoutesWithPaginationQueryHandler.cs new file mode 100644 index 0000000..28be0bc --- /dev/null +++ b/AutobusApi.Application/Routes/Queries/GetRoutesWithPagination/GetRoutesWithPaginationQueryHandler.cs @@ -0,0 +1,32 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Application.Common.Models; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; + +namespace AutobusApi.Application.Routes.Queries.GetRoutesWithPagination; + +public class GetRoutesWithPaginationQueryHandler : IRequestHandler> +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetRoutesWithPaginationQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task> Handle( + GetRoutesWithPaginationQuery request, + CancellationToken cancellationToken) + { + return await _dbContext.Routes + .ProjectTo(_mapper.ConfigurationProvider) + .ApplySort(request.Sort) + .PaginatedListAsync(request.PageNumber, request.PageSize); + } +} diff --git a/AutobusApi.Application/Routes/Queries/GetRoutesWithPagination/GetRoutesWithPaginationQueryValidator.cs b/AutobusApi.Application/Routes/Queries/GetRoutesWithPagination/GetRoutesWithPaginationQueryValidator.cs new file mode 100644 index 0000000..544c7d6 --- /dev/null +++ b/AutobusApi.Application/Routes/Queries/GetRoutesWithPagination/GetRoutesWithPaginationQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.Routes.Queries.GetRoutesWithPagination; + +public class GetRoutesWithPaginationQueryValidator : AbstractValidator +{ + public GetRoutesWithPaginationQueryValidator() + { + RuleFor(v => v.PageNumber).GreaterThanOrEqualTo(1); + + RuleFor(v => v.PageSize).GreaterThanOrEqualTo(1).LessThanOrEqualTo(50); + } +} diff --git a/AutobusApi.Application/Routes/Queries/RouteDto.cs b/AutobusApi.Application/Routes/Queries/RouteDto.cs new file mode 100644 index 0000000..b9e6bad --- /dev/null +++ b/AutobusApi.Application/Routes/Queries/RouteDto.cs @@ -0,0 +1,64 @@ +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Domain.Entities; +using AutoMapper; + +namespace AutobusApi.Application.Routes.Queries; + +public class RouteDto : IMapFrom +{ + public int Id { get; set; } + + public List Addresses { get; set; } = null!; + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember(d => d.Addresses, opt => opt.MapFrom(s => s.RouteAddresses)); + } +} + +public class RouteAddressDto : IMapFrom +{ + public int Id { get; set; } + + public string Name { get; set; } = null!; + + public int Order { get; set; } + + public double Latitude { get; set; } + + public double Longitude { get; set; } + + public string VehicleType { get; set; } = null!; + + public int RouteAddressId { get; set; } + + public int CityId { get; set; } + + public string CityName { get; set; } = null!; + + public int RegionId { get; set; } + + public string RegionName { get; set; } = null!; + + public int CountryId { get; set; } + + public string CountryName { get; set; } = null!; + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember(d => d.Id, opt => opt.MapFrom(s => s.AddressId)) + .ForMember(d => d.Name, opt => opt.MapFrom(s => s.Address.Name)) + .ForMember(d => d.RouteAddressId, opt => opt.MapFrom(s => s.Id)) + .ForMember(d => d.CityId, opt => opt.MapFrom(s => s.Address.CityId)) + .ForMember(d => d.CityName, opt => opt.MapFrom(s => s.Address.City.Name)) + .ForMember(d => d.RegionId, opt => opt.MapFrom(s => s.Address.City.RegionId)) + .ForMember(d => d.RegionName, opt => opt.MapFrom(s => s.Address.City.Region.Name)) + .ForMember(d => d.CountryId, opt => opt.MapFrom(s => s.Address.City.Region.CountryId)) + .ForMember(d => d.CountryName, opt => opt.MapFrom(s => s.Address.City.Region.Country.Name)) + .ForMember(d => d.Latitude, opt => opt.MapFrom(s => s.Address.Location.Latitude)) + .ForMember(d => d.Longitude, opt => opt.MapFrom(s => s.Address.Location.Longitude)) + .ForMember(d => d.VehicleType, opt => opt.MapFrom(s => s.Address.VehicleType.ToString())); + } +} diff --git a/AutobusApi.Application/TicketGroups/Commands/CreateTicketGroup/CreateTicketGroupCommand.cs b/AutobusApi.Application/TicketGroups/Commands/CreateTicketGroup/CreateTicketGroupCommand.cs new file mode 100644 index 0000000..c63e267 --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Commands/CreateTicketGroup/CreateTicketGroupCommand.cs @@ -0,0 +1,41 @@ +using MediatR; + +namespace AutobusApi.Application.TicketGroups.Commands.CreateTicketGroup; + +public record CreateTicketGroupCommand : IRequest +{ + public int? UserId { get; set; } + + public string BuyerFirstName { get; set; } = null!; + + public string BuyerLastName { get; set; } = null!; + + public string BuyerPhoneNumber { get; set; } = null!; + + public string BuyerEmail { get; set; } = null!; + + public string PassengerFirstName { get; set; } = null!; + + public string PassengerLastName { get; set; } = null!; + + public string PassengerPatronymic { get; set; } = null!; + + public string PassengerSex { get; set; } = null!; + + public DateOnly PassengerBirthDate { get; set; } + + public string DocumentType { get; set; } = null!; + + public string DocumentInformation { get; set; } = null!; + + public List Tickets { get; set; } = null!; +} + +public record CreateTicketCommand +{ + public int VehicleEnrollmentId { get; set; } + + public int DepartureAddressId { get; set; } + + public int ArrivalAddressId { get; set; } +} diff --git a/AutobusApi.Application/TicketGroups/Commands/CreateTicketGroup/CreateTicketGroupCommandHandler.cs b/AutobusApi.Application/TicketGroups/Commands/CreateTicketGroup/CreateTicketGroupCommandHandler.cs new file mode 100644 index 0000000..b8030eb --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Commands/CreateTicketGroup/CreateTicketGroupCommandHandler.cs @@ -0,0 +1,73 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using AutobusApi.Domain.Enums; +using MediatR; + +namespace AutobusApi.Application.TicketGroups.Commands.CreateTicketGroup; + +public class CreateTicketGroupCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public CreateTicketGroupCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + CreateTicketGroupCommand request, + CancellationToken cancellationToken) + { + var ticketGroup = new TicketGroup(); + + ticketGroup.BuyerFirstName = request.BuyerFirstName; + ticketGroup.BuyerLastName = request.BuyerLastName; + ticketGroup.BuyerPhoneNumber = request.BuyerPhoneNumber; + ticketGroup.BuyerEmailAddress = request.BuyerEmail; + + ticketGroup.PassengerFirstName = request.PassengerFirstName; + ticketGroup.PassengerLastName = request.PassengerLastName; + ticketGroup.PassengerPatronymic = request.PassengerPatronymic; + ticketGroup.PassengerSex = Enum.Parse(request.PassengerSex); + ticketGroup.PassengerBirthDate = request.PassengerBirthDate; + + ticketGroup.PurchaseDateTimeUtc = DateTime.UtcNow; + + ticketGroup.TicketDocument = new TicketDocument + { + Type = Enum.Parse(request.DocumentType), + Information = request.DocumentInformation + }; + + foreach (var ticket in request.Tickets) + { + // Check if the given departure and arrival addresses are present in this vehicle enrollment + var addressCountInRoute = _dbContext.VehicleEnrollments + .Single(ve => ve.Id == ticket.VehicleEnrollmentId) + .Route.RouteAddresses + .OrderBy(ra => ra.Order) + .SkipWhile(ra => ra.AddressId != ticket.DepartureAddressId) + .TakeWhile(ra => ra.AddressId != ticket.ArrivalAddressId) + .Count(); + + if (addressCountInRoute == 0) + { + // TODO: throw custom exception + throw new NotImplementedException(); + } + + ticketGroup.Tickets.Add(new Ticket + { + VehicleEnrollmentId = ticket.VehicleEnrollmentId, + DepartureAddressId = ticket.DepartureAddressId, + ArrivalAddressId = ticket.ArrivalAddressId + }); + } + + _dbContext.TicketGroups.Add(ticketGroup); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return ticketGroup.Id; + } +} diff --git a/AutobusApi.Application/TicketGroups/Commands/CreateTicketGroup/CreateTicketGroupCommandValidator.cs b/AutobusApi.Application/TicketGroups/Commands/CreateTicketGroup/CreateTicketGroupCommandValidator.cs new file mode 100644 index 0000000..479f5ed --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Commands/CreateTicketGroup/CreateTicketGroupCommandValidator.cs @@ -0,0 +1,43 @@ +using AutobusApi.Domain.Enums; +using FluentValidation; + +namespace AutobusApi.Application.TicketGroups.Commands.CreateTicketGroup; + +public class CreateTicketGroupCommandValidator : AbstractValidator +{ + public CreateTicketGroupCommandValidator() + { + RuleFor(v => v.BuyerFirstName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.BuyerLastName).MinimumLength(2).MaximumLength(32); + + // https://regexr.com/38pvb + RuleFor(v => v.BuyerPhoneNumber) + .NotEmpty().WithMessage("Phone number is required.") + .Matches(@"^\s*(?:\+?(\d{1,3}))?([-. (]*(\d{3})[-. )]*)?((\d{3})[-. ]*(\d{2,4})(?:[-.x ]*(\d+))?)\s*$").WithMessage("Phone number is invalid."); + + // https://regexr.com/2ri2c + RuleFor(v => v.BuyerEmail) + .NotEmpty().WithMessage("Email address is required.") + .Matches(@"\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b").WithMessage("Email address is invalid."); + + RuleFor(v => v.PassengerFirstName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.PassengerLastName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.PassengerPatronymic).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.PassengerSex).Must(value => Enum.TryParse(value, true, out _)); + + RuleFor(v => v.DocumentType).Must(value => Enum.TryParse(value, true, out _)); + + RuleFor(v => v.DocumentInformation).MinimumLength(8).MaximumLength(256); + + RuleForEach(v => v.Tickets).ChildRules(ticket => + { + ticket.RuleFor(v => v.VehicleEnrollmentId).GreaterThan(0); + ticket.RuleFor(v => v.DepartureAddressId).GreaterThan(0); + ticket.RuleFor(v => v.ArrivalAddressId).GreaterThan(0); + }); + } +} diff --git a/AutobusApi.Application/TicketGroups/Commands/DeleteTicketGroup/DeleteTicketGroupCommand.cs b/AutobusApi.Application/TicketGroups/Commands/DeleteTicketGroup/DeleteTicketGroupCommand.cs new file mode 100644 index 0000000..b77805a --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Commands/DeleteTicketGroup/DeleteTicketGroupCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.TicketGroups.Commands.DeleteTicketGroup; + +public record DeleteTicketGroupCommand : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/TicketGroups/Commands/DeleteTicketGroup/DeleteTicketGroupCommandHandler.cs b/AutobusApi.Application/TicketGroups/Commands/DeleteTicketGroup/DeleteTicketGroupCommandHandler.cs new file mode 100644 index 0000000..9ab5cb4 --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Commands/DeleteTicketGroup/DeleteTicketGroupCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.TicketGroups.Commands.DeleteTicketGroup; + +public class DeleteTicketGroupCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public DeleteTicketGroupCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + DeleteTicketGroupCommand request, + CancellationToken cancellationToken) + { + var ticketGroup = await _dbContext.TicketGroups + .SingleOrDefaultAsync(c => c.Id == request.Id, cancellationToken); + + if (ticketGroup == null) + { + throw new NotFoundException(); + } + + _dbContext.TicketGroups.Remove(ticketGroup); + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/TicketGroups/Commands/DeleteTicketGroup/DeleteTicketGroupCommandValidator.cs b/AutobusApi.Application/TicketGroups/Commands/DeleteTicketGroup/DeleteTicketGroupCommandValidator.cs new file mode 100644 index 0000000..08e3d83 --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Commands/DeleteTicketGroup/DeleteTicketGroupCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.TicketGroups.Commands.DeleteTicketGroup; + +public class DeleteTicketGroupCommandValidator : AbstractValidator +{ + public DeleteTicketGroupCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/TicketGroups/Commands/UpdateTicketGroup/UpdateTicketGroupCommand.cs b/AutobusApi.Application/TicketGroups/Commands/UpdateTicketGroup/UpdateTicketGroupCommand.cs new file mode 100644 index 0000000..204cba5 --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Commands/UpdateTicketGroup/UpdateTicketGroupCommand.cs @@ -0,0 +1,49 @@ +using MediatR; + +namespace AutobusApi.Application.TicketGroups.Commands.UpdateTicketGroup; + +public record UpdateTicketGroupCommand : IRequest +{ + public int Id { get; set; } + + public int? UserId { get; set; } + + public string BuyerFirstName { get; set; } = null!; + + public string BuyerLastName { get; set; } = null!; + + public string BuyerPhoneNumber { get; set; } = null!; + + public string BuyerEmail { get; set; } = null!; + + public string PassengerFirstName { get; set; } = null!; + + public string PassengerLastName { get; set; } = null!; + + public string PassengerPatronymic { get; set; } = null!; + + public string PassengerSex { get; set; } = null!; + + public DateOnly PassengerBirthDate { get; set; } + + public DateTime PurchaseDateTime { get; set; } + + public bool IsReturned { get; set; } + + public string DocumentType { get; set; } = null!; + + public string DocumentInformation { get; set; } = null!; + + public List Tickets { get; set; } = null!; +} + +public record UpdateTicketCommand +{ + public int? Id { get; set; } + + public int VehicleEnrollmentId { get; set; } + + public int DepartureAddressId { get; set; } + + public int ArrivalAddressId { get; set; } +} diff --git a/AutobusApi.Application/TicketGroups/Commands/UpdateTicketGroup/UpdateTicketGroupCommandHandler.cs b/AutobusApi.Application/TicketGroups/Commands/UpdateTicketGroup/UpdateTicketGroupCommandHandler.cs new file mode 100644 index 0000000..87e5fee --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Commands/UpdateTicketGroup/UpdateTicketGroupCommandHandler.cs @@ -0,0 +1,129 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using AutobusApi.Domain.Enums; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.TicketGroups.Commands.UpdateTicketGroup; + +public class UpdateTicketGroupCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public UpdateTicketGroupCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + UpdateTicketGroupCommand request, + CancellationToken cancellationToken) + { + var ticketGroup = await _dbContext.TicketGroups + .Include(tg => tg.Tickets) + .SingleAsync(tg => tg.Id == request.Id, cancellationToken); + + if (ticketGroup == null) + { + throw new NotFoundException(); + } + + ticketGroup.BuyerFirstName = request.BuyerFirstName; + ticketGroup.BuyerLastName = request.BuyerLastName; + ticketGroup.BuyerPhoneNumber = request.BuyerPhoneNumber; + ticketGroup.BuyerEmailAddress = request.BuyerEmail; + + ticketGroup.PassengerFirstName = request.PassengerFirstName; + ticketGroup.PassengerLastName = request.PassengerLastName; + ticketGroup.PassengerPatronymic = request.PassengerPatronymic; + ticketGroup.PassengerSex = Enum.Parse(request.PassengerSex); + ticketGroup.PassengerBirthDate = request.PassengerBirthDate; + + ticketGroup.TicketDocument.Type = Enum.Parse(request.DocumentType); + ticketGroup.TicketDocument.Information = request.DocumentInformation; + + var compaper = new TicketEqualityComparer(); + + var ticketsToBeRemoved = ticketGroup.Tickets + .ExceptBy(request.Tickets.Select( + t => new Ticket + { + Id = t.Id ?? 0, + DepartureAddressId = t.DepartureAddressId, + ArrivalAddressId = t.ArrivalAddressId, + VehicleEnrollmentId = t.VehicleEnrollmentId + }), + t => new Ticket + { + Id = t.Id, + DepartureAddressId = t.DepartureAddressId, + ArrivalAddressId = t.ArrivalAddressId, + VehicleEnrollmentId = t.VehicleEnrollmentId + }, + compaper + ).ToList(); + + var remainingTickets = ticketGroup.Tickets + .ExceptBy(ticketsToBeRemoved.Select( + ttbr => new Ticket + { + Id = ttbr.Id, + DepartureAddressId = ttbr.DepartureAddressId, + ArrivalAddressId = ttbr.ArrivalAddressId, + VehicleEnrollmentId = ttbr.VehicleEnrollmentId + }), + t => new Ticket + { + Id = t.Id, + DepartureAddressId = t.DepartureAddressId, + ArrivalAddressId = t.ArrivalAddressId, + VehicleEnrollmentId = t.VehicleEnrollmentId + }, + compaper + ).ToList(); + + var newTickets = remainingTickets + .UnionBy(request.Tickets.Select( + t => new Ticket + { + Id = t.Id ?? 0, + DepartureAddressId = t.DepartureAddressId, + ArrivalAddressId = t.ArrivalAddressId, + VehicleEnrollmentId = t.VehicleEnrollmentId + }), + rt => new Ticket + { + Id = rt.Id, + DepartureAddressId = rt.DepartureAddressId, + ArrivalAddressId = rt.ArrivalAddressId, + VehicleEnrollmentId = rt.VehicleEnrollmentId + }, + compaper + ).ToList(); + + ticketGroup.Tickets = newTickets.ToList(); + + await _dbContext.SaveChangesAsync(cancellationToken); + } + + private class TicketEqualityComparer : IEqualityComparer + { + public bool Equals(Ticket? x, Ticket? y) + { + return + x?.Id == y?.Id && + x?.DepartureAddressId == y?.DepartureAddressId && + x?.ArrivalAddressId == y?.ArrivalAddressId && + x?.VehicleEnrollmentId == y?.VehicleEnrollmentId; + } + + public int GetHashCode(Ticket obj) + { + return + obj.ArrivalAddressId.GetHashCode() + + obj.DepartureAddressId.GetHashCode() + + obj.VehicleEnrollmentId.GetHashCode(); + } + } +} diff --git a/AutobusApi.Application/TicketGroups/Commands/UpdateTicketGroup/UpdateTicketGroupCommandValidator.cs b/AutobusApi.Application/TicketGroups/Commands/UpdateTicketGroup/UpdateTicketGroupCommandValidator.cs new file mode 100644 index 0000000..023ba1d --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Commands/UpdateTicketGroup/UpdateTicketGroupCommandValidator.cs @@ -0,0 +1,45 @@ +using AutobusApi.Domain.Enums; +using FluentValidation; + +namespace AutobusApi.Application.TicketGroups.Commands.UpdateTicketGroup; + +public class UpdateTicketGroupCommandValidator : AbstractValidator +{ + public UpdateTicketGroupCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + + RuleFor(v => v.BuyerFirstName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.BuyerLastName).MinimumLength(2).MaximumLength(32); + + // https://regexr.com/38pvb + RuleFor(v => v.BuyerPhoneNumber) + .NotEmpty().WithMessage("Phone number is required.") + .Matches(@"^\s*(?:\+?(\d{1,3}))?([-. (]*(\d{3})[-. )]*)?((\d{3})[-. ]*(\d{2,4})(?:[-.x ]*(\d+))?)\s*$").WithMessage("Phone number is invalid."); + + // https://regexr.com/2ri2c + RuleFor(v => v.BuyerEmail) + .NotEmpty().WithMessage("Email address is required.") + .Matches(@"\b[\w\.-]+@[\w\.-]+\.\w{2,4}\b").WithMessage("Email address is invalid."); + + RuleFor(v => v.PassengerFirstName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.PassengerLastName).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.PassengerPatronymic).MinimumLength(2).MaximumLength(32); + + RuleFor(v => v.PassengerSex).Must(value => Enum.TryParse(value, true, out _)); + + RuleFor(v => v.DocumentType).Must(value => Enum.TryParse(value, true, out _)); + + RuleFor(v => v.DocumentInformation).MinimumLength(8).MaximumLength(256); + + RuleForEach(v => v.Tickets).ChildRules(ticket => + { + ticket.RuleFor(v => v.VehicleEnrollmentId).GreaterThan(0); + ticket.RuleFor(v => v.DepartureAddressId).GreaterThan(0); + ticket.RuleFor(v => v.ArrivalAddressId).GreaterThan(0); + }); + } +} diff --git a/AutobusApi.Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQuery.cs b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQuery.cs new file mode 100644 index 0000000..542ba0f --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.TicketGroups.Queries.GetTicketGroup; + +public record GetTicketGroupQuery : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryHandler.cs b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryHandler.cs new file mode 100644 index 0000000..fd749aa --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryHandler.cs @@ -0,0 +1,36 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutoMapper; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.TicketGroups.Queries.GetTicketGroup; + +public class GetTicketGroupQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetTicketGroupQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task Handle( + GetTicketGroupQuery request, + CancellationToken cancellationToken) + { + var ticketGroup = await _dbContext.TicketGroups + .SingleOrDefaultAsync(c => c.Id == request.Id); + + if (ticketGroup == null) + { + throw new NotFoundException(); + } + + return _mapper.Map(ticketGroup); + } +} diff --git a/AutobusApi.Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryValidator.cs b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryValidator.cs new file mode 100644 index 0000000..bd6d7a1 --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroup/GetTicketGroupQueryValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.TicketGroups.Queries.GetTicketGroup; + +public class GetTicketGroupQueryValidator : AbstractValidator +{ + public GetTicketGroupQueryValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/TicketGroups/Queries/GetTicketGroupsWithPagination/GetTicketGroupsWithPaginationQuery.cs b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroupsWithPagination/GetTicketGroupsWithPaginationQuery.cs new file mode 100644 index 0000000..7fcf997 --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroupsWithPagination/GetTicketGroupsWithPaginationQuery.cs @@ -0,0 +1,13 @@ +using AutobusApi.Application.Common.Models; +using MediatR; + +namespace AutobusApi.Application.TicketGroups.Queries.GetTicketGroupsWithPagination; + +public record GetTicketGroupsWithPaginationQuery : IRequest> +{ + public string Sort { get; set; } = ""; + + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; +} diff --git a/AutobusApi.Application/TicketGroups/Queries/GetTicketGroupsWithPagination/GetTicketGroupsWithPaginationQueryHandler.cs b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroupsWithPagination/GetTicketGroupsWithPaginationQueryHandler.cs new file mode 100644 index 0000000..c16b740 --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroupsWithPagination/GetTicketGroupsWithPaginationQueryHandler.cs @@ -0,0 +1,32 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Application.Common.Models; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; + +namespace AutobusApi.Application.TicketGroups.Queries.GetTicketGroupsWithPagination; + +public class GetTicketGroupsWithPaginationQueryHandler : IRequestHandler> +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetTicketGroupsWithPaginationQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task> Handle( + GetTicketGroupsWithPaginationQuery request, + CancellationToken cancellationToken) + { + return await _dbContext.TicketGroups + .ProjectTo(_mapper.ConfigurationProvider) + .ApplySort(request.Sort) + .PaginatedListAsync(request.PageNumber, request.PageSize); + } +} diff --git a/AutobusApi.Application/TicketGroups/Queries/GetTicketGroupsWithPagination/GetTicketGroupsWithPaginationQueryValidator.cs b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroupsWithPagination/GetTicketGroupsWithPaginationQueryValidator.cs new file mode 100644 index 0000000..0db2e78 --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Queries/GetTicketGroupsWithPagination/GetTicketGroupsWithPaginationQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.TicketGroups.Queries.GetTicketGroupsWithPagination; + +public class GetTicketGroupsWithPaginationQueryValidator : AbstractValidator +{ + public GetTicketGroupsWithPaginationQueryValidator() + { + RuleFor(v => v.PageNumber).GreaterThanOrEqualTo(1); + + RuleFor(v => v.PageSize).GreaterThanOrEqualTo(1).LessThanOrEqualTo(50); + } +} diff --git a/AutobusApi.Application/TicketGroups/Queries/TicketGroupDto.cs b/AutobusApi.Application/TicketGroups/Queries/TicketGroupDto.cs new file mode 100644 index 0000000..24cdd38 --- /dev/null +++ b/AutobusApi.Application/TicketGroups/Queries/TicketGroupDto.cs @@ -0,0 +1,178 @@ +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Domain.Entities; +using AutoMapper; + +namespace AutobusApi.Application.TicketGroups.Queries; + +public class TicketGroupDto : IMapFrom +{ + public int Id { get; set; } + + public int? UserId { get; set; } + + public string BuyerFirstName { get; set; } = null!; + + public string BuyerLastName { get; set; } = null!; + + public string BuyerPhoneNumber { get; set; } = null!; + + public string BuyerEmailAddress { get; set; } = null!; + + public string PassengerFirstName { get; set; } = null!; + + public string PassengerLastName { get; set; } = null!; + + public string PassengerPatronymic { get; set; } = null!; + + public string PassengerSex { get; set; } = null!; + + public DateOnly PassengerBirthDate { get; set; } + + public DateTime PurchaseDateTime { get; set; } + + public bool IsReturned { get; set; } + + public string DocumentType { get; set; } = null!; + + public string DocumentInformation { get; set; } = null!; + + public List Tickets { get; set; } = null!; + + public DateTime DepartureDateTime { get; set; } + + public DateTime ArrivalDateTime { get; set; } + + public TimeSpan Time { get; set; } + + public double Cost { get; set; } + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember(d => d.PassengerSex, opt => opt.MapFrom(s => s.PassengerSex.ToString())) + .ForMember(d => d.DocumentType, opt => opt.MapFrom(s => s.TicketDocument.Type.ToString())) + .ForMember(d => d.DocumentInformation, opt => opt.MapFrom(s => s.TicketDocument.Information)) + .ForMember(d => d.Tickets, opt => opt.MapFrom(s => s.Tickets)) + .ForMember(d => d.DepartureDateTime, opt => opt.MapFrom(s => s.GetDepartureDateTimeUtc())) + .ForMember(d => d.ArrivalDateTime, opt => opt.MapFrom(s => s.GetArrivalDateTimeUtc())) + .ForMember(d => d.Time, opt => opt.MapFrom(s => s.GetTravelTime())) + .ForMember(d => d.Cost, opt => opt.MapFrom(s => s.GetCost())) + .ForMember(d => d.PurchaseDateTime, opt => opt.MapFrom(s => s.PurchaseDateTimeUtc)); + } +} + +public class TicketDto : IMapFrom +{ + public int Id { get; set; } + + public int VehicleEnrollmentId { get; set; } + + public int DepartureAddressId { get; set; } + + public string DepartureAddressName { get; set; } = null!; + + public int DepartureCityId { get; set; } + + public string DepartureCityName { get; set; } = null!; + + public int DepartureRegionId { get; set; } + + public string DepartureRegionName { get; set; } = null!; + + public int DepartureCountryId { get; set; } + + public string DepartureCountryName { get; set; } = null!; + + public DateTime DepartureDateTime { get; set; } + + public int ArrivalAddressId { get; set; } + + public string ArrivalAddressName { get; set; } = null!; + + public int ArrivalCityId { get; set; } + + public string ArrivalCityName { get; set; } = null!; + + public int ArrivalRegionId { get; set; } + + public string ArrivalRegionName { get; set; } = null!; + + public int ArrivalCountryId { get; set; } + + public string ArrivalCountryName { get; set; } = null!; + + public DateTime ArrivalDateTime { get; set; } + + public TimeSpan Time { get; set; } + + public double Cost { get; set; } + + public void Mapping(Profile profile) + { + profile.CreateMap() + .ForMember( + d => d.DepartureAddressName, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.DepartureAddressId).Address.Name)) + .ForMember( + d => d.DepartureCityId, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.DepartureAddressId).Address.CityId)) + .ForMember( + d => d.DepartureCityName, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.DepartureAddressId).Address.City.Name)) + .ForMember( + d => d.DepartureRegionId, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.DepartureAddressId).Address.City.RegionId)) + .ForMember( + d => d.DepartureRegionName, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.DepartureAddressId).Address.City.Region.Name)) + .ForMember( + d => d.DepartureCountryId, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.DepartureAddressId).Address.City.Region.CountryId)) + .ForMember( + d => d.DepartureCountryName, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.DepartureAddressId).Address.City.Region.Country.Name)) + .ForMember( + d => d.DepartureDateTime, + opt => opt.MapFrom(s => s.VehicleEnrollment.GetDepartureDateTimeUtc(s.DepartureAddressId))) + .ForMember( + d => d.ArrivalAddressName, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.ArrivalAddressId).Address.Name)) + .ForMember( + d => d.ArrivalCityId, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.ArrivalAddressId).Address.CityId)) + .ForMember( + d => d.ArrivalCityName, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.ArrivalAddressId).Address.City.Name)) + .ForMember( + d => d.ArrivalRegionId, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.ArrivalAddressId).Address.City.RegionId)) + .ForMember( + d => d.ArrivalRegionName, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.ArrivalAddressId).Address.City.Region.Name)) + .ForMember( + d => d.ArrivalCountryId, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.ArrivalAddressId).Address.City.Region.CountryId)) + .ForMember( + d => d.ArrivalCountryName, + opt => opt.MapFrom( + s => s.VehicleEnrollment.Route.RouteAddresses.Single(ra => ra.AddressId == s.ArrivalAddressId).Address.City.Region.Country.Name)) + .ForMember( + d => d.ArrivalDateTime, + opt => opt.MapFrom(s => s.VehicleEnrollment.GetArrivalDateTimeUtc(s.ArrivalAddressId))) + .ForMember(d => d.Time, opt => opt.MapFrom(s => s.VehicleEnrollment.GetTravelTime(s.DepartureAddressId, s.ArrivalAddressId))) + .ForMember(d => d.Cost, opt => opt.MapFrom(s => s.VehicleEnrollment.GetCost(s.DepartureAddressId, s.ArrivalAddressId))); + } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Commands/CreateVehicleEnrollment/CreateVehicleEnrollmentCommand.cs b/AutobusApi.Application/VehicleEnrollments/Commands/CreateVehicleEnrollment/CreateVehicleEnrollmentCommand.cs new file mode 100644 index 0000000..b50baa1 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Commands/CreateVehicleEnrollment/CreateVehicleEnrollmentCommand.cs @@ -0,0 +1,25 @@ +using MediatR; + +namespace AutobusApi.Application.VehicleEnrollments.Commands.CreateVehicleEnrollment; + +public record CreateVehicleEnrollmentCommand : IRequest +{ + public int VehicleId { get; set; } + + public int RouteId { get; set; } + + public DateTime DepartureDateTimeUtc { get; set; } + + public List RouteAddressDetails { get; set; } = null!; +} + +public record CreateRouteAddressDetailsCommand +{ + public int RouteAddressId { get; set; } + + public TimeSpan TimeToNextAddress { get; set; } + + public TimeSpan CurrentAddressStopTime { get; set; } + + public double CostToNextAddress { get; set; } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Commands/CreateVehicleEnrollment/CreateVehicleEnrollmentCommandHandler.cs b/AutobusApi.Application/VehicleEnrollments/Commands/CreateVehicleEnrollment/CreateVehicleEnrollmentCommandHandler.cs new file mode 100644 index 0000000..ec4a791 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Commands/CreateVehicleEnrollment/CreateVehicleEnrollmentCommandHandler.cs @@ -0,0 +1,61 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Domain.Entities; +using MediatR; + +namespace AutobusApi.Application.VehicleEnrollments.Commands.CreateVehicleEnrollment; + +public class CreateVehicleEnrollmentCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public CreateVehicleEnrollmentCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + CreateVehicleEnrollmentCommand request, + CancellationToken cancellationToken) + { + var vehicleEnrollment = new VehicleEnrollment(); + + vehicleEnrollment.VehicleId = request.VehicleId; + vehicleEnrollment.RouteId = request.RouteId; + vehicleEnrollment.DepartureDateTimeUtc = request.DepartureDateTimeUtc; + + var routeAddresses = _dbContext.RouteAddresses + .Where(ra => ra.RouteId == request.RouteId) + .ToList(); + + if (routeAddresses.Count != request.RouteAddressDetails.Count) + { + // TODO: Throw validation exception + throw new NotImplementedException(); + } + + vehicleEnrollment.RouteAddressDetails = new List(); + + foreach (var detail in request.RouteAddressDetails) + { + if (!routeAddresses.Any(ra => ra.Id == detail.RouteAddressId)) + { + // TODO: Throw validation exception + throw new NotImplementedException(); + } + + vehicleEnrollment.RouteAddressDetails.Add(new RouteAddressDetails() + { + RouteAddressId = detail.RouteAddressId, + TimeToNextAddress = detail.TimeToNextAddress, + CurrentAddressStopTime = detail.CurrentAddressStopTime, + CostToNextAddress = detail.CostToNextAddress + }); + } + + _dbContext.VehicleEnrollments.Add(vehicleEnrollment); + + await _dbContext.SaveChangesAsync(cancellationToken); + + return vehicleEnrollment.Id; + } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Commands/CreateVehicleEnrollment/CreateVehicleEnrollmentCommandValidator.cs b/AutobusApi.Application/VehicleEnrollments/Commands/CreateVehicleEnrollment/CreateVehicleEnrollmentCommandValidator.cs new file mode 100644 index 0000000..ada4163 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Commands/CreateVehicleEnrollment/CreateVehicleEnrollmentCommandValidator.cs @@ -0,0 +1,20 @@ +using FluentValidation; + +namespace AutobusApi.Application.VehicleEnrollments.Commands.CreateVehicleEnrollment; + +public class CreateVehicleEnrollmentCommandValidator : AbstractValidator +{ + public CreateVehicleEnrollmentCommandValidator() + { + RuleFor(v => v.VehicleId).GreaterThan(0); + RuleFor(v => v.RouteId).GreaterThan(0); + + RuleForEach(v => v.RouteAddressDetails).ChildRules(detail => + { + detail.RuleFor(v => v.RouteAddressId).GreaterThan(0); + detail.RuleFor(v => v.TimeToNextAddress).GreaterThan(TimeSpan.Zero); + detail.RuleFor(v => v.CurrentAddressStopTime).GreaterThan(TimeSpan.Zero); + detail.RuleFor(v => v.CostToNextAddress).GreaterThan(0.0); + }); + } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Commands/DeleteVehicleEnrollment/DeleteVehicleEnrollmentCommand.cs b/AutobusApi.Application/VehicleEnrollments/Commands/DeleteVehicleEnrollment/DeleteVehicleEnrollmentCommand.cs new file mode 100644 index 0000000..1d2a8c5 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Commands/DeleteVehicleEnrollment/DeleteVehicleEnrollmentCommand.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.VehicleEnrollments.Commands.DeleteVehicleEnrollment; + +public record DeleteVehicleEnrollmentCommand : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Commands/DeleteVehicleEnrollment/DeleteVehicleEnrollmentCommandHandler.cs b/AutobusApi.Application/VehicleEnrollments/Commands/DeleteVehicleEnrollment/DeleteVehicleEnrollmentCommandHandler.cs new file mode 100644 index 0000000..f645565 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Commands/DeleteVehicleEnrollment/DeleteVehicleEnrollmentCommandHandler.cs @@ -0,0 +1,33 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.VehicleEnrollments.Commands.DeleteVehicleEnrollment; + +public class DeleteVehicleEnrollmentCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public DeleteVehicleEnrollmentCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + DeleteVehicleEnrollmentCommand request, + CancellationToken cancellationToken) + { + var vehicleEnrollment = await _dbContext.VehicleEnrollments + .SingleOrDefaultAsync(c => c.Id == request.Id, cancellationToken); + + if (vehicleEnrollment == null) + { + throw new NotFoundException(); + } + + _dbContext.VehicleEnrollments.Remove(vehicleEnrollment); + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Commands/DeleteVehicleEnrollment/DeleteVehicleEnrollmentCommandValidator.cs b/AutobusApi.Application/VehicleEnrollments/Commands/DeleteVehicleEnrollment/DeleteVehicleEnrollmentCommandValidator.cs new file mode 100644 index 0000000..e8c3000 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Commands/DeleteVehicleEnrollment/DeleteVehicleEnrollmentCommandValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.VehicleEnrollments.Commands.DeleteVehicleEnrollment; + +public class DeleteVehicleEnrollmentCommandValidator : AbstractValidator +{ + public DeleteVehicleEnrollmentCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommand.cs b/AutobusApi.Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommand.cs new file mode 100644 index 0000000..33f0b7e --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommand.cs @@ -0,0 +1,24 @@ +using MediatR; + +namespace AutobusApi.Application.VehicleEnrollments.Commands.UpdateVehicleEnrollment; + +public record UpdateVehicleEnrollmentCommand : IRequest +{ + public int Id { get; set; } + + public DateTime DepartureDateTimeUtc { get; set; } + + public List RouteAddressDetails { get; set; } = null!; +} + +public record UpdateRouteAddressDetailsCommand +{ + public int RouteAddressId { get; set; } + + public TimeSpan TimeToNextAddress { get; set; } + + public TimeSpan CurrentAddressStopTime { get; set; } + + public double CostToNextAddress { get; set; } +} + diff --git a/AutobusApi.Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandHandler.cs b/AutobusApi.Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandHandler.cs new file mode 100644 index 0000000..0ca77f2 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandHandler.cs @@ -0,0 +1,44 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.VehicleEnrollments.Commands.UpdateVehicleEnrollment; + +public class UpdateVehicleEnrollmentCommandHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + + public UpdateVehicleEnrollmentCommandHandler(IApplicationDbContext dbContext) + { + _dbContext = dbContext; + } + + public async Task Handle( + UpdateVehicleEnrollmentCommand request, + CancellationToken cancellationToken) + { + var vehicleEnrollment = await _dbContext.VehicleEnrollments + .Include(ve => ve.RouteAddressDetails) + .FirstAsync(ve => ve.Id == request.Id, cancellationToken); + + if (vehicleEnrollment == null) + { + throw new NotFoundException(); + } + + vehicleEnrollment.DepartureDateTimeUtc = request.DepartureDateTimeUtc; + + foreach (var requestDetail in request.RouteAddressDetails) + { + var detail = vehicleEnrollment.RouteAddressDetails + .First(rad => rad.RouteAddressId == requestDetail.RouteAddressId); + + detail.TimeToNextAddress = requestDetail.TimeToNextAddress; + detail.CurrentAddressStopTime = requestDetail.CurrentAddressStopTime; + detail.CostToNextAddress = requestDetail.CostToNextAddress; + } + + await _dbContext.SaveChangesAsync(cancellationToken); + } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandValidator.cs b/AutobusApi.Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandValidator.cs new file mode 100644 index 0000000..ed58f2b --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Commands/UpdateVehicleEnrollment/UpdateVehicleEnrollmentCommandValidator.cs @@ -0,0 +1,19 @@ +using FluentValidation; + +namespace AutobusApi.Application.VehicleEnrollments.Commands.UpdateVehicleEnrollment; + +public class UpdateVehicleEnrollmentCommandValidator : AbstractValidator +{ + public UpdateVehicleEnrollmentCommandValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + + RuleForEach(v => v.RouteAddressDetails).ChildRules(detail => + { + detail.RuleFor(v => v.RouteAddressId).GreaterThan(0); + detail.RuleFor(v => v.TimeToNextAddress).GreaterThan(TimeSpan.Zero); + detail.RuleFor(v => v.CurrentAddressStopTime).GreaterThan(TimeSpan.Zero); + detail.RuleFor(v => v.CostToNextAddress).GreaterThan(0.0); + }); + } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQuery.cs b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQuery.cs new file mode 100644 index 0000000..c5fd936 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQuery.cs @@ -0,0 +1,8 @@ +using MediatR; + +namespace AutobusApi.Application.VehicleEnrollments.Queries.GetVehicleEnrollment; + +public record GetVehicleEnrollmentQuery : IRequest +{ + public int Id { get; set; } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQueryHandler.cs b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQueryHandler.cs new file mode 100644 index 0000000..b974b14 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQueryHandler.cs @@ -0,0 +1,36 @@ +using AutobusApi.Application.Common.Exceptions; +using AutobusApi.Application.Common.Interfaces; +using AutoMapper; +using MediatR; +using Microsoft.EntityFrameworkCore; + +namespace AutobusApi.Application.VehicleEnrollments.Queries.GetVehicleEnrollment; + +public class GetVehicleEnrollmentQueryHandler : IRequestHandler +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetVehicleEnrollmentQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task Handle( + GetVehicleEnrollmentQuery request, + CancellationToken cancellationToken) + { + var vehicleEnrollment = await _dbContext.VehicleEnrollments + .SingleOrDefaultAsync(c => c.Id == request.Id); + + if (vehicleEnrollment == null) + { + throw new NotFoundException(); + } + + return _mapper.Map(vehicleEnrollment); + } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQueryValidator.cs b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQueryValidator.cs new file mode 100644 index 0000000..ce849f5 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollment/GetVehicleEnrollmentQueryValidator.cs @@ -0,0 +1,11 @@ +using FluentValidation; + +namespace AutobusApi.Application.VehicleEnrollments.Queries.GetVehicleEnrollment; + +public class GetVehicleEnrollmentQueryValidator : AbstractValidator +{ + public GetVehicleEnrollmentQueryValidator() + { + RuleFor(v => v.Id).GreaterThan(0); + } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsWithPagination/GetVehicleEnrollmentsWithPaginationQuery.cs b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsWithPagination/GetVehicleEnrollmentsWithPaginationQuery.cs new file mode 100644 index 0000000..059b7be --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsWithPagination/GetVehicleEnrollmentsWithPaginationQuery.cs @@ -0,0 +1,13 @@ +using AutobusApi.Application.Common.Models; +using MediatR; + +namespace AutobusApi.Application.VehicleEnrollments.Queries.GetVehicleEnrollmentsWithPagination; + +public record GetVehicleEnrollmentsWithPaginationQuery : IRequest> +{ + public string Sort { get; set; } = ""; + + public int PageNumber { get; set; } = 1; + + public int PageSize { get; set; } = 10; +} diff --git a/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsWithPagination/GetVehicleEnrollmentsWithPaginationQueryHandler.cs b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsWithPagination/GetVehicleEnrollmentsWithPaginationQueryHandler.cs new file mode 100644 index 0000000..8ad7956 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsWithPagination/GetVehicleEnrollmentsWithPaginationQueryHandler.cs @@ -0,0 +1,32 @@ +using AutobusApi.Application.Common.Interfaces; +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Application.Common.Models; +using AutoMapper; +using AutoMapper.QueryableExtensions; +using MediatR; + +namespace AutobusApi.Application.VehicleEnrollments.Queries.GetVehicleEnrollmentsWithPagination; + +public class GetVehicleEnrollmentsWithPaginationQueryHandler : IRequestHandler> +{ + private readonly IApplicationDbContext _dbContext; + private readonly IMapper _mapper; + + public GetVehicleEnrollmentsWithPaginationQueryHandler( + IApplicationDbContext dbContext, + IMapper mapper) + { + _dbContext = dbContext; + _mapper = mapper; + } + + public async Task> Handle( + GetVehicleEnrollmentsWithPaginationQuery request, + CancellationToken cancellationToken) + { + return await _dbContext.VehicleEnrollments + .ProjectTo(_mapper.ConfigurationProvider) + .ApplySort(request.Sort) + .PaginatedListAsync(request.PageNumber, request.PageSize); + } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsWithPagination/GetVehicleEnrollmentsWithPaginationQueryValidator.cs b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsWithPagination/GetVehicleEnrollmentsWithPaginationQueryValidator.cs new file mode 100644 index 0000000..f4be2a2 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Queries/GetVehicleEnrollmentsWithPagination/GetVehicleEnrollmentsWithPaginationQueryValidator.cs @@ -0,0 +1,13 @@ +using FluentValidation; + +namespace AutobusApi.Application.VehicleEnrollments.Queries.GetVehicleEnrollmentsWithPagination; + +public class GetVehicleEnrollmentsWithPaginationQueryValidator : AbstractValidator +{ + public GetVehicleEnrollmentsWithPaginationQueryValidator() + { + RuleFor(v => v.PageNumber).GreaterThanOrEqualTo(1); + + RuleFor(v => v.PageSize).GreaterThanOrEqualTo(1).LessThanOrEqualTo(50); + } +} diff --git a/AutobusApi.Application/VehicleEnrollments/Queries/VehicleEnrollmentDto.cs b/AutobusApi.Application/VehicleEnrollments/Queries/VehicleEnrollmentDto.cs new file mode 100644 index 0000000..ff9a322 --- /dev/null +++ b/AutobusApi.Application/VehicleEnrollments/Queries/VehicleEnrollmentDto.cs @@ -0,0 +1,28 @@ +using AutobusApi.Application.Common.Mappings; +using AutobusApi.Domain.Entities; + +namespace AutobusApi.Application.VehicleEnrollments.Queries; + +public class VehicleEnrollmentDto : IMapFrom +{ + public int Id { get; set; } + + public int VehicleId { get; set; } + + public int RouteId { get; set; } + + public DateTime DepartureDateTimeUtc { get; set; } + + public List RouteAddressDetails { get; set; } = null!; +} + +public class RouteAddressDetailsDto : IMapFrom +{ + public int RouteAddressId { get; set; } + + public TimeSpan TimeToNextAddress { get; set; } + + public TimeSpan CurrentAddressStopTime { get; set; } + + public double CostToNextAddress { get; set; } +} diff --git a/AutobusApi.Domain/Common/EntityBase.cs b/AutobusApi.Domain/Common/EntityBase.cs index 38cd4dc..d603edd 100644 --- a/AutobusApi.Domain/Common/EntityBase.cs +++ b/AutobusApi.Domain/Common/EntityBase.cs @@ -2,7 +2,7 @@ namespace AutobusApi.Domain.Common; public class EntityBase { - public required int Id { get; set; } + public int Id { get; set; } - public required bool IsDeleted { get; set; } + public bool IsDeleted { get; set; } = false; } diff --git a/AutobusApi.Domain/Entities/Employee.cs b/AutobusApi.Domain/Entities/Employee.cs index e2f738e..d6d1c75 100644 --- a/AutobusApi.Domain/Entities/Employee.cs +++ b/AutobusApi.Domain/Entities/Employee.cs @@ -5,7 +5,7 @@ namespace AutobusApi.Domain.Entities; public class Employee : EntityBase { - public string FisrtName { get; set; } = null!; + public string FirstName { get; set; } = null!; public string LastName { get; set; } = null!; diff --git a/AutobusApi.Domain/Entities/Ticket.cs b/AutobusApi.Domain/Entities/Ticket.cs index 0c09e71..8a06097 100644 --- a/AutobusApi.Domain/Entities/Ticket.cs +++ b/AutobusApi.Domain/Entities/Ticket.cs @@ -4,6 +4,10 @@ namespace AutobusApi.Domain.Entities; public class Ticket : EntityBase { + public int DepartureAddressId { get; set; } + + public int ArrivalAddressId { get; set; } + public int TicketGroupId { get; set; } public TicketGroup TicketGroup { get; set; } = null!; diff --git a/AutobusApi.Domain/Entities/TicketGroup.cs b/AutobusApi.Domain/Entities/TicketGroup.cs index 71cf18b..3b668d9 100644 --- a/AutobusApi.Domain/Entities/TicketGroup.cs +++ b/AutobusApi.Domain/Entities/TicketGroup.cs @@ -35,5 +35,30 @@ public class TicketGroup : EntityBase public TicketDocument TicketDocument { get; set; } = null!; - public ICollection Tickets { get; set; } = null!; + public ICollection Tickets { get; set; } = new List(); + + public DateTime GetDepartureDateTimeUtc() + { + var firstTicket = Tickets.OrderBy(t => t.VehicleEnrollment.GetDepartureDateTimeUtc(t.DepartureAddressId)).First(); + + return firstTicket.VehicleEnrollment.GetDepartureDateTimeUtc(firstTicket.DepartureAddressId); + } + + public DateTime GetArrivalDateTimeUtc() + { + var lastTicket = Tickets.OrderBy(t => t.VehicleEnrollment.GetDepartureDateTimeUtc(t.DepartureAddressId)).Last(); + + return lastTicket.VehicleEnrollment.GetArrivalDateTimeUtc(lastTicket.ArrivalAddressId); + } + + public TimeSpan GetTravelTime() + { + return GetArrivalDateTimeUtc() - GetDepartureDateTimeUtc(); + } + + public double GetCost() + { + return Tickets.Aggregate(0.0, (sum, ticket) => + sum + ticket.VehicleEnrollment.GetCost(ticket.DepartureAddressId, ticket.ArrivalAddressId)); + } } diff --git a/AutobusApi.Domain/Entities/VehicleEnrollment.cs b/AutobusApi.Domain/Entities/VehicleEnrollment.cs index 895e8cd..17e4e70 100644 --- a/AutobusApi.Domain/Entities/VehicleEnrollment.cs +++ b/AutobusApi.Domain/Entities/VehicleEnrollment.cs @@ -21,4 +21,73 @@ public class VehicleEnrollment : EntityBase public ICollection Tickets { get; set; } = null!; public ICollection Reviews { get; set; } = null!; + + public DateTime GetDepartureDateTimeUtc(int departureAddressId) + { + DateTime result = DepartureDateTimeUtc; + + var routeAddressesDetails = RouteAddressDetails + .OrderBy(rad => rad.RouteAddress.Order) + .Reverse() + .SkipWhile(rad => rad.RouteAddress.AddressId != departureAddressId) + .Reverse() + .ToArray(); + + // Ignore stop time on first address + routeAddressesDetails.First().CurrentAddressStopTime = TimeSpan.Zero; + // Ignore travel time to next address on last address + routeAddressesDetails.Last().TimeToNextAddress = TimeSpan.Zero; + + foreach (var routeAddressDetails in routeAddressesDetails) + { + result += routeAddressDetails.CurrentAddressStopTime; + result += routeAddressDetails.TimeToNextAddress; + } + + return result; + } + + public DateTime GetArrivalDateTimeUtc(int arrivalAddressId) + { + DateTime result = DepartureDateTimeUtc; + + var routeAddressesDetails = RouteAddressDetails + .OrderBy(rad => rad.RouteAddress.Order) + .TakeWhile(rad => rad.RouteAddress.AddressId != arrivalAddressId) + .ToArray(); + + // Ignore stop time on first station + routeAddressesDetails.First().CurrentAddressStopTime = TimeSpan.Zero; + + foreach (var routeAddressDetails in routeAddressesDetails) + { + result += routeAddressDetails.CurrentAddressStopTime; + result += routeAddressDetails.TimeToNextAddress; + } + + return result; + } + + public TimeSpan GetTravelTime(int departureAddressId, int arrivalAddressId) + { + return GetArrivalDateTimeUtc(arrivalAddressId) - GetDepartureDateTimeUtc(departureAddressId); + } + + public double GetCost(int departureAddressId, int arrivalAddressId) + { + double result = 0.0; + + var routeAddressesDetails = RouteAddressDetails + .OrderBy(rad => rad.RouteAddress.Order) + .SkipWhile(rad => rad.RouteAddress.AddressId != departureAddressId) + .TakeWhile(rad => rad.RouteAddress.AddressId != arrivalAddressId) + .ToArray(); + + foreach (var routeAddressDetails in routeAddressesDetails) + { + result += routeAddressDetails.CostToNextAddress; + } + + return result; + } } diff --git a/AutobusApi.Infrastructure/Data/Configurations/EmployeeConfiguration.cs b/AutobusApi.Infrastructure/Data/Configurations/EmployeeConfiguration.cs index 2247427..e98b40f 100644 --- a/AutobusApi.Infrastructure/Data/Configurations/EmployeeConfiguration.cs +++ b/AutobusApi.Infrastructure/Data/Configurations/EmployeeConfiguration.cs @@ -16,7 +16,7 @@ public class EmployeeConfiguration : EntityBaseConfiguration .HasKey(e => e.Id); builder - .Property(e => e.FisrtName) + .Property(e => e.FirstName) .HasColumnName("first_name") .HasColumnType("varchar(32)") .IsRequired(); diff --git a/AutobusApi.Infrastructure/Data/Migrations/20231201120217_AddDepartureAndArrivalAddressIdsToTicket.Designer.cs b/AutobusApi.Infrastructure/Data/Migrations/20231201120217_AddDepartureAndArrivalAddressIdsToTicket.Designer.cs new file mode 100644 index 0000000..01fb681 --- /dev/null +++ b/AutobusApi.Infrastructure/Data/Migrations/20231201120217_AddDepartureAndArrivalAddressIdsToTicket.Designer.cs @@ -0,0 +1,1196 @@ +// +using System; +using AutoubsApi.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AutobusApi.Infrastructure.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20231201120217_AddDepartureAndArrivalAddressIdsToTicket")] + partial class AddDepartureAndArrivalAddressIdsToTicket + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("domain") + .HasAnnotation("ProductVersion", "7.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CityId") + .HasColumnType("int") + .HasColumnName("city_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Location") + .IsRequired() + .HasColumnType("geography(point)") + .HasColumnName("location"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.Property("VehicleType") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("vehicle_type"); + + b.HasKey("Id"); + + b.HasIndex("CityId"); + + b.ToTable("addresses", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Carriage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("int") + .HasColumnName("capacity"); + + b.Property("HasOutlets") + .HasColumnType("boolean") + .HasColumnName("has_outlets"); + + b.Property("HasWiFi") + .HasColumnType("boolean") + .HasColumnName("has_wifi"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Number") + .HasColumnType("int") + .HasColumnName("number"); + + b.Property("Type") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("type"); + + b.HasKey("Id"); + + b.ToTable("carriages", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.City", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.Property("RegionId") + .HasColumnType("int") + .HasColumnName("region_id"); + + b.HasKey("Id"); + + b.HasIndex("RegionId"); + + b.ToTable("cities", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ContactEmail") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("contact_email"); + + b.Property("ContactPhoneNumber") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("contact_phone_number"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("LegalAddress") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("legal_address"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("companies", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("countries", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BirthDate") + .HasColumnType("date") + .HasColumnName("birth_date"); + + b.Property("EmployerCompanyId") + .HasColumnType("int") + .HasColumnName("employer_company_id"); + + b.Property("FisrtName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("first_name"); + + b.Property("IdentityId") + .HasColumnType("int") + .HasColumnName("identity_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("last_name"); + + b.Property("Patronymic") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("patronymic"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("sex"); + + b.HasKey("Id"); + + b.HasIndex("EmployerCompanyId"); + + b.HasIndex("IdentityId") + .IsUnique(); + + b.ToTable("employees", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.EmployeeDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EmployeeId") + .HasColumnType("int") + .HasColumnName("employee_id"); + + b.Property("Information") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("information"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Type") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("type"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.ToTable("employee_documents", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Region", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CountryId") + .HasColumnType("int") + .HasColumnName("country_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("CountryId"); + + b.ToTable("regions", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Review", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Comment") + .IsRequired() + .HasColumnType("varchar(128)") + .HasColumnName("comment"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("PostDateTimeUtc") + .HasColumnType("timestamptz") + .HasColumnName("post_timestamp_utc"); + + b.Property("Rating") + .HasColumnType("numeric(1,0)") + .HasColumnName("rating"); + + b.Property("UserId") + .HasColumnType("int") + .HasColumnName("user_id"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("int") + .HasColumnName("vehicle_enrollment_id"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("reviews", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Route", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.HasKey("Id"); + + b.ToTable("routes", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.RouteAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AddressId") + .HasColumnType("int") + .HasColumnName("address_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Order") + .HasColumnType("int") + .HasColumnName("order"); + + b.Property("RouteId") + .HasColumnType("int") + .HasColumnName("route_id"); + + b.HasKey("Id"); + + b.HasIndex("AddressId"); + + b.HasIndex("RouteId"); + + b.ToTable("route_addresses", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.RouteAddressDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CostToNextAddress") + .HasColumnType("numeric(16,4)") + .HasColumnName("cost_to_next_address"); + + b.Property("CurrentAddressStopTime") + .HasColumnType("interval") + .HasColumnName("current_address_stop_time"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("RouteAddressId") + .HasColumnType("int") + .HasColumnName("route_address_id"); + + b.Property("TimeToNextAddress") + .HasColumnType("interval") + .HasColumnName("time_to_next_address"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("int") + .HasColumnName("vehicle_enrollment_id"); + + b.HasKey("Id"); + + b.HasIndex("RouteAddressId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("route_address_details", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ArrivalAddressId") + .HasColumnType("integer"); + + b.Property("DepartureAddressId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("TicketGroupId") + .HasColumnType("int") + .HasColumnName("ticket_group_id"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("int") + .HasColumnName("vehicle_enrollment_id"); + + b.HasKey("Id"); + + b.HasIndex("TicketGroupId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("tickets", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TicketDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Information") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("information"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("TicketGroupId") + .HasColumnType("int") + .HasColumnName("ticket_group_id"); + + b.Property("Type") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("type"); + + b.HasKey("Id"); + + b.ToTable("ticket_documents", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TicketGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BuyerEmailAddress") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("buyer_email"); + + b.Property("BuyerFirstName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("buyer_first_name"); + + b.Property("BuyerLastName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("buyer_last_name"); + + b.Property("BuyerPhoneNumber") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("buyer_phone_number"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("IsReturned") + .HasColumnType("boolean") + .HasColumnName("is_returned"); + + b.Property("PassengerBirthDate") + .HasColumnType("date") + .HasColumnName("passenger_birth_date"); + + b.Property("PassengerFirstName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("passenger_first_name"); + + b.Property("PassengerLastName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("passenger_last_name"); + + b.Property("PassengerPatronymic") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("passenger_patronymic"); + + b.Property("PassengerSex") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("passenger_sex"); + + b.Property("PurchaseDateTimeUtc") + .HasColumnType("timestamptz") + .HasColumnName("purchase_timestamp_utc"); + + b.Property("TicketDocumentId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("TicketDocumentId") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("ticket_groups", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TrainCarriage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CarriageId") + .HasColumnType("int") + .HasColumnName("carriage_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("TrainId") + .HasColumnType("int") + .HasColumnName("train_id"); + + b.HasKey("Id"); + + b.HasIndex("TrainId"); + + b.ToTable("train_carriages", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IdentityId") + .HasColumnType("int") + .HasColumnName("identity_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.HasKey("Id"); + + b.HasIndex("IdentityId") + .IsUnique(); + + b.ToTable("users", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Vehicle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CompanyId") + .HasColumnType("int") + .HasColumnName("company_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId"); + + b.ToTable("vehicles", "domain"); + + b.UseTptMappingStrategy(); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.VehicleEnrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DepartureDateTimeUtc") + .HasColumnType("timestamptz") + .HasColumnName("departure_timestamp_utc"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("RouteId") + .HasColumnType("int") + .HasColumnName("route_id"); + + b.Property("VehicleId") + .HasColumnType("int") + .HasColumnName("vehicle_id"); + + b.HasKey("Id"); + + b.HasIndex("RouteId"); + + b.HasIndex("VehicleId"); + + b.ToTable("vehicle_enrollments", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.VehicleEnrollmentEmployee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EmployeeId") + .HasColumnType("int") + .HasColumnName("employee_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("int") + .HasColumnName("vehicle_enrollment_id"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("vehicle_enrollment_employees", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Aircraft", b => + { + b.HasBaseType("AutobusApi.Domain.Entities.Vehicle"); + + b.Property("Capacity") + .HasColumnType("int") + .HasColumnName("capacity"); + + b.Property("HasMultimedia") + .HasColumnType("boolean") + .HasColumnName("has_multimedia"); + + b.Property("HasWiFi") + .HasColumnType("boolean") + .HasColumnName("has_wifi"); + + b.Property("Model") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("model"); + + b.Property("Number") + .IsRequired() + .HasColumnType("varchar(8)") + .HasColumnName("number"); + + b.ToTable("aircrafts", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Bus", b => + { + b.HasBaseType("AutobusApi.Domain.Entities.Vehicle"); + + b.Property("Capacity") + .HasColumnType("int") + .HasColumnName("capacity"); + + b.Property("HasClimateControl") + .HasColumnType("boolean") + .HasColumnName("has_climate_control"); + + b.Property("HasMultimedia") + .HasColumnType("boolean") + .HasColumnName("has_multimedia"); + + b.Property("HasOutlets") + .HasColumnType("boolean") + .HasColumnName("has_outlets"); + + b.Property("HasWC") + .HasColumnType("boolean") + .HasColumnName("has_wc"); + + b.Property("HasWiFi") + .HasColumnType("boolean") + .HasColumnName("has_wifi"); + + b.Property("Model") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("model"); + + b.Property("Number") + .IsRequired() + .HasColumnType("varchar(8)") + .HasColumnName("number"); + + b.ToTable("buses", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Train", b => + { + b.HasBaseType("AutobusApi.Domain.Entities.Vehicle"); + + b.Property("Number") + .IsRequired() + .HasColumnType("varchar(8)") + .HasColumnName("number"); + + b.ToTable("trains", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Address", b => + { + b.HasOne("AutobusApi.Domain.Entities.City", "City") + .WithMany("Addresses") + .HasForeignKey("CityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_addresses_city_id"); + + b.Navigation("City"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.City", b => + { + b.HasOne("AutobusApi.Domain.Entities.Region", "Region") + .WithMany("Cities") + .HasForeignKey("RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_cities_regions_regionId"); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Employee", b => + { + b.HasOne("AutobusApi.Domain.Entities.Company", "EmployerCompany") + .WithMany("Employees") + .HasForeignKey("EmployerCompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_employees_companies_employerCompanyId"); + + b.Navigation("EmployerCompany"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.EmployeeDocument", b => + { + b.HasOne("AutobusApi.Domain.Entities.Employee", "Employee") + .WithMany("Documents") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_employeeDocuments_employees_employeeId"); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Region", b => + { + b.HasOne("AutobusApi.Domain.Entities.Country", "Country") + .WithMany("Regions") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_regions_coutries_countryId"); + + b.Navigation("Country"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Review", b => + { + b.HasOne("AutobusApi.Domain.Entities.User", "User") + .WithMany("Reviews") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_reviews_users_userId"); + + b.HasOne("AutobusApi.Domain.Entities.VehicleEnrollment", "VehicleEnrollment") + .WithMany("Reviews") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_reviews_vehicleEnrollments_vehicleEnrollmentId"); + + b.Navigation("User"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.RouteAddress", b => + { + b.HasOne("AutobusApi.Domain.Entities.Address", "Address") + .WithMany("RouteAddresses") + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_routeAddresses_addresses_addressId"); + + b.HasOne("AutobusApi.Domain.Entities.Route", "Route") + .WithMany("RouteAddresses") + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_routeAddresses_routes_routeId"); + + b.Navigation("Address"); + + b.Navigation("Route"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.RouteAddressDetails", b => + { + b.HasOne("AutobusApi.Domain.Entities.RouteAddress", "RouteAddress") + .WithMany("RouteAddressDetails") + .HasForeignKey("RouteAddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_routeAddressDetails_routeAddress_routeAddressId"); + + b.HasOne("AutobusApi.Domain.Entities.VehicleEnrollment", "VehicleEnrollment") + .WithMany("RouteAddressDetails") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_routeAddressDetails_vehicleEnrollments_vehicleEnrollmentId"); + + b.Navigation("RouteAddress"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Ticket", b => + { + b.HasOne("AutobusApi.Domain.Entities.TicketGroup", "TicketGroup") + .WithMany("Tickets") + .HasForeignKey("TicketGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tickets_ticketGroups_ticketGroupId"); + + b.HasOne("AutobusApi.Domain.Entities.VehicleEnrollment", "VehicleEnrollment") + .WithMany("Tickets") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tickets_vehicleEnrollments_vehicleEnrollmentId"); + + b.Navigation("TicketGroup"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TicketGroup", b => + { + b.HasOne("AutobusApi.Domain.Entities.TicketDocument", "TicketDocument") + .WithOne("TicketGroup") + .HasForeignKey("AutobusApi.Domain.Entities.TicketGroup", "TicketDocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_ticketGroups_ticketDocuments_ticketDocumentId"); + + b.HasOne("AutobusApi.Domain.Entities.User", "User") + .WithMany("TicketGroups") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("fk_ticketGroups_users_userId"); + + b.Navigation("TicketDocument"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TrainCarriage", b => + { + b.HasOne("AutobusApi.Domain.Entities.Carriage", "Carriage") + .WithMany("TrainCarriage") + .HasForeignKey("TrainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_trainCarriages_trains_carriageId"); + + b.HasOne("AutobusApi.Domain.Entities.Train", "Train") + .WithMany("TrainCarriage") + .HasForeignKey("TrainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_trainCarriages_trains_trainId"); + + b.Navigation("Carriage"); + + b.Navigation("Train"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Vehicle", b => + { + b.HasOne("AutobusApi.Domain.Entities.Company", "Company") + .WithMany("Vehicles") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicles_companies_companyId"); + + b.Navigation("Company"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.VehicleEnrollment", b => + { + b.HasOne("AutobusApi.Domain.Entities.Route", "Route") + .WithMany("VehicleEnrollments") + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicleEnrollments_routes_routeId"); + + b.HasOne("AutobusApi.Domain.Entities.Vehicle", "Vehicle") + .WithMany("Enrollments") + .HasForeignKey("VehicleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicleEnrollments_vehicles_vehicleId"); + + b.Navigation("Route"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.VehicleEnrollmentEmployee", b => + { + b.HasOne("AutobusApi.Domain.Entities.Employee", "Employee") + .WithMany("Shifts") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicleEnrollmentEmployees_employees_employeeId"); + + b.HasOne("AutobusApi.Domain.Entities.VehicleEnrollment", "VehicleEnrollment") + .WithMany("Crew") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicleEnrollmentEmployees_vehicleEnrollments_vehicleEnrollmentId"); + + b.Navigation("Employee"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Aircraft", b => + { + b.HasOne("AutobusApi.Domain.Entities.Vehicle", "Vehicle") + .WithOne() + .HasForeignKey("AutobusApi.Domain.Entities.Aircraft", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_aircrafts_vehicles_id"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Bus", b => + { + b.HasOne("AutobusApi.Domain.Entities.Vehicle", "Vehicle") + .WithOne() + .HasForeignKey("AutobusApi.Domain.Entities.Bus", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_buses_vehicles_id"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Train", b => + { + b.HasOne("AutobusApi.Domain.Entities.Vehicle", "Vehicle") + .WithOne() + .HasForeignKey("AutobusApi.Domain.Entities.Train", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_trains_vehicles_id"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Address", b => + { + b.Navigation("RouteAddresses"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Carriage", b => + { + b.Navigation("TrainCarriage"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.City", b => + { + b.Navigation("Addresses"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Company", b => + { + b.Navigation("Employees"); + + b.Navigation("Vehicles"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Country", b => + { + b.Navigation("Regions"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Employee", b => + { + b.Navigation("Documents"); + + b.Navigation("Shifts"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Region", b => + { + b.Navigation("Cities"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Route", b => + { + b.Navigation("RouteAddresses"); + + b.Navigation("VehicleEnrollments"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.RouteAddress", b => + { + b.Navigation("RouteAddressDetails"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TicketDocument", b => + { + b.Navigation("TicketGroup") + .IsRequired(); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TicketGroup", b => + { + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.User", b => + { + b.Navigation("Reviews"); + + b.Navigation("TicketGroups"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Vehicle", b => + { + b.Navigation("Enrollments"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.VehicleEnrollment", b => + { + b.Navigation("Crew"); + + b.Navigation("Reviews"); + + b.Navigation("RouteAddressDetails"); + + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Train", b => + { + b.Navigation("TrainCarriage"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AutobusApi.Infrastructure/Data/Migrations/20231201120217_AddDepartureAndArrivalAddressIdsToTicket.cs b/AutobusApi.Infrastructure/Data/Migrations/20231201120217_AddDepartureAndArrivalAddressIdsToTicket.cs new file mode 100644 index 0000000..00e0184 --- /dev/null +++ b/AutobusApi.Infrastructure/Data/Migrations/20231201120217_AddDepartureAndArrivalAddressIdsToTicket.cs @@ -0,0 +1,44 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AutobusApi.Infrastructure.Data.Migrations +{ + /// + public partial class AddDepartureAndArrivalAddressIdsToTicket : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "ArrivalAddressId", + schema: "domain", + table: "tickets", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn( + name: "DepartureAddressId", + schema: "domain", + table: "tickets", + type: "integer", + nullable: false, + defaultValue: 0); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "ArrivalAddressId", + schema: "domain", + table: "tickets"); + + migrationBuilder.DropColumn( + name: "DepartureAddressId", + schema: "domain", + table: "tickets"); + } + } +} diff --git a/AutobusApi.Infrastructure/Data/Migrations/20231202194435_FixTypoInEmployeeFirstNameField.Designer.cs b/AutobusApi.Infrastructure/Data/Migrations/20231202194435_FixTypoInEmployeeFirstNameField.Designer.cs new file mode 100644 index 0000000..ff88ddb --- /dev/null +++ b/AutobusApi.Infrastructure/Data/Migrations/20231202194435_FixTypoInEmployeeFirstNameField.Designer.cs @@ -0,0 +1,1196 @@ +// +using System; +using AutoubsApi.Infrastructure.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using NetTopologySuite.Geometries; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace AutobusApi.Infrastructure.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20231202194435_FixTypoInEmployeeFirstNameField")] + partial class FixTypoInEmployeeFirstNameField + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("domain") + .HasAnnotation("ProductVersion", "7.0.14") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CityId") + .HasColumnType("int") + .HasColumnName("city_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Location") + .IsRequired() + .HasColumnType("geography(point)") + .HasColumnName("location"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.Property("VehicleType") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("vehicle_type"); + + b.HasKey("Id"); + + b.HasIndex("CityId"); + + b.ToTable("addresses", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Carriage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("int") + .HasColumnName("capacity"); + + b.Property("HasOutlets") + .HasColumnType("boolean") + .HasColumnName("has_outlets"); + + b.Property("HasWiFi") + .HasColumnType("boolean") + .HasColumnName("has_wifi"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Number") + .HasColumnType("int") + .HasColumnName("number"); + + b.Property("Type") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("type"); + + b.HasKey("Id"); + + b.ToTable("carriages", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.City", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.Property("RegionId") + .HasColumnType("int") + .HasColumnName("region_id"); + + b.HasKey("Id"); + + b.HasIndex("RegionId"); + + b.ToTable("cities", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ContactEmail") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("contact_email"); + + b.Property("ContactPhoneNumber") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("contact_phone_number"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("LegalAddress") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("legal_address"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("companies", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.ToTable("countries", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Employee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BirthDate") + .HasColumnType("date") + .HasColumnName("birth_date"); + + b.Property("EmployerCompanyId") + .HasColumnType("int") + .HasColumnName("employer_company_id"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("first_name"); + + b.Property("IdentityId") + .HasColumnType("int") + .HasColumnName("identity_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("last_name"); + + b.Property("Patronymic") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("patronymic"); + + b.Property("Sex") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("sex"); + + b.HasKey("Id"); + + b.HasIndex("EmployerCompanyId"); + + b.HasIndex("IdentityId") + .IsUnique(); + + b.ToTable("employees", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.EmployeeDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EmployeeId") + .HasColumnType("int") + .HasColumnName("employee_id"); + + b.Property("Information") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("information"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Type") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("type"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.ToTable("employee_documents", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Region", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CountryId") + .HasColumnType("int") + .HasColumnName("country_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("name"); + + b.HasKey("Id"); + + b.HasIndex("CountryId"); + + b.ToTable("regions", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Review", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Comment") + .IsRequired() + .HasColumnType("varchar(128)") + .HasColumnName("comment"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("PostDateTimeUtc") + .HasColumnType("timestamptz") + .HasColumnName("post_timestamp_utc"); + + b.Property("Rating") + .HasColumnType("numeric(1,0)") + .HasColumnName("rating"); + + b.Property("UserId") + .HasColumnType("int") + .HasColumnName("user_id"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("int") + .HasColumnName("vehicle_enrollment_id"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("reviews", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Route", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.HasKey("Id"); + + b.ToTable("routes", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.RouteAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AddressId") + .HasColumnType("int") + .HasColumnName("address_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("Order") + .HasColumnType("int") + .HasColumnName("order"); + + b.Property("RouteId") + .HasColumnType("int") + .HasColumnName("route_id"); + + b.HasKey("Id"); + + b.HasIndex("AddressId"); + + b.HasIndex("RouteId"); + + b.ToTable("route_addresses", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.RouteAddressDetails", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CostToNextAddress") + .HasColumnType("numeric(16,4)") + .HasColumnName("cost_to_next_address"); + + b.Property("CurrentAddressStopTime") + .HasColumnType("interval") + .HasColumnName("current_address_stop_time"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("RouteAddressId") + .HasColumnType("int") + .HasColumnName("route_address_id"); + + b.Property("TimeToNextAddress") + .HasColumnType("interval") + .HasColumnName("time_to_next_address"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("int") + .HasColumnName("vehicle_enrollment_id"); + + b.HasKey("Id"); + + b.HasIndex("RouteAddressId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("route_address_details", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ArrivalAddressId") + .HasColumnType("integer"); + + b.Property("DepartureAddressId") + .HasColumnType("integer"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("TicketGroupId") + .HasColumnType("int") + .HasColumnName("ticket_group_id"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("int") + .HasColumnName("vehicle_enrollment_id"); + + b.HasKey("Id"); + + b.HasIndex("TicketGroupId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("tickets", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TicketDocument", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Information") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("information"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("TicketGroupId") + .HasColumnType("int") + .HasColumnName("ticket_group_id"); + + b.Property("Type") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("type"); + + b.HasKey("Id"); + + b.ToTable("ticket_documents", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TicketGroup", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BuyerEmailAddress") + .IsRequired() + .HasColumnType("varchar(256)") + .HasColumnName("buyer_email"); + + b.Property("BuyerFirstName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("buyer_first_name"); + + b.Property("BuyerLastName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("buyer_last_name"); + + b.Property("BuyerPhoneNumber") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("buyer_phone_number"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("IsReturned") + .HasColumnType("boolean") + .HasColumnName("is_returned"); + + b.Property("PassengerBirthDate") + .HasColumnType("date") + .HasColumnName("passenger_birth_date"); + + b.Property("PassengerFirstName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("passenger_first_name"); + + b.Property("PassengerLastName") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("passenger_last_name"); + + b.Property("PassengerPatronymic") + .IsRequired() + .HasColumnType("varchar(32)") + .HasColumnName("passenger_patronymic"); + + b.Property("PassengerSex") + .IsRequired() + .HasColumnType("varchar(16)") + .HasColumnName("passenger_sex"); + + b.Property("PurchaseDateTimeUtc") + .HasColumnType("timestamptz") + .HasColumnName("purchase_timestamp_utc"); + + b.Property("TicketDocumentId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int") + .HasColumnName("user_id"); + + b.HasKey("Id"); + + b.HasIndex("TicketDocumentId") + .IsUnique(); + + b.HasIndex("UserId"); + + b.ToTable("ticket_groups", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TrainCarriage", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CarriageId") + .HasColumnType("int") + .HasColumnName("carriage_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("TrainId") + .HasColumnType("int") + .HasColumnName("train_id"); + + b.HasKey("Id"); + + b.HasIndex("TrainId"); + + b.ToTable("train_carriages", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IdentityId") + .HasColumnType("int") + .HasColumnName("identity_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.HasKey("Id"); + + b.HasIndex("IdentityId") + .IsUnique(); + + b.ToTable("users", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Vehicle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CompanyId") + .HasColumnType("int") + .HasColumnName("company_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId"); + + b.ToTable("vehicles", "domain"); + + b.UseTptMappingStrategy(); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.VehicleEnrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("DepartureDateTimeUtc") + .HasColumnType("timestamptz") + .HasColumnName("departure_timestamp_utc"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("RouteId") + .HasColumnType("int") + .HasColumnName("route_id"); + + b.Property("VehicleId") + .HasColumnType("int") + .HasColumnName("vehicle_id"); + + b.HasKey("Id"); + + b.HasIndex("RouteId"); + + b.HasIndex("VehicleId"); + + b.ToTable("vehicle_enrollments", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.VehicleEnrollmentEmployee", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EmployeeId") + .HasColumnType("int") + .HasColumnName("employee_id"); + + b.Property("IsDeleted") + .HasColumnType("boolean") + .HasColumnName("is_deleted"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("int") + .HasColumnName("vehicle_enrollment_id"); + + b.HasKey("Id"); + + b.HasIndex("EmployeeId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("vehicle_enrollment_employees", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Aircraft", b => + { + b.HasBaseType("AutobusApi.Domain.Entities.Vehicle"); + + b.Property("Capacity") + .HasColumnType("int") + .HasColumnName("capacity"); + + b.Property("HasMultimedia") + .HasColumnType("boolean") + .HasColumnName("has_multimedia"); + + b.Property("HasWiFi") + .HasColumnType("boolean") + .HasColumnName("has_wifi"); + + b.Property("Model") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("model"); + + b.Property("Number") + .IsRequired() + .HasColumnType("varchar(8)") + .HasColumnName("number"); + + b.ToTable("aircrafts", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Bus", b => + { + b.HasBaseType("AutobusApi.Domain.Entities.Vehicle"); + + b.Property("Capacity") + .HasColumnType("int") + .HasColumnName("capacity"); + + b.Property("HasClimateControl") + .HasColumnType("boolean") + .HasColumnName("has_climate_control"); + + b.Property("HasMultimedia") + .HasColumnType("boolean") + .HasColumnName("has_multimedia"); + + b.Property("HasOutlets") + .HasColumnType("boolean") + .HasColumnName("has_outlets"); + + b.Property("HasWC") + .HasColumnType("boolean") + .HasColumnName("has_wc"); + + b.Property("HasWiFi") + .HasColumnType("boolean") + .HasColumnName("has_wifi"); + + b.Property("Model") + .IsRequired() + .HasColumnType("varchar(64)") + .HasColumnName("model"); + + b.Property("Number") + .IsRequired() + .HasColumnType("varchar(8)") + .HasColumnName("number"); + + b.ToTable("buses", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Train", b => + { + b.HasBaseType("AutobusApi.Domain.Entities.Vehicle"); + + b.Property("Number") + .IsRequired() + .HasColumnType("varchar(8)") + .HasColumnName("number"); + + b.ToTable("trains", "domain"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Address", b => + { + b.HasOne("AutobusApi.Domain.Entities.City", "City") + .WithMany("Addresses") + .HasForeignKey("CityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_addresses_city_id"); + + b.Navigation("City"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.City", b => + { + b.HasOne("AutobusApi.Domain.Entities.Region", "Region") + .WithMany("Cities") + .HasForeignKey("RegionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_cities_regions_regionId"); + + b.Navigation("Region"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Employee", b => + { + b.HasOne("AutobusApi.Domain.Entities.Company", "EmployerCompany") + .WithMany("Employees") + .HasForeignKey("EmployerCompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_employees_companies_employerCompanyId"); + + b.Navigation("EmployerCompany"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.EmployeeDocument", b => + { + b.HasOne("AutobusApi.Domain.Entities.Employee", "Employee") + .WithMany("Documents") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_employeeDocuments_employees_employeeId"); + + b.Navigation("Employee"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Region", b => + { + b.HasOne("AutobusApi.Domain.Entities.Country", "Country") + .WithMany("Regions") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_regions_coutries_countryId"); + + b.Navigation("Country"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Review", b => + { + b.HasOne("AutobusApi.Domain.Entities.User", "User") + .WithMany("Reviews") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_reviews_users_userId"); + + b.HasOne("AutobusApi.Domain.Entities.VehicleEnrollment", "VehicleEnrollment") + .WithMany("Reviews") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_reviews_vehicleEnrollments_vehicleEnrollmentId"); + + b.Navigation("User"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.RouteAddress", b => + { + b.HasOne("AutobusApi.Domain.Entities.Address", "Address") + .WithMany("RouteAddresses") + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_routeAddresses_addresses_addressId"); + + b.HasOne("AutobusApi.Domain.Entities.Route", "Route") + .WithMany("RouteAddresses") + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_routeAddresses_routes_routeId"); + + b.Navigation("Address"); + + b.Navigation("Route"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.RouteAddressDetails", b => + { + b.HasOne("AutobusApi.Domain.Entities.RouteAddress", "RouteAddress") + .WithMany("RouteAddressDetails") + .HasForeignKey("RouteAddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_routeAddressDetails_routeAddress_routeAddressId"); + + b.HasOne("AutobusApi.Domain.Entities.VehicleEnrollment", "VehicleEnrollment") + .WithMany("RouteAddressDetails") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_routeAddressDetails_vehicleEnrollments_vehicleEnrollmentId"); + + b.Navigation("RouteAddress"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Ticket", b => + { + b.HasOne("AutobusApi.Domain.Entities.TicketGroup", "TicketGroup") + .WithMany("Tickets") + .HasForeignKey("TicketGroupId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tickets_ticketGroups_ticketGroupId"); + + b.HasOne("AutobusApi.Domain.Entities.VehicleEnrollment", "VehicleEnrollment") + .WithMany("Tickets") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_tickets_vehicleEnrollments_vehicleEnrollmentId"); + + b.Navigation("TicketGroup"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TicketGroup", b => + { + b.HasOne("AutobusApi.Domain.Entities.TicketDocument", "TicketDocument") + .WithOne("TicketGroup") + .HasForeignKey("AutobusApi.Domain.Entities.TicketGroup", "TicketDocumentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_ticketGroups_ticketDocuments_ticketDocumentId"); + + b.HasOne("AutobusApi.Domain.Entities.User", "User") + .WithMany("TicketGroups") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("fk_ticketGroups_users_userId"); + + b.Navigation("TicketDocument"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TrainCarriage", b => + { + b.HasOne("AutobusApi.Domain.Entities.Carriage", "Carriage") + .WithMany("TrainCarriage") + .HasForeignKey("TrainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_trainCarriages_trains_carriageId"); + + b.HasOne("AutobusApi.Domain.Entities.Train", "Train") + .WithMany("TrainCarriage") + .HasForeignKey("TrainId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_trainCarriages_trains_trainId"); + + b.Navigation("Carriage"); + + b.Navigation("Train"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Vehicle", b => + { + b.HasOne("AutobusApi.Domain.Entities.Company", "Company") + .WithMany("Vehicles") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicles_companies_companyId"); + + b.Navigation("Company"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.VehicleEnrollment", b => + { + b.HasOne("AutobusApi.Domain.Entities.Route", "Route") + .WithMany("VehicleEnrollments") + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicleEnrollments_routes_routeId"); + + b.HasOne("AutobusApi.Domain.Entities.Vehicle", "Vehicle") + .WithMany("Enrollments") + .HasForeignKey("VehicleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicleEnrollments_vehicles_vehicleId"); + + b.Navigation("Route"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.VehicleEnrollmentEmployee", b => + { + b.HasOne("AutobusApi.Domain.Entities.Employee", "Employee") + .WithMany("Shifts") + .HasForeignKey("EmployeeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicleEnrollmentEmployees_employees_employeeId"); + + b.HasOne("AutobusApi.Domain.Entities.VehicleEnrollment", "VehicleEnrollment") + .WithMany("Crew") + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_vehicleEnrollmentEmployees_vehicleEnrollments_vehicleEnrollmentId"); + + b.Navigation("Employee"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Aircraft", b => + { + b.HasOne("AutobusApi.Domain.Entities.Vehicle", "Vehicle") + .WithOne() + .HasForeignKey("AutobusApi.Domain.Entities.Aircraft", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_aircrafts_vehicles_id"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Bus", b => + { + b.HasOne("AutobusApi.Domain.Entities.Vehicle", "Vehicle") + .WithOne() + .HasForeignKey("AutobusApi.Domain.Entities.Bus", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_buses_vehicles_id"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Train", b => + { + b.HasOne("AutobusApi.Domain.Entities.Vehicle", "Vehicle") + .WithOne() + .HasForeignKey("AutobusApi.Domain.Entities.Train", "Id") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("fk_trains_vehicles_id"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Address", b => + { + b.Navigation("RouteAddresses"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Carriage", b => + { + b.Navigation("TrainCarriage"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.City", b => + { + b.Navigation("Addresses"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Company", b => + { + b.Navigation("Employees"); + + b.Navigation("Vehicles"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Country", b => + { + b.Navigation("Regions"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Employee", b => + { + b.Navigation("Documents"); + + b.Navigation("Shifts"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Region", b => + { + b.Navigation("Cities"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Route", b => + { + b.Navigation("RouteAddresses"); + + b.Navigation("VehicleEnrollments"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.RouteAddress", b => + { + b.Navigation("RouteAddressDetails"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TicketDocument", b => + { + b.Navigation("TicketGroup") + .IsRequired(); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.TicketGroup", b => + { + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.User", b => + { + b.Navigation("Reviews"); + + b.Navigation("TicketGroups"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Vehicle", b => + { + b.Navigation("Enrollments"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.VehicleEnrollment", b => + { + b.Navigation("Crew"); + + b.Navigation("Reviews"); + + b.Navigation("RouteAddressDetails"); + + b.Navigation("Tickets"); + }); + + modelBuilder.Entity("AutobusApi.Domain.Entities.Train", b => + { + b.Navigation("TrainCarriage"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/AutobusApi.Infrastructure/Data/Migrations/20231202194435_FixTypoInEmployeeFirstNameField.cs b/AutobusApi.Infrastructure/Data/Migrations/20231202194435_FixTypoInEmployeeFirstNameField.cs new file mode 100644 index 0000000..af7354d --- /dev/null +++ b/AutobusApi.Infrastructure/Data/Migrations/20231202194435_FixTypoInEmployeeFirstNameField.cs @@ -0,0 +1,22 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace AutobusApi.Infrastructure.Data.Migrations +{ + /// + public partial class FixTypoInEmployeeFirstNameField : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/AutobusApi.Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/AutobusApi.Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs index 9f888fa..4534a74 100644 --- a/AutobusApi.Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/AutobusApi.Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -19,7 +19,7 @@ namespace AutobusApi.Infrastructure.Data.Migrations #pragma warning disable 612, 618 modelBuilder .HasDefaultSchema("domain") - .HasAnnotation("ProductVersion", "7.0.13") + .HasAnnotation("ProductVersion", "7.0.14") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresExtension(modelBuilder, "postgis"); @@ -210,7 +210,7 @@ namespace AutobusApi.Infrastructure.Data.Migrations .HasColumnType("int") .HasColumnName("employer_company_id"); - b.Property("FisrtName") + b.Property("FirstName") .IsRequired() .HasColumnType("varchar(32)") .HasColumnName("first_name"); @@ -457,6 +457,12 @@ namespace AutobusApi.Infrastructure.Data.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("ArrivalAddressId") + .HasColumnType("integer"); + + b.Property("DepartureAddressId") + .HasColumnType("integer"); + b.Property("IsDeleted") .HasColumnType("boolean") .HasColumnName("is_deleted"); diff --git a/AutobusApi.Infrastructure/DbInitializer.cs b/AutobusApi.Infrastructure/DbInitializer.cs index 69d1561..3a2fcae 100644 --- a/AutobusApi.Infrastructure/DbInitializer.cs +++ b/AutobusApi.Infrastructure/DbInitializer.cs @@ -3,6 +3,7 @@ using AutobusApi.Infrastructure.Identity; using AutoubsApi.Infrastructure.Data; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; +using Npgsql; namespace AutobusApi.Infrastructure; @@ -18,6 +19,11 @@ public static class DbInitializer if (domainAppliedMigrations.Count() == 0) { dbContext.Database.Migrate(); + + // https://github.com/npgsql/efcore.pg/issues/292 + dbContext.Database.OpenConnection(); + ((NpgsqlConnection)dbContext.Database.GetDbConnection()).ReloadTypes(); + InitializeDomain(dbContext); } @@ -26,15 +32,15 @@ public static class DbInitializer identityDbContext.Database.Migrate(); InitializeIdentity(identityDbContext); } - } - else - { - dbContext.Database.EnsureCreated(); - InitializeDomain(dbContext); - identityDbContext.Database.EnsureCreated(); - InitializeIdentity(identityDbContext); + return; } + + dbContext.Database.EnsureCreated(); + InitializeDomain(dbContext); + + identityDbContext.Database.EnsureCreated(); + InitializeIdentity(identityDbContext); } private static void InitializeDomain(ApplicationDbContext dbContext) diff --git a/AutobusApi.Infrastructure/Services/IdentityService.cs b/AutobusApi.Infrastructure/Services/IdentityService.cs index ffbb6ea..d45263e 100644 --- a/AutobusApi.Infrastructure/Services/IdentityService.cs +++ b/AutobusApi.Infrastructure/Services/IdentityService.cs @@ -27,7 +27,7 @@ public class IdentityService : IIdentityService _configuration = configuration; } - public async Task RegisterAsync( + public async Task RegisterAsync( string email, string password, CancellationToken cancellationToken) @@ -46,6 +46,8 @@ public class IdentityService : IIdentityService var createUserResult = await _userManager.CreateAsync(newUser, password); var addToRoleResult = await _userManager.AddToRoleAsync(newUser, IdentityRoles.User.ToString()); + + return (await _userManager.FindByEmailAsync(email)).Id; } public async Task LoginAsync( diff --git a/AutobusApi.UnitTests/Common/Mappings/MappingTests.cs b/AutobusApi.UnitTests/Common/Mappings/MappingTests.cs index 73cf8c1..66d62bc 100644 --- a/AutobusApi.UnitTests/Common/Mappings/MappingTests.cs +++ b/AutobusApi.UnitTests/Common/Mappings/MappingTests.cs @@ -1,4 +1,14 @@ -using AutobusApi.Application.RouteSearch; +using AutobusApi.Application.Addresses.Queries; +using AutobusApi.Application.Buses.Queries; +using AutobusApi.Application.Cities.Queries; +using AutobusApi.Application.Companies.Queries; +using AutobusApi.Application.Countries.Queries; +using AutobusApi.Application.Employees.Queries; +using AutobusApi.Application.Regions.Queries; +using AutobusApi.Application.Routes.Queries; +using AutobusApi.Application.TicketGroups.Queries; +using AutobusApi.Application.VehicleEnrollments.Queries; +using AutobusApi.Domain.Entities; using AutoMapper; namespace AutobusApi.UnitTests.Common.Mappings; @@ -21,6 +31,20 @@ public class MappingTests : IClassFixture } [Theory] + [InlineData(typeof(Country), typeof(CountryDto))] + [InlineData(typeof(Region), typeof(RegionDto))] + [InlineData(typeof(City), typeof(CityDto))] + [InlineData(typeof(Address), typeof(AddressDto))] + [InlineData(typeof(Route), typeof(RouteDto))] + [InlineData(typeof(RouteAddress), typeof(RouteAddressDto))] + [InlineData(typeof(VehicleEnrollment), typeof(VehicleEnrollmentDto))] + [InlineData(typeof(RouteAddressDetails), typeof(RouteAddressDetailsDto))] + // [InlineData(typeof(TicketGroup), typeof(TicketGroupDto))] + [InlineData(typeof(Ticket), typeof(TicketDto))] + [InlineData(typeof(Company), typeof(CompanyDto))] + [InlineData(typeof(Employee), typeof(EmployeeDto))] + [InlineData(typeof(EmployeeDocument), typeof(EmployeeDocumentDto))] + [InlineData(typeof(Bus), typeof(BusDto))] // [InlineData(typeof(), typeof())] public void ShouldSupportMappingFromSourceDoDestination(Type source, Type destination) { diff --git a/script.sh b/script.sh new file mode 100755 index 0000000..4d495e7 --- /dev/null +++ b/script.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +echo -n 'Country: '; read Country +echo -n 'Countries: '; read Countries + +country=$(echo ${Country} | sed 's/[A-Z]/\L&/g') +countries=$(echo ${Countries} | sed 's/[A-Z]/\L&/g') + +rm -Rf ./AutobusApi.Application/${Countries}/* + +cp -r ./AutobusApi.Application/Countries/* ./AutobusApi.Application/${Countries}/ + +find ./AutobusApi.Application/${Countries} -type f | xargs -I {} perl -0777 -i -pe "s/Countries/${Countries}/g" {} +find ./AutobusApi.Application/${Countries} -type f | xargs -I {} perl -0777 -i -pe "s/Country/${Country}/g" {} +find ./AutobusApi.Application/${Countries} -type f | xargs -I {} perl -0777 -i -pe "s/countries/${countries}/g" {} +find ./AutobusApi.Application/${Countries} -type f | xargs -I {} perl -0777 -i -pe "s/country/${country}/g" {} + +for dir in $(find ./AutobusApi.Application/${Countries} -type d | grep Country); do + mv ${dir} $(echo ${dir} | sed -e "s/Country/${Country}/g") +done + +for dir in $(find ./AutobusApi.Application/${Countries} -type d | grep Countries); do + mv ${dir} $(echo ${dir} | sed -e "s/Countries/${Countries}/g") +done + +for file in $(find ./AutobusApi.Application/${Countries} -type f | grep Country); do + mv ${file} $(echo ${file} | sed -e "s/Country/${Country}/g") +done + +for file in $(find ./AutobusApi.Application/${Countries} -type f | grep Countries); do + mv ${file} $(echo ${file} | sed -e "s/Countries/${Countries}/g") +done + +cp ./AutobusApi.Api/Controllers/CountryController.cs ./AutobusApi.Api/Controllers/${Country}Controller.cs +sed -i -e "s/Country/${Country}/g" ./AutobusApi.Api/Controllers/${Country}Controller.cs +sed -i -e "s/Countries/${Countries}/g" ./AutobusApi.Api/Controllers/${Country}Controller.cs +sed -i -e "s/country/${country}/g" ./AutobusApi.Api/Controllers/${Country}Controller.cs +sed -i -e "s/countries/${countries}/g" ./AutobusApi.Api/Controllers/${Country}Controller.cs