From 6f2fc702df03bd6e104652ee98fa8bd8f9540d9c Mon Sep 17 00:00:00 2001 From: cuqmbr Date: Sun, 13 Nov 2022 13:36:07 +0200 Subject: [PATCH] chore: add CRUD api endpoints for all remaining entities exept from AspNetUser --- Server/Configurations/MapperInitializer.cs | 31 + .../CompanyManagementController.cs | 101 +++ .../Controllers/ReviewManagementController.cs | 101 +++ .../RouteAddressManagementController.cs | 101 +++ .../Controllers/RouteManagementController.cs | 101 +++ Server/Controllers/TestingController.cs | 6 + .../Controllers/TicketManagementController.cs | 101 +++ .../VehicleEnrollmentManagementController.cs | 102 +++ .../VehicleManagementController.cs | 101 +++ Server/Data/ApplicationDbContext.cs | 14 - ...omposite_Keys_With_Single_Ones.Designer.cs | 798 ++++++++++++++++++ ...Replace_Composite_Keys_With_Single_Ones.cs | 133 +++ ..._DateTime_in_VehicleEnrollment.Designer.cs | 793 +++++++++++++++++ ...Only_into_DateTime_in_VehicleEnrollment.cs | 103 +++ .../ApplicationDbContextModelSnapshot.cs | 87 +- Server/Models/Review.cs | 6 +- Server/Models/RouteAddress.cs | 5 +- Server/Models/Ticket.cs | 4 + Server/Models/VehicleEnrollment.cs | 11 +- Server/Program.cs | 32 +- Server/Services/AddressManagementService.cs | 8 +- Server/Services/CityManagementService.cs | 8 +- Server/Services/CompanyManagementService.cs | 183 ++++ Server/Services/CountryManagementService.cs | 8 +- Server/Services/IAddressManagementService.cs | 1 - Server/Services/ICityManagementService.cs | 1 - Server/Services/ICompanyManagementService.cs | 16 + Server/Services/ICountryManagementService.cs | 1 - Server/Services/IReviewManagementService.cs | 16 + .../IRouteAddressManagementService.cs | 16 + Server/Services/IRouteManagementService.cs | 16 + Server/Services/IStateManagementService.cs | 1 - Server/Services/ITicketManagementService.cs | 16 + .../IVehicleEnrollmentManagementService.cs | 16 + Server/Services/IVehicleManagementService.cs | 16 + Server/Services/ReviewManagementService.cs | 170 ++++ .../Services/RouteAddressManagementService.cs | 168 ++++ Server/Services/RouteManagementService.cs | 170 ++++ Server/Services/StateManagementService.cs | 8 +- Server/Services/TicketManagementService.cs | 172 ++++ .../VehicleEnrollmentManagementService.cs | 222 +++++ Server/Services/VehicleManagementService.cs | 306 +++++++ .../DataTransferObjects/AddressDto.cs | 2 - .../DataTransferObjects/CompanyDto.cs | 29 + SharedModels/DataTransferObjects/ReviewDto.cs | 40 + .../DataTransferObjects/RouteAddressDto.cs | 30 +- SharedModels/DataTransferObjects/RouteDto.cs | 15 +- SharedModels/DataTransferObjects/TicketDto.cs | 31 + .../DataTransferObjects/VehicleDto.cs | 78 ++ .../VehicleEnrollmentDto.cs | 44 + .../CompanyParameters.cs | 15 + .../QueryStringParameters/ReviewParameters.cs | 15 + .../RouteAddressParameters.cs | 16 + .../QueryStringParameters/RouteParameters.cs | 14 + .../QueryStringParameters/TicketParameters.cs | 16 + .../VehicleEnrollmentParameters.cs | 20 + .../VehicleParameters.cs | 28 + 57 files changed, 4570 insertions(+), 93 deletions(-) create mode 100644 Server/Controllers/CompanyManagementController.cs create mode 100644 Server/Controllers/ReviewManagementController.cs create mode 100644 Server/Controllers/RouteAddressManagementController.cs create mode 100644 Server/Controllers/RouteManagementController.cs create mode 100644 Server/Controllers/TicketManagementController.cs create mode 100644 Server/Controllers/VehicleEnrollmentManagementController.cs create mode 100644 Server/Controllers/VehicleManagementController.cs create mode 100644 Server/Migrations/20221110081525_Replace_Composite_Keys_With_Single_Ones.Designer.cs create mode 100644 Server/Migrations/20221110081525_Replace_Composite_Keys_With_Single_Ones.cs create mode 100644 Server/Migrations/20221113104931_Merge_departure_DateOnly_and_TimeOnly_into_DateTime_in_VehicleEnrollment.Designer.cs create mode 100644 Server/Migrations/20221113104931_Merge_departure_DateOnly_and_TimeOnly_into_DateTime_in_VehicleEnrollment.cs create mode 100644 Server/Services/CompanyManagementService.cs create mode 100644 Server/Services/ICompanyManagementService.cs create mode 100644 Server/Services/IReviewManagementService.cs create mode 100644 Server/Services/IRouteAddressManagementService.cs create mode 100644 Server/Services/IRouteManagementService.cs create mode 100644 Server/Services/ITicketManagementService.cs create mode 100644 Server/Services/IVehicleEnrollmentManagementService.cs create mode 100644 Server/Services/IVehicleManagementService.cs create mode 100644 Server/Services/ReviewManagementService.cs create mode 100644 Server/Services/RouteAddressManagementService.cs create mode 100644 Server/Services/RouteManagementService.cs create mode 100644 Server/Services/TicketManagementService.cs create mode 100644 Server/Services/VehicleEnrollmentManagementService.cs create mode 100644 Server/Services/VehicleManagementService.cs create mode 100644 SharedModels/DataTransferObjects/CompanyDto.cs create mode 100644 SharedModels/DataTransferObjects/ReviewDto.cs create mode 100644 SharedModels/DataTransferObjects/TicketDto.cs create mode 100644 SharedModels/DataTransferObjects/VehicleDto.cs create mode 100644 SharedModels/DataTransferObjects/VehicleEnrollmentDto.cs create mode 100644 SharedModels/QueryStringParameters/CompanyParameters.cs create mode 100644 SharedModels/QueryStringParameters/ReviewParameters.cs create mode 100644 SharedModels/QueryStringParameters/RouteAddressParameters.cs create mode 100644 SharedModels/QueryStringParameters/RouteParameters.cs create mode 100644 SharedModels/QueryStringParameters/TicketParameters.cs create mode 100644 SharedModels/QueryStringParameters/VehicleEnrollmentParameters.cs create mode 100644 SharedModels/QueryStringParameters/VehicleParameters.cs diff --git a/Server/Configurations/MapperInitializer.cs b/Server/Configurations/MapperInitializer.cs index 3b44eaf..2cb5d5e 100644 --- a/Server/Configurations/MapperInitializer.cs +++ b/Server/Configurations/MapperInitializer.cs @@ -34,8 +34,39 @@ public class MapperInitializer : Profile CreateMap().ReverseMap(); CreateMap().ReverseMap(); + CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + + + + + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + + // CreateMap().ReverseMap(); + // CreateMap().ReverseMap(); + // CreateMap().ReverseMap(); } } \ No newline at end of file diff --git a/Server/Controllers/CompanyManagementController.cs b/Server/Controllers/CompanyManagementController.cs new file mode 100644 index 0000000..8d86713 --- /dev/null +++ b/Server/Controllers/CompanyManagementController.cs @@ -0,0 +1,101 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Server.Services; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Controllers; + +[Route("api/companies")] +[ApiController] +public class CompanyManagementController : ControllerBase +{ + private readonly ICompanyManagementService _companyManagementService; + + public CompanyManagementController(ICompanyManagementService companyManagementService) + { + _companyManagementService = companyManagementService; + } + + [HttpPost] + public async Task AddCompany(CreateCompanyDto company) + { + var result = await _companyManagementService.AddCompany(company); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return CreatedAtAction(nameof(GetCompany), new {id = result.company.Id}, result.company); + } + + [HttpGet] + public async Task GetCompanies([FromQuery] CompanyParameters parameters) + { + var result = await _companyManagementService.GetCompanies(parameters); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata)); + + return Ok(result.companies); + } + + [HttpGet("{id}")] + public async Task GetCompany(int id, [FromQuery] string? fields) + { + if (!await _companyManagementService.IsCompanyExists(id)) + { + return NotFound(); + } + + var result = await _companyManagementService.GetCompany(id, fields); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.company); + } + + [HttpPut("{id}")] + public async Task UpdateCompany(int id, UpdateCompanyDto company) + { + if (id != company.Id) + { + return BadRequest(); + } + + var result = await _companyManagementService.UpdateCompany(company); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.company); + } + + [HttpDelete("{id}")] + public async Task DeleteCompany(int id) + { + if (!await _companyManagementService.IsCompanyExists(id)) + { + return NotFound(); + } + + var result = await _companyManagementService.DeleteCompany(id); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return NoContent(); + } +} \ No newline at end of file diff --git a/Server/Controllers/ReviewManagementController.cs b/Server/Controllers/ReviewManagementController.cs new file mode 100644 index 0000000..bd96895 --- /dev/null +++ b/Server/Controllers/ReviewManagementController.cs @@ -0,0 +1,101 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Server.Services; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Controllers; + +[Route("api/reviews")] +[ApiController] +public class ReviewManagementController : ControllerBase +{ + private readonly IReviewManagementService _reviewManagementService; + + public ReviewManagementController(IReviewManagementService reviewManagementService) + { + _reviewManagementService = reviewManagementService; + } + + [HttpPost] + public async Task AddReview(CreateReviewDto review) + { + var result = await _reviewManagementService.AddReview(review); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return CreatedAtAction(nameof(GetReview), new {id = result.review.Id}, result.review); + } + + [HttpGet] + public async Task GetReviews([FromQuery] ReviewParameters parameters) + { + var result = await _reviewManagementService.GetReviews(parameters); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata)); + + return Ok(result.reviews); + } + + [HttpGet("{id}")] + public async Task GetReview(int id, [FromQuery] string? fields) + { + if (!await _reviewManagementService.IsReviewExists(id)) + { + return NotFound(); + } + + var result = await _reviewManagementService.GetReview(id, fields); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.review); + } + + [HttpPut("{id}")] + public async Task UpdateReview(int id, UpdateReviewDto review) + { + if (id != review.Id) + { + return BadRequest(); + } + + var result = await _reviewManagementService.UpdateReview(review); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.review); + } + + [HttpDelete("{id}")] + public async Task DeleteReview(int id) + { + if (!await _reviewManagementService.IsReviewExists(id)) + { + return NotFound(); + } + + var result = await _reviewManagementService.DeleteReview(id); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return NoContent(); + } +} \ No newline at end of file diff --git a/Server/Controllers/RouteAddressManagementController.cs b/Server/Controllers/RouteAddressManagementController.cs new file mode 100644 index 0000000..b65aa26 --- /dev/null +++ b/Server/Controllers/RouteAddressManagementController.cs @@ -0,0 +1,101 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Server.Services; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Controllers; + +[Route("api/routeAddresses")] +[ApiController] +public class RouteAddressManagementController : ControllerBase +{ + private readonly IRouteAddressManagementService _routeAddressManagementService; + + public RouteAddressManagementController(IRouteAddressManagementService routeAddressManagementService) + { + _routeAddressManagementService = routeAddressManagementService; + } + + [HttpPost] + public async Task AddRouteAddress(CreateRouteAddressDto routeAddress) + { + var result = await _routeAddressManagementService.AddRouteAddress(routeAddress); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return CreatedAtAction(nameof(GetRouteAddress), new {id = result.routeAddress.Id}, result.routeAddress); + } + + [HttpGet] + public async Task GetRouteAddresses([FromQuery] RouteAddressParameters parameters) + { + var result = await _routeAddressManagementService.GetRouteAddresses(parameters); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata)); + + return Ok(result.routeAddresses); + } + + [HttpGet("{id}")] + public async Task GetRouteAddress(int id, [FromQuery] string? fields) + { + if (!await _routeAddressManagementService.IsRouteAddressExists(id)) + { + return NotFound(); + } + + var result = await _routeAddressManagementService.GetRouteAddress(id, fields); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.routeAddress); + } + + [HttpPut("{id}")] + public async Task UpdateRouteAddress(int id, UpdateRouteAddressDto routeAddress) + { + if (id != routeAddress.Id) + { + return BadRequest(); + } + + var result = await _routeAddressManagementService.UpdateRouteAddress(routeAddress); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.routeAddress); + } + + [HttpDelete("{id}")] + public async Task DeleteRouteAddress(int id) + { + if (!await _routeAddressManagementService.IsRouteAddressExists(id)) + { + return NotFound(); + } + + var result = await _routeAddressManagementService.DeleteRouteAddress(id); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return NoContent(); + } +} \ No newline at end of file diff --git a/Server/Controllers/RouteManagementController.cs b/Server/Controllers/RouteManagementController.cs new file mode 100644 index 0000000..51e6132 --- /dev/null +++ b/Server/Controllers/RouteManagementController.cs @@ -0,0 +1,101 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Server.Services; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Controllers; + +[Route("api/routes")] +[ApiController] +public class RouteManagementController : ControllerBase +{ + private readonly IRouteManagementService _routeManagementService; + + public RouteManagementController(IRouteManagementService routeManagementService) + { + _routeManagementService = routeManagementService; + } + + [HttpPost] + public async Task AddRoute(CreateRouteDto route) + { + var result = await _routeManagementService.AddRoute(route); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return CreatedAtAction(nameof(GetRoute), new {id = result.route.Id}, result.route); + } + + [HttpGet] + public async Task GetRoutes([FromQuery] RouteParameters parameters) + { + var result = await _routeManagementService.GetRoutes(parameters); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata)); + + return Ok(result.routes); + } + + [HttpGet("{id}")] + public async Task GetRoute(int id, [FromQuery] string? fields) + { + if (!await _routeManagementService.IsRouteExists(id)) + { + return NotFound(); + } + + var result = await _routeManagementService.GetRoute(id, fields); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.route); + } + + [HttpPut("{id}")] + public async Task UpdateRoute(int id, UpdateRouteDto route) + { + if (id != route.Id) + { + return BadRequest(); + } + + var result = await _routeManagementService.UpdateRoute(route); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.route); + } + + [HttpDelete("{id}")] + public async Task DeleteRoute(int id) + { + if (!await _routeManagementService.IsRouteExists(id)) + { + return NotFound(); + } + + var result = await _routeManagementService.DeleteRoute(id); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return NoContent(); + } +} \ No newline at end of file diff --git a/Server/Controllers/TestingController.cs b/Server/Controllers/TestingController.cs index 06595cb..70c63ac 100644 --- a/Server/Controllers/TestingController.cs +++ b/Server/Controllers/TestingController.cs @@ -27,5 +27,11 @@ public class TestingController : ControllerBase return NotFound(); } + + [HttpPost("timespan")] + public async Task TestTimeSpan([FromBody] TimeSpan ts) + { + return Ok(); + } } diff --git a/Server/Controllers/TicketManagementController.cs b/Server/Controllers/TicketManagementController.cs new file mode 100644 index 0000000..3392007 --- /dev/null +++ b/Server/Controllers/TicketManagementController.cs @@ -0,0 +1,101 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Server.Services; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Controllers; + +[Route("api/tickets")] +[ApiController] +public class TicketManagementController : ControllerBase +{ + private readonly ITicketManagementService _ticketManagementService; + + public TicketManagementController(ITicketManagementService ticketManagementService) + { + _ticketManagementService = ticketManagementService; + } + + [HttpPost] + public async Task AddTicket(CreateTicketDto ticket) + { + var result = await _ticketManagementService.AddTicket(ticket); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return CreatedAtAction(nameof(GetTicket), new {id = result.ticket.Id}, result.ticket); + } + + [HttpGet] + public async Task GetTickets([FromQuery] TicketParameters parameters) + { + var result = await _ticketManagementService.GetTickets(parameters); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata)); + + return Ok(result.tickets); + } + + [HttpGet("{id}")] + public async Task GetTicket(int id, [FromQuery] string? fields) + { + if (!await _ticketManagementService.IsTicketExists(id)) + { + return NotFound(); + } + + var result = await _ticketManagementService.GetTicket(id, fields); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.ticket); + } + + [HttpPut("{id}")] + public async Task UpdateTicket(int id, UpdateTicketDto ticket) + { + if (id != ticket.Id) + { + return BadRequest(); + } + + var result = await _ticketManagementService.UpdateTicket(ticket); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.ticket); + } + + [HttpDelete("{id}")] + public async Task DeleteTicket(int id) + { + if (!await _ticketManagementService.IsTicketExists(id)) + { + return NotFound(); + } + + var result = await _ticketManagementService.DeleteTicket(id); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return NoContent(); + } +} \ No newline at end of file diff --git a/Server/Controllers/VehicleEnrollmentManagementController.cs b/Server/Controllers/VehicleEnrollmentManagementController.cs new file mode 100644 index 0000000..db576b1 --- /dev/null +++ b/Server/Controllers/VehicleEnrollmentManagementController.cs @@ -0,0 +1,102 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Server.Services; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Controllers; + + +[Route("api/vehicleEnrollments")] +[ApiController] +public class VehicleEnrollmentManagementController : ControllerBase +{ + private readonly IVehicleEnrollmentManagementService _vehicleEnrollmentManagementService; + + public VehicleEnrollmentManagementController(IVehicleEnrollmentManagementService vehicleEnrollmentManagementService) + { + _vehicleEnrollmentManagementService = vehicleEnrollmentManagementService; + } + + [HttpPost] + public async Task AddEnrollment(CreateVehicleEnrollmentDto enrollment) + { + var result = await _vehicleEnrollmentManagementService.AddEnrollment(enrollment); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return CreatedAtAction(nameof(GetEnrollment), new {id = result.enrollment.Id}, result.enrollment); + } + + [HttpGet] + public async Task GetEnrollments([FromQuery] VehicleEnrollmentParameters parameters) + { + var result = await _vehicleEnrollmentManagementService.GetEnrollments(parameters); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata)); + + return Ok(result.enrollments); + } + + [HttpGet("{id}")] + public async Task GetEnrollment(int id, [FromQuery] string? fields) + { + if (!await _vehicleEnrollmentManagementService.IsEnrollmentExists(id)) + { + return NotFound(); + } + + var result = await _vehicleEnrollmentManagementService.GetEnrollment(id, fields); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.enrollment); + } + + [HttpPut("{id}")] + public async Task UpdateVehicle(int id, UpdateVehicleEnrollmentDto enrollment) + { + if (id != enrollment.Id) + { + return BadRequest(); + } + + var result = await _vehicleEnrollmentManagementService.UpdateEnrollment(enrollment); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.enrollment); + } + + [HttpDelete("{id}")] + public async Task DeleteEnrollment(int id) + { + if (!await _vehicleEnrollmentManagementService.IsEnrollmentExists(id)) + { + return NotFound(); + } + + var result = await _vehicleEnrollmentManagementService.DeleteEnrollment(id); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return NoContent(); + } +} \ No newline at end of file diff --git a/Server/Controllers/VehicleManagementController.cs b/Server/Controllers/VehicleManagementController.cs new file mode 100644 index 0000000..3da8158 --- /dev/null +++ b/Server/Controllers/VehicleManagementController.cs @@ -0,0 +1,101 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Server.Services; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Controllers; + +[Route("api/vehicles")] +[ApiController] +public class VehicleManagementController : ControllerBase +{ + private readonly IVehicleManagementService _vehicleManagementService; + + public VehicleManagementController(IVehicleManagementService vehicleManagementService) + { + _vehicleManagementService = vehicleManagementService; + } + + [HttpPost] + public async Task AddVehicle(CreateVehicleDto vehicle) + { + var result = await _vehicleManagementService.AddVehicle(vehicle); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return CreatedAtAction(nameof(GetVehicle), new {id = result.vehicle.Id}, result.vehicle); + } + + [HttpGet] + public async Task GetVehicles([FromQuery] VehicleParameters parameters) + { + var result = await _vehicleManagementService.GetVehicles(parameters); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata)); + + return Ok(result.vehicles); + } + + [HttpGet("{id}")] + public async Task GetVehicle(int id, [FromQuery] string? fields) + { + if (!await _vehicleManagementService.IsVehicleExists(id)) + { + return NotFound(); + } + + var result = await _vehicleManagementService.GetVehicle(id, fields); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.vehicle); + } + + [HttpPut("{id}")] + public async Task UpdateVehicle(int id, UpdateVehicleDto vehicle) + { + if (id != vehicle.Id) + { + return BadRequest(); + } + + var result = await _vehicleManagementService.UpdateVehicle(vehicle); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return Ok(result.vehicle); + } + + [HttpDelete("{id}")] + public async Task DeleteVehicle(int id) + { + if (!await _vehicleManagementService.IsVehicleExists(id)) + { + return NotFound(); + } + + var result = await _vehicleManagementService.DeleteVehicle(id); + + if (!result.isSucceed) + { + return BadRequest(result.message); + } + + return NoContent(); + } +} \ No newline at end of file diff --git a/Server/Data/ApplicationDbContext.cs b/Server/Data/ApplicationDbContext.cs index 22e1d99..62ea5f8 100644 --- a/Server/Data/ApplicationDbContext.cs +++ b/Server/Data/ApplicationDbContext.cs @@ -23,18 +23,4 @@ public class ApplicationDbContext : IdentityDbContext public DbSet Countries { get; set; } = null!; public DbSet Tickets { get; set; } = null!; public DbSet Reviews { get; set; } = null!; - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.Entity() - .HasKey(ra => new {ra.RouteId, ra.AddressId}); - - modelBuilder.Entity() - .HasKey(t => new {t.UserId, t.VehicleEnrollmentId}); - - modelBuilder.Entity() - .HasKey(t => new {t.UserId, t.VehicleEnrollmentId}); - } } \ No newline at end of file diff --git a/Server/Migrations/20221110081525_Replace_Composite_Keys_With_Single_Ones.Designer.cs b/Server/Migrations/20221110081525_Replace_Composite_Keys_With_Single_Ones.Designer.cs new file mode 100644 index 0000000..0b7fddc --- /dev/null +++ b/Server/Migrations/20221110081525_Replace_Composite_Keys_With_Single_Ones.Designer.cs @@ -0,0 +1,798 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Server.Data; + +#nullable disable + +namespace Server.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20221110081525_Replace_Composite_Keys_With_Single_Ones")] + partial class Replace_Composite_Keys_With_Single_Ones + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CityId") + .HasColumnType("integer"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CityId"); + + b.ToTable("Addresses"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StateId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("StateId"); + + b.ToTable("Cities"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("Companies"); + }); + + modelBuilder.Entity("Server.Models.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Countries"); + }); + + modelBuilder.Entity("Server.Models.Review", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Comment") + .IsRequired() + .HasColumnType("text"); + + b.Property("Rating") + .HasColumnType("integer"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("Reviews"); + }); + + modelBuilder.Entity("Server.Models.Route", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Routes"); + }); + + modelBuilder.Entity("Server.Models.RouteAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AddressId") + .HasColumnType("integer"); + + b.Property("CostToNextCity") + .HasColumnType("double precision"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RouteId") + .HasColumnType("integer"); + + b.Property("TimeSpanToNextCity") + .HasColumnType("interval"); + + b.Property("WaitTimeSpan") + .HasColumnType("interval"); + + b.HasKey("Id"); + + b.HasIndex("AddressId"); + + b.HasIndex("RouteId"); + + b.ToTable("RouteAddresses"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CountryId") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CountryId"); + + b.ToTable("States"); + }); + + modelBuilder.Entity("Server.Models.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsReturned") + .HasColumnType("boolean"); + + b.Property("PurchaseDateTimeUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Server.Models.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Server.Models.Vehicle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("CompanyId") + .HasColumnType("integer"); + + b.Property("HasBelts") + .HasColumnType("boolean"); + + b.Property("HasClimateControl") + .HasColumnType("boolean"); + + b.Property("HasOutlet") + .HasColumnType("boolean"); + + b.Property("HasStewardess") + .HasColumnType("boolean"); + + b.Property("HasTV") + .HasColumnType("boolean"); + + b.Property("HasWC") + .HasColumnType("boolean"); + + b.Property("HasWiFi") + .HasColumnType("boolean"); + + b.Property("Number") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId"); + + b.ToTable("Vehicles"); + }); + + modelBuilder.Entity("Server.Models.VehicleEnrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CancelationComment") + .IsRequired() + .HasColumnType("text"); + + b.Property("DelayTimeSpan") + .HasColumnType("interval"); + + b.Property("DepartureDateOnly") + .HasColumnType("date"); + + b.Property("DepartureTimeOnlyUtc") + .HasColumnType("time without time zone"); + + b.Property("IsCanceled") + .HasColumnType("boolean"); + + b.Property("RouteId") + .HasColumnType("integer"); + + b.Property("VehicleId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RouteId"); + + b.HasIndex("VehicleId"); + + b.ToTable("VehicleEnrollments"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.HasOne("Server.Models.City", "City") + .WithMany("Addresses") + .HasForeignKey("CityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("City"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.HasOne("Server.Models.State", "State") + .WithMany("Cities") + .HasForeignKey("StateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("State"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.HasOne("Server.Models.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Server.Models.Review", b => + { + b.HasOne("Server.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment") + .WithMany() + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("Server.Models.RouteAddress", b => + { + b.HasOne("Server.Models.Address", "Address") + .WithMany("RouteAddresses") + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.Route", "Route") + .WithMany("RouteAddresses") + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Address"); + + b.Navigation("Route"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.HasOne("Server.Models.Country", "Country") + .WithMany("States") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Country"); + }); + + modelBuilder.Entity("Server.Models.Ticket", b => + { + b.HasOne("Server.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment") + .WithMany() + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("Server.Models.User", b => + { + b.OwnsMany("Server.Models.RefreshToken", "RefreshTokens", b1 => + { + b1.Property("UserId") + .HasColumnType("text"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Created") + .HasColumnType("timestamp with time zone"); + + b1.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b1.Property("Revoked") + .HasColumnType("timestamp with time zone"); + + b1.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("UserId", "Id"); + + b1.ToTable("RefreshToken"); + + b1.WithOwner() + .HasForeignKey("UserId"); + }); + + b.Navigation("RefreshTokens"); + }); + + modelBuilder.Entity("Server.Models.Vehicle", b => + { + b.HasOne("Server.Models.Company", "Company") + .WithMany("Vehicles") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Company"); + }); + + modelBuilder.Entity("Server.Models.VehicleEnrollment", b => + { + b.HasOne("Server.Models.Route", "Route") + .WithMany() + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.Vehicle", "Vehicle") + .WithMany() + .HasForeignKey("VehicleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Route"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.Navigation("RouteAddresses"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.Navigation("Addresses"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.Navigation("Vehicles"); + }); + + modelBuilder.Entity("Server.Models.Country", b => + { + b.Navigation("States"); + }); + + modelBuilder.Entity("Server.Models.Route", b => + { + b.Navigation("RouteAddresses"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.Navigation("Cities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/Migrations/20221110081525_Replace_Composite_Keys_With_Single_Ones.cs b/Server/Migrations/20221110081525_Replace_Composite_Keys_With_Single_Ones.cs new file mode 100644 index 0000000..168cffb --- /dev/null +++ b/Server/Migrations/20221110081525_Replace_Composite_Keys_With_Single_Ones.cs @@ -0,0 +1,133 @@ +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Server.Migrations +{ + public partial class Replace_Composite_Keys_With_Single_Ones : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropPrimaryKey( + name: "PK_Tickets", + table: "Tickets"); + + migrationBuilder.DropPrimaryKey( + name: "PK_RouteAddresses", + table: "RouteAddresses"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Reviews", + table: "Reviews"); + + migrationBuilder.AddColumn( + name: "Id", + table: "Tickets", + type: "integer", + nullable: false, + defaultValue: 0) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + migrationBuilder.AddColumn( + name: "Id", + table: "RouteAddresses", + type: "integer", + nullable: false, + defaultValue: 0) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + migrationBuilder.AddColumn( + name: "Id", + table: "Reviews", + type: "integer", + nullable: false, + defaultValue: 0) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + migrationBuilder.AddPrimaryKey( + name: "PK_Tickets", + table: "Tickets", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_RouteAddresses", + table: "RouteAddresses", + column: "Id"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Reviews", + table: "Reviews", + column: "Id"); + + migrationBuilder.CreateIndex( + name: "IX_Tickets_UserId", + table: "Tickets", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_RouteAddresses_RouteId", + table: "RouteAddresses", + column: "RouteId"); + + migrationBuilder.CreateIndex( + name: "IX_Reviews_UserId", + table: "Reviews", + column: "UserId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropPrimaryKey( + name: "PK_Tickets", + table: "Tickets"); + + migrationBuilder.DropIndex( + name: "IX_Tickets_UserId", + table: "Tickets"); + + migrationBuilder.DropPrimaryKey( + name: "PK_RouteAddresses", + table: "RouteAddresses"); + + migrationBuilder.DropIndex( + name: "IX_RouteAddresses_RouteId", + table: "RouteAddresses"); + + migrationBuilder.DropPrimaryKey( + name: "PK_Reviews", + table: "Reviews"); + + migrationBuilder.DropIndex( + name: "IX_Reviews_UserId", + table: "Reviews"); + + migrationBuilder.DropColumn( + name: "Id", + table: "Tickets"); + + migrationBuilder.DropColumn( + name: "Id", + table: "RouteAddresses"); + + migrationBuilder.DropColumn( + name: "Id", + table: "Reviews"); + + migrationBuilder.AddPrimaryKey( + name: "PK_Tickets", + table: "Tickets", + columns: new[] { "UserId", "VehicleEnrollmentId" }); + + migrationBuilder.AddPrimaryKey( + name: "PK_RouteAddresses", + table: "RouteAddresses", + columns: new[] { "RouteId", "AddressId" }); + + migrationBuilder.AddPrimaryKey( + name: "PK_Reviews", + table: "Reviews", + columns: new[] { "UserId", "VehicleEnrollmentId" }); + } + } +} diff --git a/Server/Migrations/20221113104931_Merge_departure_DateOnly_and_TimeOnly_into_DateTime_in_VehicleEnrollment.Designer.cs b/Server/Migrations/20221113104931_Merge_departure_DateOnly_and_TimeOnly_into_DateTime_in_VehicleEnrollment.Designer.cs new file mode 100644 index 0000000..4ba21ef --- /dev/null +++ b/Server/Migrations/20221113104931_Merge_departure_DateOnly_and_TimeOnly_into_DateTime_in_VehicleEnrollment.Designer.cs @@ -0,0 +1,793 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using Server.Data; + +#nullable disable + +namespace Server.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20221113104931_Merge_departure_DateOnly_and_TimeOnly_into_DateTime_in_VehicleEnrollment")] + partial class Merge_departure_DateOnly_and_TimeOnly_into_DateTime_in_VehicleEnrollment + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "6.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Name") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedName") + .IsUnique() + .HasDatabaseName("RoleNameIndex"); + + b.ToTable("AspNetRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("RoleId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetRoleClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("ClaimType") + .HasColumnType("text"); + + b.Property("ClaimValue") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserClaims", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("ProviderKey") + .HasColumnType("text"); + + b.Property("ProviderDisplayName") + .HasColumnType("text"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("LoginProvider", "ProviderKey"); + + b.HasIndex("UserId"); + + b.ToTable("AspNetUserLogins", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("text"); + + b.HasKey("UserId", "RoleId"); + + b.HasIndex("RoleId"); + + b.ToTable("AspNetUserRoles", (string)null); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.Property("UserId") + .HasColumnType("text"); + + b.Property("LoginProvider") + .HasColumnType("text"); + + b.Property("Name") + .HasColumnType("text"); + + b.Property("Value") + .HasColumnType("text"); + + b.HasKey("UserId", "LoginProvider", "Name"); + + b.ToTable("AspNetUserTokens", (string)null); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CityId") + .HasColumnType("integer"); + + b.Property("Latitude") + .HasColumnType("double precision"); + + b.Property("Longitude") + .HasColumnType("double precision"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CityId"); + + b.ToTable("Addresses"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("StateId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("StateId"); + + b.ToTable("Cities"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("OwnerId") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("OwnerId"); + + b.ToTable("Companies"); + }); + + modelBuilder.Entity("Server.Models.Country", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Code") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Countries"); + }); + + modelBuilder.Entity("Server.Models.Review", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Comment") + .HasColumnType("text"); + + b.Property("Rating") + .HasColumnType("integer"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("Reviews"); + }); + + modelBuilder.Entity("Server.Models.Route", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Routes"); + }); + + modelBuilder.Entity("Server.Models.RouteAddress", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("AddressId") + .HasColumnType("integer"); + + b.Property("CostToNextCity") + .HasColumnType("double precision"); + + b.Property("Order") + .HasColumnType("integer"); + + b.Property("RouteId") + .HasColumnType("integer"); + + b.Property("TimeSpanToNextCity") + .HasColumnType("interval"); + + b.Property("WaitTimeSpan") + .HasColumnType("interval"); + + b.HasKey("Id"); + + b.HasIndex("AddressId"); + + b.HasIndex("RouteId"); + + b.ToTable("RouteAddresses"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CountryId") + .HasColumnType("integer"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CountryId"); + + b.ToTable("States"); + }); + + modelBuilder.Entity("Server.Models.Ticket", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("IsReturned") + .HasColumnType("boolean"); + + b.Property("PurchaseDateTimeUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.HasIndex("VehicleEnrollmentId"); + + b.ToTable("Tickets"); + }); + + modelBuilder.Entity("Server.Models.User", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("AccessFailedCount") + .HasColumnType("integer"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FirstName") + .HasColumnType("text"); + + b.Property("LastName") + .HasColumnType("text"); + + b.Property("LockoutEnabled") + .HasColumnType("boolean"); + + b.Property("LockoutEnd") + .HasColumnType("timestamp with time zone"); + + b.Property("NormalizedEmail") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("NormalizedUserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("PasswordHash") + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.Property("UserName") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + b.HasIndex("NormalizedUserName") + .IsUnique() + .HasDatabaseName("UserNameIndex"); + + b.ToTable("AspNetUsers", (string)null); + }); + + modelBuilder.Entity("Server.Models.Vehicle", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Capacity") + .HasColumnType("integer"); + + b.Property("CompanyId") + .HasColumnType("integer"); + + b.Property("HasBelts") + .HasColumnType("boolean"); + + b.Property("HasClimateControl") + .HasColumnType("boolean"); + + b.Property("HasOutlet") + .HasColumnType("boolean"); + + b.Property("HasStewardess") + .HasColumnType("boolean"); + + b.Property("HasTV") + .HasColumnType("boolean"); + + b.Property("HasWC") + .HasColumnType("boolean"); + + b.Property("HasWiFi") + .HasColumnType("boolean"); + + b.Property("Number") + .IsRequired() + .HasColumnType("text"); + + b.Property("Type") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("CompanyId"); + + b.ToTable("Vehicles"); + }); + + modelBuilder.Entity("Server.Models.VehicleEnrollment", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("CancelationComment") + .HasColumnType("text"); + + b.Property("DelayTimeSpan") + .HasColumnType("interval"); + + b.Property("DepartureDateTimeUtc") + .HasColumnType("timestamp with time zone"); + + b.Property("IsCanceled") + .HasColumnType("boolean"); + + b.Property("RouteId") + .HasColumnType("integer"); + + b.Property("VehicleId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("RouteId"); + + b.HasIndex("VehicleId"); + + b.ToTable("VehicleEnrollments"); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => + { + b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) + .WithMany() + .HasForeignKey("RoleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => + { + b.HasOne("Server.Models.User", null) + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.HasOne("Server.Models.City", "City") + .WithMany("Addresses") + .HasForeignKey("CityId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("City"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.HasOne("Server.Models.State", "State") + .WithMany("Cities") + .HasForeignKey("StateId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("State"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.HasOne("Server.Models.User", "Owner") + .WithMany() + .HasForeignKey("OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Server.Models.Review", b => + { + b.HasOne("Server.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment") + .WithMany() + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("Server.Models.RouteAddress", b => + { + b.HasOne("Server.Models.Address", "Address") + .WithMany("RouteAddresses") + .HasForeignKey("AddressId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.Route", "Route") + .WithMany("RouteAddresses") + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Address"); + + b.Navigation("Route"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.HasOne("Server.Models.Country", "Country") + .WithMany("States") + .HasForeignKey("CountryId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Country"); + }); + + modelBuilder.Entity("Server.Models.Ticket", b => + { + b.HasOne("Server.Models.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment") + .WithMany() + .HasForeignKey("VehicleEnrollmentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + + b.Navigation("VehicleEnrollment"); + }); + + modelBuilder.Entity("Server.Models.User", b => + { + b.OwnsMany("Server.Models.RefreshToken", "RefreshTokens", b1 => + { + b1.Property("UserId") + .HasColumnType("text"); + + b1.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property("Id")); + + b1.Property("Created") + .HasColumnType("timestamp with time zone"); + + b1.Property("Expires") + .HasColumnType("timestamp with time zone"); + + b1.Property("Revoked") + .HasColumnType("timestamp with time zone"); + + b1.Property("Token") + .IsRequired() + .HasColumnType("text"); + + b1.HasKey("UserId", "Id"); + + b1.ToTable("RefreshToken"); + + b1.WithOwner() + .HasForeignKey("UserId"); + }); + + b.Navigation("RefreshTokens"); + }); + + modelBuilder.Entity("Server.Models.Vehicle", b => + { + b.HasOne("Server.Models.Company", "Company") + .WithMany("Vehicles") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Company"); + }); + + modelBuilder.Entity("Server.Models.VehicleEnrollment", b => + { + b.HasOne("Server.Models.Route", "Route") + .WithMany() + .HasForeignKey("RouteId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.Vehicle", "Vehicle") + .WithMany() + .HasForeignKey("VehicleId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Route"); + + b.Navigation("Vehicle"); + }); + + modelBuilder.Entity("Server.Models.Address", b => + { + b.Navigation("RouteAddresses"); + }); + + modelBuilder.Entity("Server.Models.City", b => + { + b.Navigation("Addresses"); + }); + + modelBuilder.Entity("Server.Models.Company", b => + { + b.Navigation("Vehicles"); + }); + + modelBuilder.Entity("Server.Models.Country", b => + { + b.Navigation("States"); + }); + + modelBuilder.Entity("Server.Models.Route", b => + { + b.Navigation("RouteAddresses"); + }); + + modelBuilder.Entity("Server.Models.State", b => + { + b.Navigation("Cities"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Server/Migrations/20221113104931_Merge_departure_DateOnly_and_TimeOnly_into_DateTime_in_VehicleEnrollment.cs b/Server/Migrations/20221113104931_Merge_departure_DateOnly_and_TimeOnly_into_DateTime_in_VehicleEnrollment.cs new file mode 100644 index 0000000..e2dacc2 --- /dev/null +++ b/Server/Migrations/20221113104931_Merge_departure_DateOnly_and_TimeOnly_into_DateTime_in_VehicleEnrollment.cs @@ -0,0 +1,103 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Server.Migrations +{ + public partial class Merge_departure_DateOnly_and_TimeOnly_into_DateTime_in_VehicleEnrollment : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DepartureDateOnly", + table: "VehicleEnrollments"); + + migrationBuilder.DropColumn( + name: "DepartureTimeOnlyUtc", + table: "VehicleEnrollments"); + + migrationBuilder.AlterColumn( + name: "DelayTimeSpan", + table: "VehicleEnrollments", + type: "interval", + nullable: true, + oldClrType: typeof(TimeSpan), + oldType: "interval"); + + migrationBuilder.AlterColumn( + name: "CancelationComment", + table: "VehicleEnrollments", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + + migrationBuilder.AddColumn( + name: "DepartureDateTimeUtc", + table: "VehicleEnrollments", + type: "timestamp with time zone", + nullable: false, + defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified)); + + migrationBuilder.AlterColumn( + name: "Comment", + table: "Reviews", + type: "text", + nullable: true, + oldClrType: typeof(string), + oldType: "text"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DepartureDateTimeUtc", + table: "VehicleEnrollments"); + + migrationBuilder.AlterColumn( + name: "DelayTimeSpan", + table: "VehicleEnrollments", + type: "interval", + nullable: false, + defaultValue: new TimeSpan(0, 0, 0, 0, 0), + oldClrType: typeof(TimeSpan), + oldType: "interval", + oldNullable: true); + + migrationBuilder.AlterColumn( + name: "CancelationComment", + table: "VehicleEnrollments", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + + migrationBuilder.AddColumn( + name: "DepartureDateOnly", + table: "VehicleEnrollments", + type: "date", + nullable: false, + defaultValue: new DateOnly(1, 1, 1)); + + migrationBuilder.AddColumn( + name: "DepartureTimeOnlyUtc", + table: "VehicleEnrollments", + type: "time without time zone", + nullable: false, + defaultValue: new TimeOnly(0, 0, 0)); + + migrationBuilder.AlterColumn( + name: "Comment", + table: "Reviews", + type: "text", + nullable: false, + defaultValue: "", + oldClrType: typeof(string), + oldType: "text", + oldNullable: true); + } + } +} diff --git a/Server/Migrations/ApplicationDbContextModelSnapshot.cs b/Server/Migrations/ApplicationDbContextModelSnapshot.cs index 1613970..fc2d6fa 100644 --- a/Server/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Server/Migrations/ApplicationDbContextModelSnapshot.cs @@ -179,7 +179,7 @@ namespace Server.Migrations b.HasIndex("CityId"); - b.ToTable("Addresses", (string)null); + b.ToTable("Addresses"); }); modelBuilder.Entity("Server.Models.City", b => @@ -201,7 +201,7 @@ namespace Server.Migrations b.HasIndex("StateId"); - b.ToTable("Cities", (string)null); + b.ToTable("Cities"); }); modelBuilder.Entity("Server.Models.Company", b => @@ -224,7 +224,7 @@ namespace Server.Migrations b.HasIndex("OwnerId"); - b.ToTable("Companies", (string)null); + b.ToTable("Companies"); }); modelBuilder.Entity("Server.Models.Country", b => @@ -245,29 +245,37 @@ namespace Server.Migrations b.HasKey("Id"); - b.ToTable("Countries", (string)null); + b.ToTable("Countries"); }); modelBuilder.Entity("Server.Models.Review", b => { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("VehicleEnrollmentId") + b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("integer"); + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("Comment") - .IsRequired() .HasColumnType("text"); b.Property("Rating") .HasColumnType("integer"); - b.HasKey("UserId", "VehicleEnrollmentId"); + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); b.HasIndex("VehicleEnrollmentId"); - b.ToTable("Reviews", (string)null); + b.ToTable("Reviews"); }); modelBuilder.Entity("Server.Models.Route", b => @@ -284,14 +292,17 @@ namespace Server.Migrations b.HasKey("Id"); - b.ToTable("Routes", (string)null); + b.ToTable("Routes"); }); modelBuilder.Entity("Server.Models.RouteAddress", b => { - b.Property("RouteId") + b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("integer"); + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("AddressId") .HasColumnType("integer"); @@ -301,17 +312,22 @@ namespace Server.Migrations b.Property("Order") .HasColumnType("integer"); + b.Property("RouteId") + .HasColumnType("integer"); + b.Property("TimeSpanToNextCity") .HasColumnType("interval"); b.Property("WaitTimeSpan") .HasColumnType("interval"); - b.HasKey("RouteId", "AddressId"); + b.HasKey("Id"); b.HasIndex("AddressId"); - b.ToTable("RouteAddresses", (string)null); + b.HasIndex("RouteId"); + + b.ToTable("RouteAddresses"); }); modelBuilder.Entity("Server.Models.State", b => @@ -333,28 +349,37 @@ namespace Server.Migrations b.HasIndex("CountryId"); - b.ToTable("States", (string)null); + b.ToTable("States"); }); modelBuilder.Entity("Server.Models.Ticket", b => { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("VehicleEnrollmentId") + b.Property("Id") + .ValueGeneratedOnAdd() .HasColumnType("integer"); + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("IsReturned") .HasColumnType("boolean"); b.Property("PurchaseDateTimeUtc") .HasColumnType("timestamp with time zone"); - b.HasKey("UserId", "VehicleEnrollmentId"); + b.Property("UserId") + .IsRequired() + .HasColumnType("text"); + + b.Property("VehicleEnrollmentId") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); b.HasIndex("VehicleEnrollmentId"); - b.ToTable("Tickets", (string)null); + b.ToTable("Tickets"); }); modelBuilder.Entity("Server.Models.User", b => @@ -474,7 +499,7 @@ namespace Server.Migrations b.HasIndex("CompanyId"); - b.ToTable("Vehicles", (string)null); + b.ToTable("Vehicles"); }); modelBuilder.Entity("Server.Models.VehicleEnrollment", b => @@ -486,17 +511,13 @@ namespace Server.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); b.Property("CancelationComment") - .IsRequired() .HasColumnType("text"); - b.Property("DelayTimeSpan") + b.Property("DelayTimeSpan") .HasColumnType("interval"); - b.Property("DepartureDateOnly") - .HasColumnType("date"); - - b.Property("DepartureTimeOnlyUtc") - .HasColumnType("time without time zone"); + b.Property("DepartureDateTimeUtc") + .HasColumnType("timestamp with time zone"); b.Property("IsCanceled") .HasColumnType("boolean"); @@ -513,7 +534,7 @@ namespace Server.Migrations b.HasIndex("VehicleId"); - b.ToTable("VehicleEnrollments", (string)null); + b.ToTable("VehicleEnrollments"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => @@ -670,7 +691,7 @@ namespace Server.Migrations modelBuilder.Entity("Server.Models.User", b => { - b.OwnsMany("Server.Models.User.RefreshTokens#Server.Models.RefreshToken", "RefreshTokens", b1 => + b.OwnsMany("Server.Models.RefreshToken", "RefreshTokens", b1 => { b1.Property("UserId") .HasColumnType("text"); @@ -696,7 +717,7 @@ namespace Server.Migrations b1.HasKey("UserId", "Id"); - b1.ToTable("RefreshToken", (string)null); + b1.ToTable("RefreshToken"); b1.WithOwner() .HasForeignKey("UserId"); diff --git a/Server/Models/Review.cs b/Server/Models/Review.cs index 90cfac6..9b7490e 100644 --- a/Server/Models/Review.cs +++ b/Server/Models/Review.cs @@ -1,9 +1,13 @@ +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Server.Models; public class Review { + [Key] + public int Id { get; set; } + [ForeignKey("UserId")] public string UserId { get; set; } = null!; public User User { get; set; } = null!; @@ -13,5 +17,5 @@ public class Review public VehicleEnrollment VehicleEnrollment { get; set; } = null!; public int Rating { get; set; } - public string Comment { get; set; } = null!; + public string? Comment { get; set; } } \ No newline at end of file diff --git a/Server/Models/RouteAddress.cs b/Server/Models/RouteAddress.cs index d72de61..a97874b 100644 --- a/Server/Models/RouteAddress.cs +++ b/Server/Models/RouteAddress.cs @@ -1,10 +1,13 @@ +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using SharedModels.DataTransferObjects; namespace Server.Models; public class RouteAddress { + [Key] + public int Id { get; set; } + [ForeignKey("RouteId")] public int RouteId { get; set; } public Route Route { get; set; } = null!; diff --git a/Server/Models/Ticket.cs b/Server/Models/Ticket.cs index e798c84..e244a42 100644 --- a/Server/Models/Ticket.cs +++ b/Server/Models/Ticket.cs @@ -1,9 +1,13 @@ +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Server.Models; public class Ticket { + [Key] + public int Id { get; set; } + [ForeignKey("UserId")] public string UserId { get; set; } = null!; public User User { get; set; } = null!; diff --git a/Server/Models/VehicleEnrollment.cs b/Server/Models/VehicleEnrollment.cs index 840c9d7..554dca4 100644 --- a/Server/Models/VehicleEnrollment.cs +++ b/Server/Models/VehicleEnrollment.cs @@ -16,11 +16,10 @@ public class VehicleEnrollment public int RouteId { get; set; } public Route Route { get; set; } = null!; - public DateOnly DepartureDateOnly { get; set; } - public TimeOnly DepartureTimeOnlyUtc { get; set; } + public DateTime DepartureDateTimeUtc { get; set; } - public TimeSpan DelayTimeSpan { get; set; } - - public bool IsCanceled {get; set; } - public string CancelationComment { get; set; } = null!; + public TimeSpan? DelayTimeSpan { get; set; } + + public bool IsCanceled { get; set; } = false; + public string? CancelationComment { get; set; } = null!; } \ No newline at end of file diff --git a/Server/Program.cs b/Server/Program.cs index 3c5e85d..48f9fad 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -11,6 +11,7 @@ using Server.Data; using Server.Helpers; using Server.Models; using Server.Services; +using Route = Server.Models.Route; var builder = WebApplication.CreateBuilder(args); @@ -94,18 +95,37 @@ builder.Services.AddScoped( builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); - -builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped, SortHelper>(); builder.Services.AddScoped, SortHelper>(); builder.Services.AddScoped, SortHelper>(); builder.Services.AddScoped, SortHelper
>(); +builder.Services.AddScoped, SortHelper>(); +builder.Services.AddScoped, SortHelper>(); +builder.Services.AddScoped, SortHelper>(); +builder.Services.AddScoped, SortHelper>(); +builder.Services.AddScoped, SortHelper>(); +builder.Services.AddScoped, SortHelper>(); +builder.Services.AddScoped, SortHelper>(); builder.Services.AddScoped, DataShaper>(); builder.Services.AddScoped, DataShaper>(); builder.Services.AddScoped, DataShaper>(); builder.Services.AddScoped, DataShaper
>(); +builder.Services.AddScoped, DataShaper>(); +builder.Services.AddScoped, DataShaper>(); +builder.Services.AddScoped, DataShaper>(); +builder.Services.AddScoped, DataShaper>(); +builder.Services.AddScoped, DataShaper>(); +builder.Services.AddScoped, DataShaper>(); +builder.Services.AddScoped, DataShaper>(); // Adding DB Context with PostgreSQL var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); @@ -124,10 +144,10 @@ if (app.Environment.IsDevelopment()) app.UseHttpsRedirection(); // Data seeding -using var scope = app.Services.CreateScope(); -var userManager = (UserManager)scope.ServiceProvider.GetService(typeof(UserManager))!; -var roleManager = (RoleManager)scope.ServiceProvider.GetService(typeof(RoleManager))!; -await ApplicationDbContextSeed.SeedEssentialsAsync(userManager, roleManager); +// using var scope = app.Services.CreateScope(); +// var userManager = (UserManager)scope.ServiceProvider.GetService(typeof(UserManager))!; +// var roleManager = (RoleManager)scope.ServiceProvider.GetService(typeof(RoleManager))!; +// await ApplicationDbContextSeed.SeedEssentialsAsync(userManager, roleManager); app.MapControllers(); diff --git a/Server/Services/AddressManagementService.cs b/Server/Services/AddressManagementService.cs index 9762b1d..3ea8cbf 100644 --- a/Server/Services/AddressManagementService.cs +++ b/Server/Services/AddressManagementService.cs @@ -43,8 +43,8 @@ public class AddressManagementService : IAddressManagementService .AsQueryable(); SearchByAllAddressFields(ref dbAddresses, parameters.Search); - SearchByAddressName(ref dbAddresses, parameters.Name); - SearchByCityId(ref dbAddresses, parameters.CityId); + FilterByAddressName(ref dbAddresses, parameters.Name); + FilterByCityId(ref dbAddresses, parameters.CityId); try { @@ -79,7 +79,7 @@ public class AddressManagementService : IAddressManagementService a.Name.ToLower().Contains(search.ToLower())); } - void SearchByCityId(ref IQueryable
addresses, + void FilterByCityId(ref IQueryable
addresses, int? cityId) { if (!addresses.Any() || cityId == null) @@ -90,7 +90,7 @@ public class AddressManagementService : IAddressManagementService addresses = addresses.Where(a => a.CityId == cityId); } - void SearchByAddressName(ref IQueryable
addresses, + void FilterByAddressName(ref IQueryable
addresses, string? addressName) { if (!addresses.Any() || String.IsNullOrWhiteSpace(addressName)) diff --git a/Server/Services/CityManagementService.cs b/Server/Services/CityManagementService.cs index f32c86e..ad475ee 100644 --- a/Server/Services/CityManagementService.cs +++ b/Server/Services/CityManagementService.cs @@ -43,8 +43,8 @@ public class CityManagementService : ICityManagementService .AsQueryable(); SearchByAllCityFields(ref dbCities, parameters.Search); - SearchByCityName(ref dbCities, parameters.Name); - SearchByStateId(ref dbCities, parameters.StateId); + FilterByCityName(ref dbCities, parameters.Name); + FilterByStateId(ref dbCities, parameters.StateId); try { @@ -79,7 +79,7 @@ public class CityManagementService : ICityManagementService s.Name.ToLower().Contains(search.ToLower())); } - void SearchByStateId(ref IQueryable cities, + void FilterByStateId(ref IQueryable cities, int? stateId) { if (!cities.Any() || stateId == null) @@ -90,7 +90,7 @@ public class CityManagementService : ICityManagementService cities = cities.Where(s => s.StateId == stateId); } - void SearchByCityName(ref IQueryable cities, + void FilterByCityName(ref IQueryable cities, string? cityName) { if (!cities.Any() || String.IsNullOrWhiteSpace(cityName)) diff --git a/Server/Services/CompanyManagementService.cs b/Server/Services/CompanyManagementService.cs new file mode 100644 index 0000000..7ae4fb7 --- /dev/null +++ b/Server/Services/CompanyManagementService.cs @@ -0,0 +1,183 @@ +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using Server.Data; +using Server.Helpers; +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public class CompanyManagementService : ICompanyManagementService +{ + private readonly ApplicationDbContext _dbContext; + private readonly IMapper _mapper; + private readonly ISortHelper _companySortHelper; + private readonly IDataShaper _companyDataShaper; + + public CompanyManagementService(ApplicationDbContext dbContext, + IMapper mapper, ISortHelper companySortHelper, + IDataShaper companyDataShaper) + { + _dbContext = dbContext; + _mapper = mapper; + _companySortHelper = companySortHelper; + _companyDataShaper = companyDataShaper; + } + + public async Task<(bool isSucceed, string message, CompanyDto company)> AddCompany(CreateCompanyDto createCompanyDto) + { + var company = _mapper.Map(createCompanyDto); + + await _dbContext.Companies.AddAsync(company); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty, _mapper.Map(company)); + } + + public async Task<(bool isSucceed, string message, IEnumerable companies, + PagingMetadata pagingMetadata)> GetCompanies(CompanyParameters parameters) + { + var dbCompanies = _dbContext.Companies + .AsQueryable(); + + var v = dbCompanies.Any(); + + SearchByAllCompanyFields(ref dbCompanies, parameters.Search); + FilterByCompanyName(ref dbCompanies, parameters.Name); + FilterByCompanyOwnerId(ref dbCompanies, parameters.OwnerId); + + try + { + dbCompanies = _companySortHelper.ApplySort(dbCompanies, parameters.Sort); + + // By calling Any() we will check if LINQ to Entities Query will be + // executed. If not it will throw an InvalidOperationException exception + var isExecuted = dbCompanies.Any(); + } + catch (Exception e) + { + return (false, "Invalid sorting string", null, null)!; + } + + var pagingMetadata = ApplyPaging(ref dbCompanies, parameters.PageNumber, + parameters.PageSize); + + var shapedCompaniesData = _companyDataShaper.ShapeData(dbCompanies, parameters.Fields); + var companyDtos = shapedCompaniesData.ToList().ConvertAll(c => _mapper.Map(c)); + + return (true, "", companyDtos, pagingMetadata); + + void SearchByAllCompanyFields(ref IQueryable companies, + string? search) + { + if (!companies.Any() || String.IsNullOrWhiteSpace(search)) + { + return; + } + + companies = companies.Where(c => + c.Name.ToLower().Contains(search.ToLower())); + } + + void FilterByCompanyName(ref IQueryable companies, + string? name) + { + if (!companies.Any() || String.IsNullOrWhiteSpace(name)) + { + return; + } + + companies = companies.Where(c => c.Name == name); + } + + void FilterByCompanyOwnerId(ref IQueryable companies, + string? ownerId) + { + if (!companies.Any() || String.IsNullOrWhiteSpace(ownerId)) + { + return; + } + + companies = companies.Where(c => c.OwnerId == ownerId); + } + + PagingMetadata ApplyPaging(ref IQueryable companies, + int pageNumber, int pageSize) + { + var metadata = new PagingMetadata(companies, + pageNumber, pageSize); + + companies = companies + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize); + + return metadata; + } + } + + public async Task<(bool isSucceed, string message, CompanyDto company)> GetCompany(int id, string? fields) + { + var dbCompany = await _dbContext.Companies.Where(c => c.Id == id) + .FirstOrDefaultAsync(); + + if (dbCompany == null) + { + return (false, $"Company doesn't exist", null)!; + } + + if (String.IsNullOrWhiteSpace(fields)) + { + fields = CompanyParameters.DefaultFields; + } + + var shapedCompanyData = _companyDataShaper.ShapeData(dbCompany, fields); + var companyDto = _mapper.Map(shapedCompanyData); + + return (true, "", companyDto); + } + + public async Task<(bool isSucceed, string message, UpdateCompanyDto company)> UpdateCompany(UpdateCompanyDto updateCompanyDto) + { + var company = _mapper.Map(updateCompanyDto); + _dbContext.Entry(company).State = EntityState.Modified; + + try + { + await _dbContext.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!await IsCompanyExists(updateCompanyDto.Id)) + { + return (false, $"Company with id:{updateCompanyDto.Id} doesn'c exist", null)!; + } + + throw; + } + + var dbCompany = await _dbContext.Companies.FirstOrDefaultAsync(c => c.Id == company.Id); + + return (true, String.Empty, _mapper.Map(dbCompany)); + } + + public async Task<(bool isSucceed, string message)> DeleteCompany(int id) + { + var dbCompany = await _dbContext.Companies.FirstOrDefaultAsync(c => c.Id == id); + + if (dbCompany == null) + { + return (false, $"Company with id:{id} doesn't exist"); + } + + _dbContext.Companies.Remove(dbCompany); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty); + } + + public async Task IsCompanyExists(int id) + { + return await _dbContext.Companies.AnyAsync(c => c.Id == id); + } +} \ No newline at end of file diff --git a/Server/Services/CountryManagementService.cs b/Server/Services/CountryManagementService.cs index 58b1bdb..d9b9e91 100644 --- a/Server/Services/CountryManagementService.cs +++ b/Server/Services/CountryManagementService.cs @@ -44,8 +44,8 @@ public class CountryManagementService : ICountryManagementService .AsQueryable(); SearchByAllCountryFields(ref dbCountries, parameters.Search); - SearchByCountryCode(ref dbCountries, parameters.Code); - SearchByCountryName(ref dbCountries, parameters.Name); + FilterByCountryCode(ref dbCountries, parameters.Code); + FilterByCountryName(ref dbCountries, parameters.Name); try { @@ -81,7 +81,7 @@ public class CountryManagementService : ICountryManagementService c.Name.ToLower().Contains(search.ToLower())); } - void SearchByCountryCode(ref IQueryable countries, + void FilterByCountryCode(ref IQueryable countries, string? countryCode) { if (!countries.Any() || String.IsNullOrWhiteSpace(countryCode)) @@ -93,7 +93,7 @@ public class CountryManagementService : ICountryManagementService c.Code.ToLower().Contains(countryCode.Trim().ToLower())); } - void SearchByCountryName(ref IQueryable countries, + void FilterByCountryName(ref IQueryable countries, string? countryName) { if (!countries.Any() || String.IsNullOrWhiteSpace(countryName)) diff --git a/Server/Services/IAddressManagementService.cs b/Server/Services/IAddressManagementService.cs index 736d9a3..f909bf5 100644 --- a/Server/Services/IAddressManagementService.cs +++ b/Server/Services/IAddressManagementService.cs @@ -7,7 +7,6 @@ namespace Server.Services; public interface IAddressManagementService { Task<(bool isSucceed, string message, AddressDto address)> AddAddress(CreateAddressDto createAddressDto); - Task<(bool isSucceed, string message, IEnumerable addresses, PagingMetadata
pagingMetadata)> GetAddresses(AddressParameters parameters); Task<(bool isSucceed, string message, AddressDto address)> GetAddress(int id, string? fields); diff --git a/Server/Services/ICityManagementService.cs b/Server/Services/ICityManagementService.cs index bda8445..4bb203f 100644 --- a/Server/Services/ICityManagementService.cs +++ b/Server/Services/ICityManagementService.cs @@ -7,7 +7,6 @@ namespace Server.Services; public interface ICityManagementService { Task<(bool isSucceed, string message, CityDto city)> AddCity(CreateCityDto createCityDto); - Task<(bool isSucceed, string message, IEnumerable cities, PagingMetadata pagingMetadata)> GetCities(CityParameters parameters); Task<(bool isSucceed, string message, CityDto city)> GetCity(int id, string? fields); diff --git a/Server/Services/ICompanyManagementService.cs b/Server/Services/ICompanyManagementService.cs new file mode 100644 index 0000000..cfcadcd --- /dev/null +++ b/Server/Services/ICompanyManagementService.cs @@ -0,0 +1,16 @@ +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public interface ICompanyManagementService +{ + Task<(bool isSucceed, string message, CompanyDto company)> AddCompany(CreateCompanyDto createCompanyDto); + Task<(bool isSucceed, string message, IEnumerable companies, + PagingMetadata pagingMetadata)> GetCompanies(CompanyParameters parameters); + Task<(bool isSucceed, string message, CompanyDto company)> GetCompany(int id, string? fields); + Task<(bool isSucceed, string message, UpdateCompanyDto company)> UpdateCompany(UpdateCompanyDto updateCompanyDto); + Task<(bool isSucceed, string message)> DeleteCompany(int id); + Task IsCompanyExists(int id); +} \ No newline at end of file diff --git a/Server/Services/ICountryManagementService.cs b/Server/Services/ICountryManagementService.cs index f320171..74741a1 100644 --- a/Server/Services/ICountryManagementService.cs +++ b/Server/Services/ICountryManagementService.cs @@ -7,7 +7,6 @@ namespace Server.Services; public interface ICountryManagementService { Task<(bool isSucceed, string message, CountryDto country)> AddCountry(CreateCountryDto createCountryDto); - Task<(bool isSucceed, string message, IEnumerable countries, PagingMetadata pagingMetadata)> GetCountries(CountryParameters parameters); Task<(bool isSucceed, string message, CountryDto country)> GetCountry(int id, string? fields); diff --git a/Server/Services/IReviewManagementService.cs b/Server/Services/IReviewManagementService.cs new file mode 100644 index 0000000..7e181bb --- /dev/null +++ b/Server/Services/IReviewManagementService.cs @@ -0,0 +1,16 @@ +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public interface IReviewManagementService +{ + Task<(bool isSucceed, string message, ReviewDto review)> AddReview(CreateReviewDto createReviewDto); + Task<(bool isSucceed, string message, IEnumerable reviews, + PagingMetadata pagingMetadata)> GetReviews(ReviewParameters parameters); + Task<(bool isSucceed, string message, ReviewDto review)> GetReview(int id, string? fields); + Task<(bool isSucceed, string message, UpdateReviewDto review)> UpdateReview(UpdateReviewDto updateReviewDto); + Task<(bool isSucceed, string message)> DeleteReview(int id); + Task IsReviewExists(int id); +} \ No newline at end of file diff --git a/Server/Services/IRouteAddressManagementService.cs b/Server/Services/IRouteAddressManagementService.cs new file mode 100644 index 0000000..644a52d --- /dev/null +++ b/Server/Services/IRouteAddressManagementService.cs @@ -0,0 +1,16 @@ +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public interface IRouteAddressManagementService +{ + Task<(bool isSucceed, string message, RouteAddressDto routeAddress)> AddRouteAddress(CreateRouteAddressDto createRouteAddressDto); + Task<(bool isSucceed, string message, IEnumerable routeAddresses, + PagingMetadata pagingMetadata)> GetRouteAddresses(RouteAddressParameters parameters); + Task<(bool isSucceed, string message, RouteAddressDto routeAddress)> GetRouteAddress(int id, string? fields); + Task<(bool isSucceed, string message, UpdateRouteAddressDto routeAddress)> UpdateRouteAddress(UpdateRouteAddressDto updateRouteAddressDto); + Task<(bool isSucceed, string message)> DeleteRouteAddress(int id); + Task IsRouteAddressExists(int id); +} \ No newline at end of file diff --git a/Server/Services/IRouteManagementService.cs b/Server/Services/IRouteManagementService.cs new file mode 100644 index 0000000..7227bad --- /dev/null +++ b/Server/Services/IRouteManagementService.cs @@ -0,0 +1,16 @@ +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; +using Route = Server.Models.Route; + +namespace Server.Services; + +public interface IRouteManagementService +{ + Task<(bool isSucceed, string message, RouteDto route)> AddRoute(CreateRouteDto createRouteDto); + Task<(bool isSucceed, string message, IEnumerable routes, + PagingMetadata pagingMetadata)> GetRoutes(RouteParameters parameters); + Task<(bool isSucceed, string message, RouteDto route)> GetRoute(int id, string? fields); + Task<(bool isSucceed, string message, UpdateRouteDto route)> UpdateRoute(UpdateRouteDto updateRouteDto); + Task<(bool isSucceed, string message)> DeleteRoute(int id); + Task IsRouteExists(int id); +} \ No newline at end of file diff --git a/Server/Services/IStateManagementService.cs b/Server/Services/IStateManagementService.cs index 5d17b60..7daabbe 100644 --- a/Server/Services/IStateManagementService.cs +++ b/Server/Services/IStateManagementService.cs @@ -7,7 +7,6 @@ namespace Server.Services; public interface IStateManagementService { Task<(bool isSucceed, string message, StateDto state)> AddState(CreateStateDto createStateDto); - Task<(bool isSucceed, string message, IEnumerable states, PagingMetadata pagingMetadata)> GetStates(StateParameters parameters); Task<(bool isSucceed, string message, StateDto state)> GetState(int id, string? fields); diff --git a/Server/Services/ITicketManagementService.cs b/Server/Services/ITicketManagementService.cs new file mode 100644 index 0000000..e298bb9 --- /dev/null +++ b/Server/Services/ITicketManagementService.cs @@ -0,0 +1,16 @@ +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public interface ITicketManagementService +{ + Task<(bool isSucceed, string message, TicketDto ticket)> AddTicket(CreateTicketDto createTicketDto); + Task<(bool isSucceed, string message, IEnumerable tickets, + PagingMetadata pagingMetadata)> GetTickets(TicketParameters parameters); + Task<(bool isSucceed, string message, TicketDto ticket)> GetTicket(int id, string? fields); + Task<(bool isSucceed, string message, UpdateTicketDto ticket)> UpdateTicket(UpdateTicketDto updateTicketDto); + Task<(bool isSucceed, string message)> DeleteTicket(int id); + Task IsTicketExists(int id); +} \ No newline at end of file diff --git a/Server/Services/IVehicleEnrollmentManagementService.cs b/Server/Services/IVehicleEnrollmentManagementService.cs new file mode 100644 index 0000000..70e014d --- /dev/null +++ b/Server/Services/IVehicleEnrollmentManagementService.cs @@ -0,0 +1,16 @@ +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public interface IVehicleEnrollmentManagementService +{ + Task<(bool isSucceed, string message, VehicleEnrollmentDto enrollment)> AddEnrollment(CreateVehicleEnrollmentDto createEnrollmentDto); + Task<(bool isSucceed, string message, IEnumerable enrollments, + PagingMetadata pagingMetadata)> GetEnrollments(VehicleEnrollmentParameters parameters); + Task<(bool isSucceed, string message, VehicleEnrollmentDto enrollment)> GetEnrollment(int id, string? fields); + Task<(bool isSucceed, string message, UpdateVehicleEnrollmentDto enrollment)> UpdateEnrollment(UpdateVehicleEnrollmentDto updateEnrollmentDto); + Task<(bool isSucceed, string message)> DeleteEnrollment(int id); + Task IsEnrollmentExists(int id); +} \ No newline at end of file diff --git a/Server/Services/IVehicleManagementService.cs b/Server/Services/IVehicleManagementService.cs new file mode 100644 index 0000000..debbf9d --- /dev/null +++ b/Server/Services/IVehicleManagementService.cs @@ -0,0 +1,16 @@ +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public interface IVehicleManagementService +{ + Task<(bool isSucceed, string message, VehicleDto vehicle)> AddVehicle(CreateVehicleDto createVehicleDto); + Task<(bool isSucceed, string message, IEnumerable vehicles, + PagingMetadata pagingMetadata)> GetVehicles(VehicleParameters parameters); + Task<(bool isSucceed, string message, VehicleDto vehicle)> GetVehicle(int id, string? fields); + Task<(bool isSucceed, string message, UpdateVehicleDto vehicle)> UpdateVehicle(UpdateVehicleDto updateVehicleDto); + Task<(bool isSucceed, string message)> DeleteVehicle(int id); + Task IsVehicleExists(int id); +} \ No newline at end of file diff --git a/Server/Services/ReviewManagementService.cs b/Server/Services/ReviewManagementService.cs new file mode 100644 index 0000000..6fb8d8d --- /dev/null +++ b/Server/Services/ReviewManagementService.cs @@ -0,0 +1,170 @@ +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using Server.Data; +using Server.Helpers; +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public class ReviewManagementService : IReviewManagementService +{ + private readonly ApplicationDbContext _dbContext; + private readonly IMapper _mapper; + private readonly ISortHelper _reviewSortHelper; + private readonly IDataShaper _reviewDataShaper; + + public ReviewManagementService(ApplicationDbContext dbContext, + IMapper mapper, ISortHelper reviewSortHelper, + IDataShaper reviewDataShaper) + { + _dbContext = dbContext; + _mapper = mapper; + _reviewSortHelper = reviewSortHelper; + _reviewDataShaper = reviewDataShaper; + } + + public async Task<(bool isSucceed, string message, ReviewDto review)> AddReview(CreateReviewDto createReviewDto) + { + var review = _mapper.Map(createReviewDto); + + await _dbContext.Reviews.AddAsync(review); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty, _mapper.Map(review)); + } + + public async Task<(bool isSucceed, string message, IEnumerable reviews, + PagingMetadata pagingMetadata)> GetReviews(ReviewParameters parameters) + { + var dbReviews = _dbContext.Reviews + .AsQueryable(); + + FilterByReviewRating(ref dbReviews, parameters.Rating); + FilterByReviewComment(ref dbReviews, parameters.Comment); + + try + { + dbReviews = _reviewSortHelper.ApplySort(dbReviews, parameters.Sort); + + // By calling Any() we will check if LINQ to Entities Query will be + // executed. If not it will throw an InvalidOperationException exception + var isExecuted = dbReviews.Any(); + } + catch (Exception e) + { + return (false, "Invalid sorting string", null, null)!; + } + + var pagingMetadata = ApplyPaging(ref dbReviews, parameters.PageNumber, + parameters.PageSize); + + var shapedReviewsData = _reviewDataShaper.ShapeData(dbReviews, parameters.Fields); + var reviewDtos = shapedReviewsData.ToList().ConvertAll(r => _mapper.Map(r)); + + return (true, "", reviewDtos, pagingMetadata); + + void FilterByReviewRating(ref IQueryable reviews, + int? rating) + { + if (!reviews.Any() || rating == null) + { + return; + } + + reviews = reviews.Where(r => r.Rating == rating); + } + + void FilterByReviewComment(ref IQueryable reviews, + string? comment) + { + if (!reviews.Any() || String.IsNullOrWhiteSpace(comment)) + { + return; + } + + reviews = reviews.Where(r => + r.Comment != null && + r.Comment.ToLower().Contains(comment.ToLower())); + } + + PagingMetadata ApplyPaging(ref IQueryable reviews, + int pageNumber, int pageSize) + { + var metadata = new PagingMetadata(reviews, + pageNumber, pageSize); + + reviews = reviews + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize); + + return metadata; + } + } + + public async Task<(bool isSucceed, string message, ReviewDto review)> GetReview(int id, string? fields) + { + var dbReview = await _dbContext.Reviews.Where(r => r.Id == id) + .FirstOrDefaultAsync(); + + if (dbReview == null) + { + return (false, $"Review doesn't exist", null)!; + } + + if (String.IsNullOrWhiteSpace(fields)) + { + fields = ReviewParameters.DefaultFields; + } + + var shapedReviewData = _reviewDataShaper.ShapeData(dbReview, fields); + var reviewDto = _mapper.Map(shapedReviewData); + + return (true, "", reviewDto); + } + + public async Task<(bool isSucceed, string message, UpdateReviewDto review)> UpdateReview(UpdateReviewDto updateReviewDto) + { + var review = _mapper.Map(updateReviewDto); + _dbContext.Entry(review).State = EntityState.Modified; + + try + { + await _dbContext.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!await IsReviewExists(updateReviewDto.Id)) + { + return (false, $"Review with id:{updateReviewDto.Id} doesn't exist", null)!; + } + + throw; + } + + var dbReview = await _dbContext.Reviews.FirstOrDefaultAsync(r => r.Id == review.Id); + + return (true, String.Empty, _mapper.Map(dbReview)); + } + + public async Task<(bool isSucceed, string message)> DeleteReview(int id) + { + var dbReview = await _dbContext.Reviews.FirstOrDefaultAsync(r => r.Id == id); + + if (dbReview == null) + { + return (false, $"Review with id:{id} doesn't exist"); + } + + _dbContext.Reviews.Remove(dbReview); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty); + } + + public async Task IsReviewExists(int id) + { + return await _dbContext.Reviews.AnyAsync(r => r.Id == id); + } +} \ No newline at end of file diff --git a/Server/Services/RouteAddressManagementService.cs b/Server/Services/RouteAddressManagementService.cs new file mode 100644 index 0000000..5f3bd25 --- /dev/null +++ b/Server/Services/RouteAddressManagementService.cs @@ -0,0 +1,168 @@ +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using Server.Data; +using Server.Helpers; +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public class RouteAddressManagementService : IRouteAddressManagementService +{ + private readonly ApplicationDbContext _dbContext; + private readonly IMapper _mapper; + private readonly ISortHelper _routeAddressSortHelper; + private readonly IDataShaper _routeAddressDataShaper; + + public RouteAddressManagementService(ApplicationDbContext dbContext, + IMapper mapper, ISortHelper routeAddressSortHelper, + IDataShaper routeAddressDataShaper) + { + _dbContext = dbContext; + _mapper = mapper; + _routeAddressSortHelper = routeAddressSortHelper; + _routeAddressDataShaper = routeAddressDataShaper; + } + + public async Task<(bool isSucceed, string message, RouteAddressDto routeAddress)> AddRouteAddress(CreateRouteAddressDto createRouteAddressDto) + { + var routeAddress = _mapper.Map(createRouteAddressDto); + + await _dbContext.RouteAddresses.AddAsync(routeAddress); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty, _mapper.Map(routeAddress)); + } + + public async Task<(bool isSucceed, string message, IEnumerable routeAddresses, + PagingMetadata pagingMetadata)> GetRouteAddresses(RouteAddressParameters parameters) + { + var dbRouteAddresses = _dbContext.RouteAddresses + .AsQueryable(); + + FilterByRouteAddressRouteId(ref dbRouteAddresses, parameters.RouteId); + FilterByRouteAddressAddressId(ref dbRouteAddresses, parameters.AddressId); + + try + { + dbRouteAddresses = _routeAddressSortHelper.ApplySort(dbRouteAddresses, parameters.Sort); + + // By calling Any() we will check if LINQ to Entities Query will be + // executed. If not it will throw an InvalidOperationException exception + var isExecuted = dbRouteAddresses.Any(); + } + catch (Exception e) + { + return (false, "Invalid sorting string", null, null)!; + } + + var pagingMetadata = ApplyPaging(ref dbRouteAddresses, parameters.PageNumber, + parameters.PageSize); + + var shapedRouteAddressesData = _routeAddressDataShaper.ShapeData(dbRouteAddresses, parameters.Fields); + var routeAddressDtos = shapedRouteAddressesData.ToList().ConvertAll(ra => _mapper.Map(ra)); + + return (true, "", routeAddressDtos, pagingMetadata); + + void FilterByRouteAddressRouteId(ref IQueryable routeAddresses, + int? routeId) + { + if (!routeAddresses.Any() || routeId == null) + { + return; + } + + routeAddresses = routeAddresses.Where(ra => ra.RouteId == routeId); + } + + void FilterByRouteAddressAddressId(ref IQueryable routeAddresses, + int? addressId) + { + if (!routeAddresses.Any() || addressId == null) + { + return; + } + + routeAddresses = routeAddresses.Where(ra => ra.AddressId == addressId); + } + + PagingMetadata ApplyPaging(ref IQueryable routeAddresses, + int pageNumber, int pageSize) + { + var metadata = new PagingMetadata(routeAddresses, + pageNumber, pageSize); + + routeAddresses = routeAddresses + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize); + + return metadata; + } + } + + public async Task<(bool isSucceed, string message, RouteAddressDto routeAddress)> GetRouteAddress(int id, string? fields) + { + var dbRouteAddress = await _dbContext.RouteAddresses.Where(ra => ra.Id == id) + .FirstOrDefaultAsync(); + + if (dbRouteAddress == null) + { + return (false, $"RouteAddress doesn't exist", null)!; + } + + if (String.IsNullOrWhiteSpace(fields)) + { + fields = RouteAddressParameters.DefaultFields; + } + + var shapedRouteAddressData = _routeAddressDataShaper.ShapeData(dbRouteAddress, fields); + var routeAddressDto = _mapper.Map(shapedRouteAddressData); + + return (true, "", routeAddressDto); + } + + public async Task<(bool isSucceed, string message, UpdateRouteAddressDto routeAddress)> UpdateRouteAddress(UpdateRouteAddressDto updateRouteAddressDto) + { + var routeAddress = _mapper.Map(updateRouteAddressDto); + _dbContext.Entry(routeAddress).State = EntityState.Modified; + + try + { + await _dbContext.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!await IsRouteAddressExists(updateRouteAddressDto.Id)) + { + return (false, $"RouteAddress with id:{updateRouteAddressDto.Id} doesn't exist", null)!; + } + + throw; + } + + var dbRouteAddress = await _dbContext.RouteAddresses.FirstOrDefaultAsync(ra => ra.Id == routeAddress.Id); + + return (true, String.Empty, _mapper.Map(dbRouteAddress)); + } + + public async Task<(bool isSucceed, string message)> DeleteRouteAddress(int id) + { + var dbRouteAddress = await _dbContext.RouteAddresses.FirstOrDefaultAsync(ra => ra.Id == id); + + if (dbRouteAddress == null) + { + return (false, $"RouteAddress with id:{id} doesn't exist"); + } + + _dbContext.RouteAddresses.Remove(dbRouteAddress); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty); + } + + public async Task IsRouteAddressExists(int id) + { + return await _dbContext.RouteAddresses.AnyAsync(ra => ra.Id == id); + } +} \ No newline at end of file diff --git a/Server/Services/RouteManagementService.cs b/Server/Services/RouteManagementService.cs new file mode 100644 index 0000000..1fb7a71 --- /dev/null +++ b/Server/Services/RouteManagementService.cs @@ -0,0 +1,170 @@ +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using Server.Data; +using Server.Helpers; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; +using Route = Server.Models.Route; + +namespace Server.Services; + +public class RouteManagementService : IRouteManagementService +{ + private readonly ApplicationDbContext _dbContext; + private readonly IMapper _mapper; + private readonly ISortHelper _routeSortHelper; + private readonly IDataShaper _routeDataShaper; + + public RouteManagementService(ApplicationDbContext dbContext, + IMapper mapper, ISortHelper routeSortHelper, + IDataShaper routeDataShaper) + { + _dbContext = dbContext; + _mapper = mapper; + _routeSortHelper = routeSortHelper; + _routeDataShaper = routeDataShaper; + } + + public async Task<(bool isSucceed, string message, RouteDto route)> AddRoute(CreateRouteDto createRouteDto) + { + var route = _mapper.Map(createRouteDto); + + await _dbContext.Routes.AddAsync(route); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty, _mapper.Map(route)); + } + + public async Task<(bool isSucceed, string message, IEnumerable routes, + PagingMetadata pagingMetadata)> GetRoutes(RouteParameters parameters) + { + var dbRoutes = _dbContext.Routes + .AsQueryable(); + + SearchByAllRouteFields(ref dbRoutes, parameters.Search); + FilterByRouteType(ref dbRoutes, parameters.Type); + + try + { + dbRoutes = _routeSortHelper.ApplySort(dbRoutes, parameters.Sort); + + // By calling Any() we will check if LINQ to Entities Query will be + // executed. If not it will throw an InvalidOperationException exception + var isExecuted = dbRoutes.Any(); + } + catch (Exception e) + { + return (false, "Invalid sorting string", null, null)!; + } + + var pagingMetadata = ApplyPaging(ref dbRoutes, parameters.PageNumber, + parameters.PageSize); + + var shapedRoutesData = _routeDataShaper.ShapeData(dbRoutes, parameters.Fields); + var routeDtos = shapedRoutesData.ToList().ConvertAll(r => _mapper.Map(r)); + + return (true, "", routeDtos, pagingMetadata); + + void SearchByAllRouteFields(ref IQueryable route, + string? search) + { + if (!route.Any() || String.IsNullOrWhiteSpace(search)) + { + return; + } + + route = route.Where(c => + c.Type.ToLower().Contains(search.ToLower())); + } + + void FilterByRouteType(ref IQueryable routes, + string? type) + { + if (!routes.Any() || String.IsNullOrWhiteSpace(type)) + { + return; + } + + routes = routes.Where(r => r.Type == type); + } + + + PagingMetadata ApplyPaging(ref IQueryable routes, + int pageNumber, int pageSize) + { + var metadata = new PagingMetadata(routes, + pageNumber, pageSize); + + routes = routes + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize); + + return metadata; + } + } + + public async Task<(bool isSucceed, string message, RouteDto route)> GetRoute(int id, string? fields) + { + var dbRoute = await _dbContext.Routes.Where(r => r.Id == id) + .FirstOrDefaultAsync(); + + if (dbRoute == null) + { + return (false, $"Route doesn't exist", null)!; + } + + if (String.IsNullOrWhiteSpace(fields)) + { + fields = RouteParameters.DefaultFields; + } + + var shapedRouteData = _routeDataShaper.ShapeData(dbRoute, fields); + var routeDto = _mapper.Map(shapedRouteData); + + return (true, "", routeDto); + } + + public async Task<(bool isSucceed, string message, UpdateRouteDto route)> UpdateRoute(UpdateRouteDto updateRouteDto) + { + var route = _mapper.Map(updateRouteDto); + _dbContext.Entry(route).State = EntityState.Modified; + + try + { + await _dbContext.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!await IsRouteExists(updateRouteDto.Id)) + { + return (false, $"Route with id:{updateRouteDto.Id} doesn't exist", null)!; + } + + throw; + } + + var dbRoute = await _dbContext.Routes.FirstOrDefaultAsync(r => r.Id == route.Id); + + return (true, String.Empty, _mapper.Map(dbRoute)); + } + + public async Task<(bool isSucceed, string message)> DeleteRoute(int id) + { + var dbRoute = await _dbContext.Routes.FirstOrDefaultAsync(r => r.Id == id); + + if (dbRoute == null) + { + return (false, $"Route with id:{id} doesn't exist"); + } + + _dbContext.Routes.Remove(dbRoute); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty); + } + + public async Task IsRouteExists(int id) + { + return await _dbContext.Routes.AnyAsync(r => r.Id == id); + } +} \ No newline at end of file diff --git a/Server/Services/StateManagementService.cs b/Server/Services/StateManagementService.cs index 68c954f..0409de0 100644 --- a/Server/Services/StateManagementService.cs +++ b/Server/Services/StateManagementService.cs @@ -44,8 +44,8 @@ public class StateManagementService : IStateManagementService .ThenInclude(c => c.Addresses).AsQueryable(); SearchByAllStateFields(ref dbStates, parameters.Search); - SearchByStateName(ref dbStates, parameters.Name); - SearchByCountryId(ref dbStates, parameters.CountryId); + FilterByStateName(ref dbStates, parameters.Name); + FilterByCountryId(ref dbStates, parameters.CountryId); try { @@ -80,7 +80,7 @@ public class StateManagementService : IStateManagementService s.Name.ToLower().Contains(search.ToLower())); } - void SearchByCountryId(ref IQueryable states, + void FilterByCountryId(ref IQueryable states, int? countryId) { if (!states.Any() || countryId == null) @@ -91,7 +91,7 @@ public class StateManagementService : IStateManagementService states = states.Where(s => s.CountryId == countryId); } - void SearchByStateName(ref IQueryable states, + void FilterByStateName(ref IQueryable states, string? stateName) { if (!states.Any() || String.IsNullOrWhiteSpace(stateName)) diff --git a/Server/Services/TicketManagementService.cs b/Server/Services/TicketManagementService.cs new file mode 100644 index 0000000..c48af5a --- /dev/null +++ b/Server/Services/TicketManagementService.cs @@ -0,0 +1,172 @@ +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using Server.Data; +using Server.Helpers; +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public class TicketManagementService : ITicketManagementService +{ + private readonly ApplicationDbContext _dbContext; + private readonly IMapper _mapper; + private readonly ISortHelper _ticketSortHelper; + private readonly IDataShaper _ticketDataShaper; + + public TicketManagementService(ApplicationDbContext dbContext, + IMapper mapper, ISortHelper ticketSortHelper, + IDataShaper ticketDataShaper) + { + _dbContext = dbContext; + _mapper = mapper; + _ticketSortHelper = ticketSortHelper; + _ticketDataShaper = ticketDataShaper; + } + + public async Task<(bool isSucceed, string message, TicketDto ticket)> AddTicket(CreateTicketDto createTicketDto) + { + var ticket = _mapper.Map(createTicketDto); + + await _dbContext.Tickets.AddAsync(ticket); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty, _mapper.Map(ticket)); + } + + public async Task<(bool isSucceed, string message, IEnumerable tickets, + PagingMetadata pagingMetadata)> GetTickets(TicketParameters parameters) + { + var dbTickets = _dbContext.Tickets + .AsQueryable(); + + FilterByTicketPurchaseDateTime(ref dbTickets, + parameters.FromPurchaseDateTimeUtc, + parameters.ToPurchaseDateTimeUtc); + FilterByTicketReturnedState(ref dbTickets, parameters.IsReturned); + + try + { + dbTickets = _ticketSortHelper.ApplySort(dbTickets, parameters.Sort); + + // By calling Any() we will check if LINQ to Entities Query will be + // executed. If not it will throw an InvalidOperationException exception + var isExecuted = dbTickets.Any(); + } + catch (Exception e) + { + return (false, "Invalid sorting string", null, null)!; + } + + var pagingMetadata = ApplyPaging(ref dbTickets, parameters.PageNumber, + parameters.PageSize); + + var shapedTicketsData = _ticketDataShaper.ShapeData(dbTickets, parameters.Fields); + var ticketDtos = shapedTicketsData.ToList().ConvertAll(t => _mapper.Map(t)); + + return (true, "", ticketDtos, pagingMetadata); + + void FilterByTicketPurchaseDateTime(ref IQueryable tickets, + DateTime? fromDateTime, DateTime? toDateTime) + { + if (!tickets.Any() || fromDateTime == null || toDateTime == null) + { + return; + } + + tickets = tickets.Where(t => + t.PurchaseDateTimeUtc >= fromDateTime.Value.ToUniversalTime() && + t.PurchaseDateTimeUtc <= toDateTime.Value.ToUniversalTime()); + } + + void FilterByTicketReturnedState(ref IQueryable tickets, + bool? isReturned) + { + if (!tickets.Any() || !isReturned.HasValue) + { + return; + } + + tickets = tickets.Where(t => t.IsReturned == isReturned); + } + + PagingMetadata ApplyPaging(ref IQueryable tickets, + int pageNumber, int pageSize) + { + var metadata = new PagingMetadata(tickets, + pageNumber, pageSize); + + tickets = tickets + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize); + + return metadata; + } + } + + public async Task<(bool isSucceed, string message, TicketDto ticket)> GetTicket(int id, string? fields) + { + var dbTicket = await _dbContext.Tickets.Where(t => t.Id == id) + .FirstOrDefaultAsync(); + + if (dbTicket == null) + { + return (false, $"Ticket doesn't exist", null)!; + } + + if (String.IsNullOrWhiteSpace(fields)) + { + fields = TicketParameters.DefaultFields; + } + + var shapedTicketData = _ticketDataShaper.ShapeData(dbTicket, fields); + var ticketDto = _mapper.Map(shapedTicketData); + + return (true, "", ticketDto); + } + + public async Task<(bool isSucceed, string message, UpdateTicketDto ticket)> UpdateTicket(UpdateTicketDto updateTicketDto) + { + var ticket = _mapper.Map(updateTicketDto); + _dbContext.Entry(ticket).State = EntityState.Modified; + + try + { + await _dbContext.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!await IsTicketExists(updateTicketDto.Id)) + { + return (false, $"Ticket with id:{updateTicketDto.Id} doesn't exist", null)!; + } + + throw; + } + + var dbTicket = await _dbContext.Tickets.FirstOrDefaultAsync(t => t.Id == ticket.Id); + + return (true, String.Empty, _mapper.Map(dbTicket)); + } + + public async Task<(bool isSucceed, string message)> DeleteTicket(int id) + { + var dbTicket = await _dbContext.Tickets.FirstOrDefaultAsync(t => t.Id == id); + + if (dbTicket == null) + { + return (false, $"Ticket with id:{id} doesn't exist"); + } + + _dbContext.Tickets.Remove(dbTicket); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty); + } + + public async Task IsTicketExists(int id) + { + return await _dbContext.Tickets.AnyAsync(t => t.Id == id); + } +} \ No newline at end of file diff --git a/Server/Services/VehicleEnrollmentManagementService.cs b/Server/Services/VehicleEnrollmentManagementService.cs new file mode 100644 index 0000000..75e82ee --- /dev/null +++ b/Server/Services/VehicleEnrollmentManagementService.cs @@ -0,0 +1,222 @@ +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using Server.Data; +using Server.Helpers; +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public class VehicleEnrollmentManagementService : IVehicleEnrollmentManagementService +{ + private readonly ApplicationDbContext _dbContext; + private readonly IMapper _mapper; + private readonly ISortHelper _enrollmentSortHelper; + private readonly IDataShaper _enrollmentDataShaper; + + public VehicleEnrollmentManagementService(ApplicationDbContext dbContext, + IMapper mapper, ISortHelper enrollmentSortHelper, + IDataShaper enrollmentDataShaper) + { + _dbContext = dbContext; + _mapper = mapper; + _enrollmentSortHelper = enrollmentSortHelper; + _enrollmentDataShaper = enrollmentDataShaper; + } + + public async Task<(bool isSucceed, string message, VehicleEnrollmentDto enrollment)> AddEnrollment(CreateVehicleEnrollmentDto createEnrollmentDto) + { + var enrollment = _mapper.Map(createEnrollmentDto); + + await _dbContext.VehicleEnrollments.AddAsync(enrollment); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty, _mapper.Map(enrollment)); + } + + public async Task<(bool isSucceed, string message, IEnumerable enrollments, + PagingMetadata pagingMetadata)> GetEnrollments(VehicleEnrollmentParameters parameters) + { + var dbEnrollments = _dbContext.VehicleEnrollments + .AsQueryable(); + + SearchByAllEnrollmentFields(ref dbEnrollments, parameters.Search); + FilterByEnrollmentVehicleId(ref dbEnrollments, parameters.VehicleId); + FilterByEnrollmentRouteId(ref dbEnrollments, parameters.RouteId); + FilterByEnrollmentDepartureDateTime(ref dbEnrollments, + parameters.FromDepartureDateTime, parameters.ToDepartureDateTime); + FilterByEnrollmentDelayedValue(ref dbEnrollments, parameters.IsDelayed); + FilterByEnrollmentCancelledValue(ref dbEnrollments, parameters.IsCanceled); + + try + { + dbEnrollments = _enrollmentSortHelper.ApplySort(dbEnrollments, parameters.Sort); + + // By calling Any() we will check if LINQ to Entities Query will be + // executed. If not it will throw an InvalidOperationException exception + var isExecuted = dbEnrollments.Any(); + } + catch (Exception e) + { + return (false, "Invalid sorting string", null, null)!; + } + + var pagingMetadata = ApplyPaging(ref dbEnrollments, parameters.PageNumber, + parameters.PageSize); + + var shapedEnrollmentsData = _enrollmentDataShaper.ShapeData(dbEnrollments, parameters.Fields); + var enrollmentDtos = shapedEnrollmentsData.ToList().ConvertAll(e => _mapper.Map(e)); + + return (true, "", enrollmentDtos, pagingMetadata); + + void SearchByAllEnrollmentFields(ref IQueryable enrollment, + string? search) + { + if (!enrollment.Any() || String.IsNullOrWhiteSpace(search)) + { + return; + } + + enrollment = enrollment.Where(e => + e.CancelationComment.ToLower().Contains(search.ToLower())); + } + + void FilterByEnrollmentVehicleId(ref IQueryable enrollments, + int? vehicleId) + { + if (!enrollments.Any() || vehicleId == null) + { + return; + } + + enrollments = enrollments.Where(e => e.VehicleId == vehicleId); + } + + void FilterByEnrollmentRouteId(ref IQueryable enrollments, + int? routeId) + { + if (!enrollments.Any() || routeId == null) + { + return; + } + + enrollments = enrollments.Where(e => e.RouteId == routeId); + } + + void FilterByEnrollmentDepartureDateTime(ref IQueryable enrollments, + DateTime? fromDateTime, DateTime? toDateTime) + { + if (!enrollments.Any() || fromDateTime == null || toDateTime == null) + { + return; + } + + enrollments = enrollments.Where(e => + e.DepartureDateTimeUtc >= fromDateTime.Value.ToUniversalTime() && + e.DepartureDateTimeUtc <= toDateTime.Value.ToUniversalTime()); + } + + void FilterByEnrollmentDelayedValue(ref IQueryable enrollments, + bool? isDelayed) + { + if (!enrollments.Any() || !isDelayed.HasValue) + { + return; + } + + enrollments = isDelayed.Value + ? enrollments.Where(e => e.DelayTimeSpan != null) + : enrollments.Where(e => e.DelayTimeSpan == null); + } + + void FilterByEnrollmentCancelledValue(ref IQueryable enrollments, + bool? isCancelled) + { + if (!enrollments.Any() || !isCancelled.HasValue) + { + return; + } + + enrollments = enrollments.Where(e => e.IsCanceled == isCancelled); + } + + PagingMetadata ApplyPaging(ref IQueryable enrollments, + int pageNumber, int pageSize) + { + var metadata = new PagingMetadata(enrollments, + pageNumber, pageSize); + + enrollments = enrollments + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize); + + return metadata; + } + } + + public async Task<(bool isSucceed, string message, VehicleEnrollmentDto enrollment)> GetEnrollment(int id, string? fields) + { + var dbEnrollment = await _dbContext.VehicleEnrollments.Where(e => e.Id == id) + .FirstOrDefaultAsync(); + + if (dbEnrollment == null) + { + return (false, $"Enrollment doesn't exist", null)!; + } + + if (String.IsNullOrWhiteSpace(fields)) + { + fields = VehicleEnrollmentParameters.DefaultFields; + } + + var shapedEnrollmentData = _enrollmentDataShaper.ShapeData(dbEnrollment, fields); + var enrollmentDto = _mapper.Map(shapedEnrollmentData); + + return (true, "", enrollmentDto); + } + + public async Task<(bool isSucceed, string message, UpdateVehicleEnrollmentDto enrollment)> UpdateEnrollment(UpdateVehicleEnrollmentDto updateEnrollmentDto) + { + var enrollment = _mapper.Map(updateEnrollmentDto); + _dbContext.Entry(enrollment).State = EntityState.Modified; + + try + { + await _dbContext.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!await IsEnrollmentExists(updateEnrollmentDto.Id)) + { + return (false, $"Enrollment with id:{updateEnrollmentDto.Id} doesn't exist", null)!; + } + + throw; + } + + var dbEnrollment = await _dbContext.VehicleEnrollments.FirstOrDefaultAsync(e => e.Id == enrollment.Id); + + return (true, String.Empty, _mapper.Map(dbEnrollment)); + } + + public async Task<(bool isSucceed, string message)> DeleteEnrollment(int id) + { + var dbEnrollment = await _dbContext.VehicleEnrollments.FirstOrDefaultAsync(e => e.Id == id); + + if (dbEnrollment == null) + { + return (false, $"Enrollment with id:{id} doesn't exist"); + } + + _dbContext.VehicleEnrollments.Remove(dbEnrollment); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty); + } + + public async Task IsEnrollmentExists(int id) + { + return await _dbContext.VehicleEnrollments.AnyAsync(e => e.Id == id); + } +} \ No newline at end of file diff --git a/Server/Services/VehicleManagementService.cs b/Server/Services/VehicleManagementService.cs new file mode 100644 index 0000000..93febb6 --- /dev/null +++ b/Server/Services/VehicleManagementService.cs @@ -0,0 +1,306 @@ +using AutoMapper; +using Microsoft.EntityFrameworkCore; +using Server.Data; +using Server.Helpers; +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryStringParameters; + +namespace Server.Services; + +public class VehicleManagementService : IVehicleManagementService +{ + private readonly ApplicationDbContext _dbContext; + private readonly IMapper _mapper; + private readonly ISortHelper _vehicleSortHelper; + private readonly IDataShaper _vehicleDataShaper; + + public VehicleManagementService(ApplicationDbContext dbContext, + IMapper mapper, ISortHelper vehicleSortHelper, + IDataShaper vehicleDataShaper) + { + _dbContext = dbContext; + _mapper = mapper; + _vehicleSortHelper = vehicleSortHelper; + _vehicleDataShaper = vehicleDataShaper; + } + + public async Task<(bool isSucceed, string message, VehicleDto vehicle)> AddVehicle(CreateVehicleDto createVehicleDto) + { + var vehicle = _mapper.Map(createVehicleDto); + + await _dbContext.Vehicles.AddAsync(vehicle); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty, _mapper.Map(vehicle)); + } + + public async Task<(bool isSucceed, string message, IEnumerable vehicles, + PagingMetadata pagingMetadata)> GetVehicles(VehicleParameters parameters) + { + var dbVehicles = _dbContext.Vehicles + .AsQueryable(); + + bool a = dbVehicles.Any(); + + SearchByAllVehicleFields(ref dbVehicles, parameters.Search); + a = dbVehicles.Any(); + FilterByVehicleNumber(ref dbVehicles, parameters.Number); + a = dbVehicles.Any(); + FilterByVehicleType(ref dbVehicles, parameters.Type); + a = dbVehicles.Any(); + FilterByVehicleCapacity(ref dbVehicles, parameters.FromCapacity, + parameters.ToCapacity); + a = dbVehicles.Any(); + FilterByVehicleClimateControlAvailability(ref dbVehicles, parameters.HasClimateControl); + a = dbVehicles.Any(); + FilterByVehicleWiFiAvailability(ref dbVehicles, parameters.HasWiFi); + a = dbVehicles.Any(); + FilterByVehicleWCAvailability(ref dbVehicles, parameters.HasWC); + a = dbVehicles.Any(); + FilterByStewardessAvailability(ref dbVehicles, parameters.HasStewardess); + a = dbVehicles.Any(); + FilterByVehicleTVAvailability(ref dbVehicles, parameters.HasTV); + a = dbVehicles.Any(); + FilterByVehicleOutletAvailability(ref dbVehicles, parameters.HasOutlet); + a = dbVehicles.Any(); + FilterByVehicleBeltsAvailability(ref dbVehicles, parameters.HasBelts); + a = dbVehicles.Any(); + FilterByVehicleCompanyId(ref dbVehicles, parameters.CompanyId); + a = dbVehicles.Any(); + + try + { + dbVehicles = _vehicleSortHelper.ApplySort(dbVehicles, parameters.Sort); + + // By calling Any() we will check if LINQ to Entities Query will be + // executed. If not it will throw an InvalidOperationException exception + var isExecuted = dbVehicles.Any(); + } + catch (Exception e) + { + return (false, "Invalid sorting string", null, null)!; + } + + var pagingMetadata = ApplyPaging(ref dbVehicles, parameters.PageNumber, + parameters.PageSize); + + var shapedVehiclesData = _vehicleDataShaper.ShapeData(dbVehicles, parameters.Fields); + var vehicleDtos = shapedVehiclesData.ToList().ConvertAll(v => _mapper.Map(v)); + + return (true, "", vehicleDtos, pagingMetadata); + + void SearchByAllVehicleFields(ref IQueryable vehicle, + string? search) + { + if (!vehicle.Any() || String.IsNullOrWhiteSpace(search)) + { + return; + } + + vehicle = vehicle.Where(v => + v.Number.ToLower().Contains(search.ToLower()) || + v.Type.ToLower().Contains(search.ToLower())); + } + + void FilterByVehicleNumber(ref IQueryable vehicles, + string? number) + { + if (!vehicles.Any() || String.IsNullOrWhiteSpace(number)) + { + return; + } + + vehicles = vehicles.Where(v => v.Number == number); + } + + void FilterByVehicleType(ref IQueryable vehicles, + string? type) + { + if (!vehicles.Any() || String.IsNullOrWhiteSpace(type)) + { + return; + } + + vehicles = vehicles.Where(v => v.Type == type); + } + + void FilterByVehicleCapacity(ref IQueryable vehicles, + int? fromCapacity, int? toCapacity) + { + if (!vehicles.Any() || fromCapacity == null && toCapacity == null) + { + return; + } + + vehicles = vehicles.Where(v => v.Capacity >= fromCapacity && + v.Capacity <= toCapacity); + } + + void FilterByVehicleClimateControlAvailability(ref IQueryable vehicles, + bool? hasClimateControl) + { + if (!vehicles.Any() || hasClimateControl == null) + { + return; + } + + vehicles = vehicles.Where(v => v.HasClimateControl == hasClimateControl); + } + + void FilterByVehicleWiFiAvailability(ref IQueryable vehicles, + bool? hasWiFi) + { + if (!vehicles.Any() || hasWiFi == null) + { + return; + } + + vehicles = vehicles.Where(v => v.HasWiFi == hasWiFi); + } + + void FilterByVehicleWCAvailability(ref IQueryable vehicles, + bool? hasWC) + { + if (!vehicles.Any() || hasWC == null) + { + return; + } + + vehicles = vehicles.Where(v => v.HasWC == hasWC); + } + + void FilterByStewardessAvailability(ref IQueryable vehicles, + bool? hasStewardess) + { + if (!vehicles.Any() || hasStewardess == null) + { + return; + } + + vehicles = vehicles.Where(v => v.HasStewardess == hasStewardess); + } + + void FilterByVehicleTVAvailability(ref IQueryable vehicles, + bool? hasTV) + { + if (!vehicles.Any() || hasTV == null) + { + return; + } + + vehicles = vehicles.Where(v => v.HasTV == hasTV); + } + + void FilterByVehicleOutletAvailability(ref IQueryable vehicles, + bool? hasOutlet) + { + if (!vehicles.Any() || hasOutlet == null) + { + return; + } + + vehicles = vehicles.Where(v => v.HasOutlet == hasOutlet); + } + + void FilterByVehicleBeltsAvailability(ref IQueryable vehicles, + bool? hasBelts) + { + if (!vehicles.Any() || hasBelts == null) + { + return; + } + + vehicles = vehicles.Where(v => v.HasBelts == hasBelts); + } + + void FilterByVehicleCompanyId(ref IQueryable vehicles, + int? companyId) + { + if (!vehicles.Any() || companyId == null) + { + return; + } + + vehicles = vehicles.Where(v => v.CompanyId == companyId); + } + + PagingMetadata ApplyPaging(ref IQueryable vehicles, + int pageNumber, int pageSize) + { + var metadata = new PagingMetadata(vehicles, + pageNumber, pageSize); + + vehicles = vehicles + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize); + + return metadata; + } + } + + public async Task<(bool isSucceed, string message, VehicleDto vehicle)> GetVehicle(int id, string? fields) + { + var dbVehicle = await _dbContext.Vehicles.Where(v => v.Id == id) + .FirstOrDefaultAsync(); + + if (dbVehicle == null) + { + return (false, $"Vehicle doesn't exist", null)!; + } + + if (String.IsNullOrWhiteSpace(fields)) + { + fields = VehicleParameters.DefaultFields; + } + + var shapedVehicleData = _vehicleDataShaper.ShapeData(dbVehicle, fields); + var vehicleDto = _mapper.Map(shapedVehicleData); + + return (true, "", vehicleDto); + } + + public async Task<(bool isSucceed, string message, UpdateVehicleDto vehicle)> UpdateVehicle(UpdateVehicleDto updateVehicleDto) + { + var vehicle = _mapper.Map(updateVehicleDto); + _dbContext.Entry(vehicle).State = EntityState.Modified; + + try + { + await _dbContext.SaveChangesAsync(); + } + catch (DbUpdateConcurrencyException) + { + if (!await IsVehicleExists(updateVehicleDto.Id)) + { + return (false, $"Vehicle with id:{updateVehicleDto.Id} doesn't exist", null)!; + } + + throw; + } + + var dbVehicle = await _dbContext.Vehicles.FirstOrDefaultAsync(v => v.Id == vehicle.Id); + + return (true, String.Empty, _mapper.Map(dbVehicle)); + } + + public async Task<(bool isSucceed, string message)> DeleteVehicle(int id) + { + var dbVehicle = await _dbContext.Vehicles.FirstOrDefaultAsync(v => v.Id == id); + + if (dbVehicle == null) + { + return (false, $"Vehicle with id:{id} doesn't exist"); + } + + _dbContext.Vehicles.Remove(dbVehicle); + await _dbContext.SaveChangesAsync(); + + return (true, String.Empty); + } + + public async Task IsVehicleExists(int id) + { + return await _dbContext.Vehicles.AnyAsync(v => v.Id == id); + } +} \ No newline at end of file diff --git a/SharedModels/DataTransferObjects/AddressDto.cs b/SharedModels/DataTransferObjects/AddressDto.cs index b328d02..11a6faf 100644 --- a/SharedModels/DataTransferObjects/AddressDto.cs +++ b/SharedModels/DataTransferObjects/AddressDto.cs @@ -17,11 +17,9 @@ public class CreateAddressDto [StringLength(maximumLength: 250, ErrorMessage = "Address name is too long")] public string Name { get; set; } = null!; - [Required] [Range(-90, 90, ErrorMessage = "Latitude must be in range(-90, 90)")] public double Latitude { get; set; } - [Required] [Range(-180, 180, ErrorMessage = "Longitude must be in range(-180, 180)")] public double Longitude { get; set; } diff --git a/SharedModels/DataTransferObjects/CompanyDto.cs b/SharedModels/DataTransferObjects/CompanyDto.cs new file mode 100644 index 0000000..1e74214 --- /dev/null +++ b/SharedModels/DataTransferObjects/CompanyDto.cs @@ -0,0 +1,29 @@ +using System.ComponentModel.DataAnnotations; + +namespace SharedModels.DataTransferObjects; + +public class CompanyDto +{ + public int Id { get; set; } + + public string Name { get; set; } = null!; + public string OwnerId { get; set; } = null!; +} + +public class CreateCompanyDto +{ + [Required] + public string Name { get; set; } = null!; + + [Required] + public string OwnerId { get; set; } = null!; +} + +public class UpdateCompanyDto +{ + [Required] + public int Id { get; set; } + + public string Name { get; set; } = null!; + public string OwnerId { get; set; } = null!; +} \ No newline at end of file diff --git a/SharedModels/DataTransferObjects/ReviewDto.cs b/SharedModels/DataTransferObjects/ReviewDto.cs new file mode 100644 index 0000000..d1eb431 --- /dev/null +++ b/SharedModels/DataTransferObjects/ReviewDto.cs @@ -0,0 +1,40 @@ +using System.ComponentModel.DataAnnotations; + +namespace SharedModels.DataTransferObjects; + +public class ReviewDto +{ + public int Id { get; set; } + public int Rating { get; set; } + public string Comment { get; set; } = null!; +} + +public class CreateReviewDto +{ + [Required] + public string UserId { get; set; } = null!; + + [Required] + public int VehicleEnrollmentId { get; set; } + + [Range(0,100)] + public int Rating { get; set; } + + [MaxLength(255)] + public string Comment { get; set; } = null!; +} + +public class UpdateReviewDto +{ + [Required] + public int Id { get; set; } + + public string UserId { get; set; } = null!; + public int VehicleEnrollmentId { get; set; } + + [Range(0,100)] + public int Rating { get; set; } + + [MaxLength(255)] + public string Comment { get; set; } = null!; +} \ No newline at end of file diff --git a/SharedModels/DataTransferObjects/RouteAddressDto.cs b/SharedModels/DataTransferObjects/RouteAddressDto.cs index 0fa3774..0fd6f2b 100644 --- a/SharedModels/DataTransferObjects/RouteAddressDto.cs +++ b/SharedModels/DataTransferObjects/RouteAddressDto.cs @@ -2,13 +2,18 @@ using System.ComponentModel.DataAnnotations; namespace SharedModels.DataTransferObjects; -public class RouteAddressDto : CreateRouteAddressDto +public class RouteAddressDto { public int Id { get; set; } - - public RouteDto Route { get; set; } = null!; - - public AddressDto Address { get; set; } = null!; + public int RouteId { get; set; } + public int AddressId { get; set; } + public int Order { get; set; } + [DataType(DataType.Duration)] + public TimeSpan TimeSpanToNextCity { get; set; } + [DataType(DataType.Duration)] + public TimeSpan WaitTimeSpan { get; set; } + [DataType(DataType.Currency)] + public double CostToNextCity { get; set; } } public class CreateRouteAddressDto @@ -33,4 +38,19 @@ public class CreateRouteAddressDto [Required] [DataType(DataType.Currency)] public double CostToNextCity { get; set; } +} + +public class UpdateRouteAddressDto +{ + [Required] + public int Id { get; set; } + public int RouteId { get; set; } + public int AddressId { get; set; } + public int Order { get; set; } + [DataType(DataType.Duration)] + public TimeSpan TimeSpanToNextCity { get; set; } + [DataType(DataType.Duration)] + public TimeSpan WaitTimeSpan { get; set; } + [DataType(DataType.Currency)] + public double CostToNextCity { get; set; } } \ No newline at end of file diff --git a/SharedModels/DataTransferObjects/RouteDto.cs b/SharedModels/DataTransferObjects/RouteDto.cs index fbb6ea3..c34b903 100644 --- a/SharedModels/DataTransferObjects/RouteDto.cs +++ b/SharedModels/DataTransferObjects/RouteDto.cs @@ -2,18 +2,23 @@ using System.ComponentModel.DataAnnotations; namespace SharedModels.DataTransferObjects; -public class RouteDto : CreateRouteDto +public class RouteDto { public int Id { get; set; } - - public virtual IList RouteAddresses { get; set; } = null!; + + public string Type { get; set; } = null!; } public class CreateRouteDto { [Required] public string Type { get; set; } = null!; - +} + +public class UpdateRouteDto +{ [Required] - public TimeOnly IntendedDepartureTimeOnlyUtc { get; set; } + public int Id { get; set; } + + public string Type { get; set; } = null!; } \ No newline at end of file diff --git a/SharedModels/DataTransferObjects/TicketDto.cs b/SharedModels/DataTransferObjects/TicketDto.cs new file mode 100644 index 0000000..78eeea1 --- /dev/null +++ b/SharedModels/DataTransferObjects/TicketDto.cs @@ -0,0 +1,31 @@ +using System.ComponentModel.DataAnnotations; + +namespace SharedModels.DataTransferObjects; + +public class TicketDto : CreateTicketDto +{ + public int Id { get; set; } + + [DataType(DataType.DateTime)] + public DateTime PurchaseDateTimeUtc { get; set; } + public bool IsReturned { get; set; } = false; +} + +public class CreateTicketDto +{ + [Required] + public string UserId { get; set; } = null!; + + [Required] + public int VehicleEnrollmentId { get; set; } +} + +public class UpdateTicketDto : CreateTicketDto +{ + [Required] + public int Id { get; set; } + + [DataType(DataType.DateTime)] + public DateTime PurchaseDateTimeUtc { get; set; } + public bool IsReturned { get; set; } = false; +} \ No newline at end of file diff --git a/SharedModels/DataTransferObjects/VehicleDto.cs b/SharedModels/DataTransferObjects/VehicleDto.cs new file mode 100644 index 0000000..8715390 --- /dev/null +++ b/SharedModels/DataTransferObjects/VehicleDto.cs @@ -0,0 +1,78 @@ +using System.ComponentModel.DataAnnotations; + +namespace SharedModels.DataTransferObjects; + +public class VehicleDto +{ + public int Id { get; set; } + public string? Number { get; set; } + public string? Type { get; set; } + public int Capacity { get; set; } + public bool HasClimateControl { get; set; } + public bool HasWiFi { get; set; } + public bool HasWC { get; set; } + public bool HasStewardess { get; set; } + public bool HasTV { get; set; } + public bool HasOutlet { get; set; } + public bool HasBelts { get; set; } + public int CompanyId { get; set; } +} + +public class CreateVehicleDto +{ + [Required] + [MaxLength(8)] + public string? Number { get; set; } + + [Required] + public string? Type { get; set; } + + [Required] + [Range(10, 100)] + public int Capacity { get; set; } + + [Required] + public bool HasClimateControl { get; set; } = false; + + [Required] + public bool HasWiFi { get; set; } = false; + + [Required] + public bool HasWC { get; set; } = false; + + [Required] + public bool HasStewardess { get; set; } = false; + + [Required] + public bool HasTV { get; set; } = false; + + [Required] + public bool HasOutlet { get; set; } = false; + + [Required] + public bool HasBelts { get; set; } = false; + + [Required] + public int CompanyId { get; set; } +} + +public class UpdateVehicleDto +{ + [Required] + public int Id { get; set; } + + [MaxLength(8)] + public string? Number { get; set; } + public string? Type { get; set; } + + [Range(10, 100)] + public int Capacity { get; set; } + public bool HasClimateControl { get; set; } = false; + public bool HasWiFi { get; set; } = false; + public bool HasWC { get; set; } = false; + public bool HasStewardess { get; set; } = false; + public bool HasTV { get; set; } = false; + public bool HasOutlet { get; set; } = false; + public bool HasBelts { get; set; } = false; + public int CompanyId { get; set; } +} \ No newline at end of file diff --git a/SharedModels/DataTransferObjects/VehicleEnrollmentDto.cs b/SharedModels/DataTransferObjects/VehicleEnrollmentDto.cs new file mode 100644 index 0000000..9d993e6 --- /dev/null +++ b/SharedModels/DataTransferObjects/VehicleEnrollmentDto.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; + +namespace SharedModels.DataTransferObjects; + +public class VehicleEnrollmentDto +{ + public int Id { get; set; } + public int VehicleId { get; set; } + public int RouteId { get; set; } + public DateTime DepartureDateTimeUtc { get; set; } + public TimeSpan DelayTimeSpan { get; set; } + public bool IsCanceled {get; set; } + public string CancelationComment { get; set; } = null!; +} + +public class CreateVehicleEnrollmentDto +{ + [Required] + public int VehicleId { get; set; } + + [Required] + public int RouteId { get; set; } + + [Required] + [DataType(DataType.DateTime)] + public DateTime DepartureDateTimeUtc { get; set; } + + public TimeSpan DelayTimeSpan { get; set; } = TimeSpan.Zero; + + public bool IsCanceled { get; set; } = false; + public string? CancelationComment { get; set; } = null!; +} + +public class UpdateVehicleEnrollmentDto +{ + [Required] + public int Id { get; set; } + public int VehicleId { get; set; } + public int RouteId { get; set; } + public DateTime DepartureDateTimeUtc { get; set; } + public TimeSpan DelayTimeSpan { get; set; } + public bool IsCanceled {get; set; } + public string CancelationComment { get; set; } = null!; +} \ No newline at end of file diff --git a/SharedModels/QueryStringParameters/CompanyParameters.cs b/SharedModels/QueryStringParameters/CompanyParameters.cs new file mode 100644 index 0000000..b428c64 --- /dev/null +++ b/SharedModels/QueryStringParameters/CompanyParameters.cs @@ -0,0 +1,15 @@ +namespace SharedModels.QueryStringParameters; + +public class CompanyParameters : QueryStringParameters +{ + public const string DefaultFields = "id,ownerId,name"; + + public CompanyParameters() + { + Sort = "id"; + Fields = DefaultFields; + } + + public string? Name { get; set; } + public string? OwnerId { get; set; } +} \ No newline at end of file diff --git a/SharedModels/QueryStringParameters/ReviewParameters.cs b/SharedModels/QueryStringParameters/ReviewParameters.cs new file mode 100644 index 0000000..2cdcc40 --- /dev/null +++ b/SharedModels/QueryStringParameters/ReviewParameters.cs @@ -0,0 +1,15 @@ +namespace SharedModels.QueryStringParameters; + +public class ReviewParameters : QueryStringParameters +{ + public const string DefaultFields = "id,userId,vehicleEnrollmentId,rating,comment"; + + public ReviewParameters() + { + Sort = "id"; + Fields = DefaultFields; + } + + public int? Rating { get; set; } + public string? Comment { get; set; } +} \ No newline at end of file diff --git a/SharedModels/QueryStringParameters/RouteAddressParameters.cs b/SharedModels/QueryStringParameters/RouteAddressParameters.cs new file mode 100644 index 0000000..856f3f8 --- /dev/null +++ b/SharedModels/QueryStringParameters/RouteAddressParameters.cs @@ -0,0 +1,16 @@ +namespace SharedModels.QueryStringParameters; + +public class RouteAddressParameters : QueryStringParameters +{ + public const string DefaultFields = "id,routeId,addressId,order,timeSpanToNextCity," + + "waitTimeSpan,costToNextCity"; + + public RouteAddressParameters() + { + Sort = "id"; + Fields = DefaultFields; + } + + public int? RouteId { get; set; } + public int? AddressId { get; set; } +} \ No newline at end of file diff --git a/SharedModels/QueryStringParameters/RouteParameters.cs b/SharedModels/QueryStringParameters/RouteParameters.cs new file mode 100644 index 0000000..c3275fa --- /dev/null +++ b/SharedModels/QueryStringParameters/RouteParameters.cs @@ -0,0 +1,14 @@ +namespace SharedModels.QueryStringParameters; + +public class RouteParameters : QueryStringParameters +{ + public const string DefaultFields = "id,type"; + + public RouteParameters() + { + Sort = "id"; + Fields = DefaultFields; + } + + public string? Type { get; set; } +} \ No newline at end of file diff --git a/SharedModels/QueryStringParameters/TicketParameters.cs b/SharedModels/QueryStringParameters/TicketParameters.cs new file mode 100644 index 0000000..5ac6890 --- /dev/null +++ b/SharedModels/QueryStringParameters/TicketParameters.cs @@ -0,0 +1,16 @@ +namespace SharedModels.QueryStringParameters; + +public class TicketParameters : QueryStringParameters +{ + public const string DefaultFields = "id,userId,vehicleEnrollmentId,purchaseDateTimeUtc,isReturned"; + + public TicketParameters() + { + Sort = "id"; + Fields = DefaultFields; + } + + public DateTime? FromPurchaseDateTimeUtc { get; set; } + public DateTime? ToPurchaseDateTimeUtc { get; set; } + public bool? IsReturned { get; set; } +} \ No newline at end of file diff --git a/SharedModels/QueryStringParameters/VehicleEnrollmentParameters.cs b/SharedModels/QueryStringParameters/VehicleEnrollmentParameters.cs new file mode 100644 index 0000000..5f13d33 --- /dev/null +++ b/SharedModels/QueryStringParameters/VehicleEnrollmentParameters.cs @@ -0,0 +1,20 @@ +namespace SharedModels.QueryStringParameters; + +public class VehicleEnrollmentParameters : QueryStringParameters +{ + public const string DefaultFields = "id,vehicleId,routeId,departureDateTimeUtc," + + "delayTimeSpan,isCancelled,cancellationComment"; + + public VehicleEnrollmentParameters() + { + Sort = "id"; + Fields = DefaultFields; + } + + public int? VehicleId { get; set; } + public int? RouteId { get; set; } + public DateTime? FromDepartureDateTime { get; set; } + public DateTime? ToDepartureDateTime { get; set; } + public bool? IsDelayed { get; set; } + public bool? IsCanceled {get; set; } +} \ No newline at end of file diff --git a/SharedModels/QueryStringParameters/VehicleParameters.cs b/SharedModels/QueryStringParameters/VehicleParameters.cs new file mode 100644 index 0000000..fd13159 --- /dev/null +++ b/SharedModels/QueryStringParameters/VehicleParameters.cs @@ -0,0 +1,28 @@ +namespace SharedModels.QueryStringParameters; + +public class VehicleParameters : QueryStringParameters +{ + public const string DefaultFields = "id,companyId,number,type,capacity,hasClimateControl," + + "hasWiFi,hasWC,hasStewardess,hasTV,hasOutlet,hasBelts"; + + public VehicleParameters() + { + Sort = "id"; + Fields = DefaultFields; + } + + public string? Number { get; set; } + public string? Type { get; set; } + public int? FromCapacity { get; set; } + public int? ToCapacity { get; set; } + + public bool? HasClimateControl { get; set; } + public bool? HasWiFi { get; set; } + public bool? HasWC { get; set; } + public bool? HasStewardess { get; set; } + public bool? HasTV { get; set; } + public bool? HasOutlet { get; set; } + public bool? HasBelts { get; set; } + + public int? CompanyId { get; set; } +} \ No newline at end of file