diff --git a/Server/Configurations/MapperInitializer.cs b/Server/Configurations/MapperInitializer.cs
index faec9dd..e4950be 100644
--- a/Server/Configurations/MapperInitializer.cs
+++ b/Server/Configurations/MapperInitializer.cs
@@ -31,16 +31,20 @@ 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();
diff --git a/Server/Controllers/RouteController.cs b/Server/Controllers/RouteController.cs
index 93746e0..c8db57e 100644
--- a/Server/Controllers/RouteController.cs
+++ b/Server/Controllers/RouteController.cs
@@ -30,6 +30,19 @@ public class RouteController : ControllerBase
return CreatedAtAction(nameof(GetRoute), new {id = result.route.Id}, result.route);
}
+ [HttpPost("withAddresses")]
+ public async Task AddRouteWithAddresses(CreateRouteWithAddressesDto route)
+ {
+ var result = await _routeManagementService.AddRouteWithAddresses(route);
+
+ if (!result.isSucceed)
+ {
+ return result.actionResult;
+ }
+
+ return CreatedAtAction(nameof(GetRoute), new {id = result.route.Id}, result.route);
+ }
+
[HttpGet]
public async Task GetRoutes([FromQuery] RouteParameters parameters)
{
@@ -45,6 +58,21 @@ public class RouteController : ControllerBase
return Ok(result.routes);
}
+ [HttpGet("withAddresses")]
+ public async Task GetRouteWithAddresses([FromQuery] RouteWithAddressesParameters parameters)
+ {
+ var result = await _routeManagementService.GetRoutesWithAddresses(parameters);
+
+ if (!result.isSucceed)
+ {
+ return result.actionResult;
+ }
+
+ Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata));
+
+ return Ok(result.routes);
+ }
+
[HttpGet("{id}")]
public async Task GetRoute(int id, [FromQuery] string? fields)
{
@@ -62,6 +90,24 @@ public class RouteController : ControllerBase
return Ok(result.route);
}
+
+ [HttpGet("withAddresses/{id}")]
+ public async Task GetRouteWithAddresses(int id, [FromQuery] string? fields)
+ {
+ if (!await _routeManagementService.IsRouteExists(id))
+ {
+ return NotFound();
+ }
+
+ var result = await _routeManagementService.GetRouteWithAddresses(id, fields);
+
+ if (!result.isSucceed)
+ {
+ return BadRequest(result.message);
+ }
+
+ return Ok(result.route);
+ }
[HttpPut("{id}")]
public async Task UpdateRoute(int id, UpdateRouteDto route)
diff --git a/Server/Data/ApplicationDbContext.cs b/Server/Data/ApplicationDbContext.cs
index 2dfc8f0..003c09b 100644
--- a/Server/Data/ApplicationDbContext.cs
+++ b/Server/Data/ApplicationDbContext.cs
@@ -18,6 +18,8 @@ public class ApplicationDbContext : IdentityDbContext
public DbSet Routes { get; set; } = null!;
public DbSet RouteAddresses { get; set; } = null!;
public DbSet Addresses { get; set; } = null!;
+
+ public DbSet RouteAddressDetails { get; set; } = null!;
public DbSet Cities { get; set; } = null!;
public DbSet States { get; set; } = null!;
public DbSet Countries { get; set; } = null!;
diff --git a/Server/Helpers/IPager.cs b/Server/Helpers/IPager.cs
new file mode 100644
index 0000000..5838b22
--- /dev/null
+++ b/Server/Helpers/IPager.cs
@@ -0,0 +1,8 @@
+using SharedModels.QueryParameters;
+
+namespace Server.Helpers;
+
+public interface IPager
+{
+ PagingMetadata ApplyPaging(ref IQueryable obj, int pageNumber, int pageSize);
+}
\ No newline at end of file
diff --git a/Server/Helpers/Pager.cs b/Server/Helpers/Pager.cs
new file mode 100644
index 0000000..834a096
--- /dev/null
+++ b/Server/Helpers/Pager.cs
@@ -0,0 +1,19 @@
+using SharedModels.QueryParameters;
+
+namespace Server.Helpers;
+
+public class Pager : IPager
+{
+ public PagingMetadata ApplyPaging(ref IQueryable obj,
+ int pageNumber, int pageSize)
+ {
+ var metadata = new PagingMetadata(obj,
+ pageNumber, pageSize);
+
+ obj = obj
+ .Skip((pageNumber - 1) * pageSize)
+ .Take(pageSize);
+
+ return metadata;
+ }
+}
\ No newline at end of file
diff --git a/Server/Helpers/SortHelper.cs b/Server/Helpers/SortHelper.cs
index 79dad5d..0fb4b02 100644
--- a/Server/Helpers/SortHelper.cs
+++ b/Server/Helpers/SortHelper.cs
@@ -1,3 +1,4 @@
+using System.Dynamic;
using System.Linq.Dynamic.Core;
using System.Reflection;
using System.Text;
@@ -14,8 +15,9 @@ public class SortHelper : ISortHelper
}
var orderParams = orderByQueryString.Trim().Split(",");
- var propertyInfos =
- typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
+ var propertyStrings = typeof(T) == typeof(ExpandoObject) ?
+ (entities.First() as ExpandoObject).ToDictionary(o => o.Key, o => o.Value).Keys.ToList() :
+ typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).ToList().ConvertAll(o => o.GetType().ToString());
var orderQueryBuilder = new StringBuilder();
foreach (var param in orderParams)
@@ -26,8 +28,8 @@ public class SortHelper : ISortHelper
}
var propertyFromQueryName = param[0] == '-' || param[0] == '+' ? param.Substring(1) : param;
- var objectProperty = propertyInfos.FirstOrDefault(pi =>
- pi.Name.Equals(propertyFromQueryName, StringComparison.InvariantCultureIgnoreCase));
+ var objectProperty = propertyStrings.FirstOrDefault(ps =>
+ ps.Equals(propertyFromQueryName, StringComparison.InvariantCultureIgnoreCase));
if (objectProperty == null)
{
@@ -36,11 +38,13 @@ public class SortHelper : ISortHelper
var sortingOrder = param[0] == '-' ? "descending" : "ascending";
- orderQueryBuilder.Append($"{objectProperty.Name} {sortingOrder}, ");
+ orderQueryBuilder.Append($"{objectProperty} {sortingOrder}, ");
}
var orderQuery = orderQueryBuilder.ToString().TrimEnd(',', ' ');
- return entities.OrderBy(orderQuery);
+ return typeof(T) == typeof(ExpandoObject) ?
+ entities.Cast().OrderBy(orderQuery).Cast() :
+ entities.OrderBy(orderQuery);
}
}
\ No newline at end of file
diff --git a/Server/Migrations/20221129160345_Decouple_RouteAddress_and_RouteAddressDetails.Designer.cs b/Server/Migrations/20221129160345_Decouple_RouteAddress_and_RouteAddressDetails.Designer.cs
new file mode 100644
index 0000000..3274a1c
--- /dev/null
+++ b/Server/Migrations/20221129160345_Decouple_RouteAddress_and_RouteAddressDetails.Designer.cs
@@ -0,0 +1,915 @@
+//
+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("20221129160345_Decouple_RouteAddress_and_RouteAddressDetails")]
+ partial class Decouple_RouteAddress_and_RouteAddressDetails
+ {
+ 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("PostDateTimeUtc")
+ .HasColumnType("timestamp with time zone");
+
+ 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("Order")
+ .HasColumnType("integer");
+
+ b.Property("RouteAddressDetailsId")
+ .HasColumnType("integer");
+
+ b.Property("RouteId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AddressId");
+
+ b.HasIndex("RouteId");
+
+ b.ToTable("RouteAddresses");
+ });
+
+ modelBuilder.Entity("Server.Models.RouteAddressDetails", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CostToNextCity")
+ .HasColumnType("double precision");
+
+ b.Property("RouteAddressId")
+ .HasColumnType("integer");
+
+ b.Property("TimeSpanToNextCity")
+ .HasColumnType("interval");
+
+ b.Property("VehicleEnrollmentId")
+ .HasColumnType("integer");
+
+ b.Property("WaitTimeSpan")
+ .HasColumnType("interval");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RouteAddressId");
+
+ b.HasIndex("VehicleEnrollmentId");
+
+ b.ToTable("RouteAddressDetails");
+ });
+
+ 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("FirstRouteAddressId")
+ .HasColumnType("integer");
+
+ b.Property("IsMissed")
+ .HasColumnType("boolean");
+
+ b.Property("IsReturned")
+ .HasColumnType("boolean");
+
+ b.Property("LastRouteAddressId")
+ .HasColumnType("integer");
+
+ b.Property("PurchaseDateTimeUtc")
+ .HasColumnType("timestamp with time zone");
+
+ b.Property("TicketGroupId")
+ .HasColumnType("integer");
+
+ b.Property("VehicleEnrollmentId")
+ .HasColumnType("integer");
+
+ b.HasKey("Id");
+
+ b.HasIndex("TicketGroupId");
+
+ b.HasIndex("VehicleEnrollmentId");
+
+ b.ToTable("Tickets");
+ });
+
+ modelBuilder.Entity("Server.Models.TicketGroup", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("UserId")
+ .IsRequired()
+ .HasColumnType("text");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("TicketGroups");
+ });
+
+ 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("RouteAddressDetailsId")
+ .HasColumnType("integer");
+
+ 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("Reviews")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
+ .WithMany("Reviews")
+ .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.RouteAddressDetails", b =>
+ {
+ b.HasOne("Server.Models.RouteAddress", "RouteAddress")
+ .WithMany("RouteAddressDetails")
+ .HasForeignKey("RouteAddressId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
+ .WithMany("RouteAddressDetails")
+ .HasForeignKey("VehicleEnrollmentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("RouteAddress");
+
+ b.Navigation("VehicleEnrollment");
+ });
+
+ 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.TicketGroup", "TicketGroup")
+ .WithMany("Tickets")
+ .HasForeignKey("TicketGroupId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
+ .WithMany("Tickets")
+ .HasForeignKey("VehicleEnrollmentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("TicketGroup");
+
+ b.Navigation("VehicleEnrollment");
+ });
+
+ modelBuilder.Entity("Server.Models.TicketGroup", b =>
+ {
+ b.HasOne("Server.Models.User", "User")
+ .WithMany("TicketGroups")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ 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("CreationDateTime")
+ .HasColumnType("timestamp with time zone");
+
+ b1.Property("ExpiryDateTime")
+ .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("VehicleEnrollments")
+ .HasForeignKey("RouteId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Server.Models.Vehicle", "Vehicle")
+ .WithMany("VehicleEnrollments")
+ .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");
+
+ b.Navigation("VehicleEnrollments");
+ });
+
+ modelBuilder.Entity("Server.Models.RouteAddress", b =>
+ {
+ b.Navigation("RouteAddressDetails");
+ });
+
+ modelBuilder.Entity("Server.Models.State", b =>
+ {
+ b.Navigation("Cities");
+ });
+
+ modelBuilder.Entity("Server.Models.TicketGroup", b =>
+ {
+ b.Navigation("Tickets");
+ });
+
+ modelBuilder.Entity("Server.Models.User", b =>
+ {
+ b.Navigation("Reviews");
+
+ b.Navigation("TicketGroups");
+ });
+
+ modelBuilder.Entity("Server.Models.Vehicle", b =>
+ {
+ b.Navigation("VehicleEnrollments");
+ });
+
+ modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
+ {
+ b.Navigation("Reviews");
+
+ b.Navigation("RouteAddressDetails");
+
+ b.Navigation("Tickets");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Server/Migrations/20221129160345_Decouple_RouteAddress_and_RouteAddressDetails.cs b/Server/Migrations/20221129160345_Decouple_RouteAddress_and_RouteAddressDetails.cs
new file mode 100644
index 0000000..38630ca
--- /dev/null
+++ b/Server/Migrations/20221129160345_Decouple_RouteAddress_and_RouteAddressDetails.cs
@@ -0,0 +1,114 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
+
+#nullable disable
+
+namespace Server.Migrations
+{
+ public partial class Decouple_RouteAddress_and_RouteAddressDetails : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "CostToNextCity",
+ table: "RouteAddresses");
+
+ migrationBuilder.DropColumn(
+ name: "TimeSpanToNextCity",
+ table: "RouteAddresses");
+
+ migrationBuilder.DropColumn(
+ name: "WaitTimeSpan",
+ table: "RouteAddresses");
+
+ migrationBuilder.AddColumn(
+ name: "RouteAddressDetailsId",
+ table: "VehicleEnrollments",
+ type: "integer",
+ nullable: false,
+ defaultValue: 0);
+
+ migrationBuilder.AddColumn(
+ name: "RouteAddressDetailsId",
+ table: "RouteAddresses",
+ type: "integer",
+ nullable: false,
+ defaultValue: 0);
+
+ migrationBuilder.CreateTable(
+ name: "RouteAddressDetails",
+ columns: table => new
+ {
+ Id = table.Column(type: "integer", nullable: false)
+ .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
+ VehicleEnrollmentId = table.Column(type: "integer", nullable: false),
+ RouteAddressId = table.Column(type: "integer", nullable: false),
+ TimeSpanToNextCity = table.Column(type: "interval", nullable: false),
+ WaitTimeSpan = table.Column(type: "interval", nullable: false),
+ CostToNextCity = table.Column(type: "double precision", nullable: false)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_RouteAddressDetails", x => x.Id);
+ table.ForeignKey(
+ name: "FK_RouteAddressDetails_RouteAddresses_RouteAddressId",
+ column: x => x.RouteAddressId,
+ principalTable: "RouteAddresses",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK_RouteAddressDetails_VehicleEnrollments_VehicleEnrollmentId",
+ column: x => x.VehicleEnrollmentId,
+ principalTable: "VehicleEnrollments",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_RouteAddressDetails_RouteAddressId",
+ table: "RouteAddressDetails",
+ column: "RouteAddressId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_RouteAddressDetails_VehicleEnrollmentId",
+ table: "RouteAddressDetails",
+ column: "VehicleEnrollmentId");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "RouteAddressDetails");
+
+ migrationBuilder.DropColumn(
+ name: "RouteAddressDetailsId",
+ table: "VehicleEnrollments");
+
+ migrationBuilder.DropColumn(
+ name: "RouteAddressDetailsId",
+ table: "RouteAddresses");
+
+ migrationBuilder.AddColumn(
+ name: "CostToNextCity",
+ table: "RouteAddresses",
+ type: "double precision",
+ nullable: false,
+ defaultValue: 0.0);
+
+ migrationBuilder.AddColumn(
+ name: "TimeSpanToNextCity",
+ table: "RouteAddresses",
+ type: "interval",
+ nullable: false,
+ defaultValue: new TimeSpan(0, 0, 0, 0, 0));
+
+ migrationBuilder.AddColumn(
+ name: "WaitTimeSpan",
+ table: "RouteAddresses",
+ type: "interval",
+ nullable: false,
+ defaultValue: new TimeSpan(0, 0, 0, 0, 0));
+ }
+ }
+}
diff --git a/Server/Migrations/ApplicationDbContextModelSnapshot.cs b/Server/Migrations/ApplicationDbContextModelSnapshot.cs
index 8d956f8..daf3e39 100644
--- a/Server/Migrations/ApplicationDbContextModelSnapshot.cs
+++ b/Server/Migrations/ApplicationDbContextModelSnapshot.cs
@@ -309,21 +309,15 @@ namespace Server.Migrations
b.Property("AddressId")
.HasColumnType("integer");
- b.Property("CostToNextCity")
- .HasColumnType("double precision");
-
b.Property("Order")
.HasColumnType("integer");
+ b.Property("RouteAddressDetailsId")
+ .HasColumnType("integer");
+
b.Property("RouteId")
.HasColumnType("integer");
- b.Property("TimeSpanToNextCity")
- .HasColumnType("interval");
-
- b.Property("WaitTimeSpan")
- .HasColumnType("interval");
-
b.HasKey("Id");
b.HasIndex("AddressId");
@@ -333,6 +327,38 @@ namespace Server.Migrations
b.ToTable("RouteAddresses");
});
+ modelBuilder.Entity("Server.Models.RouteAddressDetails", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("integer");
+
+ NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id"));
+
+ b.Property("CostToNextCity")
+ .HasColumnType("double precision");
+
+ b.Property("RouteAddressId")
+ .HasColumnType("integer");
+
+ b.Property("TimeSpanToNextCity")
+ .HasColumnType("interval");
+
+ b.Property("VehicleEnrollmentId")
+ .HasColumnType("integer");
+
+ b.Property("WaitTimeSpan")
+ .HasColumnType("interval");
+
+ b.HasKey("Id");
+
+ b.HasIndex("RouteAddressId");
+
+ b.HasIndex("VehicleEnrollmentId");
+
+ b.ToTable("RouteAddressDetails");
+ });
+
modelBuilder.Entity("Server.Models.State", b =>
{
b.Property("Id")
@@ -552,6 +578,9 @@ namespace Server.Migrations
b.Property("IsCanceled")
.HasColumnType("boolean");
+ b.Property("RouteAddressDetailsId")
+ .HasColumnType("integer");
+
b.Property("RouteId")
.HasColumnType("integer");
@@ -689,6 +718,25 @@ namespace Server.Migrations
b.Navigation("Route");
});
+ modelBuilder.Entity("Server.Models.RouteAddressDetails", b =>
+ {
+ b.HasOne("Server.Models.RouteAddress", "RouteAddress")
+ .WithMany("RouteAddressDetails")
+ .HasForeignKey("RouteAddressId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
+ .WithMany("RouteAddressDetails")
+ .HasForeignKey("VehicleEnrollmentId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("RouteAddress");
+
+ b.Navigation("VehicleEnrollment");
+ });
+
modelBuilder.Entity("Server.Models.State", b =>
{
b.HasOne("Server.Models.Country", "Country")
@@ -824,6 +872,11 @@ namespace Server.Migrations
b.Navigation("VehicleEnrollments");
});
+ modelBuilder.Entity("Server.Models.RouteAddress", b =>
+ {
+ b.Navigation("RouteAddressDetails");
+ });
+
modelBuilder.Entity("Server.Models.State", b =>
{
b.Navigation("Cities");
@@ -850,6 +903,8 @@ namespace Server.Migrations
{
b.Navigation("Reviews");
+ b.Navigation("RouteAddressDetails");
+
b.Navigation("Tickets");
});
#pragma warning restore 612, 618
diff --git a/Server/Models/Address.cs b/Server/Models/Address.cs
index 7f003a5..a6c6135 100644
--- a/Server/Models/Address.cs
+++ b/Server/Models/Address.cs
@@ -10,18 +10,25 @@ public class Address
public int Id { get; set; }
public string Name { get; set; } = null!;
+
public double Latitude { get; set; }
public double Longitude { get; set; }
[ForeignKey("CityId")]
public int CityId { get; set; }
- public City? City { get; set; }
+ public City City { get; set; } = null!;
public virtual IList RouteAddresses { get; set; } = null!;
- public override string ToString()
+ public string GetFullName()
{
- return $"{City.State.Country.Name}, {City.State.Name}, " +
- $"{City.Name}, {this.Name}";
+ if (City == null || City.State == null || City.State.Country == null)
+ {
+ throw new NullReferenceException(
+ $"Properties {nameof(City)}, {nameof(City.State)}, " +
+ $"{nameof(City.State.Country)} must not be null");
+ }
+
+ return $"{City.GetFullName()}, {Name}";
}
}
\ No newline at end of file
diff --git a/Server/Models/City.cs b/Server/Models/City.cs
index 1927105..72cd52a 100644
--- a/Server/Models/City.cs
+++ b/Server/Models/City.cs
@@ -10,10 +10,23 @@ public class City
public int Id { get; set; }
public string Name { get; set; } = null!;
-
+
public virtual IList? Addresses { get; set; }
[ForeignKey("StateId")]
public int StateId { get; set; }
- public State? State { get; set; }
+
+ public State State { get; set; } = null!;
+
+ public string GetFullName()
+ {
+ if (State == null || State.Country == null)
+ {
+ throw new NullReferenceException(
+ $"Properties {nameof(State)}, " +
+ $"{nameof(State.Country)} must not be null");
+ }
+
+ return $"{State.GetFullName()}, {Name}";
+ }
}
\ No newline at end of file
diff --git a/Server/Models/Country.cs b/Server/Models/Country.cs
index 77b533b..6f1f93e 100644
--- a/Server/Models/Country.cs
+++ b/Server/Models/Country.cs
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
using SharedModels.DataTransferObjects;
namespace Server.Models;
@@ -12,4 +13,9 @@ public class Country
public string Name { get; set; } = null!;
public virtual IList States { get; set; } = null!;
+
+ public string GetFullName()
+ {
+ return $"{Name}";
+ }
}
\ No newline at end of file
diff --git a/Server/Models/RouteAddress.cs b/Server/Models/RouteAddress.cs
index a97874b..ff0262d 100644
--- a/Server/Models/RouteAddress.cs
+++ b/Server/Models/RouteAddress.cs
@@ -16,8 +16,9 @@ public class RouteAddress
public int AddressId { get; set; }
public Address Address { get; set; } = null!;
+ [ForeignKey("RouteAddressDetailsId")]
+ public int RouteAddressDetailsId { get; set; }
+ public virtual IList RouteAddressDetails { get; set; } = null!;
+
public int Order { get; set; }
- public TimeSpan TimeSpanToNextCity { get; set; }
- public TimeSpan WaitTimeSpan { get; set; }
- public double CostToNextCity { get; set; }
}
\ No newline at end of file
diff --git a/Server/Models/RouteAddressDetails.cs b/Server/Models/RouteAddressDetails.cs
new file mode 100644
index 0000000..a63f6b5
--- /dev/null
+++ b/Server/Models/RouteAddressDetails.cs
@@ -0,0 +1,22 @@
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace Server.Models;
+
+public class RouteAddressDetails
+{
+ [Key]
+ public int Id { get; set; }
+
+ [ForeignKey("VehicleEnrollmentId")]
+ public int VehicleEnrollmentId { get; set; }
+ public VehicleEnrollment VehicleEnrollment { get; set; } = null!;
+
+ [ForeignKey("RouteAddressId")]
+ public int RouteAddressId { get; set; }
+ public RouteAddress RouteAddress { get; set; } = null!;
+
+ public TimeSpan TimeSpanToNextCity { get; set; }
+ public TimeSpan WaitTimeSpan { get; set; }
+ public double CostToNextCity { get; set; }
+}
\ No newline at end of file
diff --git a/Server/Models/State.cs b/Server/Models/State.cs
index 2db6547..44f6408 100644
--- a/Server/Models/State.cs
+++ b/Server/Models/State.cs
@@ -10,10 +10,20 @@ public class State
public int Id { get; set; }
public string Name { get; set; } = null!;
-
+
public virtual IList Cities { get; set; } = null!;
[ForeignKey("CountryId")]
public int CountryId { get; set; }
- public Country? Country { get; set; } = null!;
+ public Country Country { get; set; } = null!;
+
+ public string GetFullName()
+ {
+ if (Country == null)
+ {
+ throw new NullReferenceException($"Property {nameof(Country)} must not be null");
+ }
+
+ return $"{Country.GetFullName()}, {Name}";
+ }
}
\ No newline at end of file
diff --git a/Server/Models/VehicleEnrollment.cs b/Server/Models/VehicleEnrollment.cs
index 7271d85..a4fe704 100644
--- a/Server/Models/VehicleEnrollment.cs
+++ b/Server/Models/VehicleEnrollment.cs
@@ -16,6 +16,10 @@ public class VehicleEnrollment
public int RouteId { get; set; }
public Route Route { get; set; } = null!;
+ [ForeignKey("RouteAddressDetailsId")]
+ public int RouteAddressDetailsId { get; set; }
+ public virtual IList RouteAddressDetails { get; set; } = null!;
+
public DateTime DepartureDateTimeUtc { get; set; }
public TimeSpan? DelayTimeSpan { get; set; }
diff --git a/Server/Program.cs b/Server/Program.cs
index 7ed9313..a76ac41 100644
--- a/Server/Program.cs
+++ b/Server/Program.cs
@@ -1,3 +1,4 @@
+using System.Dynamic;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Identity;
@@ -105,7 +106,7 @@ builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
-
+builder.Services.AddScoped();
builder.Services.AddScoped, SortHelper>();
builder.Services.AddScoped, SortHelper>();
@@ -132,9 +133,15 @@ builder.Services.AddScoped, DataShaper, DataShaper>();
builder.Services.AddScoped, DataShaper>();
+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, Pager>();
// Adding DB Context with PostgreSQL
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
diff --git a/Server/Server.csproj b/Server/Server.csproj
index f6c6bbf..8da995d 100644
--- a/Server/Server.csproj
+++ b/Server/Server.csproj
@@ -22,8 +22,12 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
diff --git a/Server/Services/IRouteManagementService.cs b/Server/Services/IRouteManagementService.cs
index 6f26e54..0c888c3 100644
--- a/Server/Services/IRouteManagementService.cs
+++ b/Server/Services/IRouteManagementService.cs
@@ -1,3 +1,5 @@
+using System.Dynamic;
+using Microsoft.AspNetCore.Mvc;
using SharedModels.DataTransferObjects;
using SharedModels.QueryParameters;
using SharedModels.QueryParameters.Objects;
@@ -8,9 +10,13 @@ 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, IActionResult? actionResult, RouteWithAddressesDto route)> AddRouteWithAddresses(CreateRouteWithAddressesDto createRouteWithAddressesDto);
+ Task<(bool isSucceed, string message, IEnumerable routes,
+ PagingMetadata pagingMetadata)> GetRoutes(RouteParameters parameters);
+ Task<(bool isSucceed, IActionResult? actionResult, IEnumerable routes,
+ PagingMetadata pagingMetadata)> GetRoutesWithAddresses(RouteWithAddressesParameters parameters);
+ Task<(bool isSucceed, string message, ExpandoObject route)> GetRoute(int id, string? fields);
+ Task<(bool isSucceed, string message, ExpandoObject route)> GetRouteWithAddresses(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);
diff --git a/Server/Services/ReportService.cs b/Server/Services/ReportService.cs
index 9d50661..dcc354f 100644
--- a/Server/Services/ReportService.cs
+++ b/Server/Services/ReportService.cs
@@ -26,6 +26,7 @@ public class ReportService : IReportService
.ThenInclude(t => t.VehicleEnrollment)
.ThenInclude(ve => ve.Vehicle)
.ThenInclude(v => v.Company)
+
.Include(tg => tg.User)
.Include(tg => tg.Tickets)
.ThenInclude(t => t.VehicleEnrollment)
@@ -35,6 +36,12 @@ public class ReportService : IReportService
.ThenInclude(a => a.City)
.ThenInclude(c => c.State)
.ThenInclude(s => s.Country)
+
+ .Include(tg => tg.User)
+ .Include(tg => tg.Tickets)
+ .ThenInclude(t => t.VehicleEnrollment)
+ .ThenInclude(ve => ve.RouteAddressDetails)
+
.FirstOrDefaultAsync(tg => tg.Id == ticketGroupId);
// Define document
@@ -374,13 +381,16 @@ public class ReportService : IReportService
foreach (var routeAddress in routeAddresses)
{
+ var details = routeAddress.RouteAddressDetails
+ .First(rad => rad.RouteAddressId == routeAddress.Id);
+
if (routeAddress.AddressId == ticket.FirstRouteAddressId)
{
break;
}
- departureDateTimeUtc += routeAddress.TimeSpanToNextCity;
- departureDateTimeUtc += routeAddress.WaitTimeSpan;
+ departureDateTimeUtc += details.TimeSpanToNextCity;
+ departureDateTimeUtc += details.WaitTimeSpan;
}
return departureDateTimeUtc;
@@ -395,12 +405,15 @@ public class ReportService : IReportService
foreach (var routeAddress in routeAddresses)
{
+ var details = routeAddress.RouteAddressDetails
+ .First(rad => rad.RouteAddressId == routeAddress.Id);
+
if (routeAddress.AddressId == ticket.LastRouteAddressId)
{
break;
}
- arrivalDateTimeUtc += routeAddress.TimeSpanToNextCity;
+ arrivalDateTimeUtc += details.TimeSpanToNextCity;
}
return arrivalDateTimeUtc;
@@ -432,7 +445,10 @@ public class ReportService : IReportService
foreach (var routeAddress in routeAddresses)
{
- cost += routeAddress.CostToNextCity;
+ var details = routeAddress.RouteAddressDetails
+ .First(rad => rad.RouteAddressId == routeAddress.Id);
+
+ cost += details.CostToNextCity;
}
return cost;
diff --git a/Server/Services/RouteManagementService.cs b/Server/Services/RouteManagementService.cs
index b6e2f92..0f41a6d 100644
--- a/Server/Services/RouteManagementService.cs
+++ b/Server/Services/RouteManagementService.cs
@@ -1,4 +1,7 @@
+using System.Dynamic;
using AutoMapper;
+using AutoMapper.QueryableExtensions;
+using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Server.Data;
using Server.Helpers;
@@ -13,17 +16,23 @@ public class RouteManagementService : IRouteManagementService
{
private readonly ApplicationDbContext _dbContext;
private readonly IMapper _mapper;
- private readonly ISortHelper _routeSortHelper;
- private readonly IDataShaper _routeDataShaper;
+ private readonly ISortHelper