diff --git a/Tsi1.Api/Tsi1.Api.sln b/Tsi1.Api/Tsi1.Api.sln index d8cbb37464d1f64b8116052f7e82c4b19753afd3..f6d5e83fd98c503609b2069f1f5c2c8a3ddedfba 100644 --- a/Tsi1.Api/Tsi1.Api.sln +++ b/Tsi1.Api/Tsi1.Api.sln @@ -3,11 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30523.141 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tsi1.Api", "Tsi1.Api\Tsi1.Api.csproj", "{02E63197-0AEA-4942-941F-DFB7AD145A1E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tsi1.Api", "Tsi1.Api\Tsi1.Api.csproj", "{02E63197-0AEA-4942-941F-DFB7AD145A1E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tsi1.BusinessLayer", "Tsi1.BusinessLayer\Tsi1.BusinessLayer.csproj", "{3B46AFDD-AB4A-4D0E-8A94-49559E1D2083}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tsi1.BusinessLayer", "Tsi1.BusinessLayer\Tsi1.BusinessLayer.csproj", "{3B46AFDD-AB4A-4D0E-8A94-49559E1D2083}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tsi1.DataLayer", "Tsi1.DataLayer\Tsi1.DataLayer.csproj", "{57C4932A-C228-4A49-8FAC-8E9447F0E6E0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tsi1.DataLayer", "Tsi1.DataLayer\Tsi1.DataLayer.csproj", "{57C4932A-C228-4A49-8FAC-8E9447F0E6E0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/Tsi1.Api/Tsi1.Api/Controllers/UserController.cs b/Tsi1.Api/Tsi1.Api/Controllers/UserController.cs index c952152dc9ddc1d0a5f5913c957fea1d84821af0..56e1e6dc9226d4864088385d994062e3dfa88254 100644 --- a/Tsi1.Api/Tsi1.Api/Controllers/UserController.cs +++ b/Tsi1.Api/Tsi1.Api/Controllers/UserController.cs @@ -5,6 +5,8 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Tsi1.Api.Infrastructure; using Tsi1.Api.Models; +using Tsi1.BusinessLayer.Dtos; +using Tsi1.BusinessLayer.Helpers; using Tsi1.BusinessLayer.Interfaces; namespace Tsi1.Api.Controllers @@ -14,26 +16,30 @@ namespace Tsi1.Api.Controllers [ApiController] public class UserController : ControllerBase { - private readonly IUserService _userService; private readonly IJwtAuthManager _jwtAuthManager; + private readonly IUserService _userService; + private readonly IUserTypeService _userTypeService; - public UserController(IUserService userService, IJwtAuthManager jwtAuthManager) + public UserController(IJwtAuthManager jwtAuthManager, IUserService userService, IUserTypeService userTypeService) { - _userService = userService; _jwtAuthManager = jwtAuthManager; + _userService = userService; + _userTypeService = userTypeService; } [AllowAnonymous] [HttpPost("Login")] public async Task<IActionResult> Login(LoginRequest request) { - var user = await _userService.Authenticate(request.UserName, request.Password); + var result = await _userService.Authenticate(request.UserName, request.Password); - if (user == null) + if (result.HasError) { - return BadRequest(); + return BadRequest(result.Message); } + var user = result.Data; + var claims = new[] { new Claim(ClaimTypes.Name,user.Username), @@ -51,11 +57,27 @@ namespace Tsi1.Api.Controllers }); } - [HttpGet("Register")] - public async Task<IActionResult> Register() + [Authorize(Roles = UserTypes.FacultyAdmin)] + [HttpPost("Register")] + public async Task<IActionResult> Register(UserRegisterDto dto) { + var userTypeResult = await _userTypeService.GetById(dto.UserTypeId); + + if (userTypeResult.HasError) + { + BadRequest(userTypeResult.Message); + } + + var userType = userTypeResult.Data; + + var userServiceResult = await _userService.Create(dto, userType.Name); + + if (userServiceResult.HasError) + { + BadRequest(userServiceResult.Message); + } + return Ok(); } - } } diff --git a/Tsi1.Api/Tsi1.Api/Infrastructure/JwtAuthManager.cs b/Tsi1.Api/Tsi1.Api/Infrastructure/JwtAuthManager.cs index b8c4a7b47aa63f84cc285c80cb4beafd2cfb683d..69fe71fbcabbcda7d2cc8fa475e3691df8333400 100644 --- a/Tsi1.Api/Tsi1.Api/Infrastructure/JwtAuthManager.cs +++ b/Tsi1.Api/Tsi1.Api/Infrastructure/JwtAuthManager.cs @@ -48,12 +48,14 @@ namespace Tsi1.Api.Infrastructure public JwtAuthResult GenerateTokens(string username, Claim[] claims, DateTime now) { var shouldAddAudienceClaim = string.IsNullOrWhiteSpace(claims?.FirstOrDefault(x => x.Type == JwtRegisteredClaimNames.Aud)?.Value); + var jwtToken = new JwtSecurityToken( _jwtTokenConfig.Issuer, shouldAddAudienceClaim ? _jwtTokenConfig.Audience : string.Empty, claims, expires: now.AddMinutes(_jwtTokenConfig.AccessTokenExpiration), signingCredentials: new SigningCredentials(new SymmetricSecurityKey(_secret), SecurityAlgorithms.HmacSha256Signature)); + var accessToken = new JwtSecurityTokenHandler().WriteToken(jwtToken); var refreshToken = new RefreshToken diff --git a/Tsi1.Api/Tsi1.Api/Models/LoginResult.cs b/Tsi1.Api/Tsi1.Api/Models/LoginResult.cs index 3fd35ddba714f89b0b6bc43f705af662ffd3c6dc..eb78ddbbe7e3582e706f94e9d63d8a75511affdc 100644 --- a/Tsi1.Api/Tsi1.Api/Models/LoginResult.cs +++ b/Tsi1.Api/Tsi1.Api/Models/LoginResult.cs @@ -14,9 +14,6 @@ namespace Tsi1.Api.Models [JsonPropertyName("role")] public string Role { get; set; } - [JsonPropertyName("originalUserName")] - public string OriginalUserName { get; set; } - [JsonPropertyName("accessToken")] public string AccessToken { get; set; } diff --git a/Tsi1.Api/Tsi1.Api/Startup.cs b/Tsi1.Api/Tsi1.Api/Startup.cs index 766b76c5b01dde462ff86d5cbbacbb965cb87b7e..b90bb57de9fed6d8c5d07b97c07c54bd6e6bbacd 100644 --- a/Tsi1.Api/Tsi1.Api/Startup.cs +++ b/Tsi1.Api/Tsi1.Api/Startup.cs @@ -38,9 +38,13 @@ namespace Tsi1.Api services.AddDbContext<Tsi1Context>(x => x.UseNpgsql(Configuration.GetConnectionString("PostgreSql"))); services.AddScoped<IUserService, UserService>(); + services.AddScoped<IUserTypeService, UserTypeService>(); + + services.AddCors(); var jwtTokenConfig = Configuration.GetSection("jwtTokenConfig").Get<JwtTokenConfig>(); services.AddSingleton(jwtTokenConfig); + services.AddAuthentication(x => { x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; @@ -61,6 +65,7 @@ namespace Tsi1.Api ClockSkew = TimeSpan.FromMinutes(1) }; }); + services.AddSingleton<IJwtAuthManager, JwtAuthManager>(); services.AddHostedService<JwtRefreshTokenCache>(); @@ -105,7 +110,6 @@ namespace Tsi1.Api { c.SwaggerEndpoint("/swagger/v1/swagger.json", "Tsi1 api V1"); c.DocumentTitle = "Tsi1 api"; - //c.RoutePrefix = string.Empty; }); app.UseRouting(); diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Dtos/UserRegisterDto.cs b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/UserRegisterDto.cs new file mode 100644 index 0000000000000000000000000000000000000000..24616b382bf5c2ef36249f7b8af52e727fbcdae1 --- /dev/null +++ b/Tsi1.Api/Tsi1.BusinessLayer/Dtos/UserRegisterDto.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Text; + +namespace Tsi1.BusinessLayer.Dtos +{ + public class UserRegisterDto + { + [Required] + public int UserTypeId { get; set; } + + [Required] + public string Username { get; set; } + + [Required] + public string Password { get; set; } + + [Required] + public string FirstName { get; set; } + + [Required] + public string LastName { get; set; } + + [Required] + public string Email { get; set; } + + [Required] + public string IdentityCard { get; set; } + + public int Age { get; set; } + } +} diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs new file mode 100644 index 0000000000000000000000000000000000000000..e36e8f1980228f519813ae1e19ef6452e8a0a365 --- /dev/null +++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ErrorMessages.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tsi1.BusinessLayer.Helpers +{ + public static class ErrorMessages + { + public const string UserDoesNotExist = "El usuario '{0}' no existe"; + public const string IncorrectPassword = "Contraseña incorrecta"; + public const string UserTypeDoesNotExist = "El tipo de usuario con id '{0}' no existe"; + } +} diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ServiceResult.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ServiceResult.cs new file mode 100644 index 0000000000000000000000000000000000000000..ae294d7826fb64ea7a4037019e9003de5d84f9bc --- /dev/null +++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/ServiceResult.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tsi1.BusinessLayer.Helpers +{ + public class ServiceResult<T> + { + public T Data { get; set; } + public string Message { get; set; } + public bool HasError { get; set; } + } +} diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Helpers/UserTypes.cs b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/UserTypes.cs new file mode 100644 index 0000000000000000000000000000000000000000..9fb3b0e8bfd691ed0833bde757acb92626676838 --- /dev/null +++ b/Tsi1.Api/Tsi1.BusinessLayer/Helpers/UserTypes.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tsi1.BusinessLayer.Helpers +{ + public static class UserTypes + { + public const string FacultyAdmin = nameof(FacultyAdmin); + public const string Professor = nameof(Professor); + public const string Student = nameof(Student); + } +} diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IUserService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IUserService.cs index 2c715b938f377846db00cc3d129c9aeaf7ac9e5f..fb5470f6ae4f7e64e5ebdee73b060926276e18a3 100644 --- a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IUserService.cs +++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IUserService.cs @@ -2,12 +2,15 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; +using Tsi1.BusinessLayer.Dtos; +using Tsi1.BusinessLayer.Helpers; using Tsi1.DataLayer.Entities; namespace Tsi1.BusinessLayer.Interfaces { public interface IUserService { - Task<User> Authenticate(string username, string password); + Task<ServiceResult<User>> Authenticate(string username, string password); + Task<ServiceResult<User>> Create(UserRegisterDto dto, string type); } } diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IUserTypeService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IUserTypeService.cs new file mode 100644 index 0000000000000000000000000000000000000000..733e3d045d6811b1d29fcdae88e6427ab51876b0 --- /dev/null +++ b/Tsi1.Api/Tsi1.BusinessLayer/Interfaces/IUserTypeService.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Tsi1.BusinessLayer.Helpers; +using Tsi1.DataLayer.Entities; + +namespace Tsi1.BusinessLayer.Interfaces +{ + public interface IUserTypeService + { + public Task<ServiceResult<UserType>> GetById(int id); + } +} diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/UserService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/UserService.cs index 935bddd00e09eee75706f501b247e7c3602a4554..b134703ea0c88827d65103896b470236209503b4 100644 --- a/Tsi1.Api/Tsi1.BusinessLayer/Services/UserService.cs +++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/UserService.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; +using Tsi1.BusinessLayer.Dtos; +using Tsi1.BusinessLayer.Helpers; using Tsi1.BusinessLayer.Interfaces; using Tsi1.DataLayer; using Tsi1.DataLayer.Entities; @@ -18,32 +20,69 @@ namespace Tsi1.BusinessLayer.Services _context = context; } - public async Task<User> Authenticate(string username, string password) + public async Task<ServiceResult<User>> Authenticate(string username, string password) { - var user = await _context.Users.FirstOrDefaultAsync(x => x.Username == username); + var result = new ServiceResult<User>(); - user = new User() - { - Id = 1, - Username = "lucca", - UserType = new UserType() - { - Id = 1, - Name = "admin" - } - }; + var user = await _context.Users + .Include(x => x.UserType) + .FirstOrDefaultAsync(x => x.Username == username); if (user == null) { - // no existe el usuario + result.HasError = true; + result.Message = string.Format(ErrorMessages.UserDoesNotExist, username); + return result; } if (user.Password != password) { - // contraseña incorrecta + result.HasError = true; + result.Message = ErrorMessages.IncorrectPassword; + return result; + } + + result.Data = user; + + return result; + } + + public async Task<ServiceResult<User>> Create(UserRegisterDto dto, string type) + { + var result = new ServiceResult<User>(); + + var user = new User() + { + UserTypeId = dto.UserTypeId, + Username = dto.Username, + Password = dto.Password, + Email = dto.Email, + FirstName = dto.FirstName, + LastName = dto.LastName + }; + + if (type == UserTypes.Student) + { + user.Student = new Student() + { + IdentityCard = dto.IdentityCard, + Age = dto.Age + }; + } + + if (type == UserTypes.Professor) + { + user.Professor = new Professor() + { + IdentityCard = dto.IdentityCard + }; } - return user; + _context.Users.Add(user); + await _context.SaveChangesAsync(); + result.Data = user; + + return result; } } } diff --git a/Tsi1.Api/Tsi1.BusinessLayer/Services/UserTypeService.cs b/Tsi1.Api/Tsi1.BusinessLayer/Services/UserTypeService.cs new file mode 100644 index 0000000000000000000000000000000000000000..18180fd57ad7c186d9ff40588009a08457956f56 --- /dev/null +++ b/Tsi1.Api/Tsi1.BusinessLayer/Services/UserTypeService.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Tsi1.BusinessLayer.Helpers; +using Tsi1.BusinessLayer.Interfaces; +using Tsi1.DataLayer; +using Tsi1.DataLayer.Entities; + +namespace Tsi1.BusinessLayer.Services +{ + public class UserTypeService : IUserTypeService + { + private readonly Tsi1Context _context; + + public UserTypeService(Tsi1Context context) + { + _context = context; + } + + public async Task<ServiceResult<UserType>> GetById(int id) + { + var result = new ServiceResult<UserType>(); + + var userType = await _context.UserTypes.FirstOrDefaultAsync(x => x.Id == id); + + if (userType == null) + { + result.HasError = true; + result.Message = string.Format(ErrorMessages.UserTypeDoesNotExist, id); + } + + result.Data = userType; + + return result; + } + } +} diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Professor.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Professor.cs index b72802f9d1ba2255ea1492228b4645f72797784e..a095017f2316ce399aa783be02564ac58fc19a48 100644 --- a/Tsi1.Api/Tsi1.DataLayer/Entities/Professor.cs +++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Professor.cs @@ -12,7 +12,6 @@ namespace Tsi1.DataLayer.Entities } public int Id { get; set; } - public int UserId { get; set; } public string IdentityCard { get; set; } public User User { get; set; } diff --git a/Tsi1.Api/Tsi1.DataLayer/Entities/Student.cs b/Tsi1.Api/Tsi1.DataLayer/Entities/Student.cs index bf168e33d74bc58bedbcc7c74fdd01bce40dc053..acc85724b189d884047106a3d7423a138d5c639e 100644 --- a/Tsi1.Api/Tsi1.DataLayer/Entities/Student.cs +++ b/Tsi1.Api/Tsi1.DataLayer/Entities/Student.cs @@ -12,7 +12,6 @@ namespace Tsi1.DataLayer.Entities } public int Id { get; set; } - public int UserId { get; set; } public string IdentityCard { get; set; } public int Age { get; set; } diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201016201059_remove-UserId-from-Student-Professor.Designer.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201016201059_remove-UserId-from-Student-Professor.Designer.cs new file mode 100644 index 0000000000000000000000000000000000000000..295fe18563f2963d0dbc89f32b245840b9b5e317 --- /dev/null +++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201016201059_remove-UserId-from-Student-Professor.Designer.cs @@ -0,0 +1,234 @@ +// <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 Tsi1.DataLayer; + +namespace Tsi1.DataLayer.Migrations +{ + [DbContext(typeof(Tsi1Context))] + [Migration("20201016201059_remove-UserId-from-Student-Professor")] + partial class removeUserIdfromStudentProfessor + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn) + .HasAnnotation("ProductVersion", "3.1.4") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Course", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("Courses"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Professor", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("IdentityCard") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityCard") + .IsUnique(); + + b.ToTable("Professors"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b => + { + b.Property<int>("ProfessorId") + .HasColumnType("integer"); + + b.Property<int>("CourseId") + .HasColumnType("integer"); + + b.HasKey("ProfessorId", "CourseId"); + + b.HasIndex("CourseId"); + + b.ToTable("ProfessorCourses"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.Student", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<int>("Age") + .HasColumnType("integer"); + + b.Property<string>("IdentityCard") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("IdentityCard") + .IsUnique(); + + b.ToTable("Students"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b => + { + b.Property<int>("StudentId") + .HasColumnType("integer"); + + b.Property<int>("CourseId") + .HasColumnType("integer"); + + b.HasKey("StudentId", "CourseId"); + + b.HasIndex("CourseId"); + + b.ToTable("StudentCourses"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Email") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<string>("FirstName") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<string>("LastName") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<string>("Password") + .IsRequired() + .HasColumnType("character varying(255)"); + + b.Property<int?>("ProfessorId") + .HasColumnType("integer"); + + b.Property<int?>("StudentId") + .HasColumnType("integer"); + + b.Property<int>("UserTypeId") + .HasColumnType("integer"); + + b.Property<string>("Username") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("ProfessorId") + .IsUnique(); + + b.HasIndex("StudentId") + .IsUnique(); + + b.HasIndex("UserTypeId"); + + b.HasIndex("Username") + .IsUnique(); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.UserType", b => + { + b.Property<int>("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer") + .HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn); + + b.Property<string>("Name") + .IsRequired() + .HasColumnType("character varying(50)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .IsUnique(); + + b.ToTable("UserTypes"); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.ProfessorCourse", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Course", "Course") + .WithMany("ProfessorCourses") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor") + .WithMany("ProfessorCourses") + .HasForeignKey("ProfessorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.StudentCourse", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Course", "Course") + .WithMany("StudentCourses") + .HasForeignKey("CourseId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Tsi1.DataLayer.Entities.Student", "Student") + .WithMany("StudentCourses") + .HasForeignKey("StudentId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Tsi1.DataLayer.Entities.User", b => + { + b.HasOne("Tsi1.DataLayer.Entities.Professor", "Professor") + .WithOne("User") + .HasForeignKey("Tsi1.DataLayer.Entities.User", "ProfessorId"); + + b.HasOne("Tsi1.DataLayer.Entities.Student", "Student") + .WithOne("User") + .HasForeignKey("Tsi1.DataLayer.Entities.User", "StudentId"); + + b.HasOne("Tsi1.DataLayer.Entities.UserType", "UserType") + .WithMany() + .HasForeignKey("UserTypeId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/20201016201059_remove-UserId-from-Student-Professor.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201016201059_remove-UserId-from-Student-Professor.cs new file mode 100644 index 0000000000000000000000000000000000000000..41a552ea80083b691d644d8b17510083a5519f08 --- /dev/null +++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/20201016201059_remove-UserId-from-Student-Professor.cs @@ -0,0 +1,35 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Tsi1.DataLayer.Migrations +{ + public partial class removeUserIdfromStudentProfessor : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "UserId", + table: "Students"); + + migrationBuilder.DropColumn( + name: "UserId", + table: "Professors"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn<int>( + name: "UserId", + table: "Students", + type: "integer", + nullable: false, + defaultValue: 0); + + migrationBuilder.AddColumn<int>( + name: "UserId", + table: "Professors", + type: "integer", + nullable: false, + defaultValue: 0); + } + } +} diff --git a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs index 036e9605f4f8d0d7d8f6da51b64a99e3b41e8dcc..ce5698a5c0c92dd12caec2c8cf6bd1e23ed34368 100644 --- a/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs +++ b/Tsi1.Api/Tsi1.DataLayer/Migrations/Tsi1ContextModelSnapshot.cs @@ -49,9 +49,6 @@ namespace Tsi1.DataLayer.Migrations .IsRequired() .HasColumnType("character varying(50)"); - b.Property<int>("UserId") - .HasColumnType("integer"); - b.HasKey("Id"); b.HasIndex("IdentityCard") @@ -89,9 +86,6 @@ namespace Tsi1.DataLayer.Migrations .IsRequired() .HasColumnType("character varying(50)"); - b.Property<int>("UserId") - .HasColumnType("integer"); - b.HasKey("Id"); b.HasIndex("IdentityCard") diff --git a/Tsi1.Api/Tsi1.DataLayer/Tsi1.DataLayer.csproj b/Tsi1.Api/Tsi1.DataLayer/Tsi1.DataLayer.csproj index f7ccef3b5756bb86b63f32218d76f276a30f1c08..5b5d0ce158fa6a261517baa9e4efd29e61dd2275 100644 --- a/Tsi1.Api/Tsi1.DataLayer/Tsi1.DataLayer.csproj +++ b/Tsi1.Api/Tsi1.DataLayer/Tsi1.DataLayer.csproj @@ -9,6 +9,10 @@ <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> + <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.4"> + <PrivateAssets>all</PrivateAssets> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + </PackageReference> <PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="3.1.9" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.9" /> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.4" />