From f35a7aecd313547a6f6478a056fb2d5593f1c07b Mon Sep 17 00:00:00 2001 From: Syndamia Date: Sat, 15 May 2021 22:20:53 +0300 Subject: Big daddy refactor --- .../Configurations/ServiceUserMappings.cs | 15 +++ .../ExamTemplate.Services.csproj | 20 ++++ .../Interfaces/IBaseService.cs | 21 ++++ .../Interfaces/ICloudinaryService.cs | 11 ++ .../Interfaces/IUserService.cs | 25 ++++ .../ExamTemplate.Services/Services/BaseService.cs | 91 +++++++++++++++ .../Services/CloudinaryService.cs | 50 ++++++++ .../ExamTemplate.Services/Services/UserService.cs | 129 +++++++++++++++++++++ 8 files changed, 362 insertions(+) create mode 100644 ExamTemplate/Services/ExamTemplate.Services/Configurations/ServiceUserMappings.cs create mode 100644 ExamTemplate/Services/ExamTemplate.Services/ExamTemplate.Services.csproj create mode 100644 ExamTemplate/Services/ExamTemplate.Services/Interfaces/IBaseService.cs create mode 100644 ExamTemplate/Services/ExamTemplate.Services/Interfaces/ICloudinaryService.cs create mode 100644 ExamTemplate/Services/ExamTemplate.Services/Interfaces/IUserService.cs create mode 100644 ExamTemplate/Services/ExamTemplate.Services/Services/BaseService.cs create mode 100644 ExamTemplate/Services/ExamTemplate.Services/Services/CloudinaryService.cs create mode 100644 ExamTemplate/Services/ExamTemplate.Services/Services/UserService.cs (limited to 'ExamTemplate/Services/ExamTemplate.Services') diff --git a/ExamTemplate/Services/ExamTemplate.Services/Configurations/ServiceUserMappings.cs b/ExamTemplate/Services/ExamTemplate.Services/Configurations/ServiceUserMappings.cs new file mode 100644 index 0000000..787b90f --- /dev/null +++ b/ExamTemplate/Services/ExamTemplate.Services/Configurations/ServiceUserMappings.cs @@ -0,0 +1,15 @@ +using AutoMapper; +using ExamTemplate.Data.Models; +using ExamTemplate.Services.Models.User; + +namespace ExamTemplate.Services.Configurations +{ + public class ServiceUserMappings : Profile + { + public ServiceUserMappings() + { + CreateMap(); + CreateMap(); + } + } +} diff --git a/ExamTemplate/Services/ExamTemplate.Services/ExamTemplate.Services.csproj b/ExamTemplate/Services/ExamTemplate.Services/ExamTemplate.Services.csproj new file mode 100644 index 0000000..b84928f --- /dev/null +++ b/ExamTemplate/Services/ExamTemplate.Services/ExamTemplate.Services.csproj @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + net5.0 + + + diff --git a/ExamTemplate/Services/ExamTemplate.Services/Interfaces/IBaseService.cs b/ExamTemplate/Services/ExamTemplate.Services/Interfaces/IBaseService.cs new file mode 100644 index 0000000..66de7b8 --- /dev/null +++ b/ExamTemplate/Services/ExamTemplate.Services/Interfaces/IBaseService.cs @@ -0,0 +1,21 @@ +using System; +using System.Threading.Tasks; +using System.Collections.Generic; + +namespace ExamTemplate.Services.Interfaces +{ + public interface IBaseService + where DbModel : class + where ServiceModel : class + { + Task CreateAsync(ServiceModel serviceModel); + + Task GetByIdAsync(Guid id); + + Task> GetAllAsync(); + + Task EditAsync(ServiceModel serviceModel); + + Task DeleteAsync(Guid id); + } +} diff --git a/ExamTemplate/Services/ExamTemplate.Services/Interfaces/ICloudinaryService.cs b/ExamTemplate/Services/ExamTemplate.Services/Interfaces/ICloudinaryService.cs new file mode 100644 index 0000000..9c4d884 --- /dev/null +++ b/ExamTemplate/Services/ExamTemplate.Services/Interfaces/ICloudinaryService.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; + +namespace ExamTemplate.Services.Interfaces +{ + public interface ICloudinaryService + { + Task> UploadFilesToCloud(List formFiles); + } +} diff --git a/ExamTemplate/Services/ExamTemplate.Services/Interfaces/IUserService.cs b/ExamTemplate/Services/ExamTemplate.Services/Interfaces/IUserService.cs new file mode 100644 index 0000000..35f14e3 --- /dev/null +++ b/ExamTemplate/Services/ExamTemplate.Services/Interfaces/IUserService.cs @@ -0,0 +1,25 @@ +using System.Security.Claims; +using System.Threading.Tasks; +using ExamTemplate.Services.Models.User; + +namespace ExamTemplate.Services.Interfaces +{ + public interface IUserService + { + Task RegisterUserAsync(RegisterUserServiceModel registerUserServiceModel); + + Task LoginUserAsync(LoginUserServiceModel loginUserServiceModel); + + Task LogoutAsync(); + + Task GetUserByUsernameAsync(string username); + + Task GetUserByClaimsAsync(ClaimsPrincipal claimsPrincipal); + + Task EditUserAsync(ClaimsPrincipal claimsPrincipal, UserServiceModel userServiceModel); + + Task DeleteUserAsync(ClaimsPrincipal claimsPrincipal); + + bool IsSignedIn(ClaimsPrincipal claimsPrincipal); + } +} diff --git a/ExamTemplate/Services/ExamTemplate.Services/Services/BaseService.cs b/ExamTemplate/Services/ExamTemplate.Services/Services/BaseService.cs new file mode 100644 index 0000000..b1b823e --- /dev/null +++ b/ExamTemplate/Services/ExamTemplate.Services/Services/BaseService.cs @@ -0,0 +1,91 @@ +using System; +using System.Threading.Tasks; +using AutoMapper; +using ExamTemplate.Data; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; +using System.Linq; +using ExamTemplate.Services.Interfaces; +using ExamTemplate.Common; + +namespace ExamTemplate.Services.Services +{ + public abstract class BaseService : IBaseService + where DbModel : class + where ServiceModel : class + { + protected IMapper _autoMapper { get; private set; } + protected TemplateContext _context { get; private set; } + + protected BaseService(IMapper autoMapper, TemplateContext context) + { + this._autoMapper = autoMapper; + this._context = context; + } + + public virtual async Task CreateAsync(ServiceModel serviceModel) + { + if (serviceModel == null) + throw new ArgumentNullException(ErrorMessages.NullObject(typeof(ServiceModel))); + + DbModel newEntity = this._autoMapper.Map(serviceModel); + + await this.GetDbSet() + .AddAsync(newEntity); + + return await this.SaveChangesAsync(); + } + + public virtual async Task GetByIdAsync(Guid id) + { + if (id == Guid.Empty) + throw new ArgumentNullException(ErrorMessages.NullObject(typeof(Guid))); + + DbModel entity = await this.GetDbSet() + .FindAsync(id); + + return this._autoMapper.Map(entity); + } + + public virtual async Task> GetAllAsync() + { + return await this.GetDbSet() + .Select(x => this._autoMapper.Map(x)) + .ToListAsync(); + } + + public virtual async Task EditAsync(ServiceModel serviceModel) + { + if (serviceModel == null) + throw new ArgumentNullException(ErrorMessages.NullObject(typeof(ServiceModel))); + + DbModel entity = this._autoMapper.Map(serviceModel); + + this._context.Update(entity); + + return await this.SaveChangesAsync(); + } + + public virtual async Task DeleteAsync(Guid id) + { + DbModel entity = this.GetDbSet() + .Find(id); + + this._context.Remove(entity); + + return await this.SaveChangesAsync(); + } + + /// Get, Create, Edit and Delete use this method + /// Override it to add include statements (and so all other methods will work with includes too) + protected virtual DbSet GetDbSet() + { + return this._context.Set(); + } + + protected async Task SaveChangesAsync() + { + return await this._context.SaveChangesAsync() >= 1; + } + } +} diff --git a/ExamTemplate/Services/ExamTemplate.Services/Services/CloudinaryService.cs b/ExamTemplate/Services/ExamTemplate.Services/Services/CloudinaryService.cs new file mode 100644 index 0000000..40772d9 --- /dev/null +++ b/ExamTemplate/Services/ExamTemplate.Services/Services/CloudinaryService.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using CloudinaryDotNet; +using CloudinaryDotNet.Actions; +using Microsoft.AspNetCore.Http; +using ExamTemplate.Services.Interfaces; + +namespace ExamTemplate.Services.Services +{ + public class CloudinaryService : ICloudinaryService + { + // Regex for getting the filename without (final) filename extension + // So, from image.png, it will match image, and from doc.my.txt will match doc.my + private static readonly Regex s_imageRegex = new(".*(?=\\.)"); + + private readonly Cloudinary _cloudinary; + + public CloudinaryService(string cloudName, string apiKey, string apiSecret) + { + this._cloudinary = new Cloudinary(new Account(cloudName, apiKey, apiSecret)); + } + + public async Task> UploadFilesToCloud(List formFiles) + { + List fileUrls = new(); + foreach (var formFile in formFiles) + { + string fileName = s_imageRegex.Match(formFile.FileName).ToString(); + + using var ms = new MemoryStream(); + formFile.CopyTo(ms); + byte[] formBytes = ms.ToArray(); + + RawUploadParams rawUploadParams = new() + { + File = new FileDescription(fileName, new MemoryStream(formBytes)), + PublicId = fileName, + UseFilename = true + }; + + RawUploadResult rawUploadResult = await this._cloudinary.UploadAsync(rawUploadParams); + fileUrls.Add(rawUploadResult.Url.AbsoluteUri); + } + + return fileUrls; + } + } +} diff --git a/ExamTemplate/Services/ExamTemplate.Services/Services/UserService.cs b/ExamTemplate/Services/ExamTemplate.Services/Services/UserService.cs new file mode 100644 index 0000000..4e85688 --- /dev/null +++ b/ExamTemplate/Services/ExamTemplate.Services/Services/UserService.cs @@ -0,0 +1,129 @@ +using System; +using System.Security.Claims; +using System.Threading.Tasks; +using AutoMapper; +using ExamTemplate.Data; +using ExamTemplate.Data.Models; +using ExamTemplate.Services.Models.User; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using ExamTemplate.Services.Interfaces; +using ExamTemplate.Common; + +namespace ExamTemplate.Services.Services +{ + public class UserService : IUserService + { + private readonly IMapper _autoMapper; + private readonly TemplateContext _context; + private readonly SignInManager _signInManager; + private readonly UserManager _userManager; + private readonly RoleManager> _roleManager; + + public UserService(IMapper autoMapper, TemplateContext templateContext, SignInManager signInManager, UserManager userManager, RoleManager> roleManager) + { + this._autoMapper = autoMapper; + this._context = templateContext; + this._signInManager = signInManager; + this._userManager = userManager; + this._roleManager = roleManager; + } + + public async Task RegisterUserAsync(RegisterUserServiceModel registerUserServiceModel) + { + if (registerUserServiceModel == null) + throw new ArgumentNullException(ErrorMessages.NullObject(typeof(RegisterUserServiceModel))); + + User user = this._autoMapper.Map(registerUserServiceModel); + + user.PasswordHash = this._userManager.PasswordHasher.HashPassword(user, registerUserServiceModel.Password); + IdentityResult userCreateResult = await this._userManager.CreateAsync(user); + + if (!userCreateResult.Succeeded) + { + await this._userManager.DeleteAsync(user); + return false; + } + + IdentityResult addRoleResult; + if (await this._userManager.Users.CountAsync() == 1) // 1, because we added the user in line 37 + addRoleResult = await this._userManager.AddToRoleAsync(user, RoleConst.Admin); + else + addRoleResult = await this._userManager.AddToRoleAsync(user, RoleConst.User); + + return addRoleResult.Succeeded; + } + + public async Task LoginUserAsync(LoginUserServiceModel loginUserServiceModel) + { + if (loginUserServiceModel == null) + throw new ArgumentNullException(ErrorMessages.NullObject(typeof(LoginUserServiceModel))); + + SignInResult result = await this._signInManager.PasswordSignInAsync(loginUserServiceModel.Username, loginUserServiceModel.Password, false, false); + + return result.Succeeded; + } + + public async Task LogoutAsync() + { + await this._signInManager.SignOutAsync(); + } + + public async Task GetUserByUsernameAsync(string username) + { + if (username == null) + throw new ArgumentNullException(ErrorMessages.NullObject(typeof(string))); + + User user = await this._userManager.Users + .FirstOrDefaultAsync(x => x.UserName == username); + + return this._autoMapper.Map(user); + } + + public async Task GetUserByClaimsAsync(ClaimsPrincipal claimsPrincipal) + { + if (claimsPrincipal == null) + throw new ArgumentNullException(ErrorMessages.NullObject(typeof(ClaimsPrincipal))); + + User user = await this._userManager.GetUserAsync(claimsPrincipal); + + return this._autoMapper.Map(user); + } + + public async Task EditUserAsync(ClaimsPrincipal claimsPrincipal, UserServiceModel userServiceModel) + { + if (claimsPrincipal == null) + throw new ArgumentNullException(ErrorMessages.NullObject(typeof(ClaimsPrincipal))); + if (userServiceModel == null) + throw new ArgumentNullException(ErrorMessages.NullObject(typeof(UserServiceModel))); + + User user = await this._userManager.GetUserAsync(claimsPrincipal); + + user.UserName = userServiceModel.Username; + user.FirstName = userServiceModel.FirstName; + user.LastName = userServiceModel.LastName; + + IdentityResult result = await this._userManager.UpdateAsync(user); + return result.Succeeded; + } + + public async Task DeleteUserAsync(ClaimsPrincipal claimsPrincipal) + { + if (claimsPrincipal == null) + throw new ArgumentNullException(ErrorMessages.NullObject(typeof(ClaimsPrincipal))); + + User user = await this._userManager.GetUserAsync(claimsPrincipal); + + IdentityResult result = await this._userManager.DeleteAsync(user); + return result.Succeeded; + } + + public bool IsSignedIn(ClaimsPrincipal claimsPrincipal) + { + if (claimsPrincipal == null) + throw new ArgumentNullException(ErrorMessages.NullObject(typeof(ClaimsPrincipal))); + + return this._signInManager.IsSignedIn(claimsPrincipal); + } + } +} -- cgit v1.2.3