feat: many thing

- Database model
- Data transfer objects
- Country management controller
This commit is contained in:
cuqmbr 2022-10-10 20:34:09 +03:00
parent bf3debb6e6
commit 95028ed18b
39 changed files with 2153 additions and 69 deletions

View File

@ -1,4 +1,4 @@
namespace Server.Settings;
namespace Server.Configurations;
public class Jwt
{

View File

@ -0,0 +1,31 @@
using AutoMapper;
using Server.Models;
using SharedModels.DataTransferObjects;
using Route = Server.Models.Route;
namespace Server.Configurations;
public class MapperInitializer : Profile
{
public MapperInitializer()
{
CreateMap<Country, CountryDto>().ReverseMap();
CreateMap<Country, CreateCountryDto>().ReverseMap();
CreateMap<Country, UpdateCountryDto>().ReverseMap();
CreateMap<State, StateDto>().ReverseMap();
CreateMap<State, CreateStateDto>().ReverseMap();
CreateMap<City, CityDto>().ReverseMap();
CreateMap<City, CreateCityDto>().ReverseMap();
CreateMap<Address, AddressDto>().ReverseMap();
CreateMap<Address, CreateAddressDto>().ReverseMap();
CreateMap<RouteAddress, RouteAddressDto>().ReverseMap();
CreateMap<RouteAddress, CreateRouteAddressDto>().ReverseMap();
CreateMap<Route, RouteDto>().ReverseMap();
CreateMap<Route, CreateRouteDto>().ReverseMap();
}
}

View File

@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using Server.Configurations;
using Server.Services;
using Server.Settings;
using SharedModels.Requests;
using SharedModels.Responses;

View File

@ -0,0 +1,98 @@
using Microsoft.AspNetCore.Mvc;
using Server.Data;
using Server.Services;
using SharedModels.DataTransferObjects;
namespace Server.Controllers;
[Route("api/countries")]
[ApiController]
public class CountryManagementController : ControllerBase
{
private readonly ICountryManagementService _countryManagementService;
public CountryManagementController(ICountryManagementService countryManagementService)
{
_countryManagementService = countryManagementService;
}
[HttpPost]
public async Task<IActionResult> AddCountry(CreateCountryDto country)
{
var result = await _countryManagementService.AddCountry(country);
if (!result.isSucceed)
{
return BadRequest(result.message);
}
return CreatedAtAction(nameof(GetCountry), new {id = result.country.Id}, result.country);
}
[HttpGet]
public async Task<IActionResult> GetCountries()
{
var result = await _countryManagementService.GetCountries();
if (!result.isSucceed)
{
return BadRequest(result.message);
}
return Ok(result.countries);
}
[HttpGet("{id}")]
public async Task<IActionResult> GetCountry(int id)
{
if (!await _countryManagementService.IsCountryExists(id))
{
return NotFound();
}
var result = await _countryManagementService.GetCountry(id);
if (!result.isSucceed)
{
return BadRequest(result.message);
}
return Ok(result.country);
}
[HttpPut("{id}")]
public async Task<IActionResult> UpdateRoute(int id, UpdateCountryDto country)
{
if (id != country.Id)
{
return BadRequest();
}
var result = await _countryManagementService.UpdateCountry(country);
if (!result.isSucceed)
{
return BadRequest(result.message);
}
return Ok(result);
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteRoute(int id)
{
if (!await _countryManagementService.IsCountryExists(id))
{
return NotFound();
}
var result = await _countryManagementService.DeleteCountry(id);
if (!result.isSucceed)
{
return BadRequest(result.message);
}
return NoContent();
}
}

View File

@ -1,20 +0,0 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SharedModels.Responses;
namespace Server.Controllers;
[Authorize(Roles = "Admin")]
[Route("api/secured")]
[ApiController]
public class SecuredController : ControllerBase
{
[HttpGet]
public async Task<IActionResult> GetSecuredData()
{
return Ok(new ResponseBase
{
Message = "This Secured Data is available only for Authenticated Users with Admin role."
});
}
}

View File

@ -0,0 +1,31 @@
using Microsoft.AspNetCore.Mvc;
namespace Server.Controllers;
[Route("api/[controller]")]
[ApiController]
public class TestingController : ControllerBase
{
[HttpPost("timezone")]
public async Task<IActionResult> SetTimeZone(string timeZone)
{
var cookieOptions = new CookieOptions()
{
Expires = DateTimeOffset.MaxValue
};
Response.Cookies.Append("timeZone", timeZone, cookieOptions);
return Ok();
}
[HttpGet("timezone")]
public async Task<IActionResult> GetTimeZone()
{
if (Request.Cookies.TryGetValue("timeZone", out string? tz))
{
return Ok(tz);
}
return NotFound();
}
}

View File

@ -1,13 +1,37 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Server.Models;
using Route = Server.Models.Route;
namespace Server.Data;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
public class ApplicationDbContext : IdentityDbContext<User>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<Company> Companies { get; set; }
public DbSet<Vehicle> Vehicles { get; set; }
public DbSet<VehicleEnrollment> VehicleEnrollments { get; set; }
public DbSet<Route> Routes { get; set; }
public DbSet<RouteAddress> RouteAddresses { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<City> Cities { get; set; }
public DbSet<State> States { get; set; }
public DbSet<Country> Countries { get; set; }
public DbSet<Ticket> Tickets { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<RouteAddress>()
.HasKey(ra => new {ra.RouteId, ra.AddressId});
modelBuilder.Entity<Ticket>()
.HasKey(t => new {t.UserId, t.VehicleEnrollmentId});
}
}

View File

@ -6,7 +6,7 @@ namespace Server.Data;
public class ApplicationDbContextSeed
{
public static async Task SeedEssentialsAsync(UserManager<ApplicationUser> userManager,
public static async Task SeedEssentialsAsync(UserManager<User> userManager,
RoleManager<IdentityRole> roleManager)
{
//Seed Roles
@ -14,7 +14,7 @@ public class ApplicationDbContextSeed
await roleManager.CreateAsync(new IdentityRole(Authorization.Roles.User.ToString()));
//Seed Default User
var defaultUser = new ApplicationUser
var defaultUser = new User
{
UserName = Authorization.DefaultUsername,
Email = Authorization.DefaultEmail,

View File

@ -0,0 +1,703 @@
// <auto-generated />
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("20221010172816_Main_Infrastructure")]
partial class Main_Infrastructure
{
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<string>("Id")
.HasColumnType("text");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("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<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("ClaimType")
.HasColumnType("text");
b.Property<string>("ClaimValue")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("ProviderKey")
.HasColumnType("text");
b.Property<string>("ProviderDisplayName")
.HasColumnType("text");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("text");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("RoleId")
.HasColumnType("text");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<string>("LoginProvider")
.HasColumnType("text");
b.Property<string>("Name")
.HasColumnType("text");
b.Property<string>("Value")
.HasColumnType("text");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Server.Models.Address", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("CityId")
.HasColumnType("integer");
b.Property<double>("Latitude")
.HasColumnType("double precision");
b.Property<double>("Longitude")
.HasColumnType("double precision");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CityId");
b.ToTable("Addresses");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("StateId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("StateId");
b.ToTable("Cities");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("OwnerId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.ToTable("Companies");
});
modelBuilder.Entity("Server.Models.Country", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Code")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Countries");
});
modelBuilder.Entity("Server.Models.Route", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<TimeOnly>("IntendedDepartureTimeOnlyUtc")
.HasColumnType("time without time zone");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Routes");
});
modelBuilder.Entity("Server.Models.RouteAddress", b =>
{
b.Property<int>("RouteId")
.HasColumnType("integer");
b.Property<int>("AddressId")
.HasColumnType("integer");
b.Property<double>("CostToNextCity")
.HasColumnType("double precision");
b.Property<int>("Order")
.HasColumnType("integer");
b.Property<TimeSpan>("TimeSpanToNextCity")
.HasColumnType("interval");
b.Property<TimeSpan>("WaitTimeSpan")
.HasColumnType("interval");
b.HasKey("RouteId", "AddressId");
b.HasIndex("AddressId");
b.ToTable("RouteAddresses");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("CountryId")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CountryId");
b.ToTable("States");
});
modelBuilder.Entity("Server.Models.Ticket", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<int>("VehicleEnrollmentId")
.HasColumnType("integer");
b.Property<bool>("IsReturned")
.HasColumnType("boolean");
b.Property<DateTime>("PurchaseDateTimeUtc")
.HasColumnType("timestamp with time zone");
b.HasKey("UserId", "VehicleEnrollmentId");
b.HasIndex("VehicleEnrollmentId");
b.ToTable("Tickets");
});
modelBuilder.Entity("Server.Models.User", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
b.Property<int>("AccessFailedCount")
.HasColumnType("integer");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("text");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<bool>("EmailConfirmed")
.HasColumnType("boolean");
b.Property<string>("FirstName")
.HasColumnType("text");
b.Property<string>("LastName")
.HasColumnType("text");
b.Property<bool>("LockoutEnabled")
.HasColumnType("boolean");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("timestamp with time zone");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.Property<string>("PasswordHash")
.HasColumnType("text");
b.Property<string>("PhoneNumber")
.HasColumnType("text");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("boolean");
b.Property<string>("SecurityStamp")
.HasColumnType("text");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("boolean");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("character varying(256)");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Server.Models.Vehicle", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("Capacity")
.HasColumnType("integer");
b.Property<int>("CompanyId")
.HasColumnType("integer");
b.Property<string>("Number")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CompanyId");
b.ToTable("Vehicles");
});
modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<TimeSpan>("DelayTimeSpan")
.HasColumnType("interval");
b.Property<DateOnly>("DepartureDateOnly")
.HasColumnType("date");
b.Property<int>("RouteId")
.HasColumnType("integer");
b.Property<int>("VehicleId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("RouteId");
b.HasIndex("VehicleId");
b.ToTable("VehicleEnrollments");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", 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<string>", b =>
{
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Server.Models.Address", b =>
{
b.HasOne("Server.Models.City", "City")
.WithMany("Addresses")
.HasForeignKey("CityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("City");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.HasOne("Server.Models.State", "State")
.WithMany("Cities")
.HasForeignKey("StateId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("State");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.HasOne("Server.Models.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
});
modelBuilder.Entity("Server.Models.RouteAddress", b =>
{
b.HasOne("Server.Models.Address", "Address")
.WithMany("RouteAddresses")
.HasForeignKey("AddressId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.Route", "Route")
.WithMany("RouteAddresses")
.HasForeignKey("RouteId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Address");
b.Navigation("Route");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.HasOne("Server.Models.Country", "Country")
.WithMany("States")
.HasForeignKey("CountryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Country");
});
modelBuilder.Entity("Server.Models.Ticket", b =>
{
b.HasOne("Server.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
.WithMany()
.HasForeignKey("VehicleEnrollmentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
b.Navigation("VehicleEnrollment");
});
modelBuilder.Entity("Server.Models.User", b =>
{
b.OwnsMany("Server.Models.RefreshToken", "RefreshTokens", b1 =>
{
b1.Property<string>("UserId")
.HasColumnType("text");
b1.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b1.Property<int>("Id"));
b1.Property<DateTime>("Created")
.HasColumnType("timestamp with time zone");
b1.Property<DateTime>("Expires")
.HasColumnType("timestamp with time zone");
b1.Property<DateTime?>("Revoked")
.HasColumnType("timestamp with time zone");
b1.Property<string>("Token")
.IsRequired()
.HasColumnType("text");
b1.HasKey("UserId", "Id");
b1.ToTable("RefreshToken");
b1.WithOwner()
.HasForeignKey("UserId");
});
b.Navigation("RefreshTokens");
});
modelBuilder.Entity("Server.Models.Vehicle", b =>
{
b.HasOne("Server.Models.Company", "Company")
.WithMany("Vehicles")
.HasForeignKey("CompanyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Company");
});
modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
{
b.HasOne("Server.Models.Route", "Route")
.WithMany()
.HasForeignKey("RouteId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.Vehicle", "Vehicle")
.WithMany()
.HasForeignKey("VehicleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Route");
b.Navigation("Vehicle");
});
modelBuilder.Entity("Server.Models.Address", b =>
{
b.Navigation("RouteAddresses");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.Navigation("Addresses");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.Navigation("Vehicles");
});
modelBuilder.Entity("Server.Models.Country", b =>
{
b.Navigation("States");
});
modelBuilder.Entity("Server.Models.Route", b =>
{
b.Navigation("RouteAddresses");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.Navigation("Cities");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,340 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
#nullable disable
namespace Server.Migrations
{
public partial class Main_Infrastructure : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_RefreshToken_AspNetUsers_ApplicationUserId",
table: "RefreshToken");
migrationBuilder.RenameColumn(
name: "ApplicationUserId",
table: "RefreshToken",
newName: "UserId");
migrationBuilder.CreateTable(
name: "Companies",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Name = table.Column<string>(type: "text", nullable: false),
OwnerId = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Companies", x => x.Id);
table.ForeignKey(
name: "FK_Companies_AspNetUsers_OwnerId",
column: x => x.OwnerId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Countries",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Code = table.Column<string>(type: "text", nullable: false),
Name = table.Column<string>(type: "text", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Countries", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Routes",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Type = table.Column<string>(type: "text", nullable: false),
IntendedDepartureTimeOnlyUtc = table.Column<TimeOnly>(type: "time without time zone", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Routes", x => x.Id);
});
migrationBuilder.CreateTable(
name: "Vehicles",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Number = table.Column<string>(type: "text", nullable: false),
Type = table.Column<string>(type: "text", nullable: false),
Capacity = table.Column<int>(type: "integer", nullable: false),
CompanyId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Vehicles", x => x.Id);
table.ForeignKey(
name: "FK_Vehicles_Companies_CompanyId",
column: x => x.CompanyId,
principalTable: "Companies",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "States",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Name = table.Column<string>(type: "text", nullable: false),
CountryId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_States", x => x.Id);
table.ForeignKey(
name: "FK_States_Countries_CountryId",
column: x => x.CountryId,
principalTable: "Countries",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "VehicleEnrollments",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
VehicleId = table.Column<int>(type: "integer", nullable: false),
RouteId = table.Column<int>(type: "integer", nullable: false),
DepartureDateOnly = table.Column<DateOnly>(type: "date", nullable: false),
DelayTimeSpan = table.Column<TimeSpan>(type: "interval", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_VehicleEnrollments", x => x.Id);
table.ForeignKey(
name: "FK_VehicleEnrollments_Routes_RouteId",
column: x => x.RouteId,
principalTable: "Routes",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_VehicleEnrollments_Vehicles_VehicleId",
column: x => x.VehicleId,
principalTable: "Vehicles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Cities",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Name = table.Column<string>(type: "text", nullable: false),
StateId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Cities", x => x.Id);
table.ForeignKey(
name: "FK_Cities_States_StateId",
column: x => x.StateId,
principalTable: "States",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Tickets",
columns: table => new
{
UserId = table.Column<string>(type: "text", nullable: false),
VehicleEnrollmentId = table.Column<int>(type: "integer", nullable: false),
PurchaseDateTimeUtc = table.Column<DateTime>(type: "timestamp with time zone", nullable: false),
IsReturned = table.Column<bool>(type: "boolean", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Tickets", x => new { x.UserId, x.VehicleEnrollmentId });
table.ForeignKey(
name: "FK_Tickets_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_Tickets_VehicleEnrollments_VehicleEnrollmentId",
column: x => x.VehicleEnrollmentId,
principalTable: "VehicleEnrollments",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "Addresses",
columns: table => new
{
Id = table.Column<int>(type: "integer", nullable: false)
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
Name = table.Column<string>(type: "text", nullable: false),
Latitude = table.Column<double>(type: "double precision", nullable: false),
Longitude = table.Column<double>(type: "double precision", nullable: false),
CityId = table.Column<int>(type: "integer", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Addresses", x => x.Id);
table.ForeignKey(
name: "FK_Addresses_Cities_CityId",
column: x => x.CityId,
principalTable: "Cities",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "RouteAddresses",
columns: table => new
{
RouteId = table.Column<int>(type: "integer", nullable: false),
AddressId = table.Column<int>(type: "integer", nullable: false),
Order = table.Column<int>(type: "integer", nullable: false),
TimeSpanToNextCity = table.Column<TimeSpan>(type: "interval", nullable: false),
WaitTimeSpan = table.Column<TimeSpan>(type: "interval", nullable: false),
CostToNextCity = table.Column<double>(type: "double precision", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_RouteAddresses", x => new { x.RouteId, x.AddressId });
table.ForeignKey(
name: "FK_RouteAddresses_Addresses_AddressId",
column: x => x.AddressId,
principalTable: "Addresses",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_RouteAddresses_Routes_RouteId",
column: x => x.RouteId,
principalTable: "Routes",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Addresses_CityId",
table: "Addresses",
column: "CityId");
migrationBuilder.CreateIndex(
name: "IX_Cities_StateId",
table: "Cities",
column: "StateId");
migrationBuilder.CreateIndex(
name: "IX_Companies_OwnerId",
table: "Companies",
column: "OwnerId");
migrationBuilder.CreateIndex(
name: "IX_RouteAddresses_AddressId",
table: "RouteAddresses",
column: "AddressId");
migrationBuilder.CreateIndex(
name: "IX_States_CountryId",
table: "States",
column: "CountryId");
migrationBuilder.CreateIndex(
name: "IX_Tickets_VehicleEnrollmentId",
table: "Tickets",
column: "VehicleEnrollmentId");
migrationBuilder.CreateIndex(
name: "IX_VehicleEnrollments_RouteId",
table: "VehicleEnrollments",
column: "RouteId");
migrationBuilder.CreateIndex(
name: "IX_VehicleEnrollments_VehicleId",
table: "VehicleEnrollments",
column: "VehicleId");
migrationBuilder.CreateIndex(
name: "IX_Vehicles_CompanyId",
table: "Vehicles",
column: "CompanyId");
migrationBuilder.AddForeignKey(
name: "FK_RefreshToken_AspNetUsers_UserId",
table: "RefreshToken",
column: "UserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropForeignKey(
name: "FK_RefreshToken_AspNetUsers_UserId",
table: "RefreshToken");
migrationBuilder.DropTable(
name: "RouteAddresses");
migrationBuilder.DropTable(
name: "Tickets");
migrationBuilder.DropTable(
name: "Addresses");
migrationBuilder.DropTable(
name: "VehicleEnrollments");
migrationBuilder.DropTable(
name: "Cities");
migrationBuilder.DropTable(
name: "Routes");
migrationBuilder.DropTable(
name: "Vehicles");
migrationBuilder.DropTable(
name: "States");
migrationBuilder.DropTable(
name: "Companies");
migrationBuilder.DropTable(
name: "Countries");
migrationBuilder.RenameColumn(
name: "UserId",
table: "RefreshToken",
newName: "ApplicationUserId");
migrationBuilder.AddForeignKey(
name: "FK_RefreshToken_AspNetUsers_ApplicationUserId",
table: "RefreshToken",
column: "ApplicationUserId",
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
}
}

View File

@ -17,7 +17,7 @@ namespace Server.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "6.0.8")
.HasAnnotation("ProductVersion", "6.0.9")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@ -154,7 +154,191 @@ namespace Server.Migrations
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("Server.Models.ApplicationUser", b =>
modelBuilder.Entity("Server.Models.Address", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("CityId")
.HasColumnType("integer");
b.Property<double>("Latitude")
.HasColumnType("double precision");
b.Property<double>("Longitude")
.HasColumnType("double precision");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CityId");
b.ToTable("Addresses");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<int>("StateId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("StateId");
b.ToTable("Cities");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.Property<string>("OwnerId")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("OwnerId");
b.ToTable("Companies");
});
modelBuilder.Entity("Server.Models.Country", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<string>("Code")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Countries");
});
modelBuilder.Entity("Server.Models.Route", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<TimeOnly>("IntendedDepartureTimeOnlyUtc")
.HasColumnType("time without time zone");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.ToTable("Routes");
});
modelBuilder.Entity("Server.Models.RouteAddress", b =>
{
b.Property<int>("RouteId")
.HasColumnType("integer");
b.Property<int>("AddressId")
.HasColumnType("integer");
b.Property<double>("CostToNextCity")
.HasColumnType("double precision");
b.Property<int>("Order")
.HasColumnType("integer");
b.Property<TimeSpan>("TimeSpanToNextCity")
.HasColumnType("interval");
b.Property<TimeSpan>("WaitTimeSpan")
.HasColumnType("interval");
b.HasKey("RouteId", "AddressId");
b.HasIndex("AddressId");
b.ToTable("RouteAddresses");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("CountryId")
.HasColumnType("integer");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CountryId");
b.ToTable("States");
});
modelBuilder.Entity("Server.Models.Ticket", b =>
{
b.Property<string>("UserId")
.HasColumnType("text");
b.Property<int>("VehicleEnrollmentId")
.HasColumnType("integer");
b.Property<bool>("IsReturned")
.HasColumnType("boolean");
b.Property<DateTime>("PurchaseDateTimeUtc")
.HasColumnType("timestamp with time zone");
b.HasKey("UserId", "VehicleEnrollmentId");
b.HasIndex("VehicleEnrollmentId");
b.ToTable("Tickets");
});
modelBuilder.Entity("Server.Models.User", b =>
{
b.Property<string>("Id")
.HasColumnType("text");
@ -224,6 +408,64 @@ namespace Server.Migrations
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("Server.Models.Vehicle", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<int>("Capacity")
.HasColumnType("integer");
b.Property<int>("CompanyId")
.HasColumnType("integer");
b.Property<string>("Number")
.IsRequired()
.HasColumnType("text");
b.Property<string>("Type")
.IsRequired()
.HasColumnType("text");
b.HasKey("Id");
b.HasIndex("CompanyId");
b.ToTable("Vehicles");
});
modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("integer");
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
b.Property<TimeSpan>("DelayTimeSpan")
.HasColumnType("interval");
b.Property<DateOnly>("DepartureDateOnly")
.HasColumnType("date");
b.Property<int>("RouteId")
.HasColumnType("integer");
b.Property<int>("VehicleId")
.HasColumnType("integer");
b.HasKey("Id");
b.HasIndex("RouteId");
b.HasIndex("VehicleId");
b.ToTable("VehicleEnrollments");
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null)
@ -235,7 +477,7 @@ namespace Server.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("Server.Models.ApplicationUser", null)
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
@ -244,7 +486,7 @@ namespace Server.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("Server.Models.ApplicationUser", null)
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
@ -259,7 +501,7 @@ namespace Server.Migrations
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.ApplicationUser", null)
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
@ -268,18 +510,100 @@ namespace Server.Migrations
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("Server.Models.ApplicationUser", null)
b.HasOne("Server.Models.User", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Server.Models.ApplicationUser", b =>
modelBuilder.Entity("Server.Models.Address", b =>
{
b.OwnsMany("Server.Entities.RefreshToken", "RefreshTokens", b1 =>
b.HasOne("Server.Models.City", "City")
.WithMany("Addresses")
.HasForeignKey("CityId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("City");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.HasOne("Server.Models.State", "State")
.WithMany("Cities")
.HasForeignKey("StateId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("State");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.HasOne("Server.Models.User", "Owner")
.WithMany()
.HasForeignKey("OwnerId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Owner");
});
modelBuilder.Entity("Server.Models.RouteAddress", b =>
{
b.HasOne("Server.Models.Address", "Address")
.WithMany("RouteAddresses")
.HasForeignKey("AddressId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.Route", "Route")
.WithMany("RouteAddresses")
.HasForeignKey("RouteId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Address");
b.Navigation("Route");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.HasOne("Server.Models.Country", "Country")
.WithMany("States")
.HasForeignKey("CountryId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Country");
});
modelBuilder.Entity("Server.Models.Ticket", b =>
{
b.HasOne("Server.Models.User", "User")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.VehicleEnrollment", "VehicleEnrollment")
.WithMany()
.HasForeignKey("VehicleEnrollmentId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("User");
b.Navigation("VehicleEnrollment");
});
modelBuilder.Entity("Server.Models.User", b =>
{
b.OwnsMany("Server.Models.RefreshToken", "RefreshTokens", b1 =>
{
b1.Property<string>("ApplicationUserId")
b1.Property<string>("UserId")
.HasColumnType("text");
b1.Property<int>("Id")
@ -301,16 +625,76 @@ namespace Server.Migrations
.IsRequired()
.HasColumnType("text");
b1.HasKey("ApplicationUserId", "Id");
b1.HasKey("UserId", "Id");
b1.ToTable("RefreshToken");
b1.WithOwner()
.HasForeignKey("ApplicationUserId");
.HasForeignKey("UserId");
});
b.Navigation("RefreshTokens");
});
modelBuilder.Entity("Server.Models.Vehicle", b =>
{
b.HasOne("Server.Models.Company", "Company")
.WithMany("Vehicles")
.HasForeignKey("CompanyId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Company");
});
modelBuilder.Entity("Server.Models.VehicleEnrollment", b =>
{
b.HasOne("Server.Models.Route", "Route")
.WithMany()
.HasForeignKey("RouteId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("Server.Models.Vehicle", "Vehicle")
.WithMany()
.HasForeignKey("VehicleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Route");
b.Navigation("Vehicle");
});
modelBuilder.Entity("Server.Models.Address", b =>
{
b.Navigation("RouteAddresses");
});
modelBuilder.Entity("Server.Models.City", b =>
{
b.Navigation("Addresses");
});
modelBuilder.Entity("Server.Models.Company", b =>
{
b.Navigation("Vehicles");
});
modelBuilder.Entity("Server.Models.Country", b =>
{
b.Navigation("States");
});
modelBuilder.Entity("Server.Models.Route", b =>
{
b.Navigation("RouteAddresses");
});
modelBuilder.Entity("Server.Models.State", b =>
{
b.Navigation("Cities");
});
#pragma warning restore 612, 618
}
}

27
Server/Models/Address.cs Normal file
View File

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using SharedModels.DataTransferObjects;
namespace Server.Models;
public class Address
{
[Key]
public int Id { get; set; }
public string Name { get; set; } = null!;
public double Latitude { get; set; }
public double Longitude { get; set; }
[ForeignKey("CityId")]
public int CityId { get; set; }
public City? City { get; set; }
public virtual IList<RouteAddress> RouteAddresses { get; set; } = null!;
public override string ToString()
{
return $"{City.State.Country.Name}, {City.State.Name}, " +
$"{City.Name}, {this.Name}";
}
}

19
Server/Models/City.cs Normal file
View File

@ -0,0 +1,19 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using SharedModels.DataTransferObjects;
namespace Server.Models;
public class City
{
[Key]
public int Id { get; set; }
public string Name { get; set; } = null!;
public virtual IList<Address>? Addresses { get; set; }
[ForeignKey("StateId")]
public int StateId { get; set; }
public State? State { get; set; }
}

18
Server/Models/Company.cs Normal file
View File

@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Server.Models;
public class Company
{
[Key]
public int Id { get; set; }
public string Name { get; set; } = null!;
[ForeignKey("OwnerId")]
public string OwnerId { get; set; } = null!;
public User Owner { get; set; } = null!;
public List<Vehicle>? Vehicles { get; set; }
}

15
Server/Models/Country.cs Normal file
View File

@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using SharedModels.DataTransferObjects;
namespace Server.Models;
public class Country
{
[Key]
public int Id { get; set; }
public string Code { get; set; } = null!;
public string Name { get; set; } = null!;
public virtual IList<State> States { get; set; } = null!;
}

View File

@ -1,6 +1,6 @@
using Microsoft.EntityFrameworkCore;
namespace Server.Entities;
namespace Server.Models;
[Owned]
public class RefreshToken

15
Server/Models/Route.cs Normal file
View File

@ -0,0 +1,15 @@
using System.ComponentModel.DataAnnotations;
using SharedModels.DataTransferObjects;
namespace Server.Models;
public class Route
{
[Key]
public int Id { get; set; }
public string Type { get; set; } = null!;
public TimeOnly IntendedDepartureTimeOnlyUtc { get; set; }
public virtual IList<RouteAddress> RouteAddresses { get; set; } = null!;
}

View File

@ -0,0 +1,20 @@
using System.ComponentModel.DataAnnotations.Schema;
using SharedModels.DataTransferObjects;
namespace Server.Models;
public class RouteAddress
{
[ForeignKey("RouteId")]
public int RouteId { get; set; }
public Route Route { get; set; } = null!;
[ForeignKey("AddressId")]
public int AddressId { get; set; }
public Address Address { get; set; } = null!;
public int Order { get; set; }
public TimeSpan TimeSpanToNextCity { get; set; }
public TimeSpan WaitTimeSpan { get; set; }
public double CostToNextCity { get; set; }
}

19
Server/Models/State.cs Normal file
View File

@ -0,0 +1,19 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using SharedModels.DataTransferObjects;
namespace Server.Models;
public class State
{
[Key]
public int Id { get; set; }
public string Name { get; set; } = null!;
public virtual IList<City> Cities { get; set; } = null!;
[ForeignKey("CountryId")]
public int CountryId { get; set; }
public Country? Country { get; set; } = null!;
}

17
Server/Models/Ticket.cs Normal file
View File

@ -0,0 +1,17 @@
using System.ComponentModel.DataAnnotations.Schema;
namespace Server.Models;
public class Ticket
{
[ForeignKey("UserId")]
public string UserId { get; set; } = null!;
public User User { get; set; } = null!;
[ForeignKey("VehicleEnrollmentId")]
public int VehicleEnrollmentId { get; set; }
public VehicleEnrollment VehicleEnrollment { get; set; } = null!;
public DateTime PurchaseDateTimeUtc { get; set; }
public bool IsReturned { get; set; } = false;
}

View File

@ -1,9 +1,8 @@
using Microsoft.AspNetCore.Identity;
using Server.Entities;
namespace Server.Models;
public class ApplicationUser : IdentityUser
public class User : IdentityUser
{
public string? FirstName { get; set; }
public string? LastName { get; set; }

18
Server/Models/Vehicle.cs Normal file
View File

@ -0,0 +1,18 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Server.Models;
public class Vehicle
{
[Key]
public int Id { get; set; }
public string Number { get; set; } = null!;
public string Type { get; set; } = null!;
public int Capacity { get; set; }
[ForeignKey("CompanyId")]
public int CompanyId { get; set; }
public Company Company { get; set; } = null!;
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Server.Models;
public class VehicleEnrollment
{
[Key]
public int Id { get; set; }
[ForeignKey("VehicleId")]
public int VehicleId { get; set; }
public Vehicle Vehicle { get; set; } = null!;
[ForeignKey("RouteId")]
public int RouteId { get; set; }
public Route Route { get; set; } = null!;
public DateOnly DepartureDateOnly { get; set; }
public TimeSpan DelayTimeSpan { get; set; }
}

View File

@ -4,16 +4,18 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Newtonsoft.Json;
using Server.Configurations;
using Server.Data;
using Server.Models;
using Server.Services;
using Server.Settings;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
builder.Services.AddControllers().AddNewtonsoftJson(options =>
options.SerializerSettings.Formatting = Formatting.Indented);
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
@ -42,19 +44,18 @@ builder.Services.AddSwaggerGen(options => {
});
var corsPolicyName = "defaultCorsPolicy";
builder.Services.AddCors(options =>
{
builder.Services.AddCors(options => {
options.AddPolicy(corsPolicyName,
policy => policy.WithOrigins("http://localhost:4200").AllowCredentials()
.AllowAnyHeader().AllowAnyMethod());
});
//Configuration from AppSettings
// Configuration from AppSettings
builder.Services.Configure<Jwt>(builder.Configuration.GetSection("Jwt"));
//User Manager Service
builder.Services.AddIdentity<ApplicationUser, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();
// User Manager Service
builder.Services.AddIdentity<User, IdentityRole>().AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddScoped<IAuthenticationService, AuthenticationService>();
//Adding Authentication - JWT
// Adding Authentication - JWT
builder.Services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
@ -78,7 +79,12 @@ builder.Services.AddAuthentication(options => {
});
builder.Services.AddAuthorization();
//Adding DB Context with PostgreSQL
builder.Services.AddAutoMapper(typeof(MapperInitializer));
builder.Services.AddScoped<ICountryManagementService, CountryManagementService>();
builder.Services.AddScoped<IDateTimeService, DateTimeService>();
// Adding DB Context with PostgreSQL
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(connectionString));
@ -96,7 +102,7 @@ app.UseHttpsRedirection();
// Data seeding
// using var scope = app.Services.CreateScope();
// var userManager = (UserManager<ApplicationUser>)scope.ServiceProvider.GetService(typeof(UserManager<ApplicationUser>))!;
// var userManager = (UserManager<User>)scope.ServiceProvider.GetService(typeof(UserManager<User>))!;
// var roleManager = (RoleManager<IdentityRole>)scope.ServiceProvider.GetService(typeof(RoleManager<IdentityRole>))!;
// await ApplicationDbContextSeed.SeedEssentialsAsync(userManager, roleManager);

View File

@ -7,21 +7,26 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.8" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.8">
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.9" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.8">
<PackageReference Include="Microsoft.EntityFrameworkCore.Proxies" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.9">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.8" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.6" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.22.1" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.10" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.7" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.23.1" />
</ItemGroup>
<ItemGroup>

View File

@ -6,10 +6,9 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Server.Configurations;
using Server.Constants;
using Server.Entities;
using Server.Models;
using Server.Settings;
using SharedModels.Requests;
using SharedModels.Responses;
@ -17,11 +16,11 @@ namespace Server.Services;
public class AuthenticationService : IAuthenticationService
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly UserManager<User> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
private readonly Jwt _jwt;
public AuthenticationService(UserManager<ApplicationUser> userManager,
public AuthenticationService(UserManager<User> userManager,
RoleManager<IdentityRole> roleManager, IOptions<Jwt> jwt)
{
_userManager = userManager;
@ -37,7 +36,7 @@ public class AuthenticationService : IAuthenticationService
return (false, $"Email {regRequest.Email} is already registered.");
}
var user = new ApplicationUser {UserName = regRequest.Username, Email = regRequest.Email};
var user = new User {UserName = regRequest.Username, Email = regRequest.Email};
var result = await _userManager.CreateAsync(user, regRequest.Password);
if (!result.Succeeded)
@ -154,7 +153,7 @@ public class AuthenticationService : IAuthenticationService
return true;
}
private async Task<JwtSecurityToken> CreateJwtToken(ApplicationUser user)
private async Task<JwtSecurityToken> CreateJwtToken(User user)
{
var userClaims = await _userManager.GetClaimsAsync(user);
var roles = await _userManager.GetRolesAsync(user);

View File

@ -0,0 +1,94 @@
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;
using Server.Data;
using Server.Models;
using SharedModels.DataTransferObjects;
namespace Server.Services;
public class CountryManagementService : ICountryManagementService
{
private readonly ApplicationDbContext _dbContext;
private readonly IMapper _mapper;
public CountryManagementService(ApplicationDbContext dbContext,
IMapper mapper)
{
_dbContext = dbContext;
_mapper = mapper;
}
public async Task<(bool isSucceed, string message, CountryDto country)> AddCountry(CreateCountryDto createCountryDto)
{
var country = _mapper.Map<Country>(createCountryDto);
await _dbContext.Countries.AddAsync(country);
await _dbContext.SaveChangesAsync();
return (true, String.Empty, _mapper.Map<CountryDto>(country));
}
public async Task<(bool isSucceed, string message, IEnumerable<CountryDto> countries)> GetCountries()
{
throw new NotImplementedException();
}
public async Task<(bool isSucceed, string message, CountryDto country)> GetCountry(int id)
{
var dbCountry = await _dbContext.Countries.Where(c => c.Id == id)
.ProjectTo<CountryDto>(_mapper.ConfigurationProvider)
.FirstOrDefaultAsync();
if (dbCountry == null)
{
return (false, $"Country doesn't exist", null)!;
}
return (true, "", dbCountry);
}
public async Task<(bool isSucceed, string message, CountryDto country)> UpdateCountry(UpdateCountryDto updateCountryDto)
{
var country = _mapper.Map<Country>(updateCountryDto);
_dbContext.Entry(country).State = EntityState.Modified;
try
{
await _dbContext.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!await IsCountryExists(updateCountryDto.Id))
{
return (false, $"Country with id:{updateCountryDto.Id} doesn't exist", null)!;
}
throw;
}
var dbCountry = await _dbContext.Countries.FirstOrDefaultAsync(c => c.Id == country.Id);
return (true, String.Empty, _mapper.Map<CountryDto>(dbCountry));
}
public async Task<(bool isSucceed, string message)> DeleteCountry(int id)
{
var dbCountry = await _dbContext.Countries.FirstOrDefaultAsync(c => c.Id == id);
if (dbCountry == null)
{
return (false, $"Country with id:{id} doesn't exist");
}
_dbContext.Countries.Remove(dbCountry);
await _dbContext.SaveChangesAsync();
return (true, String.Empty);
}
public async Task<bool> IsCountryExists(int id)
{
return await _dbContext.Countries.AnyAsync(c => c.Id == id);
}
}

View File

@ -0,0 +1,30 @@
namespace Server.Services;
public class DateTimeService : IDateTimeService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public DateTimeService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public bool TryToGetTimeZoneInfoFromCookie(out TimeZoneInfo? timeZoneInfo)
{
if (_httpContextAccessor.HttpContext == null)
{
timeZoneInfo = null;
return false;
}
if (!_httpContextAccessor.HttpContext.Request.Cookies.TryGetValue(
"timeZone", out string? timeZoneId))
{
timeZoneInfo = null;
return false;
}
timeZoneInfo = TimeZoneInfo.FindSystemTimeZoneById(timeZoneId!);
return true;
}
}

View File

@ -0,0 +1,14 @@
using SharedModels.DataTransferObjects;
namespace Server.Services;
public interface ICountryManagementService
{
Task<(bool isSucceed, string message, CountryDto country)> AddCountry(CreateCountryDto createCountryDto);
Task<(bool isSucceed, string message, IEnumerable<CountryDto> countries)> GetCountries();
Task<(bool isSucceed, string message, CountryDto country)> GetCountry(int id);
Task<(bool isSucceed, string message, CountryDto country)> UpdateCountry(UpdateCountryDto updateCountryDto);
Task<(bool isSucceed, string message)> DeleteCountry(int id);
Task<bool> IsCountryExists(int id);
}

View File

@ -0,0 +1,6 @@
namespace Server.Services;
public interface IDateTimeService
{
bool TryToGetTimeZoneInfoFromCookie(out TimeZoneInfo? timeZoneInfo);
}

View File

@ -7,7 +7,7 @@
},
"AllowedHosts": "*",
"ConnectionStrings": {
"DefaultConnection": "host=localhost;database=auto.bus;user id=postgres;password=postgres;"
"DefaultConnection": "Host=localhost;Database=auto.bus;Username=postgres;Password=postgres;"
},
"Jwt": {
"Key": "Secret which will never be exposed",

View File

@ -0,0 +1,30 @@
using System.ComponentModel.DataAnnotations;
namespace SharedModels.DataTransferObjects;
public class AddressDto : CreateAddressDto
{
public int Id { get; set; }
public CityDto City { get; set; } = null!;
public virtual IList<RouteAddressDto> RouteAddresses { get; set; } = null!;
}
public class CreateAddressDto
{
[Required]
[StringLength(maximumLength: 250, ErrorMessage = "Address name is too long")]
public string Name { get; set; } = null!;
[Required]
[Range(-90, 90, ErrorMessage = "Latitude must be in range(-90, 90)")]
public double Latitude { get; set; }
[Required]
[Range(-180, 180, ErrorMessage = "Longitude must be in range(-180, 180)")]
public double Longitude { get; set; }
[Required]
public int CityId { get; set; }
}

View File

@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations;
namespace SharedModels.DataTransferObjects;
public class CityDto : CreateCityDto
{
public int Id { get; set; }
public StateDto State { get; set; } = null!;
public virtual IList<AddressDto>? Addresses { get; set; }
}
public class CreateCityDto
{
[Required]
[StringLength(maximumLength: 40, ErrorMessage = "City Name is too long")]
public string Name { get; set; } = null!;
[Required]
public int StateId { get; set; }
}

View File

@ -0,0 +1,27 @@
using System.ComponentModel.DataAnnotations;
namespace SharedModels.DataTransferObjects;
public class CountryDto : CreateCountryDto
{
public int Id { get; set; }
public virtual IList<StateDto> States { get; set; } = null!;
}
public class CreateCountryDto
{
[Required]
[StringLength(maximumLength: 2, MinimumLength = 2, ErrorMessage = "The Code field must be 2 characters long")]
public string Code { get; set; } = null!;
[Required]
[StringLength(maximumLength: 56, ErrorMessage = "The Name field must be shorter than 56 characters")]
public string Name { get; set; } = null!;
}
public class UpdateCountryDto : CreateCountryDto
{
[Required]
public int Id { get; set; }
}

View File

@ -0,0 +1,36 @@
using System.ComponentModel.DataAnnotations;
namespace SharedModels.DataTransferObjects;
public class RouteAddressDto : CreateRouteAddressDto
{
public int Id { get; set; }
public RouteDto Route { get; set; } = null!;
public AddressDto Address { get; set; } = null!;
}
public class CreateRouteAddressDto
{
[Required]
public int RouteId { get; set; }
[Required]
public int AddressId { get; set; }
[Required]
public int Order { get; set; }
[Required]
[DataType(DataType.Duration)]
public TimeSpan TimeSpanToNextCity { get; set; }
[Required]
[DataType(DataType.Duration)]
public TimeSpan WaitTimeSpan { get; set; }
[Required]
[DataType(DataType.Currency)]
public double CostToNextCity { get; set; }
}

View File

@ -0,0 +1,19 @@
using System.ComponentModel.DataAnnotations;
namespace SharedModels.DataTransferObjects;
public class RouteDto : CreateRouteDto
{
public int Id { get; set; }
public virtual IList<RouteAddressDto> RouteAddresses { get; set; } = null!;
}
public class CreateRouteDto
{
[Required]
public string Type { get; set; } = null!;
[Required]
public TimeOnly IntendedDepartureTimeOnlyUtc { get; set; }
}

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
namespace SharedModels.DataTransferObjects;
public class StateDto : CreateStateDto
{
public int Id { get; set; }
public CountryDto Country { get; set; } = null!;
public virtual IList<CityDto> Cities { get; set; } = null!;
}
public class CreateStateDto
{
[Required]
[StringLength(maximumLength: 40, ErrorMessage = "State Name is too long")]
public string Name { get; set; } = null!;
[Required]
public int CountryId { get; set; }
}

View File

@ -6,8 +6,4 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Folder Include="DataTransferObjects" />
</ItemGroup>
</Project>