From 59857d80f3571984f636337382625cf328e7080a Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 4 Jun 2024 11:03:49 +0400 Subject: [PATCH 01/22] Role interfaces --- DataModels/DataModels.csproj | 4 ---- DataModels/IId.cs | 13 +++++++++++++ DataModels/Models/IRole.cs | 13 +++++++++++++ DataModels/Models/IUser.cs | 18 ++++++++++++++++++ 4 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 DataModels/IId.cs create mode 100644 DataModels/Models/IRole.cs create mode 100644 DataModels/Models/IUser.cs diff --git a/DataModels/DataModels.csproj b/DataModels/DataModels.csproj index c60e651..fa71b7a 100644 --- a/DataModels/DataModels.csproj +++ b/DataModels/DataModels.csproj @@ -6,8 +6,4 @@ enable - - - - diff --git a/DataModels/IId.cs b/DataModels/IId.cs new file mode 100644 index 0000000..8b39a76 --- /dev/null +++ b/DataModels/IId.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DataModels +{ + public interface IId + { + Guid Id { get; } + } +} \ No newline at end of file diff --git a/DataModels/Models/IRole.cs b/DataModels/Models/IRole.cs new file mode 100644 index 0000000..4c294fe --- /dev/null +++ b/DataModels/Models/IRole.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DataModels.Models +{ + public interface IRole : IId + { + string Name { get; } + } +} \ No newline at end of file diff --git a/DataModels/Models/IUser.cs b/DataModels/Models/IUser.cs new file mode 100644 index 0000000..7098f65 --- /dev/null +++ b/DataModels/Models/IUser.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DataModels.Models +{ + public interface IUser : IId + { + string FirstName { get; } + string SecondName { get; } + string PasswordHash { get; } + string Email { get; } + DateTime Birthday { get; } + Guid RoleId { get; } + } +} \ No newline at end of file -- 2.25.1 From 78af8e86e980872f3ef050395769f867a9ec6ba6 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 4 Jun 2024 11:57:28 +0400 Subject: [PATCH 02/22] Binding Search View models --- Contracts/BindingModels/RoleBindingModel.cs | 14 ++++++++++++++ Contracts/BindingModels/UserBindingModel.cs | 19 +++++++++++++++++++ Contracts/Contracts.csproj | 3 --- Contracts/SearchModels/RoleSearchModel.cs | 13 +++++++++++++ Contracts/SearchModels/UserSearchModel.cs | 14 ++++++++++++++ Contracts/ViewModels/RoleViewModel.cs | 14 ++++++++++++++ Contracts/ViewModels/UserViewModel.cs | 19 +++++++++++++++++++ 7 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 Contracts/BindingModels/RoleBindingModel.cs create mode 100644 Contracts/BindingModels/UserBindingModel.cs create mode 100644 Contracts/SearchModels/RoleSearchModel.cs create mode 100644 Contracts/SearchModels/UserSearchModel.cs create mode 100644 Contracts/ViewModels/RoleViewModel.cs create mode 100644 Contracts/ViewModels/UserViewModel.cs diff --git a/Contracts/BindingModels/RoleBindingModel.cs b/Contracts/BindingModels/RoleBindingModel.cs new file mode 100644 index 0000000..8e33f73 --- /dev/null +++ b/Contracts/BindingModels/RoleBindingModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.BindingModels +{ + public class RoleBindingModel + { + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Contracts/BindingModels/UserBindingModel.cs b/Contracts/BindingModels/UserBindingModel.cs new file mode 100644 index 0000000..7b6c7c6 --- /dev/null +++ b/Contracts/BindingModels/UserBindingModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.BindingModels +{ + public class UserBindingModel + { + public Guid Id { get; set; } + public string FirstName { get; set; } = string.Empty; + public string SecondName { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public string PasswordHash { get; set; } = string.Empty; + public DateTime Birthday { get; set; } + public RoleBindingModel Role { get; set; } = null!; + } +} \ No newline at end of file diff --git a/Contracts/Contracts.csproj b/Contracts/Contracts.csproj index 13d76fe..64eaa86 100644 --- a/Contracts/Contracts.csproj +++ b/Contracts/Contracts.csproj @@ -7,9 +7,6 @@ - - - diff --git a/Contracts/SearchModels/RoleSearchModel.cs b/Contracts/SearchModels/RoleSearchModel.cs new file mode 100644 index 0000000..058eda2 --- /dev/null +++ b/Contracts/SearchModels/RoleSearchModel.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.SearchModels +{ + public class RoleSearchModel + { + public Guid? Id { get; set; } + } +} \ No newline at end of file diff --git a/Contracts/SearchModels/UserSearchModel.cs b/Contracts/SearchModels/UserSearchModel.cs new file mode 100644 index 0000000..2df2131 --- /dev/null +++ b/Contracts/SearchModels/UserSearchModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.SearchModels +{ + public class UserSearchModel + { + public Guid? Id { get; set; } + public string? Email { get; set; } + } +} \ No newline at end of file diff --git a/Contracts/ViewModels/RoleViewModel.cs b/Contracts/ViewModels/RoleViewModel.cs new file mode 100644 index 0000000..98d5c6f --- /dev/null +++ b/Contracts/ViewModels/RoleViewModel.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.ViewModels +{ + public class RoleViewModel + { + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + } +} \ No newline at end of file diff --git a/Contracts/ViewModels/UserViewModel.cs b/Contracts/ViewModels/UserViewModel.cs new file mode 100644 index 0000000..4f15600 --- /dev/null +++ b/Contracts/ViewModels/UserViewModel.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.ViewModels +{ + public class UserViewModel + { + public Guid Id { get; set; } + public string FirstName { get; set; } = string.Empty; + public string SecondName { get; set; } = string.Empty; + public string Email { get; set; } = string.Empty; + public string PasswordHash { get; set; } = string.Empty; + public DateTime Birthday { get; set; } + public RoleViewModel Role { get; set; } = null!; + } +} \ No newline at end of file -- 2.25.1 From 82ad684fe54412564591a7e5852722aaf1fea751 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 4 Jun 2024 21:16:50 +0400 Subject: [PATCH 03/22] Database, models and small fixes --- DataModels/Models/IUser.cs | 1 - DatabaseImplement/Database.cs | 25 +++++++ DatabaseImplement/DatabaseImplement.csproj | 14 +++- DatabaseImplement/Models/Role.cs | 47 +++++++++++++ DatabaseImplement/Models/User.cs | 76 ++++++++++++++++++++++ 5 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 DatabaseImplement/Database.cs create mode 100644 DatabaseImplement/Models/Role.cs create mode 100644 DatabaseImplement/Models/User.cs diff --git a/DataModels/Models/IUser.cs b/DataModels/Models/IUser.cs index 7098f65..9eb2ef6 100644 --- a/DataModels/Models/IUser.cs +++ b/DataModels/Models/IUser.cs @@ -13,6 +13,5 @@ namespace DataModels.Models string PasswordHash { get; } string Email { get; } DateTime Birthday { get; } - Guid RoleId { get; } } } \ No newline at end of file diff --git a/DatabaseImplement/Database.cs b/DatabaseImplement/Database.cs new file mode 100644 index 0000000..99b74bf --- /dev/null +++ b/DatabaseImplement/Database.cs @@ -0,0 +1,25 @@ +using DatabaseImplement.Models; +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DatabaseImplement +{ + public class Database : DbContext + { + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + if (optionsBuilder.IsConfigured == false) + { + optionsBuilder.UseNpgsql("Server=192.168.191.85:32768;Database=gun_market;Username=postgres;Password=7355608;"); + } + base.OnConfiguring(optionsBuilder); + } + + public virtual DbSet Roles { get; set; } = null!; + public virtual DbSet Users { get; set; } = null!; + } +} \ No newline at end of file diff --git a/DatabaseImplement/DatabaseImplement.csproj b/DatabaseImplement/DatabaseImplement.csproj index 85b7ca3..861b25f 100644 --- a/DatabaseImplement/DatabaseImplement.csproj +++ b/DatabaseImplement/DatabaseImplement.csproj @@ -7,8 +7,20 @@ - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + diff --git a/DatabaseImplement/Models/Role.cs b/DatabaseImplement/Models/Role.cs new file mode 100644 index 0000000..f5d7970 --- /dev/null +++ b/DatabaseImplement/Models/Role.cs @@ -0,0 +1,47 @@ +using Contracts.BindingModels; +using Contracts.ViewModels; +using DataModels.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DatabaseImplement.Models +{ + public class Role : IRole + { + public Guid Id { get; set; } + + [Required] + public string Name { get; set; } = string.Empty; + + public RoleBindingModel GetBindingModel() => new() + { + Id = Id, + Name = Name, + }; + + public static Role ToRoleFromView(RoleViewModel model) => new() + { + Id = model.Id, + Name = model.Name, + }; + + public static Role ToRoleFromBinding(RoleBindingModel model) => new() + { + Id = model.Id, + Name = model.Name, + }; + + public void Update(RoleBindingModel model) + { + if (model is null) + { + throw new ArgumentNullException("Update role: bindinng model is null"); + } + Name = model.Name; + } + } +} \ No newline at end of file diff --git a/DatabaseImplement/Models/User.cs b/DatabaseImplement/Models/User.cs new file mode 100644 index 0000000..eea9542 --- /dev/null +++ b/DatabaseImplement/Models/User.cs @@ -0,0 +1,76 @@ +using Contracts.BindingModels; +using Contracts.ViewModels; +using DataModels.Models; +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DatabaseImplement.Models +{ + public class User : IUser + { + public Guid Id { get; set; } + + [Required] + public string FirstName { get; set; } = string.Empty; + + [Required] + public string SecondName { get; set; } = string.Empty; + + [Required] + public string PasswordHash { get; set; } = string.Empty; + + [Required] + public string Email { get; set; } = string.Empty; + + [Required] + public DateTime Birthday { get; set; } + + public Role? Role { get; set; } + + public UserBindingModel GetBindingModel() + { + // TODO: get binding with a role by database contetxt + throw new NotImplementedException(); + } + + public static User ToUserFromView(UserViewModel model) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + Birthday = model.Birthday, + Role = Models.Role.ToRoleFromView(model.Role) + }; + + public static User ToUserFromBinding(UserBindingModel model) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + PasswordHash = model.PasswordHash, + Birthday = model.Birthday, + Role = Models.Role.ToRoleFromBinding(model.Role) + }; + + public void Update(UserBindingModel model) + { + if (model is null) + { + throw new ArgumentNullException("Update user: binding model is null"); + } + + Email = model.Email; + FirstName = model.FirstName; + SecondName = model.SecondName; + PasswordHash = model.PasswordHash; + Birthday = model.Birthday; + Role = Models.Role.ToRoleFromBinding(model.Role); + } + } +} \ No newline at end of file -- 2.25.1 From d964ec43831be6816f8dccb0337301c2326ac0f1 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 4 Jun 2024 22:26:13 +0400 Subject: [PATCH 04/22] Storage contracts, implements and small fix --- Contracts/Contracts.csproj | 1 - Contracts/StorageContracts/IRoleStorage.cs | 24 ++++++ Contracts/StorageContracts/IUserStorage.cs | 23 +++++ DatabaseImplement/DatabaseImplement.csproj | 4 - DatabaseImplement/Implements/RoleStorage.cs | 92 ++++++++++++++++++++ DatabaseImplement/Implements/UserStorage.cs | 93 +++++++++++++++++++++ DatabaseImplement/Models/User.cs | 27 +++++- 7 files changed, 255 insertions(+), 9 deletions(-) create mode 100644 Contracts/StorageContracts/IRoleStorage.cs create mode 100644 Contracts/StorageContracts/IUserStorage.cs create mode 100644 DatabaseImplement/Implements/RoleStorage.cs create mode 100644 DatabaseImplement/Implements/UserStorage.cs diff --git a/Contracts/Contracts.csproj b/Contracts/Contracts.csproj index 64eaa86..9dc31be 100644 --- a/Contracts/Contracts.csproj +++ b/Contracts/Contracts.csproj @@ -7,7 +7,6 @@ - diff --git a/Contracts/StorageContracts/IRoleStorage.cs b/Contracts/StorageContracts/IRoleStorage.cs new file mode 100644 index 0000000..d8b7f15 --- /dev/null +++ b/Contracts/StorageContracts/IRoleStorage.cs @@ -0,0 +1,24 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.StorageContracts +{ + public interface IRoleStorage + { + RoleBindingModel? Insert(RoleBindingModel model); + + IEnumerable GetList(RoleSearchModel? model); + + RoleBindingModel? GetElement(RoleSearchModel model); + + RoleBindingModel? Update(RoleBindingModel model); + + RoleBindingModel? Delete(RoleSearchModel model); + } +} \ No newline at end of file diff --git a/Contracts/StorageContracts/IUserStorage.cs b/Contracts/StorageContracts/IUserStorage.cs new file mode 100644 index 0000000..3a38799 --- /dev/null +++ b/Contracts/StorageContracts/IUserStorage.cs @@ -0,0 +1,23 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.StorageContracts +{ + public interface IUserStorage + { + UserBindingModel? Insert(UserBindingModel model); + + IEnumerable GetList(UserSearchModel? model); + + UserBindingModel? GetElement(UserSearchModel model); + + UserBindingModel? Update(UserBindingModel model); + + UserBindingModel? Delete(UserSearchModel model); + } +} \ No newline at end of file diff --git a/DatabaseImplement/DatabaseImplement.csproj b/DatabaseImplement/DatabaseImplement.csproj index 861b25f..2929944 100644 --- a/DatabaseImplement/DatabaseImplement.csproj +++ b/DatabaseImplement/DatabaseImplement.csproj @@ -6,10 +6,6 @@ enable - - - - all diff --git a/DatabaseImplement/Implements/RoleStorage.cs b/DatabaseImplement/Implements/RoleStorage.cs new file mode 100644 index 0000000..976c5b7 --- /dev/null +++ b/DatabaseImplement/Implements/RoleStorage.cs @@ -0,0 +1,92 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.StorageContracts; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DatabaseImplement.Implements +{ + public class RoleStorage : IRoleStorage + { + public RoleBindingModel? Delete(RoleSearchModel model) + { + if (model is null || model.Id is null) + { + return null; + } + + var context = new Database(); + var role = context.Roles.FirstOrDefault(r => r.Id == model.Id); + if (role is null) + { + return null; + } + + context.Remove(role); + context.SaveChanges(); + return role.GetBindingModel(); + } + + public RoleBindingModel? GetElement(RoleSearchModel model) + { + if (model is null || model.Id is null) + { + return null; + } + var context = new Database(); + return context.Roles + .FirstOrDefault(r => r.Id == model.Id) + ?.GetBindingModel(); + } + + public IEnumerable GetList(RoleSearchModel? model) + { + var context = new Database(); + if (model is null) + { + return context.Roles.Select(r => r.GetBindingModel()); + } + if (model.Id is null) + { + return []; + } + return context.Roles + .Where(r => r.Id == model.Id) + .Select(r => r.GetBindingModel()); + } + + public RoleBindingModel? Insert(RoleBindingModel model) + { + if (model is null) + { + return null; + } + var context = new Database(); + var newRole = Models.Role.ToRoleFromBinding(model); + + context.Roles.Add(newRole); + context.SaveChanges(); + + return newRole.GetBindingModel(); + } + + public RoleBindingModel? Update(RoleBindingModel model) + { + var context = new Database(); + var role = context.Roles.FirstOrDefault(r => r.Id == model.Id); + + if (role is null) + { + return null; + } + + role.Update(model); + + context.SaveChanges(); + return role.GetBindingModel(); + } + } +} \ No newline at end of file diff --git a/DatabaseImplement/Implements/UserStorage.cs b/DatabaseImplement/Implements/UserStorage.cs new file mode 100644 index 0000000..a9bdfc5 --- /dev/null +++ b/DatabaseImplement/Implements/UserStorage.cs @@ -0,0 +1,93 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.StorageContracts; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace DatabaseImplement.Implements +{ + public class UserStorage : IUserStorage + { + public UserBindingModel? Delete(UserSearchModel model) + { + if (model is null || (model.Id is null && model.Email is null)) + { + return null; + } + + var context = new Database(); + var user = context.Users.FirstOrDefault(u => u.Equals(model)); + + if (user is null) + { + return null; + } + context.Remove(user); + context.SaveChanges(); + + return user.GetBindingModel(); + } + + public UserBindingModel? GetElement(UserSearchModel model) + { + if (model is null || (model.Id is null && model.Email is null)) + { + return null; + } + var context = new Database(); + return context.Users + .FirstOrDefault(u => u.Equals(model)) + ?.GetBindingModel(); + } + + public IEnumerable GetList(UserSearchModel? model) + { + var context = new Database(); + if (model is null) + { + return context.Users.Select(r => r.GetBindingModel()); + } + if (model.Id is null && model.Email is null) + { + return []; + } + return context.Users + .Where(u => u.Equals(model)) + .Select(r => r.GetBindingModel()); + } + + public UserBindingModel? Insert(UserBindingModel model) + { + if (model is null) + { + return null; + } + var context = new Database(); + var newUser = Models.User.ToUserFromBinding(model); + + context.Users.Add(newUser); + context.SaveChanges(); + + return newUser.GetBindingModel(); + } + + public UserBindingModel? Update(UserBindingModel model) + { + var context = new Database(); + var user = context.Users.FirstOrDefault(u => u.Id == model.Id); + + if (user is null) + { + return null; + } + + user.Update(model); + + context.SaveChanges(); + return user.GetBindingModel(); + } + } +} \ No newline at end of file diff --git a/DatabaseImplement/Models/User.cs b/DatabaseImplement/Models/User.cs index eea9542..00dcad9 100644 --- a/DatabaseImplement/Models/User.cs +++ b/DatabaseImplement/Models/User.cs @@ -1,4 +1,5 @@ using Contracts.BindingModels; +using Contracts.SearchModels; using Contracts.ViewModels; using DataModels.Models; using System; @@ -31,11 +32,16 @@ namespace DatabaseImplement.Models public Role? Role { get; set; } - public UserBindingModel GetBindingModel() + public UserBindingModel GetBindingModel() => new() { - // TODO: get binding with a role by database contetxt - throw new NotImplementedException(); - } + Id = Id, + FirstName = FirstName, + SecondName = SecondName, + Email = Email, + PasswordHash = PasswordHash, + Birthday = Birthday, + Role = Role?.GetBindingModel() ?? new() + }; public static User ToUserFromView(UserViewModel model) => new() { @@ -72,5 +78,18 @@ namespace DatabaseImplement.Models Birthday = model.Birthday; Role = Models.Role.ToRoleFromBinding(model.Role); } + + public bool Equals(UserSearchModel model) + { + if (model.Id is null) + { + return Email.Contains(model.Email!); + } + if (string.IsNullOrWhiteSpace(model.Email)) + { + return Id == model.Id; + } + return false; + } } } \ No newline at end of file -- 2.25.1 From ea9a89ac747065c3cab1ef19358e9542b2d071b6 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 4 Jun 2024 22:52:01 +0400 Subject: [PATCH 05/22] Add registration_init migration --- ...240604184007_registration_init.Designer.cs | 89 +++++++++++++++++++ .../20240604184007_registration_init.cs | 64 +++++++++++++ .../Migrations/DatabaseModelSnapshot.cs | 86 ++++++++++++++++++ RestAPI/RestAPI.csproj | 4 + 4 files changed, 243 insertions(+) create mode 100644 DatabaseImplement/Migrations/20240604184007_registration_init.Designer.cs create mode 100644 DatabaseImplement/Migrations/20240604184007_registration_init.cs create mode 100644 DatabaseImplement/Migrations/DatabaseModelSnapshot.cs diff --git a/DatabaseImplement/Migrations/20240604184007_registration_init.Designer.cs b/DatabaseImplement/Migrations/20240604184007_registration_init.Designer.cs new file mode 100644 index 0000000..3dbe69b --- /dev/null +++ b/DatabaseImplement/Migrations/20240604184007_registration_init.Designer.cs @@ -0,0 +1,89 @@ +// +using System; +using DatabaseImplement; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace DatabaseImplement.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20240604184007_registration_init")] + partial class registration_init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("DatabaseImplement.Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Birthday") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("SecondName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.User", b => + { + b.HasOne("DatabaseImplement.Models.Role", "Role") + .WithMany() + .HasForeignKey("RoleId"); + + b.Navigation("Role"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/DatabaseImplement/Migrations/20240604184007_registration_init.cs b/DatabaseImplement/Migrations/20240604184007_registration_init.cs new file mode 100644 index 0000000..8f81df4 --- /dev/null +++ b/DatabaseImplement/Migrations/20240604184007_registration_init.cs @@ -0,0 +1,64 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace DatabaseImplement.Migrations +{ + /// + public partial class registration_init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Roles", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "text", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Roles", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Users", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + FirstName = table.Column(type: "text", nullable: false), + SecondName = table.Column(type: "text", nullable: false), + PasswordHash = table.Column(type: "text", nullable: false), + Email = table.Column(type: "text", nullable: false), + Birthday = table.Column(type: "timestamp with time zone", nullable: false), + RoleId = table.Column(type: "uuid", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + table.ForeignKey( + name: "FK_Users_Roles_RoleId", + column: x => x.RoleId, + principalTable: "Roles", + principalColumn: "Id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_Users_RoleId", + table: "Users", + column: "RoleId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Users"); + + migrationBuilder.DropTable( + name: "Roles"); + } + } +} diff --git a/DatabaseImplement/Migrations/DatabaseModelSnapshot.cs b/DatabaseImplement/Migrations/DatabaseModelSnapshot.cs new file mode 100644 index 0000000..875158d --- /dev/null +++ b/DatabaseImplement/Migrations/DatabaseModelSnapshot.cs @@ -0,0 +1,86 @@ +// +using System; +using DatabaseImplement; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace DatabaseImplement.Migrations +{ + [DbContext(typeof(Database))] + partial class DatabaseModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.6") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("DatabaseImplement.Models.Role", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.ToTable("Roles"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Birthday") + .HasColumnType("timestamp with time zone"); + + b.Property("Email") + .IsRequired() + .HasColumnType("text"); + + b.Property("FirstName") + .IsRequired() + .HasColumnType("text"); + + b.Property("PasswordHash") + .IsRequired() + .HasColumnType("text"); + + b.Property("RoleId") + .HasColumnType("uuid"); + + b.Property("SecondName") + .IsRequired() + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("RoleId"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("DatabaseImplement.Models.User", b => + { + b.HasOne("DatabaseImplement.Models.Role", "Role") + .WithMany() + .HasForeignKey("RoleId"); + + b.Navigation("Role"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RestAPI/RestAPI.csproj b/RestAPI/RestAPI.csproj index 9daa180..2cf74c4 100644 --- a/RestAPI/RestAPI.csproj +++ b/RestAPI/RestAPI.csproj @@ -7,6 +7,10 @@ + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + -- 2.25.1 From c3dd4384ffd294b118d3e3dfb2d33bdd87956944 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 4 Jun 2024 22:52:59 +0400 Subject: [PATCH 06/22] fix user storage --- DatabaseImplement/Implements/UserStorage.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/DatabaseImplement/Implements/UserStorage.cs b/DatabaseImplement/Implements/UserStorage.cs index a9bdfc5..7622895 100644 --- a/DatabaseImplement/Implements/UserStorage.cs +++ b/DatabaseImplement/Implements/UserStorage.cs @@ -1,6 +1,8 @@ using Contracts.BindingModels; using Contracts.SearchModels; using Contracts.StorageContracts; +using DatabaseImplement.Models; +using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; @@ -39,6 +41,7 @@ namespace DatabaseImplement.Implements } var context = new Database(); return context.Users + .Include(u => u.Role) .FirstOrDefault(u => u.Equals(model)) ?.GetBindingModel(); } @@ -48,7 +51,9 @@ namespace DatabaseImplement.Implements var context = new Database(); if (model is null) { - return context.Users.Select(r => r.GetBindingModel()); + return context.Users + .Include(u => u.Role) + .Select(r => r.GetBindingModel()); } if (model.Id is null && model.Email is null) { @@ -56,6 +61,7 @@ namespace DatabaseImplement.Implements } return context.Users .Where(u => u.Equals(model)) + .Include(u => u.Role) .Select(r => r.GetBindingModel()); } @@ -77,7 +83,9 @@ namespace DatabaseImplement.Implements public UserBindingModel? Update(UserBindingModel model) { var context = new Database(); - var user = context.Users.FirstOrDefault(u => u.Id == model.Id); + var user = context.Users + .Include(u => u.Role) + .FirstOrDefault(u => u.Id == model.Id); if (user is null) { -- 2.25.1 From 51d26c58d7fb1733ee7b923e7315551afb02f52f Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 4 Jun 2024 23:43:15 +0400 Subject: [PATCH 07/22] view binding converters --- Contracts/Converters/RoleConverter.cs | 25 ++++++++++++++++++++ Contracts/Converters/UserConverter.cs | 33 +++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 Contracts/Converters/RoleConverter.cs create mode 100644 Contracts/Converters/UserConverter.cs diff --git a/Contracts/Converters/RoleConverter.cs b/Contracts/Converters/RoleConverter.cs new file mode 100644 index 0000000..37aa7e0 --- /dev/null +++ b/Contracts/Converters/RoleConverter.cs @@ -0,0 +1,25 @@ +using Contracts.BindingModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.Converters +{ + public static class RoleConverter + { + public static RoleViewModel ToView(RoleBindingModel model) => new() + { + Id = model.Id, + Name = model.Name, + }; + + public static RoleBindingModel ToBinding(RoleViewModel model) => new() + { + Id = model.Id, + Name = model.Name, + }; + } +} \ No newline at end of file diff --git a/Contracts/Converters/UserConverter.cs b/Contracts/Converters/UserConverter.cs new file mode 100644 index 0000000..b951d11 --- /dev/null +++ b/Contracts/Converters/UserConverter.cs @@ -0,0 +1,33 @@ +using Contracts.BindingModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.Converters +{ + public static class UserConverter + { + public static UserViewModel ToView(UserBindingModel model) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + Birthday = model.Birthday, + Role = RoleConverter.ToView(model.Role), + }; + + public static UserBindingModel ToBinding(UserViewModel model) => new() + { + Id = model.Id, + FirstName = model.FirstName, + SecondName = model.SecondName, + Email = model.Email, + Birthday = model.Birthday, + Role = RoleConverter.ToBinding(model.Role), + }; + } +} \ No newline at end of file -- 2.25.1 From be9c47aefa4c754f643769162f67bb793599a526 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 4 Jun 2024 23:43:52 +0400 Subject: [PATCH 08/22] fix storages --- DatabaseImplement/Implements/RoleStorage.cs | 8 ++------ DatabaseImplement/Implements/UserStorage.cs | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/DatabaseImplement/Implements/RoleStorage.cs b/DatabaseImplement/Implements/RoleStorage.cs index 976c5b7..4394a59 100644 --- a/DatabaseImplement/Implements/RoleStorage.cs +++ b/DatabaseImplement/Implements/RoleStorage.cs @@ -13,7 +13,7 @@ namespace DatabaseImplement.Implements { public RoleBindingModel? Delete(RoleSearchModel model) { - if (model is null || model.Id is null) + if (model.Id is null) { return null; } @@ -32,7 +32,7 @@ namespace DatabaseImplement.Implements public RoleBindingModel? GetElement(RoleSearchModel model) { - if (model is null || model.Id is null) + if (model.Id is null) { return null; } @@ -60,10 +60,6 @@ namespace DatabaseImplement.Implements public RoleBindingModel? Insert(RoleBindingModel model) { - if (model is null) - { - return null; - } var context = new Database(); var newRole = Models.Role.ToRoleFromBinding(model); diff --git a/DatabaseImplement/Implements/UserStorage.cs b/DatabaseImplement/Implements/UserStorage.cs index 7622895..59a8a2a 100644 --- a/DatabaseImplement/Implements/UserStorage.cs +++ b/DatabaseImplement/Implements/UserStorage.cs @@ -15,7 +15,7 @@ namespace DatabaseImplement.Implements { public UserBindingModel? Delete(UserSearchModel model) { - if (model is null || (model.Id is null && model.Email is null)) + if (model.Id is null && model.Email is null) { return null; } @@ -35,7 +35,7 @@ namespace DatabaseImplement.Implements public UserBindingModel? GetElement(UserSearchModel model) { - if (model is null || (model.Id is null && model.Email is null)) + if (model.Id is null && model.Email is null) { return null; } @@ -67,10 +67,6 @@ namespace DatabaseImplement.Implements public UserBindingModel? Insert(UserBindingModel model) { - if (model is null) - { - return null; - } var context = new Database(); var newUser = Models.User.ToUserFromBinding(model); -- 2.25.1 From 72e40c79e642a3f28973761ddced4c0cc1ca6329 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Tue, 4 Jun 2024 23:44:36 +0400 Subject: [PATCH 09/22] Logic contracts and implements --- BusinessLogic/BusinessLogic.csproj | 6 +- BusinessLogic/BusinessLogic/RoleLogic.cs | 99 +++++++++++++++++++ BusinessLogic/BusinessLogic/UserLogic.cs | 99 +++++++++++++++++++ .../BusinessLogicContracts/IRoleLogic.cs | 24 +++++ .../BusinessLogicContracts/IUserLogic.cs | 24 +++++ Contracts/Contracts.csproj | 4 - 6 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 BusinessLogic/BusinessLogic/RoleLogic.cs create mode 100644 BusinessLogic/BusinessLogic/UserLogic.cs create mode 100644 Contracts/BusinessLogicContracts/IRoleLogic.cs create mode 100644 Contracts/BusinessLogicContracts/IUserLogic.cs diff --git a/BusinessLogic/BusinessLogic.csproj b/BusinessLogic/BusinessLogic.csproj index d0efbf0..8e23e43 100644 --- a/BusinessLogic/BusinessLogic.csproj +++ b/BusinessLogic/BusinessLogic.csproj @@ -7,7 +7,11 @@ - + + + + + diff --git a/BusinessLogic/BusinessLogic/RoleLogic.cs b/BusinessLogic/BusinessLogic/RoleLogic.cs new file mode 100644 index 0000000..dc758e5 --- /dev/null +++ b/BusinessLogic/BusinessLogic/RoleLogic.cs @@ -0,0 +1,99 @@ +using Contracts.BindingModels; +using Contracts.BusinessLogicContracts; +using Contracts.Converters; +using Contracts.SearchModels; +using Contracts.StorageContracts; +using Contracts.ViewModels; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.BusinessLogic +{ + public class RoleLogic : IRoleLogic + { + private readonly ILogger _logger; + private readonly IRoleStorage _roleStorage; + + public RoleLogic(ILogger logger, IRoleStorage roleStorage) + { + _logger = logger; + _roleStorage = roleStorage; + } + + public RoleViewModel? Create(RoleBindingModel model) + { + ArgumentNullException.ThrowIfNull(model); + + var role = _roleStorage.Insert(model); + if (role is null) + { + _logger.LogWarning("Insert operation failed."); + return null; + } + + return RoleConverter.ToView(role); + } + + public RoleViewModel? Delete(RoleSearchModel model) + { + ArgumentNullException.ThrowIfNull(model); + + _logger.LogInformation("Delete role. Id: {0}", model.Id); + var role = _roleStorage.Delete(model); + if (role is null) + { + _logger.LogWarning("Delete operation failed."); + return null; + } + + return RoleConverter.ToView(role); + } + + public RoleViewModel? ReadElement(RoleSearchModel model) + { + ArgumentNullException.ThrowIfNull(model); + + _logger.LogInformation("ReadElement. Id: {0}", model.Id); + var role = _roleStorage.GetElement(model); + if (role is null) + { + _logger.LogWarning("ReadElement element not found"); + return null; + } + _logger.LogInformation("ReadElement find. Id: {0}", role.Id); + + return RoleConverter.ToView(role); + } + + public IEnumerable ReadElements(RoleSearchModel? model) + { + _logger.LogInformation("ReadList. Id: {Id}", model?.Id); + var list = _roleStorage.GetList(model); + if (list is null || list.Count() == 0) + { + _logger.LogWarning("ReadList return null list"); + return []; + } + _logger.LogInformation("ReadList. Count: {Count}", list.Count()); + + return list.Select(RoleConverter.ToView); + } + + public RoleViewModel? Update(RoleBindingModel model) + { + ArgumentNullException.ThrowIfNull(model); + + var role = _roleStorage.Update(model); + if (role is null) + { + _logger.LogWarning("Update operation failed."); + return null; + } + return RoleConverter.ToView(role); + } + } +} \ No newline at end of file diff --git a/BusinessLogic/BusinessLogic/UserLogic.cs b/BusinessLogic/BusinessLogic/UserLogic.cs new file mode 100644 index 0000000..34e69b4 --- /dev/null +++ b/BusinessLogic/BusinessLogic/UserLogic.cs @@ -0,0 +1,99 @@ +using Contracts.BindingModels; +using Contracts.BusinessLogicContracts; +using Contracts.Converters; +using Contracts.SearchModels; +using Contracts.StorageContracts; +using Contracts.ViewModels; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.BusinessLogic +{ + public class UserLogic : IUserLogic + { + private readonly ILogger _logger; + private readonly IUserStorage _userStorage; + + public UserLogic(ILogger logger, IUserStorage userStorage) + { + _logger = logger; + _userStorage = userStorage; + } + + public UserViewModel? Create(UserBindingModel model) + { + ArgumentNullException.ThrowIfNull(model); + + var user = _userStorage.Insert(model); + if (user is null) + { + _logger.LogWarning("Insert operation failed."); + return null; + } + + return UserConverter.ToView(user); + } + + public UserViewModel? Delete(UserSearchModel model) + { + ArgumentNullException.ThrowIfNull(model); + + _logger.LogInformation("Delete user. Id: {0}", model.Id); + var user = _userStorage.Delete(model); + if (user is null) + { + _logger.LogWarning("Delete operation failed."); + return null; + } + + return UserConverter.ToView(user); + } + + public IEnumerable ReadElements(UserSearchModel? model) + { + _logger.LogInformation("ReadList. Id: {Id}", model?.Id); + var list = _userStorage.GetList(model); + if (list is null || list.Count() == 0) + { + _logger.LogWarning("ReadList return null list"); + return []; + } + _logger.LogInformation("ReadList. Count: {Count}", list.Count()); + + return list.Select(UserConverter.ToView); + } + + public UserViewModel? ReadElement(UserSearchModel model) + { + ArgumentNullException.ThrowIfNull(model); + + _logger.LogInformation("ReadElement. Id: {0}", model.Id); + var user = _userStorage.GetElement(model); + if (user is null) + { + _logger.LogWarning("ReadElement element not found"); + return null; + } + _logger.LogInformation("ReadElement find. Id: {0}", user.Id); + + return UserConverter.ToView(user); + } + + public UserViewModel? Update(UserBindingModel model) + { + ArgumentNullException.ThrowIfNull(model); + + var user = _userStorage.Update(model); + if (user is null) + { + _logger.LogWarning("Update operation failed."); + return null; + } + return UserConverter.ToView(user); + } + } +} \ No newline at end of file diff --git a/Contracts/BusinessLogicContracts/IRoleLogic.cs b/Contracts/BusinessLogicContracts/IRoleLogic.cs new file mode 100644 index 0000000..3e6c340 --- /dev/null +++ b/Contracts/BusinessLogicContracts/IRoleLogic.cs @@ -0,0 +1,24 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.BusinessLogicContracts +{ + public interface IRoleLogic + { + RoleViewModel? Create(RoleBindingModel model); + + RoleViewModel? Update(RoleBindingModel model); + + RoleViewModel? ReadElement(RoleSearchModel model); + + IEnumerable ReadElements(RoleSearchModel? model); + + RoleViewModel? Delete(RoleSearchModel model); + } +} \ No newline at end of file diff --git a/Contracts/BusinessLogicContracts/IUserLogic.cs b/Contracts/BusinessLogicContracts/IUserLogic.cs new file mode 100644 index 0000000..2ee38bf --- /dev/null +++ b/Contracts/BusinessLogicContracts/IUserLogic.cs @@ -0,0 +1,24 @@ +using Contracts.BindingModels; +using Contracts.SearchModels; +using Contracts.ViewModels; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.BusinessLogicContracts +{ + public interface IUserLogic + { + UserViewModel? Create(UserBindingModel model); + + UserViewModel? Update(UserBindingModel model); + + UserViewModel? ReadElement(UserSearchModel model); + + IEnumerable ReadElements(UserSearchModel? model); + + UserViewModel? Delete(UserSearchModel model); + } +} \ No newline at end of file diff --git a/Contracts/Contracts.csproj b/Contracts/Contracts.csproj index 9dc31be..fa71b7a 100644 --- a/Contracts/Contracts.csproj +++ b/Contracts/Contracts.csproj @@ -6,8 +6,4 @@ enable - - - - -- 2.25.1 From 23d3ba888583fb5078e07256b8c4cbbf35880890 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 5 Jun 2024 01:22:13 +0400 Subject: [PATCH 10/22] fix logic --- BusinessLogic/BusinessLogic/RoleLogic.cs | 21 +++++++++------------ BusinessLogic/BusinessLogic/UserLogic.cs | 21 +++++++++------------ 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/BusinessLogic/BusinessLogic/RoleLogic.cs b/BusinessLogic/BusinessLogic/RoleLogic.cs index dc758e5..9be0c6c 100644 --- a/BusinessLogic/BusinessLogic/RoleLogic.cs +++ b/BusinessLogic/BusinessLogic/RoleLogic.cs @@ -1,6 +1,7 @@ using Contracts.BindingModels; using Contracts.BusinessLogicContracts; using Contracts.Converters; +using Contracts.Exceptions; using Contracts.SearchModels; using Contracts.StorageContracts; using Contracts.ViewModels; @@ -24,21 +25,20 @@ namespace BusinessLogic.BusinessLogic _roleStorage = roleStorage; } - public RoleViewModel? Create(RoleBindingModel model) + public RoleViewModel Create(RoleBindingModel model) { ArgumentNullException.ThrowIfNull(model); var role = _roleStorage.Insert(model); if (role is null) { - _logger.LogWarning("Insert operation failed."); - return null; + throw new Exception("Insert operation failed."); } return RoleConverter.ToView(role); } - public RoleViewModel? Delete(RoleSearchModel model) + public RoleViewModel Delete(RoleSearchModel model) { ArgumentNullException.ThrowIfNull(model); @@ -46,14 +46,13 @@ namespace BusinessLogic.BusinessLogic var role = _roleStorage.Delete(model); if (role is null) { - _logger.LogWarning("Delete operation failed."); - return null; + throw new Exception("Delete operation failed."); } return RoleConverter.ToView(role); } - public RoleViewModel? ReadElement(RoleSearchModel model) + public RoleViewModel ReadElement(RoleSearchModel model) { ArgumentNullException.ThrowIfNull(model); @@ -61,8 +60,7 @@ namespace BusinessLogic.BusinessLogic var role = _roleStorage.GetElement(model); if (role is null) { - _logger.LogWarning("ReadElement element not found"); - return null; + throw new ElementNotFoundException(); } _logger.LogInformation("ReadElement find. Id: {0}", role.Id); @@ -83,15 +81,14 @@ namespace BusinessLogic.BusinessLogic return list.Select(RoleConverter.ToView); } - public RoleViewModel? Update(RoleBindingModel model) + public RoleViewModel Update(RoleBindingModel model) { ArgumentNullException.ThrowIfNull(model); var role = _roleStorage.Update(model); if (role is null) { - _logger.LogWarning("Update operation failed."); - return null; + throw new Exception("Update operation failed."); } return RoleConverter.ToView(role); } diff --git a/BusinessLogic/BusinessLogic/UserLogic.cs b/BusinessLogic/BusinessLogic/UserLogic.cs index 34e69b4..d7fbc33 100644 --- a/BusinessLogic/BusinessLogic/UserLogic.cs +++ b/BusinessLogic/BusinessLogic/UserLogic.cs @@ -1,6 +1,7 @@ using Contracts.BindingModels; using Contracts.BusinessLogicContracts; using Contracts.Converters; +using Contracts.Exceptions; using Contracts.SearchModels; using Contracts.StorageContracts; using Contracts.ViewModels; @@ -24,21 +25,20 @@ namespace BusinessLogic.BusinessLogic _userStorage = userStorage; } - public UserViewModel? Create(UserBindingModel model) + public UserViewModel Create(UserBindingModel model) { ArgumentNullException.ThrowIfNull(model); var user = _userStorage.Insert(model); if (user is null) { - _logger.LogWarning("Insert operation failed."); - return null; + throw new Exception("Insert operation failed."); } return UserConverter.ToView(user); } - public UserViewModel? Delete(UserSearchModel model) + public UserViewModel Delete(UserSearchModel model) { ArgumentNullException.ThrowIfNull(model); @@ -46,8 +46,7 @@ namespace BusinessLogic.BusinessLogic var user = _userStorage.Delete(model); if (user is null) { - _logger.LogWarning("Delete operation failed."); - return null; + throw new Exception("Update operation failed."); } return UserConverter.ToView(user); @@ -67,7 +66,7 @@ namespace BusinessLogic.BusinessLogic return list.Select(UserConverter.ToView); } - public UserViewModel? ReadElement(UserSearchModel model) + public UserViewModel ReadElement(UserSearchModel model) { ArgumentNullException.ThrowIfNull(model); @@ -75,23 +74,21 @@ namespace BusinessLogic.BusinessLogic var user = _userStorage.GetElement(model); if (user is null) { - _logger.LogWarning("ReadElement element not found"); - return null; + throw new ElementNotFoundException(); } _logger.LogInformation("ReadElement find. Id: {0}", user.Id); return UserConverter.ToView(user); } - public UserViewModel? Update(UserBindingModel model) + public UserViewModel Update(UserBindingModel model) { ArgumentNullException.ThrowIfNull(model); var user = _userStorage.Update(model); if (user is null) { - _logger.LogWarning("Update operation failed."); - return null; + throw new Exception("Update operation failed."); } return UserConverter.ToView(user); } -- 2.25.1 From 9c7ada226088d1315af7650e4b2d163ad9abcad8 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 5 Jun 2024 01:22:44 +0400 Subject: [PATCH 11/22] fix logic contracts --- Contracts/BusinessLogicContracts/IRoleLogic.cs | 8 ++++---- Contracts/BusinessLogicContracts/IUserLogic.cs | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Contracts/BusinessLogicContracts/IRoleLogic.cs b/Contracts/BusinessLogicContracts/IRoleLogic.cs index 3e6c340..99e24dc 100644 --- a/Contracts/BusinessLogicContracts/IRoleLogic.cs +++ b/Contracts/BusinessLogicContracts/IRoleLogic.cs @@ -11,14 +11,14 @@ namespace Contracts.BusinessLogicContracts { public interface IRoleLogic { - RoleViewModel? Create(RoleBindingModel model); + RoleViewModel Create(RoleBindingModel model); - RoleViewModel? Update(RoleBindingModel model); + RoleViewModel Update(RoleBindingModel model); - RoleViewModel? ReadElement(RoleSearchModel model); + RoleViewModel ReadElement(RoleSearchModel model); IEnumerable ReadElements(RoleSearchModel? model); - RoleViewModel? Delete(RoleSearchModel model); + RoleViewModel Delete(RoleSearchModel model); } } \ No newline at end of file diff --git a/Contracts/BusinessLogicContracts/IUserLogic.cs b/Contracts/BusinessLogicContracts/IUserLogic.cs index 2ee38bf..c48fddb 100644 --- a/Contracts/BusinessLogicContracts/IUserLogic.cs +++ b/Contracts/BusinessLogicContracts/IUserLogic.cs @@ -11,14 +11,14 @@ namespace Contracts.BusinessLogicContracts { public interface IUserLogic { - UserViewModel? Create(UserBindingModel model); + UserViewModel Create(UserBindingModel model); - UserViewModel? Update(UserBindingModel model); + UserViewModel Update(UserBindingModel model); - UserViewModel? ReadElement(UserSearchModel model); + UserViewModel ReadElement(UserSearchModel model); IEnumerable ReadElements(UserSearchModel? model); - UserViewModel? Delete(UserSearchModel model); + UserViewModel Delete(UserSearchModel model); } } \ No newline at end of file -- 2.25.1 From 57f90eaa8204bcfd8d4d989bc93870c189304b3c Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 5 Jun 2024 01:23:35 +0400 Subject: [PATCH 12/22] add element not found exception --- .../Exceptions/ElementNotFoundException.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Contracts/Exceptions/ElementNotFoundException.cs diff --git a/Contracts/Exceptions/ElementNotFoundException.cs b/Contracts/Exceptions/ElementNotFoundException.cs new file mode 100644 index 0000000..7fad056 --- /dev/null +++ b/Contracts/Exceptions/ElementNotFoundException.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.Exceptions +{ + public class ElementNotFoundException : Exception + { + public ElementNotFoundException() : base("Element not founded.") + { + } + + public ElementNotFoundException(string message) : base(message) + { + } + + public ElementNotFoundException(string message, Exception inner) : base(message, inner) + { + } + } +} \ No newline at end of file -- 2.25.1 From 681118ff917ef25c4068cfd76ca099fbcb5c10c4 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 5 Jun 2024 01:25:18 +0400 Subject: [PATCH 13/22] Setup RestAPI, added role controller --- RestAPI/Controllers/RoleController.cs | 120 ++++++++++++++++++ .../Controllers/WeatherForecastController.cs | 33 ----- RestAPI/Program.cs | 34 ++++- RestAPI/RestAPI.csproj | 13 ++ RestAPI/RestAPI.http | 2 +- RestAPI/WeatherForecast.cs | 13 -- RestAPI/log4net.config | 16 +++ 7 files changed, 179 insertions(+), 52 deletions(-) create mode 100644 RestAPI/Controllers/RoleController.cs delete mode 100644 RestAPI/Controllers/WeatherForecastController.cs delete mode 100644 RestAPI/WeatherForecast.cs create mode 100644 RestAPI/log4net.config diff --git a/RestAPI/Controllers/RoleController.cs b/RestAPI/Controllers/RoleController.cs new file mode 100644 index 0000000..0121b8b --- /dev/null +++ b/RestAPI/Controllers/RoleController.cs @@ -0,0 +1,120 @@ +using BusinessLogic.BusinessLogic; +using Contracts.BindingModels; +using Contracts.BusinessLogicContracts; +using Contracts.Exceptions; +using Contracts.SearchModels; +using Contracts.ViewModels; +using Microsoft.AspNetCore.Http.HttpResults; +using Microsoft.AspNetCore.Mvc; + +namespace RestAPI.Controllers +{ + [Route("[controller]/[action]")] + [ApiController] + public class RoleController + { + private readonly ILogger _logger; + private readonly IRoleLogic _roleLogic; + + public RoleController(ILogger logger, IRoleLogic roleLogic) + { + _logger = logger; + _roleLogic = roleLogic; + } + + [HttpGet] + public IResult Get([FromQuery] RoleSearchModel model) + { + try + { + var res = _roleLogic.ReadElement(model); + return Results.Ok(res); + } + catch (ElementNotFoundException ex) + { + _logger.LogInformation(ex, "Element not found"); + return Results.NoContent(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error get role"); + return Results.Problem(ex.Message); + } + } + + [HttpGet] + public IResult GeFilteredtList([FromQuery] RoleSearchModel model) + { + try + { + var res = _roleLogic.ReadElements(model).ToList(); + return Results.Ok(res); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error get list role"); + return Results.Problem(ex.Message); + } + } + + [HttpGet] + public IResult GetList() + { + try + { + var res = _roleLogic.ReadElements(null).ToList(); + return Results.Ok(res); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error get list role"); + return Results.Problem(ex.Message); + } + } + + [HttpPost] + public IResult Create([FromBody] RoleBindingModel model) + { + try + { + var res = _roleLogic.Create(model); + return Results.Created(string.Empty, res); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error create role"); + return Results.Problem(ex.Message); + } + } + + [HttpPut] + public IResult Update([FromBody] RoleBindingModel model) + { + try + { + var res = _roleLogic.Update(model); + return Results.Ok(res); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error update role"); + return Results.Problem(ex.Message); + } + } + + [HttpDelete] + public IResult Delete([FromQuery] RoleSearchModel model) + { + try + { + var res = _roleLogic.Delete(model); ; + return Results.Ok(res); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error delete role"); + return Results.Problem(ex.Message); + } + } + } +} \ No newline at end of file diff --git a/RestAPI/Controllers/WeatherForecastController.cs b/RestAPI/Controllers/WeatherForecastController.cs deleted file mode 100644 index 40741f2..0000000 --- a/RestAPI/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.AspNetCore.Mvc; - -namespace RestAPI.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet(Name = "GetWeatherForecast")] - public IEnumerable Get() - { - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = Summaries[Random.Shared.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} diff --git a/RestAPI/Program.cs b/RestAPI/Program.cs index 48863a6..6fbbd7c 100644 --- a/RestAPI/Program.cs +++ b/RestAPI/Program.cs @@ -1,19 +1,43 @@ +using BusinessLogic.BusinessLogic; +using Contracts.BusinessLogicContracts; +using Contracts.StorageContracts; +using DatabaseImplement.Implements; +using Microsoft.OpenApi.Models; +using System; + +const string VERSION = "v1"; +const string TITLE = "21GunsRestAPI"; + var builder = WebApplication.CreateBuilder(args); -// Add services to the container. +builder.Logging.SetMinimumLevel(LogLevel.Trace); +builder.Logging.AddLog4Net("log4net.config"); + +#region DI + +builder.Services.AddTransient(); +builder.Services.AddTransient(); + +builder.Services.AddTransient(); +builder.Services.AddTransient(); + +#endregion DI builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +builder.Services.AddSwaggerGen(c => +{ + c.SwaggerDoc(VERSION, new OpenApiInfo { Title = TITLE, Version = VERSION }); +}); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { - app.UseSwagger(); - app.UseSwaggerUI(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint($"/swagger/{VERSION}/swagger.json", $"{TITLE} {VERSION}")); } app.UseHttpsRedirection(); @@ -22,4 +46,4 @@ app.UseAuthorization(); app.MapControllers(); -app.Run(); +app.Run(); \ No newline at end of file diff --git a/RestAPI/RestAPI.csproj b/RestAPI/RestAPI.csproj index 2cf74c4..ee6f069 100644 --- a/RestAPI/RestAPI.csproj +++ b/RestAPI/RestAPI.csproj @@ -11,7 +11,20 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + Always + + + diff --git a/RestAPI/RestAPI.http b/RestAPI/RestAPI.http index ae5b1fd..cb9c1f5 100644 --- a/RestAPI/RestAPI.http +++ b/RestAPI/RestAPI.http @@ -1,6 +1,6 @@ @RestAPI_HostAddress = http://localhost:5168 -GET {{RestAPI_HostAddress}}/weatherforecast/ +GET {{RestAPI_HostAddress}}/api/ Accept: application/json ### diff --git a/RestAPI/WeatherForecast.cs b/RestAPI/WeatherForecast.cs deleted file mode 100644 index cf5ac94..0000000 --- a/RestAPI/WeatherForecast.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace RestAPI -{ - public class WeatherForecast - { - public DateOnly Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string? Summary { get; set; } - } -} diff --git a/RestAPI/log4net.config b/RestAPI/log4net.config new file mode 100644 index 0000000..0da4e4c --- /dev/null +++ b/RestAPI/log4net.config @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file -- 2.25.1 From 78e87103efb7edc67d623e737d82a9b38c14711d Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 5 Jun 2024 13:39:08 +0400 Subject: [PATCH 14/22] fix ip database (42) --- DatabaseImplement/Database.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DatabaseImplement/Database.cs b/DatabaseImplement/Database.cs index 99b74bf..7bb94c7 100644 --- a/DatabaseImplement/Database.cs +++ b/DatabaseImplement/Database.cs @@ -14,7 +14,7 @@ namespace DatabaseImplement { if (optionsBuilder.IsConfigured == false) { - optionsBuilder.UseNpgsql("Server=192.168.191.85:32768;Database=gun_market;Username=postgres;Password=7355608;"); + optionsBuilder.UseNpgsql("Server=192.168.191.42:32768;Database=gun_market;Username=postgres;Password=7355608;"); } base.OnConfiguring(optionsBuilder); } -- 2.25.1 From c902050055559b79de7ecb16b93ca842cd73f9e8 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 5 Jun 2024 15:13:27 +0400 Subject: [PATCH 15/22] Add password hasher --- BusinessLogic/Tools/PasswordHasher.cs | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 BusinessLogic/Tools/PasswordHasher.cs diff --git a/BusinessLogic/Tools/PasswordHasher.cs b/BusinessLogic/Tools/PasswordHasher.cs new file mode 100644 index 0000000..f73edca --- /dev/null +++ b/BusinessLogic/Tools/PasswordHasher.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace BusinessLogic.Tools +{ + internal class PasswordHasher + { + /// + /// Хеширует с использование SHA256 + /// + /// Пароль + /// Хеш пароля + public static string Hash(string password) + { + using (SHA256 sha256 = SHA256.Create()) + { + byte[] bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password)); + return Convert.ToBase64String(bytes); + } + } + + /// + /// Проверяет на соответствие пароля и его хеша + /// + /// Пароль + /// Хеш пароля + /// + public static bool Verify(string password, string passHash) + { + var hash = Hash(password); + return hash == passHash; + } + } +} \ No newline at end of file -- 2.25.1 From 4f472bce41bd0a98b7a7c32b8184c54bf6125ebe Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 5 Jun 2024 15:14:25 +0400 Subject: [PATCH 16/22] fix user binding, user logic contract and add account exception --- Contracts/BindingModels/UserBindingModel.cs | 1 + .../BusinessLogicContracts/IUserLogic.cs | 2 ++ Contracts/Exceptions/AccountException.cs | 23 +++++++++++++++++++ 3 files changed, 26 insertions(+) create mode 100644 Contracts/Exceptions/AccountException.cs diff --git a/Contracts/BindingModels/UserBindingModel.cs b/Contracts/BindingModels/UserBindingModel.cs index 7b6c7c6..d5e19ae 100644 --- a/Contracts/BindingModels/UserBindingModel.cs +++ b/Contracts/BindingModels/UserBindingModel.cs @@ -13,6 +13,7 @@ namespace Contracts.BindingModels public string SecondName { get; set; } = string.Empty; public string Email { get; set; } = string.Empty; public string PasswordHash { get; set; } = string.Empty; + public string? Password { get; set; } public DateTime Birthday { get; set; } public RoleBindingModel Role { get; set; } = null!; } diff --git a/Contracts/BusinessLogicContracts/IUserLogic.cs b/Contracts/BusinessLogicContracts/IUserLogic.cs index c48fddb..300e525 100644 --- a/Contracts/BusinessLogicContracts/IUserLogic.cs +++ b/Contracts/BusinessLogicContracts/IUserLogic.cs @@ -11,6 +11,8 @@ namespace Contracts.BusinessLogicContracts { public interface IUserLogic { + UserViewModel Login(UserBindingModel model); + UserViewModel Create(UserBindingModel model); UserViewModel Update(UserBindingModel model); diff --git a/Contracts/Exceptions/AccountException.cs b/Contracts/Exceptions/AccountException.cs new file mode 100644 index 0000000..1678eda --- /dev/null +++ b/Contracts/Exceptions/AccountException.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Contracts.Exceptions +{ + public class AccountException : Exception + { + public AccountException() + { + } + + public AccountException(string message) : base(message) + { + } + + public AccountException(string message, Exception inner) : base(message, inner) + { + } + } +} \ No newline at end of file -- 2.25.1 From 054ed4b4618614244a5a4294448dc280b1a79d34 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 5 Jun 2024 15:14:38 +0400 Subject: [PATCH 17/22] fix user logic --- BusinessLogic/BusinessLogic/UserLogic.cs | 42 ++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/BusinessLogic/BusinessLogic/UserLogic.cs b/BusinessLogic/BusinessLogic/UserLogic.cs index d7fbc33..73f8156 100644 --- a/BusinessLogic/BusinessLogic/UserLogic.cs +++ b/BusinessLogic/BusinessLogic/UserLogic.cs @@ -1,4 +1,5 @@ -using Contracts.BindingModels; +using BusinessLogic.Tools; +using Contracts.BindingModels; using Contracts.BusinessLogicContracts; using Contracts.Converters; using Contracts.Exceptions; @@ -28,7 +29,10 @@ namespace BusinessLogic.BusinessLogic public UserViewModel Create(UserBindingModel model) { ArgumentNullException.ThrowIfNull(model); - + // Проверяем пароль + _validatePassword(model.Password); + // Хешируем пароль + model.PasswordHash = PasswordHasher.Hash(model.Password); var user = _userStorage.Insert(model); if (user is null) { @@ -46,7 +50,7 @@ namespace BusinessLogic.BusinessLogic var user = _userStorage.Delete(model); if (user is null) { - throw new Exception("Update operation failed."); + throw new ElementNotFoundException(); } return UserConverter.ToView(user); @@ -85,6 +89,11 @@ namespace BusinessLogic.BusinessLogic { ArgumentNullException.ThrowIfNull(model); + if (model.Password is not null) + { + _validatePassword(model.Password); + model.PasswordHash = PasswordHasher.Hash(model.Password); + } var user = _userStorage.Update(model); if (user is null) { @@ -92,5 +101,32 @@ namespace BusinessLogic.BusinessLogic } return UserConverter.ToView(user); } + + public UserViewModel Login(UserBindingModel model) + { + ArgumentNullException.ThrowIfNull(model); + + var user = _userStorage.GetElement(new() { Email = model.Email }); + + if (user is null) + { + throw new ElementNotFoundException(); + } + // Проверяем пароль + _validatePassword(model.Password); + if (PasswordHasher.Verify(model.Password, user.PasswordHash)) + { + throw new AccountException("The passwords don't match."); + } + return UserConverter.ToView(user); + } + + public void _validatePassword(string? password) + { + if (string.IsNullOrWhiteSpace(password)) + { + throw new AccountException("The password is null."); + } + } } } \ No newline at end of file -- 2.25.1 From 0517a6cfb02954f51d81bd0a55e4c5f1f90a22d9 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Wed, 5 Jun 2024 15:14:55 +0400 Subject: [PATCH 18/22] fix role controller and add user controller --- RestAPI/Controllers/RoleController.cs | 4 +- RestAPI/Controllers/UserController.cs | 128 ++++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 RestAPI/Controllers/UserController.cs diff --git a/RestAPI/Controllers/RoleController.cs b/RestAPI/Controllers/RoleController.cs index 0121b8b..9f03335 100644 --- a/RestAPI/Controllers/RoleController.cs +++ b/RestAPI/Controllers/RoleController.cs @@ -32,7 +32,7 @@ namespace RestAPI.Controllers } catch (ElementNotFoundException ex) { - _logger.LogInformation(ex, "Element not found"); + _logger.LogInformation(ex, "Role not found"); return Results.NoContent(); } catch (Exception ex) @@ -87,7 +87,7 @@ namespace RestAPI.Controllers } } - [HttpPut] + [HttpPatch] public IResult Update([FromBody] RoleBindingModel model) { try diff --git a/RestAPI/Controllers/UserController.cs b/RestAPI/Controllers/UserController.cs new file mode 100644 index 0000000..f985b4d --- /dev/null +++ b/RestAPI/Controllers/UserController.cs @@ -0,0 +1,128 @@ +using BusinessLogic.BusinessLogic; +using Contracts.BindingModels; +using Contracts.BusinessLogicContracts; +using Contracts.Exceptions; +using Contracts.SearchModels; +using Microsoft.AspNetCore.Mvc; + +namespace RestAPI.Controllers +{ + [Route("[controller]/[action]")] + [ApiController] + public class UserController + { + private readonly ILogger _logger; + private readonly IUserLogic _userLogic; + + public UserController(ILogger logger, IUserLogic userLogic) + { + _userLogic = userLogic; + _logger = logger; + } + + [HttpPost] + public IResult Login([FromBody] UserBindingModel model) + { + try + { + var res = _userLogic.Login(model); + return Results.Ok(res); + } + catch (ElementNotFoundException ex) + { + _logger.LogInformation(ex, "User not found"); + return Results.NoContent(); + } + catch (AccountException ex) + { + _logger.LogWarning(ex, "Wrong login data"); + return Results.BadRequest(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error get user"); + return Results.Problem(ex.Message); + } + } + + [HttpPost] + public IResult Registration([FromBody] UserBindingModel model) + { + try + { + var res = _userLogic.Login(model); + return Results.Ok(res); + } + catch (AccountException ex) + { + _logger.LogWarning(ex, "Wrong registration data"); + return Results.BadRequest(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error create user"); + return Results.Problem(ex.Message); + } + } + + [HttpGet] + public IResult Get([FromQuery] UserSearchModel model) + { + try + { + var res = _userLogic.ReadElement(model); + return Results.Ok(res); + } + catch (ElementNotFoundException ex) + { + _logger.LogInformation(ex, "User not found"); + return Results.NoContent(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error get user"); + return Results.Problem(ex.Message); + } + } + + [HttpPatch] + public IResult Update([FromBody] UserBindingModel model) + { + try + { + var res = _userLogic.Update(model); + return Results.Ok(res); + } + catch (AccountException ex) + { + _logger.LogWarning(ex, "Wrong update data"); + return Results.BadRequest(ex.Message); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error update user"); + return Results.Problem(ex.Message); + } + } + + [HttpDelete] + public IResult Delete(Guid id) + { + try + { + var res = _userLogic.Delete(new() { Id = id }); + return Results.Ok(res); + } + catch (ElementNotFoundException ex) + { + _logger.LogInformation(ex, "User not found"); + return Results.NoContent(); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error delete user"); + return Results.Problem(ex.Message); + } + } + } +} \ No newline at end of file -- 2.25.1 From a2b58598a67c0e74f99ff1a27e045c7807e47783 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Thu, 6 Jun 2024 17:48:03 +0400 Subject: [PATCH 19/22] fix user logic contract and user view model --- Contracts/BusinessLogicContracts/IUserLogic.cs | 3 ++- Contracts/ViewModels/UserViewModel.cs | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Contracts/BusinessLogicContracts/IUserLogic.cs b/Contracts/BusinessLogicContracts/IUserLogic.cs index 300e525..fdcc510 100644 --- a/Contracts/BusinessLogicContracts/IUserLogic.cs +++ b/Contracts/BusinessLogicContracts/IUserLogic.cs @@ -3,6 +3,7 @@ using Contracts.SearchModels; using Contracts.ViewModels; using System; using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -11,7 +12,7 @@ namespace Contracts.BusinessLogicContracts { public interface IUserLogic { - UserViewModel Login(UserBindingModel model); + UserViewModel Login(string email, string password); UserViewModel Create(UserBindingModel model); diff --git a/Contracts/ViewModels/UserViewModel.cs b/Contracts/ViewModels/UserViewModel.cs index 4f15600..6174d56 100644 --- a/Contracts/ViewModels/UserViewModel.cs +++ b/Contracts/ViewModels/UserViewModel.cs @@ -12,7 +12,6 @@ namespace Contracts.ViewModels public string FirstName { get; set; } = string.Empty; public string SecondName { get; set; } = string.Empty; public string Email { get; set; } = string.Empty; - public string PasswordHash { get; set; } = string.Empty; public DateTime Birthday { get; set; } public RoleViewModel Role { get; set; } = null!; } -- 2.25.1 From ba55b692b4063f90777446f49092c5ea53790522 Mon Sep 17 00:00:00 2001 From: mfnefd Date: Thu, 6 Jun 2024 17:50:42 +0400 Subject: [PATCH 20/22] fix user logic. password hasher is now on bcrypt --- BusinessLogic/BusinessLogic.csproj | 5 +++++ BusinessLogic/BusinessLogic/UserLogic.cs | 14 ++++++++------ BusinessLogic/Tools/PasswordHasher.cs | 9 ++------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/BusinessLogic/BusinessLogic.csproj b/BusinessLogic/BusinessLogic.csproj index 8e23e43..c105ec7 100644 --- a/BusinessLogic/BusinessLogic.csproj +++ b/BusinessLogic/BusinessLogic.csproj @@ -6,7 +6,12 @@ enable + + + + + diff --git a/BusinessLogic/BusinessLogic/UserLogic.cs b/BusinessLogic/BusinessLogic/UserLogic.cs index 73f8156..b028660 100644 --- a/BusinessLogic/BusinessLogic/UserLogic.cs +++ b/BusinessLogic/BusinessLogic/UserLogic.cs @@ -102,19 +102,21 @@ namespace BusinessLogic.BusinessLogic return UserConverter.ToView(user); } - public UserViewModel Login(UserBindingModel model) + public UserViewModel Login(string email, string password) { - ArgumentNullException.ThrowIfNull(model); - - var user = _userStorage.GetElement(new() { Email = model.Email }); + if (email is null) + { + throw new AccountException("Email is null"); + } + var user = _userStorage.GetElement(new() { Email = email }); if (user is null) { throw new ElementNotFoundException(); } // Проверяем пароль - _validatePassword(model.Password); - if (PasswordHasher.Verify(model.Password, user.PasswordHash)) + _validatePassword(password); + if (!PasswordHasher.Verify(password, user.PasswordHash)) { throw new AccountException("The passwords don't match."); } diff --git a/BusinessLogic/Tools/PasswordHasher.cs b/BusinessLogic/Tools/PasswordHasher.cs index f73edca..f52c90f 100644 --- a/BusinessLogic/Tools/PasswordHasher.cs +++ b/BusinessLogic/Tools/PasswordHasher.cs @@ -16,11 +16,7 @@ namespace BusinessLogic.Tools /// Хеш пароля public static string Hash(string password) { - using (SHA256 sha256 = SHA256.Create()) - { - byte[] bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(password)); - return Convert.ToBase64String(bytes); - } + return BCrypt.Net.BCrypt.HashPassword(password); } /// @@ -31,8 +27,7 @@ namespace BusinessLogic.Tools /// public static bool Verify(string password, string passHash) { - var hash = Hash(password); - return hash == passHash; + return BCrypt.Net.BCrypt.Verify(password, passHash); } } } \ No newline at end of file -- 2.25.1 From 5345098bbdf8478e2c9dc4d1ced73f19f88ab9ab Mon Sep 17 00:00:00 2001 From: mfnefd Date: Thu, 6 Jun 2024 17:52:09 +0400 Subject: [PATCH 21/22] fix user storage (user search) --- DatabaseImplement/Implements/UserStorage.cs | 26 +++++++++++++++------ DatabaseImplement/Models/User.cs | 25 +++++--------------- 2 files changed, 25 insertions(+), 26 deletions(-) diff --git a/DatabaseImplement/Implements/UserStorage.cs b/DatabaseImplement/Implements/UserStorage.cs index 59a8a2a..fdbdf0a 100644 --- a/DatabaseImplement/Implements/UserStorage.cs +++ b/DatabaseImplement/Implements/UserStorage.cs @@ -3,6 +3,7 @@ using Contracts.SearchModels; using Contracts.StorageContracts; using DatabaseImplement.Models; using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; using System; using System.Collections.Generic; using System.Linq; @@ -21,7 +22,9 @@ namespace DatabaseImplement.Implements } var context = new Database(); - var user = context.Users.FirstOrDefault(u => u.Equals(model)); + var user = context.Users.FirstOrDefault(u => + (model.Id.HasValue && u.Id == model.Id) + || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))); if (user is null) { @@ -42,7 +45,9 @@ namespace DatabaseImplement.Implements var context = new Database(); return context.Users .Include(u => u.Role) - .FirstOrDefault(u => u.Equals(model)) + .FirstOrDefault(u => + (model.Id.HasValue && u.Id == model.Id) + || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))) ?.GetBindingModel(); } @@ -60,7 +65,9 @@ namespace DatabaseImplement.Implements return []; } return context.Users - .Where(u => u.Equals(model)) + .Where(u => + (model.Id.HasValue && u.Id == model.Id) + || (!string.IsNullOrEmpty(u.Email) && u.Email.Contains(model.Email))) .Include(u => u.Role) .Select(r => r.GetBindingModel()); } @@ -68,7 +75,12 @@ namespace DatabaseImplement.Implements public UserBindingModel? Insert(UserBindingModel model) { var context = new Database(); - var newUser = Models.User.ToUserFromBinding(model); + var role = context.Roles.FirstOrDefault(r => r.Id == model.Role.Id); + if (role is null) + { + return null; + } + var newUser = Models.User.ToUserFromBinding(model, role); context.Users.Add(newUser); context.SaveChanges(); @@ -80,15 +92,15 @@ namespace DatabaseImplement.Implements { var context = new Database(); var user = context.Users - .Include(u => u.Role) .FirstOrDefault(u => u.Id == model.Id); + var role = context.Roles.FirstOrDefault(r => r.Id == model.Role.Id); - if (user is null) + if (user is null || role is null) { return null; } - user.Update(model); + user.Update(model, role); context.SaveChanges(); return user.GetBindingModel(); diff --git a/DatabaseImplement/Models/User.cs b/DatabaseImplement/Models/User.cs index 00dcad9..a2f2ecf 100644 --- a/DatabaseImplement/Models/User.cs +++ b/DatabaseImplement/Models/User.cs @@ -43,17 +43,17 @@ namespace DatabaseImplement.Models Role = Role?.GetBindingModel() ?? new() }; - public static User ToUserFromView(UserViewModel model) => new() + public static User ToUserFromView(UserViewModel model, Role role) => new() { Id = model.Id, FirstName = model.FirstName, SecondName = model.SecondName, Email = model.Email, Birthday = model.Birthday, - Role = Models.Role.ToRoleFromView(model.Role) + Role = role }; - public static User ToUserFromBinding(UserBindingModel model) => new() + public static User ToUserFromBinding(UserBindingModel model, Role role) => new() { Id = model.Id, FirstName = model.FirstName, @@ -61,10 +61,10 @@ namespace DatabaseImplement.Models Email = model.Email, PasswordHash = model.PasswordHash, Birthday = model.Birthday, - Role = Models.Role.ToRoleFromBinding(model.Role) + Role = role }; - public void Update(UserBindingModel model) + public void Update(UserBindingModel model, Role role) { if (model is null) { @@ -76,20 +76,7 @@ namespace DatabaseImplement.Models SecondName = model.SecondName; PasswordHash = model.PasswordHash; Birthday = model.Birthday; - Role = Models.Role.ToRoleFromBinding(model.Role); - } - - public bool Equals(UserSearchModel model) - { - if (model.Id is null) - { - return Email.Contains(model.Email!); - } - if (string.IsNullOrWhiteSpace(model.Email)) - { - return Id == model.Id; - } - return false; + Role = role; } } } \ No newline at end of file -- 2.25.1 From 2533ba90c0a0496d80fd70a2cdde24adcdac783d Mon Sep 17 00:00:00 2001 From: mfnefd Date: Thu, 6 Jun 2024 17:52:35 +0400 Subject: [PATCH 22/22] fix user controller (login) --- RestAPI/Controllers/UserController.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/RestAPI/Controllers/UserController.cs b/RestAPI/Controllers/UserController.cs index f985b4d..bbeb162 100644 --- a/RestAPI/Controllers/UserController.cs +++ b/RestAPI/Controllers/UserController.cs @@ -21,11 +21,11 @@ namespace RestAPI.Controllers } [HttpPost] - public IResult Login([FromBody] UserBindingModel model) + public IResult Login(string email, string password) { try { - var res = _userLogic.Login(model); + var res = _userLogic.Login(email, password); return Results.Ok(res); } catch (ElementNotFoundException ex) @@ -50,17 +50,19 @@ namespace RestAPI.Controllers { try { - var res = _userLogic.Login(model); + var res = _userLogic.Create(model); return Results.Ok(res); } catch (AccountException ex) { _logger.LogWarning(ex, "Wrong registration data"); + throw; return Results.BadRequest(ex.Message); } catch (Exception ex) { _logger.LogError(ex, "Error create user"); + throw; return Results.Problem(ex.Message); } } -- 2.25.1