diff --git a/Server/Configurations/MapperInitializer.cs b/Server/Configurations/MapperInitializer.cs index c859913..3bdbe6d 100644 --- a/Server/Configurations/MapperInitializer.cs +++ b/Server/Configurations/MapperInitializer.cs @@ -1,3 +1,4 @@ +using System.Dynamic; using AutoMapper; using Server.Models; using SharedModels.DataTransferObjects; @@ -82,10 +83,16 @@ public class MapperInitializer : Profile CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap(); - + CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + + CreateMap().ForMember(d => d.CompanyId, o => o.MapFrom(s => s.Employer.CompanyId)); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); + CreateMap().ReverseMap(); CreateMap().ReverseMap(); CreateMap().ReverseMap(); diff --git a/Server/Configurations/SmtpCredentials.cs b/Server/Configurations/SmtpCredentials.cs index b04c4c1..c501dae 100644 --- a/Server/Configurations/SmtpCredentials.cs +++ b/Server/Configurations/SmtpCredentials.cs @@ -3,7 +3,7 @@ namespace Server.Configurations; public class SmtpCredentials { public string Host { get; set; } = null!; - public string Port { get; set; } + public int Port { get; set; } public string User { get; set; } = null!; public string Password { get; set; } = null!; } \ No newline at end of file diff --git a/Server/Controllers/DriverController.cs b/Server/Controllers/DriverController.cs new file mode 100644 index 0000000..e52a7e4 --- /dev/null +++ b/Server/Controllers/DriverController.cs @@ -0,0 +1,86 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Server.Services; +using SharedModels.DataTransferObjects; +using SharedModels.QueryParameters.Objects; + +namespace Server.Controllers; + +[Route("api/drivers")] +[ApiController] +public class DriverController : ControllerBase +{ + private readonly IDriverManagementService _driverManagementService; + + public DriverController(IDriverManagementService driverManagementService) + { + _driverManagementService = driverManagementService; + } + + [HttpPost] + public async Task AddDriver(CreateDriverDto Driver) + { + var result = await _driverManagementService.AddDriver(Driver); + + if (!result.isSucceeded) + { + return result.actionResult; + } + + return CreatedAtAction(nameof(GetDriver), new {id = result.driver.Id}, result.driver); + } + + [HttpGet] + public async Task GetDrivers([FromQuery] CompanyDriverParameters parameters) + { + var result = await _driverManagementService.GetDrivers(parameters); + + if (!result.isSucceeded) + { + return result.actionResult; + } + + Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata)); + + return Ok(result.drivers); + } + + [HttpGet("{id}")] + public async Task GetDriver(string id, [FromQuery] string? fields) + { + var result = await _driverManagementService.GetDriver(id, fields); + + if (!result.isSucceeded) + { + return result.actionResult; + } + + return Ok(result.driver); + } + + [HttpPut("{id}")] + public async Task UpdateDriver(string id, UpdateDriverDto driver) + { + var result = await _driverManagementService.UpdateDriver(id, driver); + + if (!result.isSucceeded) + { + return result.actionResult; + } + + return Ok(result.driver); + } + + [HttpDelete("{id}")] + public async Task DeleteDriver(string id) + { + var result = await _driverManagementService.DeleteDriver(id); + + if (!result.isSucceed) + { + return result.actionResult; + } + + return NoContent(); + } +} \ No newline at end of file diff --git a/Server/Controllers/UserController.cs b/Server/Controllers/UserController.cs new file mode 100644 index 0000000..0e96512 --- /dev/null +++ b/Server/Controllers/UserController.cs @@ -0,0 +1,86 @@ +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Server.Services; +using SharedModels.DataTransferObjects; +using SharedModels.QueryParameters.Objects; + +namespace Server.Controllers; + +[Route("api/users")] +[ApiController] +public class UserController : ControllerBase +{ + private readonly IUserManagementService _userManagementService; + + public UserController(IUserManagementService userManagementService) + { + _userManagementService = userManagementService; + } + + [HttpPost] + public async Task AddUser(CreateUserDto User) + { + var result = await _userManagementService.AddUser(User); + + if (!result.isSucceeded) + { + return result.actionResult; + } + + return CreatedAtAction(nameof(GetUser), new {id = result.user.Id}, result.user); + } + + [HttpGet] + public async Task GetUsers([FromQuery] UserParameters parameters) + { + var result = await _userManagementService.GetUsers(parameters); + + if (!result.isSucceeded) + { + return result.actionResult; + } + + Response.Headers.Add("X-Pagination", JsonConvert.SerializeObject(result.pagingMetadata)); + + return Ok(result.users); + } + + [HttpGet("{id}")] + public async Task GetUser(string id, [FromQuery] string? fields) + { + var result = await _userManagementService.GetUser(id, fields); + + if (!result.isSucceeded) + { + return result.actionResult; + } + + return Ok(result.user); + } + + [HttpPut("{id}")] + public async Task UpdateUser(string id, UpdateUserDto user) + { + var result = await _userManagementService.UpdateUser(id, user); + + if (!result.isSucceeded) + { + return result.actionResult; + } + + return Ok(result.user); + } + + [HttpDelete("{id}")] + public async Task DeleteUser(string id) + { + var result = await _userManagementService.DeleteUser(id); + + if (!result.isSucceed) + { + return result.actionResult; + } + + return NoContent(); + } +} \ No newline at end of file diff --git a/Server/Data/ApplicationDbContext.cs b/Server/Data/ApplicationDbContext.cs index b097adf..a32c7cb 100644 --- a/Server/Data/ApplicationDbContext.cs +++ b/Server/Data/ApplicationDbContext.cs @@ -12,12 +12,16 @@ public class ApplicationDbContext : IdentityDbContext { Database.EnsureCreated(); } + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Entity().Ignore(u => u.UserName).Ignore(u => u.NormalizedUserName); + modelBuilder.Entity().HasKey(cd => new { cd.CompanyId, cd.DriverId }); } + public DbSet Companies { get; set; } = null!; + public DbSet CompanyDrivers { get; set; } = null!; public DbSet Vehicles { get; set; } = null!; public DbSet VehicleEnrollments { get; set; } = null!; public DbSet Routes { get; set; } = null!; diff --git a/Server/Data/SeedData.cs b/Server/Data/SeedData.cs index 420a939..f415e5e 100644 --- a/Server/Data/SeedData.cs +++ b/Server/Data/SeedData.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Identity; using Server.Models; +using Utils; +using Route = Server.Models.Route; namespace Server.Data; @@ -8,25 +10,314 @@ public class SeedData public static async Task Initialize(IServiceProvider serviceProvider) { var userManager = (UserManager)serviceProvider.GetService(typeof(UserManager))!; + userManager.UserValidators.Clear(); + var roleManager = (RoleManager)serviceProvider.GetService(typeof(RoleManager))!; + var dbContext = (ApplicationDbContext)serviceProvider.GetService(typeof(ApplicationDbContext))!; - //Seed Roles - foreach (var role in Enum.GetValues(typeof(Constants.Identity.Roles))) + // Seed Roles + foreach (var role in Enum.GetValues(typeof(Identity.Roles))) { await roleManager.CreateAsync(new IdentityRole(role.ToString())); } - - //Seed Default User - var defaultUser = new User + + User? companyUser; + User driverUser; + if (!dbContext.Users.Any()) { - Email = Constants.Identity.DefaultEmail, - EmailConfirmed = true - }; - - if (userManager.Users.All(u => u.Id != defaultUser.Id)) + // Seed Administrator user + + var adminUser = new User + { + FirstName = "user", LastName = "user", Patronymic = "user", + Email = "admin@autobus.com", + EmailConfirmed = true, + }; + + await userManager.CreateAsync(adminUser, Identity.DefaultPassword); + await userManager.AddToRoleAsync(adminUser, Identity.Roles.Administrator.ToString()); + + // Seed Company user + + companyUser = new User + { + FirstName = "user", LastName = "user", Patronymic = "user", + Email = "company@autobus.com", + EmailConfirmed = true + }; + + await userManager.CreateAsync(companyUser, Identity.DefaultPassword); + await userManager.AddToRoleAsync(companyUser, Identity.Roles.Company.ToString()); + + // Seed Driver user + + driverUser = new User + { + FirstName = "user", LastName = "user", Patronymic = "user", + Email = "driver@autobus.com", + EmailConfirmed = true + }; + + await userManager.CreateAsync(driverUser, Identity.DefaultPassword); + await userManager.AddToRoleAsync(driverUser, Identity.Roles.Driver.ToString()); + + // Seed Default user + + var defaultUser = new User + { + FirstName = "user", LastName = "user", Patronymic = "user", + Email = "user@autobus.com", + EmailConfirmed = true + }; + + await userManager.CreateAsync(defaultUser, Identity.DefaultPassword); + await userManager.AddToRoleAsync(defaultUser, Identity.Roles.User.ToString()); + } + + companyUser = await userManager.FindByEmailAsync("company@autobus.com"); + driverUser = await userManager.FindByEmailAsync("driver@autobus.com"); + + if (!dbContext.Companies.Any()) { - await userManager.CreateAsync(defaultUser, Constants.Identity.DefaultPassword); - await userManager.AddToRoleAsync(defaultUser, Constants.Identity.DefaultRole.ToString()); + // Seed County - State - City - Address relations + + await dbContext.Countries.AddRangeAsync(new Country { Name = "Ukraine", Code = "UA" }); + await dbContext.SaveChangesAsync(); + + await dbContext.States.AddRangeAsync(new State { CountryId = 1, Name = "Kharkiv Oblast" }); + await dbContext.SaveChangesAsync(); + + await dbContext.Cities.AddRangeAsync(new City { StateId = 1, Name = "Osykovyi Hai" }, + new City { StateId = 1, Name = "Malynivka" }, + new City { StateId = 1, Name = "Chuhuiv" }, + new City { StateId = 1, Name = "Kam'yana Yaruha" }, + new City { StateId = 1, Name = "Rohan'" }, + new City { StateId = 1, Name = "Kharkiv" }, + new City { StateId = 1, Name = "Korobochkyne" }, + new City { StateId = 1, Name = "Kochetok" }, + new City { StateId = 1, Name = "Kytsivka" }); + await dbContext.SaveChangesAsync(); + + await dbContext.Addresses.AddRangeAsync(new Address { CityId = 1, Name = "station near E40 road", Latitude = 36.778044, Longitude = 49.777235 }, + new Address { CityId = 2, Name = "v. Malynivka", Latitude = 36.732271, Longitude = 49.813638 }, + new Address { CityId = 3, Name = "Chuhuyivsʹka Avtostantsiya, Vulytsya Kharkivsʹka, 133", Latitude = 36.687534, Longitude = 49.838337 }, + new Address { CityId = 4, Name = "Kulynychi", Latitude = 36.595877, Longitude = 49.880585 }, + new Address { CityId = 5, Name = "v. Rohan", Latitude = 36.495910, Longitude = 49.907080 }, + new Address { CityId = 6, Name = "Kharkiv Parking Near Mcdonalds", Latitude = 36.207202, Longitude = 49.987560 }, + new Address { CityId = 7, Name = "Ukrposhta 63540, Chuhuivs'ka St, 2Б", Latitude = 36.812743, Longitude = 49.776519 }, + new Address { CityId = 8, Name = "Pivnyk", Latitude = 36.712386, Longitude = 49.867814 }, + new Address { CityId = 9, Name = "v. Kytsivka", Latitude = 36.819010, Longitude = 49.869401 }); + await dbContext.SaveChangesAsync(); + + // Seed Route - RouteAddresses relations + + await dbContext.Routes.AddRangeAsync( + new Route + { + Type = "default", + RouteAddresses = new List + { + new RouteAddress { Order = 1, AddressId = 1}, + new RouteAddress { Order = 2, AddressId = 2}, + new RouteAddress { Order = 3, AddressId = 3}, + new RouteAddress { Order = 4, AddressId = 4}, + new RouteAddress { Order = 5, AddressId = 5}, + new RouteAddress { Order = 6, AddressId = 6}, + } + }, + new Route + { + Type = "default", + RouteAddresses = new List + { + new RouteAddress { Order = 1, AddressId = 7}, + new RouteAddress { Order = 2, AddressId = 2}, + new RouteAddress { Order = 3, AddressId = 3}, + new RouteAddress { Order = 4, AddressId = 8}, + new RouteAddress { Order = 5, AddressId = 9}, + } + }); + await dbContext.SaveChangesAsync(); + + + await dbContext.Companies.AddAsync( + new Company + { + OwnerId = companyUser.Id, + Name = "Default Company", + CompanyDrivers = new [] + { + new CompanyDriver + { + DriverId = driverUser.Id + } + } + }); + await dbContext.SaveChangesAsync(); + + await dbContext.Vehicles.AddRangeAsync( + new Vehicle + { + Number = "AC8376BH", + Type = "Cruiser", + Capacity = 50, + CompanyId = 1, + HasClimateControl = true, + HasWiFi = true, + HasWC = true, + HasStewardess = true, + HasTV = false, + HasOutlet = true, + HasBelts = true, + }, + new Vehicle + { + Number = "HA8934MK", + Type = "Lon Rider", + Capacity = 25, + CompanyId = 1, + HasClimateControl = false, + HasWiFi = false, + HasWC = false, + HasStewardess = false, + HasTV = false, + HasOutlet = false, + HasBelts = false, + }); + await dbContext.SaveChangesAsync(); + + await dbContext.VehicleEnrollments.AddRangeAsync( + new VehicleEnrollment + { + RouteId = 1, + VehicleId = 1, + DepartureDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 07, 45, 00, DateTimeKind.Utc), + RouteAddressDetails = new List + { + new RouteAddressDetails + { + RouteAddressId = 1, + TimeSpanToNextCity = TimeSpan.FromMinutes(20), + CostToNextCity = 25 + }, + new RouteAddressDetails + { + RouteAddressId = 2, + TimeSpanToNextCity = TimeSpan.FromMinutes(5), + WaitTimeSpan = TimeSpan.FromMinutes(15), + CostToNextCity = 20 + }, + new RouteAddressDetails + { + RouteAddressId = 3, + TimeSpanToNextCity = TimeSpan.FromMinutes(5), + WaitTimeSpan = TimeSpan.FromMinutes(25), + CostToNextCity = 30 + }, + new RouteAddressDetails + { + RouteAddressId = 4, + TimeSpanToNextCity = TimeSpan.FromMinutes(5), + WaitTimeSpan = TimeSpan.FromMinutes(20), + CostToNextCity = 35 + }, + new RouteAddressDetails + { + RouteAddressId = 5, + TimeSpanToNextCity = TimeSpan.FromMinutes(5), + WaitTimeSpan = TimeSpan.FromMinutes(30), + CostToNextCity = 40 + }, + new RouteAddressDetails + { + RouteAddressId = 6, + } + } + }, + new VehicleEnrollment + { + RouteId = 2, + VehicleId = 2, + DepartureDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 08, 00, 00, DateTimeKind.Utc), + RouteAddressDetails = new List + { + new RouteAddressDetails + { + RouteAddressId = 7, + TimeSpanToNextCity = TimeSpan.FromMinutes(20), + CostToNextCity = 25 + }, + new RouteAddressDetails + { + RouteAddressId = 8, + TimeSpanToNextCity = TimeSpan.FromMinutes(5), + WaitTimeSpan = TimeSpan.FromMinutes(15), + CostToNextCity = 20 + }, + new RouteAddressDetails + { + RouteAddressId = 9, + TimeSpanToNextCity = TimeSpan.FromMinutes(5), + WaitTimeSpan = TimeSpan.FromMinutes(25), + CostToNextCity = 30 + }, + new RouteAddressDetails + { + RouteAddressId = 10, + TimeSpanToNextCity = TimeSpan.FromMinutes(5), + WaitTimeSpan = TimeSpan.FromMinutes(20), + CostToNextCity = 35 + }, + new RouteAddressDetails + { + RouteAddressId = 11, + } + } + }); + + await dbContext.SaveChangesAsync(); + + await dbContext.TicketGroups.AddAsync( + new TicketGroup + { + UserId = companyUser.Id, + Tickets = new List + { + new Ticket + { + VehicleEnrollmentId = 1, + PurchaseDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 08, 00, 00, DateTimeKind.Utc).AddDays(-1), + FirstRouteAddressId = 1, + LastRouteAddressId = 2 + }, + new Ticket + { + VehicleEnrollmentId = 2, + PurchaseDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 08, 00, 00, DateTimeKind.Utc).AddDays(-1), + FirstRouteAddressId = 2, + LastRouteAddressId = 9 + } + } + }); + + dbContext.Reviews.AddRangeAsync( + new Review + { + UserId = companyUser.Id, + VehicleEnrollmentId = 1, + Rating = 85, + Comment = "Good overall", + PostDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 08, 00, 00, DateTimeKind.Utc).AddDays(1), + }, new Review + { + UserId = companyUser.Id, + VehicleEnrollmentId = 2, + Rating = 100, + Comment = "Amazing", + PostDateTimeUtc = new DateTime(DateTime.UtcNow.Year, DateTime.UtcNow.Month, DateTime.UtcNow.Day, 08, 00, 00, DateTimeKind.Utc).AddDays(1), + }); + + await dbContext.SaveChangesAsync(); } } -} +} \ No newline at end of file diff --git a/Server/Migrations/20230430115750_Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table.Designer.cs b/Server/Migrations/20230430115750_Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table.Designer.cs new file mode 100644 index 0000000..c2158f6 --- /dev/null +++ b/Server/Migrations/20230430115750_Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table.Designer.cs @@ -0,0 +1,922 @@ +// +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("20230430115750_Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table")] + partial class Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table + { + 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") + .IsUnique(); + + 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("BirthDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Document") + .HasColumnType("integer"); + + b.Property("DocumentDetails") + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Gender") + .HasColumnType("integer"); + + b.Property("LastName") + .IsRequired() + .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("PasswordHash") + .HasColumnType("text"); + + b.Property("Patronymic") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + 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") + .WithOne("Company") + .HasForeignKey("Server.Models.Company", "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("Company") + .IsRequired(); + + 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/20230430115750_Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table.cs b/Server/Migrations/20230430115750_Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table.cs new file mode 100644 index 0000000..f0f295e --- /dev/null +++ b/Server/Migrations/20230430115750_Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table.cs @@ -0,0 +1,75 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Server.Migrations +{ + public partial class Add_Gender_BirthDate_Document_DocumentDetail_fields_to_User_table : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Companies_OwnerId", + table: "Companies"); + + migrationBuilder.AddColumn( + name: "BirthDate", + table: "AspNetUsers", + type: "timestamp with time zone", + nullable: true); + + migrationBuilder.AddColumn( + name: "Document", + table: "AspNetUsers", + type: "integer", + nullable: true); + + migrationBuilder.AddColumn( + name: "DocumentDetails", + table: "AspNetUsers", + type: "text", + nullable: true); + + migrationBuilder.AddColumn( + name: "Gender", + table: "AspNetUsers", + type: "integer", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_Companies_OwnerId", + table: "Companies", + column: "OwnerId", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropIndex( + name: "IX_Companies_OwnerId", + table: "Companies"); + + migrationBuilder.DropColumn( + name: "BirthDate", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "Document", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "DocumentDetails", + table: "AspNetUsers"); + + migrationBuilder.DropColumn( + name: "Gender", + table: "AspNetUsers"); + + migrationBuilder.CreateIndex( + name: "IX_Companies_OwnerId", + table: "Companies", + column: "OwnerId"); + } + } +} diff --git a/Server/Migrations/20230501105408_Add_CompanyDriver_relation.Designer.cs b/Server/Migrations/20230501105408_Add_CompanyDriver_relation.Designer.cs new file mode 100644 index 0000000..8fa2694 --- /dev/null +++ b/Server/Migrations/20230501105408_Add_CompanyDriver_relation.Designer.cs @@ -0,0 +1,962 @@ +// +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("20230501105408_Add_CompanyDriver_relation")] + partial class Add_CompanyDriver_relation + { + 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") + .IsUnique(); + + b.ToTable("Companies"); + }); + + modelBuilder.Entity("Server.Models.CompanyDriver", b => + { + b.Property("CompanyId") + .HasColumnType("integer"); + + b.Property("DriverId") + .HasColumnType("text"); + + b.HasKey("CompanyId", "DriverId"); + + b.HasIndex("DriverId") + .IsUnique(); + + b.ToTable("CompanyDrivers"); + }); + + 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("BirthDate") + .HasColumnType("timestamp with time zone"); + + b.Property("ConcurrencyStamp") + .IsConcurrencyToken() + .HasColumnType("text"); + + b.Property("Document") + .HasColumnType("integer"); + + b.Property("DocumentDetails") + .HasColumnType("text"); + + b.Property("Email") + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("EmailConfirmed") + .HasColumnType("boolean"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text"); + + b.Property("Gender") + .HasColumnType("integer"); + + b.Property("LastName") + .IsRequired() + .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("PasswordHash") + .HasColumnType("text"); + + b.Property("Patronymic") + .IsRequired() + .HasColumnType("text"); + + b.Property("PhoneNumber") + .HasColumnType("text"); + + b.Property("PhoneNumberConfirmed") + .HasColumnType("boolean"); + + b.Property("SecurityStamp") + .HasColumnType("text"); + + b.Property("TwoFactorEnabled") + .HasColumnType("boolean"); + + b.HasKey("Id"); + + b.HasIndex("NormalizedEmail") + .HasDatabaseName("EmailIndex"); + + 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") + .WithOne("Company") + .HasForeignKey("Server.Models.Company", "OwnerId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Owner"); + }); + + modelBuilder.Entity("Server.Models.CompanyDriver", b => + { + b.HasOne("Server.Models.Company", "Company") + .WithMany("CompanyDrivers") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.User", "Driver") + .WithOne("Employer") + .HasForeignKey("Server.Models.CompanyDriver", "DriverId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Company"); + + b.Navigation("Driver"); + }); + + 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("CompanyDrivers"); + + 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("Company") + .IsRequired(); + + b.Navigation("Employer") + .IsRequired(); + + 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/20230501105408_Add_CompanyDriver_relation.cs b/Server/Migrations/20230501105408_Add_CompanyDriver_relation.cs new file mode 100644 index 0000000..88f1938 --- /dev/null +++ b/Server/Migrations/20230501105408_Add_CompanyDriver_relation.cs @@ -0,0 +1,48 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Server.Migrations +{ + public partial class Add_CompanyDriver_relation : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "CompanyDrivers", + columns: table => new + { + DriverId = table.Column(type: "text", nullable: false), + CompanyId = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_CompanyDrivers", x => new { x.CompanyId, x.DriverId }); + table.ForeignKey( + name: "FK_CompanyDrivers_AspNetUsers_DriverId", + column: x => x.DriverId, + principalTable: "AspNetUsers", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_CompanyDrivers_Companies_CompanyId", + column: x => x.CompanyId, + principalTable: "Companies", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_CompanyDrivers_DriverId", + table: "CompanyDrivers", + column: "DriverId", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "CompanyDrivers"); + } + } +} diff --git a/Server/Migrations/ApplicationDbContextModelSnapshot.cs b/Server/Migrations/ApplicationDbContextModelSnapshot.cs index 69debca..1400139 100644 --- a/Server/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/Server/Migrations/ApplicationDbContextModelSnapshot.cs @@ -179,7 +179,7 @@ namespace Server.Migrations b.HasIndex("CityId"); - b.ToTable("Addresses"); + b.ToTable("Addresses", (string)null); }); modelBuilder.Entity("Server.Models.City", b => @@ -201,7 +201,7 @@ namespace Server.Migrations b.HasIndex("StateId"); - b.ToTable("Cities"); + b.ToTable("Cities", (string)null); }); modelBuilder.Entity("Server.Models.Company", b => @@ -222,9 +222,26 @@ namespace Server.Migrations b.HasKey("Id"); - b.HasIndex("OwnerId"); + b.HasIndex("OwnerId") + .IsUnique(); - b.ToTable("Companies"); + b.ToTable("Companies", (string)null); + }); + + modelBuilder.Entity("Server.Models.CompanyDriver", b => + { + b.Property("CompanyId") + .HasColumnType("integer"); + + b.Property("DriverId") + .HasColumnType("text"); + + b.HasKey("CompanyId", "DriverId"); + + b.HasIndex("DriverId") + .IsUnique(); + + b.ToTable("CompanyDrivers", (string)null); }); modelBuilder.Entity("Server.Models.Country", b => @@ -245,7 +262,7 @@ namespace Server.Migrations b.HasKey("Id"); - b.ToTable("Countries"); + b.ToTable("Countries", (string)null); }); modelBuilder.Entity("Server.Models.Review", b => @@ -278,7 +295,7 @@ namespace Server.Migrations b.HasIndex("VehicleEnrollmentId"); - b.ToTable("Reviews"); + b.ToTable("Reviews", (string)null); }); modelBuilder.Entity("Server.Models.Route", b => @@ -295,7 +312,7 @@ namespace Server.Migrations b.HasKey("Id"); - b.ToTable("Routes"); + b.ToTable("Routes", (string)null); }); modelBuilder.Entity("Server.Models.RouteAddress", b => @@ -324,7 +341,7 @@ namespace Server.Migrations b.HasIndex("RouteId"); - b.ToTable("RouteAddresses"); + b.ToTable("RouteAddresses", (string)null); }); modelBuilder.Entity("Server.Models.RouteAddressDetails", b => @@ -356,7 +373,7 @@ namespace Server.Migrations b.HasIndex("VehicleEnrollmentId"); - b.ToTable("RouteAddressDetails"); + b.ToTable("RouteAddressDetails", (string)null); }); modelBuilder.Entity("Server.Models.State", b => @@ -378,7 +395,7 @@ namespace Server.Migrations b.HasIndex("CountryId"); - b.ToTable("States"); + b.ToTable("States", (string)null); }); modelBuilder.Entity("Server.Models.Ticket", b => @@ -416,7 +433,7 @@ namespace Server.Migrations b.HasIndex("VehicleEnrollmentId"); - b.ToTable("Tickets"); + b.ToTable("Tickets", (string)null); }); modelBuilder.Entity("Server.Models.TicketGroup", b => @@ -435,7 +452,7 @@ namespace Server.Migrations b.HasIndex("UserId"); - b.ToTable("TicketGroups"); + b.ToTable("TicketGroups", (string)null); }); modelBuilder.Entity("Server.Models.User", b => @@ -446,10 +463,19 @@ namespace Server.Migrations b.Property("AccessFailedCount") .HasColumnType("integer"); + b.Property("BirthDate") + .HasColumnType("timestamp with time zone"); + b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnType("text"); + b.Property("Document") + .HasColumnType("integer"); + + b.Property("DocumentDetails") + .HasColumnType("text"); + b.Property("Email") .HasMaxLength(256) .HasColumnType("character varying(256)"); @@ -461,6 +487,9 @@ namespace Server.Migrations .IsRequired() .HasColumnType("text"); + b.Property("Gender") + .HasColumnType("integer"); + b.Property("LastName") .IsRequired() .HasColumnType("text"); @@ -549,7 +578,7 @@ namespace Server.Migrations b.HasIndex("CompanyId"); - b.ToTable("Vehicles"); + b.ToTable("Vehicles", (string)null); }); modelBuilder.Entity("Server.Models.VehicleEnrollment", b => @@ -584,7 +613,7 @@ namespace Server.Migrations b.HasIndex("VehicleId"); - b.ToTable("VehicleEnrollments"); + b.ToTable("VehicleEnrollments", (string)null); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => @@ -663,14 +692,33 @@ namespace Server.Migrations modelBuilder.Entity("Server.Models.Company", b => { b.HasOne("Server.Models.User", "Owner") - .WithMany() - .HasForeignKey("OwnerId") + .WithOne("Company") + .HasForeignKey("Server.Models.Company", "OwnerId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.Navigation("Owner"); }); + modelBuilder.Entity("Server.Models.CompanyDriver", b => + { + b.HasOne("Server.Models.Company", "Company") + .WithMany("CompanyDrivers") + .HasForeignKey("CompanyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Server.Models.User", "Driver") + .WithOne("Employer") + .HasForeignKey("Server.Models.CompanyDriver", "DriverId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Company"); + + b.Navigation("Driver"); + }); + modelBuilder.Entity("Server.Models.Review", b => { b.HasOne("Server.Models.User", "User") @@ -771,7 +819,7 @@ namespace Server.Migrations modelBuilder.Entity("Server.Models.User", b => { - b.OwnsMany("Server.Models.RefreshToken", "RefreshTokens", b1 => + b.OwnsMany("Server.Models.User.RefreshTokens#Server.Models.RefreshToken", "RefreshTokens", b1 => { b1.Property("UserId") .HasColumnType("text"); @@ -797,7 +845,7 @@ namespace Server.Migrations b1.HasKey("UserId", "Id"); - b1.ToTable("RefreshToken"); + b1.ToTable("RefreshToken", (string)null); b1.WithOwner() .HasForeignKey("UserId"); @@ -848,6 +896,8 @@ namespace Server.Migrations modelBuilder.Entity("Server.Models.Company", b => { + b.Navigation("CompanyDrivers"); + b.Navigation("Vehicles"); }); @@ -880,6 +930,12 @@ namespace Server.Migrations modelBuilder.Entity("Server.Models.User", b => { + b.Navigation("Company") + .IsRequired(); + + b.Navigation("Employer") + .IsRequired(); + b.Navigation("Reviews"); b.Navigation("TicketGroups"); diff --git a/Server/Models/Company.cs b/Server/Models/Company.cs index 60bb1a4..c09d55b 100644 --- a/Server/Models/Company.cs +++ b/Server/Models/Company.cs @@ -14,5 +14,6 @@ public class Company public string OwnerId { get; set; } = null!; public User Owner { get; set; } = null!; - public virtual List Vehicles { get; set; } = null!; + public virtual IList Vehicles { get; set; } = null!; + public virtual IList CompanyDrivers { get; set; } = null!; } \ No newline at end of file diff --git a/Server/Models/CompanyDriver.cs b/Server/Models/CompanyDriver.cs new file mode 100644 index 0000000..06e7be4 --- /dev/null +++ b/Server/Models/CompanyDriver.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations.Schema; + +namespace Server.Models; + +public class CompanyDriver +{ + [ForeignKey("UserId")] + public string DriverId { get; set; } = null!; + public User Driver { get; set; } = null!; + + [ForeignKey("CompanyId")] + public int CompanyId { get; set; } + public Company Company { get; set; } = null!; +} \ No newline at end of file diff --git a/Server/Models/User.cs b/Server/Models/User.cs index 8d8630c..d0111df 100644 --- a/Server/Models/User.cs +++ b/Server/Models/User.cs @@ -1,5 +1,6 @@ using System.ComponentModel.DataAnnotations; using Microsoft.AspNetCore.Identity; +using Utils; namespace Server.Models; @@ -14,8 +15,21 @@ public class User : IdentityUser [Required(ErrorMessage = "Patronymic is required")] public string Patronymic { get; set; } = null!; + public DateTime? BirthDate { get; set; } + + public Identity.Gender? Gender { get; set; } + + public Identity.Document? Document { get; set; } + + public string? DocumentDetails { get; set; } + public IList RefreshTokens { get; set; } = null!; + + public Company Company { get; set; } = null!; public virtual IList TicketGroups { get; set; } = null!; public virtual IList Reviews { get; set; } = null!; + public virtual CompanyDriver? Employer { get; set; } = null!; + + public string GetFullName() => $"{LastName} {FirstName} {Patronymic}"; } \ No newline at end of file diff --git a/Server/Program.cs b/Server/Program.cs index 77e50db..ef7ea98 100644 --- a/Server/Program.cs +++ b/Server/Program.cs @@ -8,12 +8,12 @@ using Microsoft.OpenApi.Models; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Server.Configurations; -using Server.Constants; using Server.Data; using Server.Helpers; using Server.Models; using Server.Services; using SharedModels.DataTransferObjects; +using Utils; var builder = WebApplication.CreateBuilder(args); var services = builder.Services; @@ -121,8 +121,8 @@ services.AddScoped(); services.AddScoped(); services.AddScoped(); services.AddScoped(); - -services.AddScoped, SortHelper>(); +services.AddScoped(); +services.AddScoped(); services.AddScoped, DataShaper>(); services.AddScoped, DataShaper>(); @@ -139,15 +139,16 @@ services.AddScoped, DataShaper, DataShaper>(); services.AddScoped, DataShaper>(); services.AddScoped, DataShaper>(); +services.AddScoped, DataShaper>(); +services.AddScoped, DataShaper>(); +services.AddScoped, DataShaper>(); +services.AddScoped, SortHelper>(); services.AddScoped, Pager>(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); -services.AddScoped, DataShaper>(); -services.AddScoped, DataShaper>(); // Adding DB Context with PostgreSQL var connectionString = configuration.GetConnectionString("DefaultConnection"); @@ -162,7 +163,7 @@ var serviceProvider = scope.ServiceProvider; await SeedData.Initialize(serviceProvider); // Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) +if (Convert.ToBoolean(configuration["UseApiExplorer"])) { app.UseSwagger(); app.UseSwaggerUI(); diff --git a/Server/Server.csproj b/Server/Server.csproj index e1bf303..e16e355 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -37,6 +37,7 @@ + diff --git a/Server/Services/AuthenticationService.cs b/Server/Services/AuthenticationService.cs index 38c600e..5262bf7 100644 --- a/Server/Services/AuthenticationService.cs +++ b/Server/Services/AuthenticationService.cs @@ -13,6 +13,7 @@ using Server.Constants; using Server.Models; using SharedModels.Requests; using SharedModels.Responses; +using Utils; namespace Server.Services; @@ -75,7 +76,7 @@ public class AuthenticationService : IAuthenticationService return (false, $"{createUserResult.Errors?.First().Description}"); } - await _userManager.AddToRoleAsync(user, Constants.Identity.DefaultRole.ToString()); + await _userManager.AddToRoleAsync(user, Identity.DefaultRole.ToString()); var token = await _userManager.GenerateEmailConfirmationTokenAsync(user); var confirmationLink = _linkGenerator.GetUriByAction(_contextAccessor.HttpContext, @@ -285,10 +286,12 @@ public class AuthenticationService : IAuthenticationService var claims = new[] { new Claim(JwtStandardClaimNames.Sub, user.Id), - new Claim(JwtStandardClaimNames.Name, $"{user.LastName} {user.FirstName} {user.Patronymic}"), + new Claim(JwtStandardClaimNames.Name, user.GetFullName()), new Claim(JwtStandardClaimNames.GivenName, user.FirstName), new Claim(JwtStandardClaimNames.FamilyName, user.LastName), new Claim(JwtStandardClaimNames.MiddleName, user.Patronymic), + new Claim(JwtStandardClaimNames.Gender, user.Gender?.ToString() ?? "Undefined"), + new Claim(JwtStandardClaimNames.BirthDate, user.BirthDate?.ToString()?? "Undefined"), new Claim(JwtStandardClaimNames.Email, user.Email), new Claim(JwtStandardClaimNames.EmailVerified, user.EmailConfirmed.ToString()), new Claim(JwtRegisteredClaimNames.Exp, DateTime.UtcNow.AddMinutes(_jwt.ValidityInMinutes).ToString(CultureInfo.InvariantCulture)) diff --git a/Server/Services/DriverManagementService.cs b/Server/Services/DriverManagementService.cs new file mode 100644 index 0000000..fc0ee58 --- /dev/null +++ b/Server/Services/DriverManagementService.cs @@ -0,0 +1,145 @@ +using System.Dynamic; +using AutoMapper; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Server.Data; +using Server.Helpers; +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryParameters; +using SharedModels.QueryParameters.Objects; + +namespace Server.Services; + +public class DriverManagementService : IDriverManagementService +{ + private readonly IUserManagementService _userManagementService; + private readonly UserManager _userManager; + private readonly ApplicationDbContext _dbContext; + private readonly IMapper _mapper; + private readonly ISortHelper _userSortHelper; + private readonly IDataShaper _userDataShaper; + private readonly IPager _pager; + + public DriverManagementService(IUserManagementService userManagementService, IMapper mapper, + UserManager userManager, ApplicationDbContext dbContext, + ISortHelper userSortHelper, IDataShaper userDataShaper, + IPager pager) + { + _userManagementService = userManagementService; + _userManager = userManager; + _dbContext = dbContext; + _userSortHelper = userSortHelper; + _userDataShaper = userDataShaper; + _pager = pager; + _mapper = mapper; + } + + public async Task<(bool isSucceeded, IActionResult? actionResult, DriverDto driver)> AddDriver(CreateDriverDto createDriverDto) + { + var createUserDto = _mapper.Map(createDriverDto); + + createUserDto.Roles = new List { "Driver" }; + createUserDto.Password = createDriverDto.Password; + + var result = await _userManagementService.AddUser(createUserDto); + + if (!result.isSucceeded) + { + return (false, result.actionResult, null); + } + + var driverDto = _mapper.Map(createUserDto); + _dbContext.CompanyDrivers.Add(new CompanyDriver { CompanyId = createDriverDto.CompanyId, DriverId = driverDto.Id }); + await _dbContext.SaveChangesAsync(); + + driverDto.Roles = result.user.Roles; + + return (true, null, driverDto); + } + + public async Task<(bool isSucceeded, IActionResult? actionResult, IEnumerable drivers, PagingMetadata pagingMetadata)> GetDrivers(CompanyDriverParameters parameters) + { + var dbUsers = _userManager.Users.Include(u => u.Employer) + .Where(u => u.Employer != null).AsQueryable(); + + FilterByCompanyId(ref dbUsers, parameters.CompanyId); + SearchByAllUserFields(ref dbUsers, parameters.Search); + + var userDtos = _mapper.ProjectTo(dbUsers); + var shapedData = _userDataShaper.ShapeData(userDtos, parameters.Fields).AsQueryable(); + + try + { + shapedData = _userSortHelper.ApplySort(shapedData, parameters.Sort); + } + catch (Exception) + { + return (false, new BadRequestObjectResult("Invalid sorting string"), null!, null!); + } + + var pagingMetadata = _pager.ApplyPaging(ref shapedData, parameters.PageNumber, parameters.PageSize); + + return (true, null, shapedData, pagingMetadata); + + void FilterByCompanyId(ref IQueryable users, int? compnayId) + { + if (!users.Any() || compnayId == null) + { + return; + } + + users = users.Where(u => u.Employer.CompanyId == compnayId); + } + + void SearchByAllUserFields(ref IQueryable users, string? search) + { + if (!users.Any() || search == null) + { + return; + } + + users = users.Where(u => + u.FirstName.ToLower().Contains(search.ToLower()) || + u.LastName.ToLower().Contains(search.ToLower()) || + u.Patronymic.ToLower().Contains(search.ToLower()) || + u.Email.ToLower().Contains(search.ToLower()) || + u.PhoneNumber.ToLower().Contains(search.ToLower())); + } + } + + public async Task<(bool isSucceeded, IActionResult? actionResult, ExpandoObject driver)> GetDriver(string id, string? fields) + { + var dbUser = await _userManager.Users.Include(u => u.Employer). + FirstOrDefaultAsync(u => u.Id == id); + + if (dbUser == null) + { + return (false, new NotFoundResult(), null!); + } + + if (String.IsNullOrWhiteSpace(fields)) + { + fields = CompanyDriverParameters.DefaultFields; + } + + var userDto = _mapper.Map(dbUser); + var shapedData = _userDataShaper.ShapeData(userDto, fields); + + return (true, null, shapedData); + } + + public async Task<(bool isSucceeded, IActionResult? actionResult, DriverDto driver)> UpdateDriver(string id, UpdateDriverDto updateDriverDto) + { + var updateUserDto = _mapper.Map(updateDriverDto); + var result = await _userManagementService.UpdateUser(id, updateUserDto); + + return (result.isSucceeded, result.actionResult, _mapper.Map(result.user)); + } + + public async Task<(bool isSucceed, IActionResult? actionResult)> DeleteDriver(string id) + { + return await _userManagementService.DeleteUser(id); + } +} \ No newline at end of file diff --git a/Server/Services/EmailSenderService.cs b/Server/Services/EmailSenderService.cs index 478bb9f..df7e5a1 100644 --- a/Server/Services/EmailSenderService.cs +++ b/Server/Services/EmailSenderService.cs @@ -34,7 +34,7 @@ public class EmailSenderService : IEmailSenderService mailMessage.Subject = $"{applicationName}. {subject}"; mailMessage.Body = new TextPart(MimeKit.Text.TextFormat.Text) { Text = message}; - await _smtpClient.ConnectAsync(_smtpCredentials.Host, Int32.Parse(_smtpCredentials.Port), false); + await _smtpClient.ConnectAsync(_smtpCredentials.Host, _smtpCredentials.Port, false); await _smtpClient.AuthenticateAsync(Encoding.ASCII, _smtpCredentials.User, _smtpCredentials.Password); await _smtpClient.SendAsync(mailMessage); await _smtpClient.DisconnectAsync(true); diff --git a/Server/Services/IDriverManagementService.cs b/Server/Services/IDriverManagementService.cs new file mode 100644 index 0000000..f6cdf5e --- /dev/null +++ b/Server/Services/IDriverManagementService.cs @@ -0,0 +1,22 @@ +using System.Dynamic; +using Microsoft.AspNetCore.Mvc; +using SharedModels.DataTransferObjects; +using SharedModels.QueryParameters; +using SharedModels.QueryParameters.Objects; + +namespace Server.Services; + +public interface IDriverManagementService +{ + + Task<(bool isSucceeded, IActionResult? actionResult, DriverDto driver)> AddDriver(CreateDriverDto createDriverDto); + + Task<(bool isSucceeded, IActionResult? actionResult, IEnumerable drivers, PagingMetadata pagingMetadata)> + GetDrivers(CompanyDriverParameters parameters); + + Task<(bool isSucceeded, IActionResult? actionResult, ExpandoObject driver)> GetDriver(string id, string? fields); + + Task<(bool isSucceeded, IActionResult? actionResult, DriverDto driver)> UpdateDriver(string id, UpdateDriverDto updateDriverDto); + + Task<(bool isSucceed, IActionResult? actionResult)> DeleteDriver(string id); +} \ No newline at end of file diff --git a/Server/Services/IUserManagementService.cs b/Server/Services/IUserManagementService.cs new file mode 100644 index 0000000..de77bb1 --- /dev/null +++ b/Server/Services/IUserManagementService.cs @@ -0,0 +1,21 @@ +using System.Dynamic; +using Microsoft.AspNetCore.Mvc; +using SharedModels.DataTransferObjects; +using SharedModels.QueryParameters; +using SharedModels.QueryParameters.Objects; + +namespace Server.Services; + +public interface IUserManagementService +{ + Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)> AddUser(CreateUserDto createUserDto); + + Task<(bool isSucceeded, IActionResult? actionResult, IEnumerable users, PagingMetadata pagingMetadata)> + GetUsers(UserParameters parameters); + + Task<(bool isSucceeded, IActionResult? actionResult, ExpandoObject user)> GetUser(string id, string? fields); + + Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)> UpdateUser(string id, UpdateUserDto updateUserDto); + + Task<(bool isSucceed, IActionResult? actionResult)> DeleteUser(string id); +} \ No newline at end of file diff --git a/Server/Services/ReviewManagementService.cs b/Server/Services/ReviewManagementService.cs index f5ff7b4..6f03b97 100644 --- a/Server/Services/ReviewManagementService.cs +++ b/Server/Services/ReviewManagementService.cs @@ -44,11 +44,14 @@ public class ReviewManagementService : IReviewManagementService PagingMetadata pagingMetadata)> GetReviews(ReviewParameters parameters) { var dbReviews = _dbContext.Reviews + .Include(r => r.VehicleEnrollment).ThenInclude(ve => ve.Vehicle) + .ThenInclude(v => v.Company).Include(r => r.User) .AsQueryable(); FilterByReviewRating(ref dbReviews, parameters.FromRating, parameters.ToRating); FilterByReviewComment(ref dbReviews, parameters.Comment); FilterByReviewUserId(ref dbReviews, parameters.UserId); + FilterByReviewCompanyId(ref dbReviews, parameters.CompanyId); var reviewDtos = _mapper.ProjectTo(dbReviews); var shapedData = _reviewDataShaper.ShapeData(reviewDtos, parameters.Fields).AsQueryable(); @@ -103,6 +106,16 @@ public class ReviewManagementService : IReviewManagementService reviews = reviews.Where(r => r.UserId.Contains(userId.ToLower())); } + + void FilterByReviewCompanyId(ref IQueryable reviews, int? companyId) + { + if (!reviews.Any() || companyId == null) + { + return; + } + + reviews = reviews.Where(r => r.VehicleEnrollment.Vehicle.CompanyId == companyId); + } } public async Task<(bool isSucceed, IActionResult? actionResult, ExpandoObject review)> GetReview(int id, string? fields) @@ -113,7 +126,8 @@ public class ReviewManagementService : IReviewManagementService } var dbReview = await _dbContext.Reviews.Where(r => r.Id == id) - .Include(r => r.VehicleEnrollment) + .Include(r => r.VehicleEnrollment).ThenInclude(ve => ve.Vehicle) + .ThenInclude(v => v.Company).Include(r => r.User) .FirstAsync(); if (String.IsNullOrWhiteSpace(fields)) diff --git a/Server/Services/UserManagementService.cs b/Server/Services/UserManagementService.cs new file mode 100644 index 0000000..894933f --- /dev/null +++ b/Server/Services/UserManagementService.cs @@ -0,0 +1,231 @@ +using System.Dynamic; +using AutoMapper; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Server.Data; +using Server.Helpers; +using Server.Models; +using SharedModels.DataTransferObjects; +using SharedModels.QueryParameters; +using SharedModels.QueryParameters.Objects; + +namespace Server.Services; + +public class UserManagementService : IUserManagementService +{ + private readonly UserManager _userManager; + private readonly RoleManager _roleManager; + private readonly IMapper _mapper; + private readonly ISortHelper _userSortHelper; + private readonly IDataShaper _userDataShaper; + private readonly IPager _pager; + + public UserManagementService(IMapper mapper, UserManager userManager, RoleManager roleManager, + ISortHelper userSortHelper, IDataShaper userDataShaper, IPager pager, + ApplicationDbContext dbContext) + { + _mapper = mapper; + _userManager = userManager; + _roleManager = roleManager; + _userSortHelper = userSortHelper; + _userDataShaper = userDataShaper; + _pager = pager; + + _userManager.UserValidators.Clear(); + } + + public async Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)> AddUser(CreateUserDto createUserDto) + { + var user = _mapper.Map(createUserDto); + user.BirthDate = user.BirthDate == null ? null : new DateTime(user.BirthDate.Value.Ticks, DateTimeKind.Utc); + + var userDto = _mapper.Map(user); + userDto.Roles = new List(); + + if (await _userManager.FindByEmailAsync(user.Email) != null) + { + return (false, new BadRequestObjectResult("Email already registered"), null); + } + + if (user.PhoneNumber != null && await _userManager.Users.FirstOrDefaultAsync(u => u.PhoneNumber == user.PhoneNumber) != null) + { + return (false, new BadRequestObjectResult("Phone number already registered"), null); + } + + if (createUserDto.Roles != null!) + { + foreach (var role in createUserDto.Roles) + { + if (!await _roleManager.RoleExistsAsync(role)) + { + return (false, new BadRequestObjectResult($"Roles \"{role}\" doesn't exist"), null); + } + + userDto.Roles.Add(role); + } + } + + await _userManager.CreateAsync(user, createUserDto.Password); + await _userManager.AddToRolesAsync(user, createUserDto.Roles); + + userDto.Id = user.Id; + + return (true, null, userDto); + } + + public async Task<(bool isSucceeded, IActionResult? actionResult, IEnumerable users, + PagingMetadata pagingMetadata)> GetUsers(UserParameters parameters) + { + var dbUsers = _userManager.Users.Include(u => u.Company) + .Include(u => u.Reviews).Include(u => u.TicketGroups) + .ThenInclude(tg => tg.Tickets).AsQueryable(); + + SearchByAllUserFields(ref dbUsers, parameters.Search); + + var userDtos = _mapper.ProjectTo(dbUsers); + var shapedData = _userDataShaper.ShapeData(userDtos, parameters.Fields).AsQueryable(); + + try + { + shapedData = _userSortHelper.ApplySort(shapedData, parameters.Sort); + } + catch (Exception) + { + return (false, new BadRequestObjectResult("Invalid sorting string"), null!, null!); + } + + var pagingMetadata = _pager.ApplyPaging(ref shapedData, parameters.PageNumber, parameters.PageSize); + + if ((bool)parameters.Fields?.Contains("roles")) + { + foreach (var user in shapedData) + { + dynamic dynamicUser = user as IDictionary; + + var roles = await _userManager.GetRolesAsync(new User { Id = dynamicUser.Id }); + dynamicUser.Roles = roles; + } + } + + return (true, null, shapedData, pagingMetadata); + + void SearchByAllUserFields(ref IQueryable users, string? search) + { + if (!users.Any() || search == null) + { + return; + } + + users = users.Where(u => + u.FirstName.ToLower().Contains(search.ToLower()) || + u.LastName.ToLower().Contains(search.ToLower()) || + u.Patronymic.ToLower().Contains(search.ToLower()) || + u.Email.ToLower().Contains(search.ToLower()) || + u.PhoneNumber.ToLower().Contains(search.ToLower())); + } + } + + public async Task<(bool isSucceeded, IActionResult? actionResult, ExpandoObject user)> + GetUser(string id, string? fields) + { + var dbUser = await _userManager.Users.Include(u => u.Employer). + FirstOrDefaultAsync(u => u.Id == id); + + if (dbUser == null) + { + return (false, new NotFoundResult(), null!); + } + + if (String.IsNullOrWhiteSpace(fields)) + { + fields = UserParameters.DefaultFields; + } + + var userDto = _mapper.Map(dbUser); + var shapedData = _userDataShaper.ShapeData(userDto, fields); + + return (true, null, shapedData); + } + + public async Task<(bool isSucceeded, IActionResult? actionResult, UserDto user)> + UpdateUser(string id, UpdateUserDto updateUserDto) + { + if (id != updateUserDto.Id) + { + return (false, new BadRequestObjectResult("Object and query ids don't match"), null); + } + + if (!await _userManager.Users.AnyAsync(u => u.Id == id)) + { + + return (false, new NotFoundResult(), null); + } + + var dbUser = await _userManager.FindByIdAsync(id); + var user = _mapper.Map(updateUserDto); + + dbUser.FirstName = user.FirstName; + dbUser.LastName = user.LastName; + dbUser.Patronymic = user.Patronymic; + dbUser.BirthDate = new DateTime(user.BirthDate.Value.Ticks, DateTimeKind.Utc); + dbUser.Gender = user.Gender; + dbUser.Document = user.Document; + dbUser.DocumentDetails = user.DocumentDetails; + dbUser.Email = user.Email; + dbUser.EmailConfirmed = user.EmailConfirmed; + dbUser.PhoneNumber = user.PhoneNumber; + dbUser.PhoneNumberConfirmed = user.PhoneNumberConfirmed; + + var userDto = _mapper.Map(user); + userDto.Roles = new List(); + + updateUserDto.Roles = updateUserDto.Roles == null ? new List() : updateUserDto.Roles; + var roles = await _userManager.GetRolesAsync(user); + var rolesToDelete = roles.Except(updateUserDto.Roles).ToArray(); + var rolesToAdd = updateUserDto.Roles.Except(roles).ToArray(); + userDto.Roles = roles.Except(rolesToDelete).Union(rolesToAdd).ToList(); + + foreach (var role in rolesToDelete) + { + if (await _roleManager.RoleExistsAsync(role)) + { + await _userManager.RemoveFromRoleAsync(dbUser, role); + } + } + + foreach (var role in rolesToAdd) + { + if (!await _roleManager.RoleExistsAsync(role)) + { + return (false, new BadRequestObjectResult($"Roles \"{role}\" doesn't exist"), null); + } + } + + await _userManager.AddToRolesAsync(dbUser, rolesToAdd); + + if (updateUserDto.Password != null) + { + await _userManager.RemovePasswordAsync(dbUser); + await _userManager.AddPasswordAsync(dbUser, updateUserDto.Password); + } + + await _userManager.UpdateAsync(dbUser); + + return (true, null, userDto); + } + + public async Task<(bool isSucceed, IActionResult? actionResult)> DeleteUser(string id) + { + var dbUser = await _userManager.FindByIdAsync(id); + + if (dbUser == null) + { + return (false, new NotFoundResult()); + } + + await _userManager.DeleteAsync(dbUser); + + return (true, null); + } +} \ No newline at end of file diff --git a/Server/appsettings.Development.json b/Server/appsettings.Development.json index 5e9add1..2ce6a28 100644 --- a/Server/appsettings.Development.json +++ b/Server/appsettings.Development.json @@ -12,7 +12,7 @@ "ApplicationName": "auto.bus", "SmtpCredentials": { "Host": "", - "Port": "", + "Port": 587, "User": "", "Password": "" }, diff --git a/Server/appsettings.json b/Server/appsettings.json index 94fd6be..717c5ab 100644 --- a/Server/appsettings.json +++ b/Server/appsettings.json @@ -9,10 +9,11 @@ "ConnectionStrings": { "DefaultConnection": "host=localhost;database=auto.bus;user id=postgres;password=postgres;Include Error Detail = true" }, + "UseApiExplorer": true, "ApplicationName": "auto.bus", "SmtpCredentials": { "Host": "", - "Port": "", + "Port": 587, "User": "", "Password": "" }, diff --git a/SharedModels/DataTransferObjects/DriverDto.cs b/SharedModels/DataTransferObjects/DriverDto.cs new file mode 100644 index 0000000..ad27339 --- /dev/null +++ b/SharedModels/DataTransferObjects/DriverDto.cs @@ -0,0 +1,18 @@ +namespace SharedModels.DataTransferObjects; + +public class DriverDto : UserDto +{ + public int CompanyId { get; set; } +} + +public class CreateDriverDto : CreateUserDto +{ + public int CompanyId { get; set; } + + public override IList? Roles { get; set; } +} + +public class UpdateDriverDto : UpdateUserDto +{ + public override IList? Roles { get; set; } +} \ No newline at end of file diff --git a/SharedModels/DataTransferObjects/ReviewDto.cs b/SharedModels/DataTransferObjects/ReviewDto.cs index 4890a96..d456cc0 100644 --- a/SharedModels/DataTransferObjects/ReviewDto.cs +++ b/SharedModels/DataTransferObjects/ReviewDto.cs @@ -9,6 +9,7 @@ public class ReviewDto : CreateReviewDto [DataType(DataType.DateTime)] public DateTime PostDateTimeUtc { get; set; } + public StrippedUserDto User { get; set; } = null!; public InReviewVehicleEnrollmentDto VehicleEnrollment { get; set; } = null!; } diff --git a/SharedModels/DataTransferObjects/UserDto.cs b/SharedModels/DataTransferObjects/UserDto.cs index 4e79e14..111418a 100644 --- a/SharedModels/DataTransferObjects/UserDto.cs +++ b/SharedModels/DataTransferObjects/UserDto.cs @@ -1,39 +1,73 @@ using System.ComponentModel.DataAnnotations; +using Utils; namespace SharedModels.DataTransferObjects; -public class UserDto : UpdateUserDto +public class UserDto { - public virtual CompanyDto Company { get; set; } = null!; + public string Id { get; set; } = null!; - public virtual IList Tickets { get; set; } = null!; - public virtual IList Ratings { get; set; } = null!; -} - -public class CreateUserDto -{ - public string? FirstName { get; set; } - public string? LastName { get; set; } - - [Required(ErrorMessage = "Username is required")] - public string UserName { get; set; } = null!; + public string FirstName { get; set; } = null!; + + public string LastName { get; set; } = null!; + + public string Patronymic { get; set; } = null!; + + [DataType(DataType.Date)] + public DateTime? BirthDate { get; set; } + + public Identity.Gender? Gender { get; set; } + + public Identity.Document? Document { get; set; } + + public string? DocumentDetails { get; set; } - [Required(ErrorMessage = "Email is required")] - [EmailAddress] public string Email { get; set; } = null!; public bool EmailConfirmed { get; set; } = false; - [Required(ErrorMessage = "Password is required")] - [DataType(DataType.Password)] - public string Password { get; set; } = null!; - [DataType(DataType.Password)] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } = null!; - public string? PhoneNumber { get; set; } public bool PhoneNumberConfirmed { get; set; } = false; - public virtual IList RoleIds { get; set; } = new List { "User" }; + public virtual IList Roles { get; set; } = null!; + + public virtual IList? TicketGroups { get; set; } = null!; + public virtual IList? Reviews { get; set; } = null!; +} + +public class CreateUserDto +{ + [Required] + public string FirstName { get; set; } = null!; + + [Required] + public string LastName { get; set; } = null!; + + [Required] + public string Patronymic { get; set; } = null!; + + [DataType(DataType.Date)] + public DateTime? BirthDate { get; set; } + + public Identity.Gender? Gender { get; set; } + + public Identity.Document? Document { get; set; } + + public string? DocumentDetails { get; set; } + + [Required] + [EmailAddress] + public string Email { get; set; } = null!; + public bool EmailConfirmed { get; set; } = false; + + [Required] + [DataType(DataType.Password)] + public string Password { get; set; } = null!; + + [DataType(DataType.PhoneNumber)] + public string? PhoneNumber { get; set; } + public bool? PhoneNumberConfirmed { get; set; } = false; + + public virtual IList Roles { get; set; } = null!; } public class UpdateUserDto @@ -41,23 +75,54 @@ public class UpdateUserDto [Required] public string Id { get; set; } = null!; - public string? FirstName { get; set; } - public string? LastName { get; set; } - - public string UserName { get; set; } = null!; + [Required] + public string FirstName { get; set; } = null!; + [Required] + public string LastName { get; set; } = null!; + + [Required] + public string Patronymic { get; set; } = null!; + + [Required] + [DataType(DataType.Date)] + public DateTime? BirthDate { get; set; } + + [Required] + public Identity.Gender? Gender { get; set; } + + [Required] + public Identity.Document? Document { get; set; } + + [Required] + public string? DocumentDetails { get; set; } + + [Required] [EmailAddress] public string Email { get; set; } = null!; - public bool EmailConfirmed { get; set; } + public bool EmailConfirmed { get; set; } = false; [DataType(DataType.Password)] - public string Password { get; set; } = null!; - [DataType(DataType.Password)] - [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] - public string ConfirmPassword { get; set; } = null!; + public string? Password { get; set; } = null!; + [Required] + [DataType(DataType.PhoneNumber)] public string? PhoneNumber { get; set; } - public bool PhoneNumberConfirmed { get; set; } + [Required] + public bool PhoneNumberConfirmed { get; set; } = false; + + public virtual IList Roles { get; set; } = null!; +} + +public class StrippedUserDto +{ + public string Id { get; set; } = null!; - public virtual IList RoleIds { get; set; } = new List { "User" }; + public string FirstName { get; set; } = null!; + + public string LastName { get; set; } = null!; + + public string Patronymic { get; set; } = null!; + + public Identity.Gender? Gender { get; set; } } \ No newline at end of file diff --git a/SharedModels/QueryParameters/Objects/CompanyDriverParameters.cs b/SharedModels/QueryParameters/Objects/CompanyDriverParameters.cs new file mode 100644 index 0000000..8a9d88c --- /dev/null +++ b/SharedModels/QueryParameters/Objects/CompanyDriverParameters.cs @@ -0,0 +1,16 @@ +namespace SharedModels.QueryParameters.Objects; + +public class CompanyDriverParameters : ParametersBase +{ + public const string DefaultFields = "id,firstName,lastName,patronymic,email,emailConfirmed,phoneNumber," + + "phoneNumberConfirmed,birthDate,gender,document,documentDetails," + + "companyId"; + + public CompanyDriverParameters() + { + Sort = ""; + Fields = DefaultFields; + } + + public int? CompanyId { get; set; } +} \ No newline at end of file diff --git a/SharedModels/QueryParameters/Objects/ReviewParameters.cs b/SharedModels/QueryParameters/Objects/ReviewParameters.cs index d2e5c04..fc26cf4 100644 --- a/SharedModels/QueryParameters/Objects/ReviewParameters.cs +++ b/SharedModels/QueryParameters/Objects/ReviewParameters.cs @@ -2,7 +2,7 @@ namespace SharedModels.QueryParameters.Objects; public class ReviewParameters : ParametersBase { - public const string DefaultFields = "id,userId,vehicleEnrollmentId,vehicleEnrollment,rating,comment"; + public const string DefaultFields = "id,rating,comment,userId,user,vehicleEnrollmentId,vehicleEnrollment"; public ReviewParameters() { @@ -14,4 +14,5 @@ public class ReviewParameters : ParametersBase public string? Comment { get; set; } public string? UserId { get; set; } + public int? CompanyId { get; set; } } \ No newline at end of file diff --git a/SharedModels/QueryParameters/Objects/UserParameters.cs b/SharedModels/QueryParameters/Objects/UserParameters.cs index 77fd0f3..fd48ffc 100644 --- a/SharedModels/QueryParameters/Objects/UserParameters.cs +++ b/SharedModels/QueryParameters/Objects/UserParameters.cs @@ -2,17 +2,13 @@ namespace SharedModels.QueryParameters.Objects; public class UserParameters : ParametersBase { - public const string DefaultFields = ""; + public const string DefaultFields = "id,firstName,lastName,patronymic,email,emailConfirmed,phoneNumber," + + "phoneNumberConfirmed,birthDate,gender,document,documentDetails," + + "roles,reviews,ticketGroups"; public UserParameters() { - Sort = "id"; + Sort = ""; Fields = DefaultFields; } - - public string? FirstName { get; set; } - public string? LastName { get; set; } - public string? Username { get; set; } - public string? Email { get; set; } - public string? Phone { get; set; } } \ No newline at end of file diff --git a/SharedModels/SharedModels.csproj b/SharedModels/SharedModels.csproj index eb2460e..d43a1b5 100644 --- a/SharedModels/SharedModels.csproj +++ b/SharedModels/SharedModels.csproj @@ -6,4 +6,8 @@ enable + + + + diff --git a/Server/Constants/Identity.cs b/Utils/Identity.cs similarity index 54% rename from Server/Constants/Identity.cs rename to Utils/Identity.cs index 80b0f68..f8f587b 100644 --- a/Server/Constants/Identity.cs +++ b/Utils/Identity.cs @@ -1,4 +1,4 @@ -namespace Server.Constants; +namespace Utils; public class Identity { @@ -10,7 +10,19 @@ public class Identity Administrator } + public enum Gender + { + Male, + Female + } + + public enum Document + { + Passport, + DriverLicence + } + public const string DefaultEmail = "admin@subdomain.domain"; public const string DefaultPassword = "123qwe!@#QWE"; - public const Roles DefaultRole = Roles.Administrator; + public const Roles DefaultRole = Roles.User; } \ No newline at end of file diff --git a/Utils/Utils.csproj b/Utils/Utils.csproj new file mode 100644 index 0000000..eb2460e --- /dev/null +++ b/Utils/Utils.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/auto.bus API.sln b/auto.bus API.sln index d72d52d..ab8a61e 100644 --- a/auto.bus API.sln +++ b/auto.bus API.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Server", "Server\Server.csproj", "{0DCB2130-10E7-4C98-AB94-5F8D68032B9F}" EndProject