From 4423dc360f0f66b82776c49d51ae9d18ffdf0e34 Mon Sep 17 00:00:00 2001 From: shept Date: Thu, 17 Apr 2025 01:06:08 +0500 Subject: [PATCH 01/10] registration and authorization --- .../Controllers/AuthController.cs | 29 ++++++++++++- .../UserDTOs/UserRegistrationMapper.cs | 17 ++++++++ SurveyBackend/SurveyBackend.API/Program.cs | 11 +++++ .../SurveyBackend.Core/Models/User.cs | 2 +- .../Repositories/IUserRepository.cs | 1 + .../Services/IPasswordHasher.cs | 2 +- .../Services/IUserService.cs | 5 ++- .../Data/ApplicationDbContext.cs | 2 +- .../Repositories/UserRepository.cs | 5 +++ .../Services/AuthorizationService.cs | 42 +++++++++++++++++++ .../Services/Sha256PasswordHasher.cs | 1 + .../Services/UserService.cs | 25 +++++++++++ 12 files changed, 136 insertions(+), 6 deletions(-) create mode 100644 SurveyBackend/SurveyBackend.API/Mappers/UserDTOs/UserRegistrationMapper.cs rename SurveyBackend/{SurveyBackend.Infrastructure => SurveyBackend.Core}/Services/IPasswordHasher.cs (74%) create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/Services/AuthorizationService.cs create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/Services/UserService.cs diff --git a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs index 93fb89a..d66e555 100644 --- a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs +++ b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs @@ -1,5 +1,7 @@ using Microsoft.AspNetCore.Mvc; using SurveyBackend.DTOs; +using SurveyBackend.Infrastructure.Services; +using SurveyBackend.Mappers.UserDTOs; namespace SurveyBackend.Controllers; @@ -7,9 +9,32 @@ namespace SurveyBackend.Controllers; [Route("auth")] public class AuthController : ControllerBase { - [HttpPost("login")] - public async Task GetToken([FromBody] UserLoginDto loginData) + private readonly AuthorizationService _authorizationService; + + public AuthController(AuthorizationService authorizationService) { + _authorizationService = authorizationService; + } + + [HttpPost("login")] + public async Task LogIn([FromBody] UserLoginDto loginData) + { + var token = await _authorizationService.LogInUser(loginData.Email, loginData.Password); + return token is null ? Unauthorized() : Ok(new { token = token }); + } + + [HttpPost("register")] + public async Task Register([FromBody] UserRegistrationDto registerData) + { + try + { + await _authorizationService.RegisterUser(UserRegistrationMapper.UserRegistrationToModel(registerData)); + } + catch (Exception ex) + { + return BadRequest(ex.Message); + } + return Ok(); } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/Mappers/UserDTOs/UserRegistrationMapper.cs b/SurveyBackend/SurveyBackend.API/Mappers/UserDTOs/UserRegistrationMapper.cs new file mode 100644 index 0000000..4427b77 --- /dev/null +++ b/SurveyBackend/SurveyBackend.API/Mappers/UserDTOs/UserRegistrationMapper.cs @@ -0,0 +1,17 @@ +using SurveyBackend.Core.Models; +using SurveyBackend.Core.Services; +using SurveyBackend.DTOs; +using SurveyBackend.Infrastructure.Services; + +namespace SurveyBackend.Mappers.UserDTOs; + +public static class UserRegistrationMapper +{ + public static User UserRegistrationToModel(UserRegistrationDto dto) => new User + { + Email = dto.Email, + FirstName = dto.FirstName, + LastName = dto.LastName, + Password = dto.Password, + }; +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/Program.cs b/SurveyBackend/SurveyBackend.API/Program.cs index 2aa90b2..d15021d 100644 --- a/SurveyBackend/SurveyBackend.API/Program.cs +++ b/SurveyBackend/SurveyBackend.API/Program.cs @@ -2,8 +2,12 @@ using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; +using SurveyBackend.Core.Repositories; +using SurveyBackend.Core.Services; using SurveyBackend.Infrastructure; using SurveyBackend.Infrastructure.Data; +using SurveyBackend.Infrastructure.Repositories; +using SurveyBackend.Infrastructure.Services; using SurveyLib.Core.Repositories; using SurveyLib.Core.Services; using SurveyLib.Infrastructure.EFCore.Data; @@ -27,6 +31,13 @@ public class Program builder.Services.AddScoped(provider => provider.GetRequiredService()); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + + builder.Services.AddScoped(); + + builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/SurveyBackend/SurveyBackend.Core/Models/User.cs b/SurveyBackend/SurveyBackend.Core/Models/User.cs index 78f2286..257d4fa 100644 --- a/SurveyBackend/SurveyBackend.Core/Models/User.cs +++ b/SurveyBackend/SurveyBackend.Core/Models/User.cs @@ -4,7 +4,7 @@ namespace SurveyBackend.Core.Models; public class User { - public string Id { get; set; } + public int Id { get; set; } public string Email { get; set; } public string FirstName { get; set; } public string LastName { get; set; } diff --git a/SurveyBackend/SurveyBackend.Core/Repositories/IUserRepository.cs b/SurveyBackend/SurveyBackend.Core/Repositories/IUserRepository.cs index 791059a..311742b 100644 --- a/SurveyBackend/SurveyBackend.Core/Repositories/IUserRepository.cs +++ b/SurveyBackend/SurveyBackend.Core/Repositories/IUserRepository.cs @@ -4,4 +4,5 @@ namespace SurveyBackend.Core.Repositories; public interface IUserRepository : IGenericRepository { + public Task GetUserByEmail(string email); } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Services/IPasswordHasher.cs b/SurveyBackend/SurveyBackend.Core/Services/IPasswordHasher.cs similarity index 74% rename from SurveyBackend/SurveyBackend.Infrastructure/Services/IPasswordHasher.cs rename to SurveyBackend/SurveyBackend.Core/Services/IPasswordHasher.cs index 52d1427..6e2ec29 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Services/IPasswordHasher.cs +++ b/SurveyBackend/SurveyBackend.Core/Services/IPasswordHasher.cs @@ -1,4 +1,4 @@ -namespace SurveyBackend.Infrastructure.Services; +namespace SurveyBackend.Core.Services; public interface IPasswordHasher { diff --git a/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs b/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs index 7d97a29..bb40cdf 100644 --- a/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs +++ b/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs @@ -1,6 +1,9 @@ +using SurveyBackend.Core.Models; + namespace SurveyBackend.Core.Services; public interface IUserService { - + public Task GetUserByEmail(string email); + public Task CreateUserAsync(User user); } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs b/SurveyBackend/SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs index 66a0468..af8eed6 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs +++ b/SurveyBackend/SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs @@ -13,7 +13,7 @@ public class ApplicationDbContext : SurveyDbContext public ApplicationDbContext(DbContextOptions options) : base(options) { - + Database.EnsureCreated(); } protected override void OnModelCreating(ModelBuilder modelBuilder) diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs b/SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs index bd1980d..86124da 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs +++ b/SurveyBackend/SurveyBackend.Infrastructure/Repositories/UserRepository.cs @@ -41,4 +41,9 @@ public class UserRepository : IUserRepository _context.Users.Remove(entity); await _context.SaveChangesAsync(); } + + public async Task GetUserByEmail(string email) + { + return await _context.Users.FirstOrDefaultAsync(u => u.Email == email); + } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Services/AuthorizationService.cs b/SurveyBackend/SurveyBackend.Infrastructure/Services/AuthorizationService.cs new file mode 100644 index 0000000..10e244e --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/Services/AuthorizationService.cs @@ -0,0 +1,42 @@ +using SurveyBackend.Core.Models; +using SurveyBackend.Core.Services; +using SurveyBackend.Infrastructure.Helpers; + +namespace SurveyBackend.Infrastructure.Services; + +public class AuthorizationService +{ + private readonly IUserService _userService; + private readonly IPasswordHasher _passwordHasher; + + public AuthorizationService(IUserService userService, IPasswordHasher passwordHasher) + { + _userService = userService; + _passwordHasher = passwordHasher; + } + + public async Task LogInUser(string email, string password) + { + var user = await _userService.GetUserByEmail(email); + if (user is null || !_passwordHasher.Verify(password, user.Password)) + { + return null; + } + + var token = TokenHelper.GetAuthToken(user); + return token; + } + + public async Task RegisterUser(User user) + { + var existingUser = await _userService.GetUserByEmail(user.Email); + if (existingUser is not null) + { + throw new Exception("Email already exists"); + } + + user.Password = _passwordHasher.HashPassword(user.Password); + + await _userService.CreateUserAsync(user); + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Services/Sha256PasswordHasher.cs b/SurveyBackend/SurveyBackend.Infrastructure/Services/Sha256PasswordHasher.cs index f513cff..b6cde0d 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Services/Sha256PasswordHasher.cs +++ b/SurveyBackend/SurveyBackend.Infrastructure/Services/Sha256PasswordHasher.cs @@ -1,4 +1,5 @@ using System.Security.Cryptography; +using SurveyBackend.Core.Services; namespace SurveyBackend.Infrastructure.Services; diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Services/UserService.cs b/SurveyBackend/SurveyBackend.Infrastructure/Services/UserService.cs new file mode 100644 index 0000000..f0f4b4e --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/Services/UserService.cs @@ -0,0 +1,25 @@ +using SurveyBackend.Core.Models; +using SurveyBackend.Core.Repositories; +using SurveyBackend.Core.Services; + +namespace SurveyBackend.Infrastructure.Services; + +public class UserService : IUserService +{ + private readonly IUserRepository _userRepository; + + public UserService(IUserRepository userRepository) + { + _userRepository = userRepository; + } + + public async Task GetUserByEmail(string email) + { + return await _userRepository.GetUserByEmail(email); + } + + public async Task CreateUserAsync(User user) + { + await _userRepository.AddAsync(user); + } +} \ No newline at end of file From 147fb683f72437bc07870364143227f93f78533c Mon Sep 17 00:00:00 2001 From: shept Date: Thu, 17 Apr 2025 01:08:18 +0500 Subject: [PATCH 02/10] say NO to Database.EnsureCreated() --- .../SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs b/SurveyBackend/SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs index af8eed6..66a0468 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs +++ b/SurveyBackend/SurveyBackend.Infrastructure/Data/ApplicationDbContext.cs @@ -13,7 +13,7 @@ public class ApplicationDbContext : SurveyDbContext public ApplicationDbContext(DbContextOptions options) : base(options) { - Database.EnsureCreated(); + } protected override void OnModelCreating(ModelBuilder modelBuilder) From 41ff1555f8eb99d430e369d7fd449122f139eea8 Mon Sep 17 00:00:00 2001 From: shept Date: Fri, 18 Apr 2025 14:12:00 +0500 Subject: [PATCH 03/10] moved service to separate project (Kontur developer said "WTF WHY ARE U STORING SERVICES IN INFRASTRUCTURE SLAVA D") --- .../Controllers/AuthController.cs | 2 +- .../Mappers/UserDTOs/UserRegistrationMapper.cs | 1 - SurveyBackend/SurveyBackend.API/Program.cs | 3 ++- .../SurveyBackend.API/SurveyBackend.API.csproj | 1 + .../AuthOptions.cs | 2 +- .../Helpers/TokenHelper.cs | 2 +- .../Services/AuthorizationService.cs | 4 ++-- .../Services/Sha256PasswordHasher.cs | 2 +- .../Services/UserService.cs | 2 +- .../SurveyBackend.Services.csproj | 17 +++++++++++++++++ SurveyBackend/SurveyBackend.sln | 6 ++++++ 11 files changed, 33 insertions(+), 9 deletions(-) rename SurveyBackend/{SurveyBackend.Infrastructure => SurveyBackend.Services}/AuthOptions.cs (96%) rename SurveyBackend/{SurveyBackend.Infrastructure => SurveyBackend.Services}/Helpers/TokenHelper.cs (94%) rename SurveyBackend/{SurveyBackend.Infrastructure => SurveyBackend.Services}/Services/AuthorizationService.cs (92%) rename SurveyBackend/{SurveyBackend.Infrastructure => SurveyBackend.Services}/Services/Sha256PasswordHasher.cs (97%) rename SurveyBackend/{SurveyBackend.Infrastructure => SurveyBackend.Services}/Services/UserService.cs (91%) create mode 100644 SurveyBackend/SurveyBackend.Services/SurveyBackend.Services.csproj diff --git a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs index d66e555..e0ecc0b 100644 --- a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs +++ b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; using SurveyBackend.DTOs; -using SurveyBackend.Infrastructure.Services; using SurveyBackend.Mappers.UserDTOs; +using SurveyBackend.Services.Services; namespace SurveyBackend.Controllers; diff --git a/SurveyBackend/SurveyBackend.API/Mappers/UserDTOs/UserRegistrationMapper.cs b/SurveyBackend/SurveyBackend.API/Mappers/UserDTOs/UserRegistrationMapper.cs index 4427b77..5d98322 100644 --- a/SurveyBackend/SurveyBackend.API/Mappers/UserDTOs/UserRegistrationMapper.cs +++ b/SurveyBackend/SurveyBackend.API/Mappers/UserDTOs/UserRegistrationMapper.cs @@ -1,7 +1,6 @@ using SurveyBackend.Core.Models; using SurveyBackend.Core.Services; using SurveyBackend.DTOs; -using SurveyBackend.Infrastructure.Services; namespace SurveyBackend.Mappers.UserDTOs; diff --git a/SurveyBackend/SurveyBackend.API/Program.cs b/SurveyBackend/SurveyBackend.API/Program.cs index d15021d..77e7695 100644 --- a/SurveyBackend/SurveyBackend.API/Program.cs +++ b/SurveyBackend/SurveyBackend.API/Program.cs @@ -7,7 +7,8 @@ using SurveyBackend.Core.Services; using SurveyBackend.Infrastructure; using SurveyBackend.Infrastructure.Data; using SurveyBackend.Infrastructure.Repositories; -using SurveyBackend.Infrastructure.Services; +using SurveyBackend.Services; +using SurveyBackend.Services.Services; using SurveyLib.Core.Repositories; using SurveyLib.Core.Services; using SurveyLib.Infrastructure.EFCore.Data; diff --git a/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj b/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj index 3764109..5a5254e 100644 --- a/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj +++ b/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj @@ -17,6 +17,7 @@ + diff --git a/SurveyBackend/SurveyBackend.Infrastructure/AuthOptions.cs b/SurveyBackend/SurveyBackend.Services/AuthOptions.cs similarity index 96% rename from SurveyBackend/SurveyBackend.Infrastructure/AuthOptions.cs rename to SurveyBackend/SurveyBackend.Services/AuthOptions.cs index cd379e8..7d92d6f 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/AuthOptions.cs +++ b/SurveyBackend/SurveyBackend.Services/AuthOptions.cs @@ -2,7 +2,7 @@ using System.Text; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; -namespace SurveyBackend.Infrastructure; +namespace SurveyBackend.Services; public static class AuthOptions { diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Helpers/TokenHelper.cs b/SurveyBackend/SurveyBackend.Services/Helpers/TokenHelper.cs similarity index 94% rename from SurveyBackend/SurveyBackend.Infrastructure/Helpers/TokenHelper.cs rename to SurveyBackend/SurveyBackend.Services/Helpers/TokenHelper.cs index a52ae64..7c401a4 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Helpers/TokenHelper.cs +++ b/SurveyBackend/SurveyBackend.Services/Helpers/TokenHelper.cs @@ -3,7 +3,7 @@ using System.Security.Claims; using Microsoft.IdentityModel.Tokens; using SurveyBackend.Core.Models; -namespace SurveyBackend.Infrastructure.Helpers; +namespace SurveyBackend.Services.Helpers; public class TokenHelper { diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Services/AuthorizationService.cs b/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs similarity index 92% rename from SurveyBackend/SurveyBackend.Infrastructure/Services/AuthorizationService.cs rename to SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs index 10e244e..25fcbdf 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Services/AuthorizationService.cs +++ b/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs @@ -1,8 +1,8 @@ using SurveyBackend.Core.Models; using SurveyBackend.Core.Services; -using SurveyBackend.Infrastructure.Helpers; +using SurveyBackend.Services.Helpers; -namespace SurveyBackend.Infrastructure.Services; +namespace SurveyBackend.Services.Services; public class AuthorizationService { diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Services/Sha256PasswordHasher.cs b/SurveyBackend/SurveyBackend.Services/Services/Sha256PasswordHasher.cs similarity index 97% rename from SurveyBackend/SurveyBackend.Infrastructure/Services/Sha256PasswordHasher.cs rename to SurveyBackend/SurveyBackend.Services/Services/Sha256PasswordHasher.cs index b6cde0d..ed54508 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Services/Sha256PasswordHasher.cs +++ b/SurveyBackend/SurveyBackend.Services/Services/Sha256PasswordHasher.cs @@ -1,7 +1,7 @@ using System.Security.Cryptography; using SurveyBackend.Core.Services; -namespace SurveyBackend.Infrastructure.Services; +namespace SurveyBackend.Services.Services; public class Sha256PasswordHasher : IPasswordHasher { diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Services/UserService.cs b/SurveyBackend/SurveyBackend.Services/Services/UserService.cs similarity index 91% rename from SurveyBackend/SurveyBackend.Infrastructure/Services/UserService.cs rename to SurveyBackend/SurveyBackend.Services/Services/UserService.cs index f0f4b4e..8ae531d 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/Services/UserService.cs +++ b/SurveyBackend/SurveyBackend.Services/Services/UserService.cs @@ -2,7 +2,7 @@ using SurveyBackend.Core.Models; using SurveyBackend.Core.Repositories; using SurveyBackend.Core.Services; -namespace SurveyBackend.Infrastructure.Services; +namespace SurveyBackend.Services.Services; public class UserService : IUserService { diff --git a/SurveyBackend/SurveyBackend.Services/SurveyBackend.Services.csproj b/SurveyBackend/SurveyBackend.Services/SurveyBackend.Services.csproj new file mode 100644 index 0000000..5765782 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Services/SurveyBackend.Services.csproj @@ -0,0 +1,17 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + diff --git a/SurveyBackend/SurveyBackend.sln b/SurveyBackend/SurveyBackend.sln index bbfd9ab..56dba6f 100644 --- a/SurveyBackend/SurveyBackend.sln +++ b/SurveyBackend/SurveyBackend.sln @@ -10,6 +10,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SurveyLib.Infrastructure.EF EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SurveyLib.Core", "..\SurveyLib\SurveyLib.Core\SurveyLib.Core.csproj", "{C17C405B-37CF-48E6-AA44-44B878F4DE56}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SurveyBackend.Services", "SurveyBackend.Services\SurveyBackend.Services.csproj", "{3CDA6495-4FB2-4F07-8B2F-15BFD2A35181}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -36,5 +38,9 @@ Global {C17C405B-37CF-48E6-AA44-44B878F4DE56}.Debug|Any CPU.Build.0 = Debug|Any CPU {C17C405B-37CF-48E6-AA44-44B878F4DE56}.Release|Any CPU.ActiveCfg = Release|Any CPU {C17C405B-37CF-48E6-AA44-44B878F4DE56}.Release|Any CPU.Build.0 = Release|Any CPU + {3CDA6495-4FB2-4F07-8B2F-15BFD2A35181}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3CDA6495-4FB2-4F07-8B2F-15BFD2A35181}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3CDA6495-4FB2-4F07-8B2F-15BFD2A35181}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3CDA6495-4FB2-4F07-8B2F-15BFD2A35181}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal From 7bff14a66e01d169d6e6080502ca70e98e1bbdf0 Mon Sep 17 00:00:00 2001 From: shept Date: Fri, 18 Apr 2025 14:15:52 +0500 Subject: [PATCH 04/10] =?UTF-8?q?bub=20fix=20=F0=9F=A5=BA=20created=20auth?= =?UTF-8?q?orization=20service=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SurveyBackend/SurveyBackend.API/Program.cs | 2 +- .../SurveyBackend.Core/Services/IAuthorizationService.cs | 9 +++++++++ .../Services/AuthorizationService.cs | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs diff --git a/SurveyBackend/SurveyBackend.API/Program.cs b/SurveyBackend/SurveyBackend.API/Program.cs index 77e7695..d56c7ab 100644 --- a/SurveyBackend/SurveyBackend.API/Program.cs +++ b/SurveyBackend/SurveyBackend.API/Program.cs @@ -37,7 +37,7 @@ public class Program builder.Services.AddScoped(); - builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs b/SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs new file mode 100644 index 0000000..1767ce2 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs @@ -0,0 +1,9 @@ +using SurveyBackend.Core.Models; + +namespace SurveyBackend.Core.Services; + +public interface IAuthorizationService +{ + public Task LogInUser(string email, string password); + public Task RegisterUser(User user); +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs b/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs index 25fcbdf..0e9907f 100644 --- a/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs +++ b/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs @@ -4,7 +4,7 @@ using SurveyBackend.Services.Helpers; namespace SurveyBackend.Services.Services; -public class AuthorizationService +public class AuthorizationService : IAuthorizationService { private readonly IUserService _userService; private readonly IPasswordHasher _passwordHasher; From eb271793ad910d426aa2052ea310b68ae2c95da1 Mon Sep 17 00:00:00 2001 From: shept Date: Fri, 18 Apr 2025 14:50:01 +0500 Subject: [PATCH 05/10] add custom exceptions and start using them in every service --- .../Exceptions/ConflictException.cs | 10 ++++++++++ .../Exceptions/ServiceException.cs | 10 ++++++++++ .../Exceptions/UnauthorizedException.cs | 10 ++++++++++ .../Services/AuthorizationService.cs | 5 +++-- 4 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 SurveyBackend/SurveyBackend.Services/Exceptions/ConflictException.cs create mode 100644 SurveyBackend/SurveyBackend.Services/Exceptions/ServiceException.cs create mode 100644 SurveyBackend/SurveyBackend.Services/Exceptions/UnauthorizedException.cs diff --git a/SurveyBackend/SurveyBackend.Services/Exceptions/ConflictException.cs b/SurveyBackend/SurveyBackend.Services/Exceptions/ConflictException.cs new file mode 100644 index 0000000..12d14ce --- /dev/null +++ b/SurveyBackend/SurveyBackend.Services/Exceptions/ConflictException.cs @@ -0,0 +1,10 @@ +namespace SurveyBackend.Services.Exceptions; + +public class ConflictException : ServiceException +{ + public override int StatusCode => 409; + + public ConflictException(string message) : base(message) + { + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Services/Exceptions/ServiceException.cs b/SurveyBackend/SurveyBackend.Services/Exceptions/ServiceException.cs new file mode 100644 index 0000000..30fb016 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Services/Exceptions/ServiceException.cs @@ -0,0 +1,10 @@ +namespace SurveyBackend.Services.Exceptions; + +public abstract class ServiceException : Exception +{ + public abstract int StatusCode { get; } + + protected ServiceException(string message) : base(message) + { + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Services/Exceptions/UnauthorizedException.cs b/SurveyBackend/SurveyBackend.Services/Exceptions/UnauthorizedException.cs new file mode 100644 index 0000000..58ea510 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Services/Exceptions/UnauthorizedException.cs @@ -0,0 +1,10 @@ +namespace SurveyBackend.Services.Exceptions; + +public class UnauthorizedException : ServiceException +{ + public override int StatusCode => 401; + + public UnauthorizedException(string message) : base(message) + { + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs b/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs index 0e9907f..05acef2 100644 --- a/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs +++ b/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs @@ -1,5 +1,6 @@ using SurveyBackend.Core.Models; using SurveyBackend.Core.Services; +using SurveyBackend.Services.Exceptions; using SurveyBackend.Services.Helpers; namespace SurveyBackend.Services.Services; @@ -20,7 +21,7 @@ public class AuthorizationService : IAuthorizationService var user = await _userService.GetUserByEmail(email); if (user is null || !_passwordHasher.Verify(password, user.Password)) { - return null; + throw new UnauthorizedException("Email or password is incorrect."); } var token = TokenHelper.GetAuthToken(user); @@ -32,7 +33,7 @@ public class AuthorizationService : IAuthorizationService var existingUser = await _userService.GetUserByEmail(user.Email); if (existingUser is not null) { - throw new Exception("Email already exists"); + throw new ConflictException("Email already exists"); } user.Password = _passwordHasher.HashPassword(user.Password); From 55e82425a9ee63bea917a95cfe486bd503f63d1d Mon Sep 17 00:00:00 2001 From: shept Date: Fri, 18 Apr 2025 15:15:32 +0500 Subject: [PATCH 06/10] say NO to try-catch in controllers - added ExceptionsMiddleware.cs - added more Exception types - removed all exceptions logic in controllers --- .../Controllers/AuthController.cs | 18 ++----- .../Middlewares/ExceptionsMiddleware.cs | 49 +++++++++++++++++++ SurveyBackend/SurveyBackend.API/Program.cs | 3 ++ .../Services/IAuthorizationService.cs | 2 +- .../Services/IUserService.cs | 2 +- .../Exceptions/NotFoundException.cs | 10 ++++ .../Services/AuthorizationService.cs | 6 +-- .../Services/UserService.cs | 5 +- 8 files changed, 75 insertions(+), 20 deletions(-) create mode 100644 SurveyBackend/SurveyBackend.API/Middlewares/ExceptionsMiddleware.cs create mode 100644 SurveyBackend/SurveyBackend.Services/Exceptions/NotFoundException.cs diff --git a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs index e0ecc0b..7a2e115 100644 --- a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs +++ b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Mvc; +using SurveyBackend.Core.Services; using SurveyBackend.DTOs; using SurveyBackend.Mappers.UserDTOs; -using SurveyBackend.Services.Services; namespace SurveyBackend.Controllers; @@ -9,9 +9,9 @@ namespace SurveyBackend.Controllers; [Route("auth")] public class AuthController : ControllerBase { - private readonly AuthorizationService _authorizationService; + private readonly IAuthorizationService _authorizationService; - public AuthController(AuthorizationService authorizationService) + public AuthController(IAuthorizationService authorizationService) { _authorizationService = authorizationService; } @@ -20,21 +20,13 @@ public class AuthController : ControllerBase public async Task LogIn([FromBody] UserLoginDto loginData) { var token = await _authorizationService.LogInUser(loginData.Email, loginData.Password); - return token is null ? Unauthorized() : Ok(new { token = token }); + return Ok(new { token = token }); } [HttpPost("register")] public async Task Register([FromBody] UserRegistrationDto registerData) { - try - { - await _authorizationService.RegisterUser(UserRegistrationMapper.UserRegistrationToModel(registerData)); - } - catch (Exception ex) - { - return BadRequest(ex.Message); - } - + await _authorizationService.RegisterUser(UserRegistrationMapper.UserRegistrationToModel(registerData)); return Ok(); } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/Middlewares/ExceptionsMiddleware.cs b/SurveyBackend/SurveyBackend.API/Middlewares/ExceptionsMiddleware.cs new file mode 100644 index 0000000..65c94f8 --- /dev/null +++ b/SurveyBackend/SurveyBackend.API/Middlewares/ExceptionsMiddleware.cs @@ -0,0 +1,49 @@ +using SurveyBackend.Services.Exceptions; + +namespace SurveyBackend.Middlewares; + +public class ExceptionsMiddleware +{ + private readonly RequestDelegate _next; + private readonly ILogger _logger; + + public ExceptionsMiddleware(RequestDelegate next, ILogger logger) + { + _next = next; + _logger = logger; + } + + public async Task InvokeAsync(HttpContext context) + { + try + { + await _next(context); + } + catch (ServiceException ex) + { + context.Response.StatusCode = ex.StatusCode; + context.Response.ContentType = "application/json"; + + var response = new + { + error = ex.Message + }; + + await context.Response.WriteAsJsonAsync(response); + } + catch (Exception ex) + { + _logger.LogError(ex.Message); + + context.Response.StatusCode = 500; + context.Response.ContentType = "application/json"; + + var response = new + { + error = "Internal Server Error. GG WP, request bub fix" + }; + + await context.Response.WriteAsJsonAsync(response); + } + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/Program.cs b/SurveyBackend/SurveyBackend.API/Program.cs index d56c7ab..6a2bf3f 100644 --- a/SurveyBackend/SurveyBackend.API/Program.cs +++ b/SurveyBackend/SurveyBackend.API/Program.cs @@ -7,6 +7,7 @@ using SurveyBackend.Core.Services; using SurveyBackend.Infrastructure; using SurveyBackend.Infrastructure.Data; using SurveyBackend.Infrastructure.Repositories; +using SurveyBackend.Middlewares; using SurveyBackend.Services; using SurveyBackend.Services.Services; using SurveyLib.Core.Repositories; @@ -71,6 +72,8 @@ public class Program app.UseSwaggerUI(); } + app.UseMiddleware(); + app.UseAuthentication(); app.UseAuthorization(); diff --git a/SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs b/SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs index 1767ce2..2af6ec7 100644 --- a/SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs +++ b/SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs @@ -4,6 +4,6 @@ namespace SurveyBackend.Core.Services; public interface IAuthorizationService { - public Task LogInUser(string email, string password); + public Task LogInUser(string email, string password); public Task RegisterUser(User user); } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs b/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs index bb40cdf..c341a4a 100644 --- a/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs +++ b/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs @@ -4,6 +4,6 @@ namespace SurveyBackend.Core.Services; public interface IUserService { - public Task GetUserByEmail(string email); + public Task GetUserByEmail(string email); public Task CreateUserAsync(User user); } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Services/Exceptions/NotFoundException.cs b/SurveyBackend/SurveyBackend.Services/Exceptions/NotFoundException.cs new file mode 100644 index 0000000..a5e5739 --- /dev/null +++ b/SurveyBackend/SurveyBackend.Services/Exceptions/NotFoundException.cs @@ -0,0 +1,10 @@ +namespace SurveyBackend.Services.Exceptions; + +public class NotFoundException : ServiceException +{ + public override int StatusCode => 404; + + public NotFoundException(string message) : base(message) + { + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs b/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs index 05acef2..4a0e006 100644 --- a/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs +++ b/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs @@ -16,12 +16,12 @@ public class AuthorizationService : IAuthorizationService _passwordHasher = passwordHasher; } - public async Task LogInUser(string email, string password) + public async Task LogInUser(string email, string password) { var user = await _userService.GetUserByEmail(email); - if (user is null || !_passwordHasher.Verify(password, user.Password)) + if (!_passwordHasher.Verify(password, user.Password)) { - throw new UnauthorizedException("Email or password is incorrect."); + throw new UnauthorizedException("Password is incorrect."); } var token = TokenHelper.GetAuthToken(user); diff --git a/SurveyBackend/SurveyBackend.Services/Services/UserService.cs b/SurveyBackend/SurveyBackend.Services/Services/UserService.cs index 8ae531d..eea4ee7 100644 --- a/SurveyBackend/SurveyBackend.Services/Services/UserService.cs +++ b/SurveyBackend/SurveyBackend.Services/Services/UserService.cs @@ -1,6 +1,7 @@ using SurveyBackend.Core.Models; using SurveyBackend.Core.Repositories; using SurveyBackend.Core.Services; +using SurveyBackend.Services.Exceptions; namespace SurveyBackend.Services.Services; @@ -13,9 +14,9 @@ public class UserService : IUserService _userRepository = userRepository; } - public async Task GetUserByEmail(string email) + public async Task GetUserByEmail(string email) { - return await _userRepository.GetUserByEmail(email); + return await _userRepository.GetUserByEmail(email) ?? throw new NotFoundException("Email not found"); } public async Task CreateUserAsync(User user) From 4bfc10d7de049e3378368ad818addb67c6b1a7eb Mon Sep 17 00:00:00 2001 From: shept Date: Fri, 18 Apr 2025 16:38:30 +0500 Subject: [PATCH 07/10] registration returns token now --- .../SurveyBackend.API/Controllers/AuthController.cs | 4 ++-- .../SurveyBackend.Core/Services/IAuthorizationService.cs | 2 +- SurveyBackend/SurveyBackend.Core/Services/IUserService.cs | 1 + .../Services/AuthorizationService.cs | 8 +++++--- .../SurveyBackend.Services/Services/UserService.cs | 5 +++++ 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs index 7a2e115..882c94b 100644 --- a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs +++ b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs @@ -26,7 +26,7 @@ public class AuthController : ControllerBase [HttpPost("register")] public async Task Register([FromBody] UserRegistrationDto registerData) { - await _authorizationService.RegisterUser(UserRegistrationMapper.UserRegistrationToModel(registerData)); - return Ok(); + var token = await _authorizationService.RegisterUser(UserRegistrationMapper.UserRegistrationToModel(registerData)); + return Ok(new { token = token }); } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs b/SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs index 2af6ec7..0237a22 100644 --- a/SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs +++ b/SurveyBackend/SurveyBackend.Core/Services/IAuthorizationService.cs @@ -5,5 +5,5 @@ namespace SurveyBackend.Core.Services; public interface IAuthorizationService { public Task LogInUser(string email, string password); - public Task RegisterUser(User user); + public Task RegisterUser(User user); } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs b/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs index c341a4a..cd34c76 100644 --- a/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs +++ b/SurveyBackend/SurveyBackend.Core/Services/IUserService.cs @@ -5,5 +5,6 @@ namespace SurveyBackend.Core.Services; public interface IUserService { public Task GetUserByEmail(string email); + public Task IsEmailTaken(string email); public Task CreateUserAsync(User user); } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs b/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs index 4a0e006..772eb27 100644 --- a/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs +++ b/SurveyBackend/SurveyBackend.Services/Services/AuthorizationService.cs @@ -28,10 +28,10 @@ public class AuthorizationService : IAuthorizationService return token; } - public async Task RegisterUser(User user) + public async Task RegisterUser(User user) { - var existingUser = await _userService.GetUserByEmail(user.Email); - if (existingUser is not null) + var isEmailTaken = await _userService.IsEmailTaken(user.Email); + if (isEmailTaken) { throw new ConflictException("Email already exists"); } @@ -39,5 +39,7 @@ public class AuthorizationService : IAuthorizationService user.Password = _passwordHasher.HashPassword(user.Password); await _userService.CreateUserAsync(user); + var token = TokenHelper.GetAuthToken(user); + return token; } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Services/Services/UserService.cs b/SurveyBackend/SurveyBackend.Services/Services/UserService.cs index eea4ee7..9c52a64 100644 --- a/SurveyBackend/SurveyBackend.Services/Services/UserService.cs +++ b/SurveyBackend/SurveyBackend.Services/Services/UserService.cs @@ -19,6 +19,11 @@ public class UserService : IUserService return await _userRepository.GetUserByEmail(email) ?? throw new NotFoundException("Email not found"); } + public async Task IsEmailTaken(string email) + { + return await _userRepository.GetUserByEmail(email) != null; + } + public async Task CreateUserAsync(User user) { await _userRepository.AddAsync(user); From e13a2f331a6843be51d8c5172e0bca138757f851 Mon Sep 17 00:00:00 2001 From: shept Date: Fri, 18 Apr 2025 17:32:47 +0500 Subject: [PATCH 08/10] downgrading EF Core --- SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj | 6 +++++- .../SurveyBackend.Core/SurveyBackend.Core.csproj | 2 +- .../SurveyBackend.Infrastructure.csproj | 8 ++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj b/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj index 5a5254e..b736412 100644 --- a/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj +++ b/SurveyBackend/SurveyBackend.API/SurveyBackend.API.csproj @@ -10,7 +10,11 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj b/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj index adbb79f..8cc2397 100644 --- a/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj +++ b/SurveyBackend/SurveyBackend.Core/SurveyBackend.Core.csproj @@ -7,7 +7,7 @@ - + diff --git a/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj b/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj index 8d6e23a..5a8e0aa 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj +++ b/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj @@ -8,6 +8,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + @@ -16,4 +20,8 @@ + + + + From 6796241f8ddbad7b30834c4c1f5d30b5ac3532ab Mon Sep 17 00:00:00 2001 From: shept Date: Fri, 18 Apr 2025 17:34:11 +0500 Subject: [PATCH 09/10] Update SurveyLib and downgrade EF Core --- .../SurveyBackend.Infrastructure.csproj | 2 +- SurveyLib | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj b/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj index 5a8e0aa..e46ce0e 100644 --- a/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj +++ b/SurveyBackend/SurveyBackend.Infrastructure/SurveyBackend.Infrastructure.csproj @@ -7,7 +7,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/SurveyLib b/SurveyLib index 7bbc78f..fe2735d 160000 --- a/SurveyLib +++ b/SurveyLib @@ -1 +1 @@ -Subproject commit 7bbc78fbd7eef3bb2497b966ea73eba31aa7032c +Subproject commit fe2735da5040501f143526a8c1af19c8023f6368 From c5a0f7368c540b4ec21da2567b02225faac15868 Mon Sep 17 00:00:00 2001 From: shept Date: Fri, 18 Apr 2025 17:35:52 +0500 Subject: [PATCH 10/10] added initial migration --- .../20250418123442_Initial.Designer.cs | 321 ++++++++++++++++++ .../Data/Migrations/20250418123442_Initial.cs | 243 +++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 318 +++++++++++++++++ 3 files changed, 882 insertions(+) create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/20250418123442_Initial.Designer.cs create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/20250418123442_Initial.cs create mode 100644 SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/20250418123442_Initial.Designer.cs b/SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/20250418123442_Initial.Designer.cs new file mode 100644 index 0000000..b8b673d --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/20250418123442_Initial.Designer.cs @@ -0,0 +1,321 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SurveyBackend.Infrastructure.Data; + +#nullable disable + +namespace SurveyBackend.Infrastructure.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20250418123442_Initial")] + partial class Initial + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.15"); + + modelBuilder.Entity("GroupUser", b => + { + b.Property("GroupsId") + .HasColumnType("INTEGER"); + + b.Property("UsersId") + .HasColumnType("INTEGER"); + + b.HasKey("GroupsId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("GroupUser"); + }); + + modelBuilder.Entity("SurveyBackend.Core.Models.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Label") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("SurveyBackend.Core.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Answer", b => + { + b.Property("CompletionId") + .HasColumnType("INTEGER"); + + b.Property("QuestionId") + .HasColumnType("INTEGER"); + + b.Property("AnswerText") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CompletionId", "QuestionId"); + + b.HasIndex("QuestionId"); + + b.ToTable("Answers"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.AnswerVariant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MultipleAnswerQuestionId") + .HasColumnType("INTEGER"); + + b.Property("QuestionId") + .HasColumnType("INTEGER"); + + b.Property("SingleAnswerQuestionId") + .HasColumnType("INTEGER"); + + b.Property("Text") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MultipleAnswerQuestionId"); + + b.HasIndex("QuestionId"); + + b.HasIndex("SingleAnswerQuestionId"); + + b.ToTable("AnswerVariant"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Completion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FinishedAt") + .HasColumnType("TEXT"); + + b.Property("SurveyId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SurveyId"); + + b.ToTable("Completions"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionBase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(34) + .HasColumnType("TEXT"); + + b.Property("SurveyId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SurveyId"); + + b.ToTable("Questions"); + + b.HasDiscriminator().HasValue("QuestionBase"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Survey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Surveys"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionVariants.MultipleAnswerQuestion", b => + { + b.HasBaseType("SurveyLib.Core.Models.QuestionBase"); + + b.HasDiscriminator().HasValue("MultipleAnswerQuestion"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionVariants.SingleAnswerQuestion", b => + { + b.HasBaseType("SurveyLib.Core.Models.QuestionBase"); + + b.HasDiscriminator().HasValue("SingleAnswerQuestion"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionVariants.TextQuestion", b => + { + b.HasBaseType("SurveyLib.Core.Models.QuestionBase"); + + b.HasDiscriminator().HasValue("TextQuestion"); + }); + + modelBuilder.Entity("GroupUser", b => + { + b.HasOne("SurveyBackend.Core.Models.Group", null) + .WithMany() + .HasForeignKey("GroupsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SurveyBackend.Core.Models.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Answer", b => + { + b.HasOne("SurveyLib.Core.Models.Completion", "Completion") + .WithMany("Answers") + .HasForeignKey("CompletionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SurveyLib.Core.Models.QuestionBase", "Question") + .WithMany("Answers") + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Completion"); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.AnswerVariant", b => + { + b.HasOne("SurveyLib.Core.Models.QuestionVariants.MultipleAnswerQuestion", null) + .WithMany("AnswerVariants") + .HasForeignKey("MultipleAnswerQuestionId"); + + b.HasOne("SurveyLib.Core.Models.QuestionBase", "Question") + .WithMany() + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SurveyLib.Core.Models.QuestionVariants.SingleAnswerQuestion", null) + .WithMany("AnswerVariants") + .HasForeignKey("SingleAnswerQuestionId"); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Completion", b => + { + b.HasOne("SurveyLib.Core.Models.Survey", "Survey") + .WithMany("Completions") + .HasForeignKey("SurveyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Survey"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionBase", b => + { + b.HasOne("SurveyLib.Core.Models.Survey", "Survey") + .WithMany("Questions") + .HasForeignKey("SurveyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Survey"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Completion", b => + { + b.Navigation("Answers"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionBase", b => + { + b.Navigation("Answers"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Survey", b => + { + b.Navigation("Completions"); + + b.Navigation("Questions"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionVariants.MultipleAnswerQuestion", b => + { + b.Navigation("AnswerVariants"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionVariants.SingleAnswerQuestion", b => + { + b.Navigation("AnswerVariants"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/20250418123442_Initial.cs b/SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/20250418123442_Initial.cs new file mode 100644 index 0000000..26fbf8b --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/20250418123442_Initial.cs @@ -0,0 +1,243 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SurveyBackend.Infrastructure.Data.Migrations +{ + /// + public partial class Initial : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Groups", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Label = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Groups", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Surveys", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Title = table.Column(type: "TEXT", nullable: false), + Description = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Surveys", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Email = table.Column(type: "TEXT", nullable: false), + FirstName = table.Column(type: "TEXT", nullable: false), + LastName = table.Column(type: "TEXT", nullable: false), + Password = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Completions", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + SurveyId = table.Column(type: "INTEGER", nullable: false), + FinishedAt = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Completions", x => x.Id); + table.ForeignKey( + name: "FK_Completions_Surveys_SurveyId", + column: x => x.SurveyId, + principalTable: "Surveys", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Questions", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + SurveyId = table.Column(type: "INTEGER", nullable: false), + Title = table.Column(type: "TEXT", nullable: false), + Discriminator = table.Column(type: "TEXT", maxLength: 34, nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Questions", x => x.Id); + table.ForeignKey( + name: "FK_Questions_Surveys_SurveyId", + column: x => x.SurveyId, + principalTable: "Surveys", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "GroupUser", + columns: table => new + { + GroupsId = table.Column(type: "INTEGER", nullable: false), + UsersId = table.Column(type: "INTEGER", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_GroupUser", x => new { x.GroupsId, x.UsersId }); + table.ForeignKey( + name: "FK_GroupUser_Groups_GroupsId", + column: x => x.GroupsId, + principalTable: "Groups", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_GroupUser_Users_UsersId", + column: x => x.UsersId, + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "Answers", + columns: table => new + { + CompletionId = table.Column(type: "INTEGER", nullable: false), + QuestionId = table.Column(type: "INTEGER", nullable: false), + AnswerText = table.Column(type: "TEXT", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Answers", x => new { x.CompletionId, x.QuestionId }); + table.ForeignKey( + name: "FK_Answers_Completions_CompletionId", + column: x => x.CompletionId, + principalTable: "Completions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_Answers_Questions_QuestionId", + column: x => x.QuestionId, + principalTable: "Questions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "AnswerVariant", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + QuestionId = table.Column(type: "INTEGER", nullable: false), + Text = table.Column(type: "TEXT", nullable: false), + MultipleAnswerQuestionId = table.Column(type: "INTEGER", nullable: true), + SingleAnswerQuestionId = table.Column(type: "INTEGER", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_AnswerVariant", x => x.Id); + table.ForeignKey( + name: "FK_AnswerVariant_Questions_MultipleAnswerQuestionId", + column: x => x.MultipleAnswerQuestionId, + principalTable: "Questions", + principalColumn: "Id"); + table.ForeignKey( + name: "FK_AnswerVariant_Questions_QuestionId", + column: x => x.QuestionId, + principalTable: "Questions", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_AnswerVariant_Questions_SingleAnswerQuestionId", + column: x => x.SingleAnswerQuestionId, + principalTable: "Questions", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Answers_QuestionId", + table: "Answers", + column: "QuestionId"); + + migrationBuilder.CreateIndex( + name: "IX_AnswerVariant_MultipleAnswerQuestionId", + table: "AnswerVariant", + column: "MultipleAnswerQuestionId"); + + migrationBuilder.CreateIndex( + name: "IX_AnswerVariant_QuestionId", + table: "AnswerVariant", + column: "QuestionId"); + + migrationBuilder.CreateIndex( + name: "IX_AnswerVariant_SingleAnswerQuestionId", + table: "AnswerVariant", + column: "SingleAnswerQuestionId"); + + migrationBuilder.CreateIndex( + name: "IX_Completions_SurveyId", + table: "Completions", + column: "SurveyId"); + + migrationBuilder.CreateIndex( + name: "IX_GroupUser_UsersId", + table: "GroupUser", + column: "UsersId"); + + migrationBuilder.CreateIndex( + name: "IX_Questions_SurveyId", + table: "Questions", + column: "SurveyId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Answers"); + + migrationBuilder.DropTable( + name: "AnswerVariant"); + + migrationBuilder.DropTable( + name: "GroupUser"); + + migrationBuilder.DropTable( + name: "Completions"); + + migrationBuilder.DropTable( + name: "Questions"); + + migrationBuilder.DropTable( + name: "Groups"); + + migrationBuilder.DropTable( + name: "Users"); + + migrationBuilder.DropTable( + name: "Surveys"); + } + } +} diff --git a/SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs b/SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs new file mode 100644 index 0000000..db4d61e --- /dev/null +++ b/SurveyBackend/SurveyBackend.Infrastructure/Data/Migrations/ApplicationDbContextModelSnapshot.cs @@ -0,0 +1,318 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SurveyBackend.Infrastructure.Data; + +#nullable disable + +namespace SurveyBackend.Infrastructure.Data.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + partial class ApplicationDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "8.0.15"); + + modelBuilder.Entity("GroupUser", b => + { + b.Property("GroupsId") + .HasColumnType("INTEGER"); + + b.Property("UsersId") + .HasColumnType("INTEGER"); + + b.HasKey("GroupsId", "UsersId"); + + b.HasIndex("UsersId"); + + b.ToTable("GroupUser"); + }); + + modelBuilder.Entity("SurveyBackend.Core.Models.Group", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Label") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Groups"); + }); + + modelBuilder.Entity("SurveyBackend.Core.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Email") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("LastName") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Password") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Answer", b => + { + b.Property("CompletionId") + .HasColumnType("INTEGER"); + + b.Property("QuestionId") + .HasColumnType("INTEGER"); + + b.Property("AnswerText") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("CompletionId", "QuestionId"); + + b.HasIndex("QuestionId"); + + b.ToTable("Answers"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.AnswerVariant", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("MultipleAnswerQuestionId") + .HasColumnType("INTEGER"); + + b.Property("QuestionId") + .HasColumnType("INTEGER"); + + b.Property("SingleAnswerQuestionId") + .HasColumnType("INTEGER"); + + b.Property("Text") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("MultipleAnswerQuestionId"); + + b.HasIndex("QuestionId"); + + b.HasIndex("SingleAnswerQuestionId"); + + b.ToTable("AnswerVariant"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Completion", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("FinishedAt") + .HasColumnType("TEXT"); + + b.Property("SurveyId") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("SurveyId"); + + b.ToTable("Completions"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionBase", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Discriminator") + .IsRequired() + .HasMaxLength(34) + .HasColumnType("TEXT"); + + b.Property("SurveyId") + .HasColumnType("INTEGER"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("SurveyId"); + + b.ToTable("Questions"); + + b.HasDiscriminator().HasValue("QuestionBase"); + + b.UseTphMappingStrategy(); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Survey", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Description") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Title") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Surveys"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionVariants.MultipleAnswerQuestion", b => + { + b.HasBaseType("SurveyLib.Core.Models.QuestionBase"); + + b.HasDiscriminator().HasValue("MultipleAnswerQuestion"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionVariants.SingleAnswerQuestion", b => + { + b.HasBaseType("SurveyLib.Core.Models.QuestionBase"); + + b.HasDiscriminator().HasValue("SingleAnswerQuestion"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionVariants.TextQuestion", b => + { + b.HasBaseType("SurveyLib.Core.Models.QuestionBase"); + + b.HasDiscriminator().HasValue("TextQuestion"); + }); + + modelBuilder.Entity("GroupUser", b => + { + b.HasOne("SurveyBackend.Core.Models.Group", null) + .WithMany() + .HasForeignKey("GroupsId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SurveyBackend.Core.Models.User", null) + .WithMany() + .HasForeignKey("UsersId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Answer", b => + { + b.HasOne("SurveyLib.Core.Models.Completion", "Completion") + .WithMany("Answers") + .HasForeignKey("CompletionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SurveyLib.Core.Models.QuestionBase", "Question") + .WithMany("Answers") + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Completion"); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.AnswerVariant", b => + { + b.HasOne("SurveyLib.Core.Models.QuestionVariants.MultipleAnswerQuestion", null) + .WithMany("AnswerVariants") + .HasForeignKey("MultipleAnswerQuestionId"); + + b.HasOne("SurveyLib.Core.Models.QuestionBase", "Question") + .WithMany() + .HasForeignKey("QuestionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("SurveyLib.Core.Models.QuestionVariants.SingleAnswerQuestion", null) + .WithMany("AnswerVariants") + .HasForeignKey("SingleAnswerQuestionId"); + + b.Navigation("Question"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Completion", b => + { + b.HasOne("SurveyLib.Core.Models.Survey", "Survey") + .WithMany("Completions") + .HasForeignKey("SurveyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Survey"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionBase", b => + { + b.HasOne("SurveyLib.Core.Models.Survey", "Survey") + .WithMany("Questions") + .HasForeignKey("SurveyId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Survey"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Completion", b => + { + b.Navigation("Answers"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionBase", b => + { + b.Navigation("Answers"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.Survey", b => + { + b.Navigation("Completions"); + + b.Navigation("Questions"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionVariants.MultipleAnswerQuestion", b => + { + b.Navigation("AnswerVariants"); + }); + + modelBuilder.Entity("SurveyLib.Core.Models.QuestionVariants.SingleAnswerQuestion", b => + { + b.Navigation("AnswerVariants"); + }); +#pragma warning restore 612, 618 + } + } +}