initial commit from an older template
This commit is contained in:
47
Services/Cookies/CookieService.cs
Executable file
47
Services/Cookies/CookieService.cs
Executable file
@@ -0,0 +1,47 @@
|
||||
using GamificationService.Exceptions.UtilServices.Cookies;
|
||||
|
||||
namespace GamificationService.Services.Cookies;
|
||||
|
||||
public class CookieService : ICookieService
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ILogger<CookieService> _logger;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public CookieService(IHttpContextAccessor httpContextAccessor, ILogger<CookieService> logger, IConfiguration configuration)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public Task<bool> SetCookie(string key, string value, CookieOptions options)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogDebug("Adding cookie {CookieKey} with value {CookieValue}", key, value);
|
||||
_httpContextAccessor.HttpContext.Response.Cookies.Append(key, value, options);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to add cookie {CookieKey}", key);
|
||||
throw new SetCookiesException(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> RemoveCookie(string key)
|
||||
{
|
||||
try
|
||||
{
|
||||
_logger.LogDebug("Deleting cookie {CookieKey}", key);
|
||||
_httpContextAccessor.HttpContext.Response.Cookies.Delete(key);
|
||||
return await Task.FromResult(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Failed to delete cookie {CookieKey}", key);
|
||||
throw new DeleteCookiesException(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
Services/Cookies/ICookieService.cs
Executable file
7
Services/Cookies/ICookieService.cs
Executable file
@@ -0,0 +1,7 @@
|
||||
namespace GamificationService.Services.Cookies;
|
||||
|
||||
public interface ICookieService
|
||||
{
|
||||
Task<bool> SetCookie(string key, string value, CookieOptions options);
|
||||
Task<bool> RemoveCookie(string key);
|
||||
}
|
||||
26
Services/CurrentUsers/CurrentUserService.cs
Normal file
26
Services/CurrentUsers/CurrentUserService.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using GamificationService.Models;
|
||||
|
||||
namespace GamificationService.Services.CurrentUsers;
|
||||
|
||||
public class CurrentUserService : ICurrentUserService
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly ILogger<ICurrentUserService> _logger;
|
||||
public CurrentUserService(IHttpContextAccessor httpContextAccessor, ILogger<ICurrentUserService> logger)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public UserSession GetCurrentUser()
|
||||
{
|
||||
UserSession currentUser = new UserSession
|
||||
{
|
||||
IsAuthenticated = _httpContextAccessor.HttpContext.User.Identity != null && _httpContextAccessor.HttpContext.User.Identity.IsAuthenticated,
|
||||
Login = _httpContextAccessor.HttpContext.User.Identity.Name
|
||||
};
|
||||
_logger.LogDebug($"Current user extracted: {currentUser.Login}");
|
||||
return currentUser;
|
||||
}
|
||||
|
||||
}
|
||||
8
Services/CurrentUsers/ICurrentUserService.cs
Normal file
8
Services/CurrentUsers/ICurrentUserService.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using GamificationService.Models;
|
||||
|
||||
namespace GamificationService.Services.CurrentUsers;
|
||||
|
||||
public interface ICurrentUserService
|
||||
{
|
||||
UserSession GetCurrentUser();
|
||||
}
|
||||
22
Services/InstructionTests/IInstructionTestsService.cs
Normal file
22
Services/InstructionTests/IInstructionTestsService.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using GamificationService.Models.Database;
|
||||
using GamificationService.Models.DTO;
|
||||
|
||||
namespace GamificationService.Services.InstructionTests;
|
||||
|
||||
public interface IInstructionTestsService
|
||||
{
|
||||
public Task<InstructionTest> CreateInstructionTest(InstructionTest instructionTest);
|
||||
public Task<InstructionTestDTO> CreateInstructionTest(InstructionTestCreateDTO instructionTest);
|
||||
public Task<bool> UpdateInstructionTest(InstructionTest instructionTest);
|
||||
public Task<bool> UpdateInstructionTest(InstructionTestCreateDTO instructionTest);
|
||||
public Task<bool> DeleteInstructionTestByIdAsync(long id);
|
||||
|
||||
public Task<InstructionTestResultDTO> SubmitInstructionTestAsync(long userId, InstructionTestSubmissionDTO submission);
|
||||
|
||||
public InstructionTestDTO GetInstructionTestById(long id);
|
||||
public List<InstructionTestDTO> GetInstructionTestsByInstructionId(long instructionId);
|
||||
public List<InstructionTestQuestionDTO> GetInstructionTestQuestionsByInstructionTestId(long instructionTestId);
|
||||
public List<InstructionTestResultDTO> GetUserInstructionTestResultsByInstructionTestId(long userId, long instructionId);
|
||||
public List<InstructionTestResultDTO> GetInstructionTestResultsByUserId(long userId);
|
||||
public List<InstructionTestResultDTO> GetCompletedInstructionTestsByUserId(long userId);
|
||||
}
|
||||
294
Services/InstructionTests/InstructionTestsService.cs
Normal file
294
Services/InstructionTests/InstructionTestsService.cs
Normal file
@@ -0,0 +1,294 @@
|
||||
using AutoMapper;
|
||||
using GamificationService.Database.Repositories;
|
||||
using GamificationService.Exceptions.Services.Instruction;
|
||||
using GamificationService.Exceptions.Services.InstructionTest;
|
||||
using GamificationService.Models.Database;
|
||||
using GamificationService.Models.DTO;
|
||||
using GamificationService.Utils.Enums;
|
||||
|
||||
namespace GamificationService.Services.InstructionTests;
|
||||
|
||||
public class InstructionTestsService : IInstructionTestsService
|
||||
{
|
||||
private readonly ILogger<InstructionTestsService> _logger;
|
||||
private readonly UnitOfWork _unitOfWork;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public InstructionTestsService(ILogger<InstructionTestsService> logger, UnitOfWork unitOfWork, IMapper mapper)
|
||||
{
|
||||
_logger = logger;
|
||||
_unitOfWork = unitOfWork;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<InstructionTestDTO> CreateInstructionTest(InstructionTestCreateDTO instructionTest)
|
||||
{
|
||||
return _mapper.Map<InstructionTestDTO>(await CreateInstructionTest(_mapper.Map<InstructionTest>(instructionTest)));
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteInstructionTestByIdAsync(long id)
|
||||
{
|
||||
var instructionTest = _unitOfWork.InstructionTestRepository.GetByID(id);
|
||||
|
||||
if (instructionTest == null)
|
||||
{
|
||||
_logger.LogError("Instruction test with id {Id} not found", id);
|
||||
throw new InstructionTestNotFoundException();
|
||||
}
|
||||
|
||||
// Find all questions
|
||||
var questions = _unitOfWork.InstructionTestQuestionRepository.Get(q => q.InstructionTestId == id);
|
||||
|
||||
// Start transaction
|
||||
await _unitOfWork.BeginTransactionAsync();
|
||||
|
||||
foreach (var question in questions)
|
||||
{
|
||||
_unitOfWork.InstructionTestQuestionRepository.Delete(question);
|
||||
}
|
||||
|
||||
// Delete instruction test
|
||||
_unitOfWork.InstructionTestRepository.Delete(instructionTest);
|
||||
|
||||
if (await _unitOfWork.SaveAsync())
|
||||
{
|
||||
await _unitOfWork.CommitAsync();
|
||||
_logger.LogInformation("Instruction test deleted ({Id})", id);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogError("Failed to delete instruction test ({Id})", id);
|
||||
throw new InstructionTestDeletionException();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<InstructionTestResultDTO> GetCompletedInstructionTestsByUserId(long userId)
|
||||
{
|
||||
var userTestAttempts = _unitOfWork.InstructionTestResultRepository.Get(
|
||||
q => q.UserId == userId).ToList();
|
||||
|
||||
var userInstructionTests = _unitOfWork.InstructionTestRepository.Get(
|
||||
q => userTestAttempts.Any(a => a.InstructionTestId == q.Id)).ToList();
|
||||
|
||||
var conclusiveUserTestResults = new List<InstructionTestResultDTO>();
|
||||
foreach (var instructionTest in userInstructionTests)
|
||||
{
|
||||
var scoreCalcMethod = instructionTest.ScoreCalcMethod;
|
||||
int maxScore = 0;
|
||||
|
||||
if (scoreCalcMethod == InstructionTestScoreCalcMethod.AverageGrade)
|
||||
{
|
||||
maxScore = (int)Math.Round(userTestAttempts.Where(q => q.InstructionTestId == instructionTest.Id).Average(q => q.Score));
|
||||
}
|
||||
else
|
||||
{
|
||||
maxScore = userTestAttempts.Where(q => q.InstructionTestId == instructionTest.Id).Max(q => q.Score);
|
||||
}
|
||||
|
||||
if (maxScore >= instructionTest.MinScore)
|
||||
{
|
||||
conclusiveUserTestResults.Add(_mapper.Map<InstructionTestResultDTO>(userTestAttempts.First(q => q.InstructionTestId == instructionTest.Id)));
|
||||
}
|
||||
}
|
||||
|
||||
return conclusiveUserTestResults;
|
||||
}
|
||||
|
||||
public InstructionTestDTO GetInstructionTestById(long id)
|
||||
{
|
||||
var instructionTest = _unitOfWork.InstructionTestRepository.GetByID(id);
|
||||
if (instructionTest == null)
|
||||
{
|
||||
_logger.LogError("Instruction test with id {Id} not found", id);
|
||||
throw new InstructionTestNotFoundException();
|
||||
}
|
||||
return _mapper.Map<InstructionTestDTO>(instructionTest);
|
||||
}
|
||||
|
||||
public List<InstructionTestQuestionDTO> GetInstructionTestQuestionsByInstructionTestId(long instructionTestId)
|
||||
{
|
||||
var questions = _unitOfWork.InstructionTestQuestionRepository.Get(q => q.InstructionTestId == instructionTestId);
|
||||
return _mapper.Map<List<InstructionTestQuestionDTO>>(questions);
|
||||
}
|
||||
|
||||
public List<InstructionTestResultDTO> GetUserInstructionTestResultsByInstructionTestId(long userId, long instructionTestId)
|
||||
{
|
||||
var userTestResults = _unitOfWork.InstructionTestResultRepository.Get(
|
||||
q => q.UserId == userId && q.InstructionTestId == instructionTestId).ToList();
|
||||
return _mapper.Map<List<InstructionTestResultDTO>>(userTestResults);
|
||||
}
|
||||
|
||||
public List<InstructionTestResultDTO> GetInstructionTestResultsByUserId(long userId)
|
||||
{
|
||||
var userTestResults = _unitOfWork.InstructionTestResultRepository.Get(
|
||||
q => q.UserId == userId).ToList();
|
||||
return _mapper.Map<List<InstructionTestResultDTO>>(userTestResults);
|
||||
}
|
||||
|
||||
public List<InstructionTestDTO> GetInstructionTestsByInstructionId(long instructionId)
|
||||
{
|
||||
var instructionTest = (_unitOfWork.InstructionRepository.GetByID(instructionId)
|
||||
?? throw new InstructionNotFoundException())
|
||||
.InstructionTest ?? throw new InstructionTestNotFoundException();
|
||||
|
||||
return _mapper.Map<List<InstructionTestDTO>>(new List<InstructionTest>() {instructionTest});
|
||||
}
|
||||
|
||||
public async Task<InstructionTestResultDTO> SubmitInstructionTestAsync(long userId, InstructionTestSubmissionDTO submission)
|
||||
{
|
||||
// Retrieve the test and questions
|
||||
var instructionTest = _unitOfWork.InstructionTestRepository.GetByID(submission.InstructionTestId);
|
||||
|
||||
if (instructionTest == null)
|
||||
{
|
||||
_logger.LogError("Instruction test with id {Id} not found", submission.InstructionTestId);
|
||||
throw new InstructionTestNotFoundException();
|
||||
}
|
||||
|
||||
// Check remaining attempts
|
||||
var userTestAttempts = _unitOfWork.InstructionTestResultRepository.Get(
|
||||
q => q.UserId == userId && q.InstructionTestId == submission.InstructionTestId).ToList();
|
||||
|
||||
if (userTestAttempts.Count >= instructionTest.MaxAttempts)
|
||||
{
|
||||
_logger.LogWarning("User {UserId}: denied submission for test {InstructionTestId}: max attempts reached", userId, submission.InstructionTestId);
|
||||
throw new InstructionTestSubmissionException();
|
||||
}
|
||||
|
||||
var questions = _unitOfWork.InstructionTestQuestionRepository.Get(q => q.InstructionTestId == submission.InstructionTestId).ToList();
|
||||
|
||||
// Verify answers amount
|
||||
if (questions.Count != submission.Answers.Count)
|
||||
{
|
||||
_logger.LogWarning("User {UserId}: denied submission for test {InstructionTestId}: wrong number of answers", userId, submission.InstructionTestId);
|
||||
throw new InstructionTestSubmissionException();
|
||||
}
|
||||
|
||||
// Evaluate answers
|
||||
double score = 0;
|
||||
int maxErrorPerQuestion = 1;
|
||||
for (int i = 0; i < questions.Count; i++)
|
||||
{
|
||||
var question = questions[i];
|
||||
|
||||
// User answers for the question without duplicate options
|
||||
var answer = submission.Answers[i].Distinct();
|
||||
|
||||
if (question.IsMultipleAnswer)
|
||||
{
|
||||
int correctUserAnswersCount = 0;
|
||||
int incorrectUserAnswersCount = 0;
|
||||
int correctAnswersCount = question.CorrectAnswers.Count;
|
||||
|
||||
foreach (var option in answer)
|
||||
{
|
||||
if (question.CorrectAnswers.Contains(option))
|
||||
{
|
||||
correctUserAnswersCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
incorrectUserAnswersCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (incorrectUserAnswersCount > maxErrorPerQuestion || correctUserAnswersCount == 0)
|
||||
{
|
||||
// Nothing scored for the question
|
||||
continue;
|
||||
}
|
||||
|
||||
// One question is worth 1 point max
|
||||
double questionScore = correctUserAnswersCount / (double)correctAnswersCount;
|
||||
|
||||
// Add the question score, or half of it if an error is present
|
||||
score += incorrectUserAnswersCount > 0 ? questionScore /= 2 : questionScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
score += question.CorrectAnswers.Contains(answer.First()) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
score = Math.Round(score / questions.Count)*100;
|
||||
|
||||
// Add test result
|
||||
await _unitOfWork.BeginTransactionAsync();
|
||||
|
||||
InstructionTestResult newTestResult = new InstructionTestResult()
|
||||
{
|
||||
UserId = userId,
|
||||
InstructionTestId = submission.InstructionTestId,
|
||||
Score = (int)score
|
||||
};
|
||||
|
||||
_unitOfWork.InstructionTestResultRepository.Insert(newTestResult);
|
||||
|
||||
if (!await _unitOfWork.SaveAsync())
|
||||
{
|
||||
_logger.LogError("Failed to save test result for user {UserId} and test {InstructionTestId}", userId, submission.InstructionTestId);
|
||||
throw new InstructionTestSubmissionException();
|
||||
}
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
return _mapper.Map<InstructionTestResultDTO>(newTestResult);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateInstructionTest(InstructionTestCreateDTO instructionTest)
|
||||
{
|
||||
return await UpdateInstructionTest(_mapper.Map<InstructionTest>(instructionTest));
|
||||
}
|
||||
|
||||
public async Task<InstructionTest> CreateInstructionTest(InstructionTest instructionTest)
|
||||
{
|
||||
instructionTest.Id = 0;
|
||||
|
||||
await _unitOfWork.BeginTransactionAsync();
|
||||
|
||||
await _unitOfWork.InstructionTestRepository.InsertAsync(instructionTest);
|
||||
|
||||
if (await _unitOfWork.SaveAsync() == false)
|
||||
{
|
||||
_logger.LogError("Failure to create instruction test");
|
||||
throw new InstructionTestCreationException();
|
||||
}
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
_logger.LogInformation($"Created instruction test {instructionTest.Id}");
|
||||
return instructionTest;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateInstructionTest(InstructionTest instructionTest)
|
||||
{
|
||||
var existingInstructionTest = _unitOfWork.InstructionTestRepository.GetByID(instructionTest.Id);
|
||||
if (existingInstructionTest == null)
|
||||
{
|
||||
throw new InstructionTestNotFoundException();
|
||||
}
|
||||
|
||||
await _unitOfWork.BeginTransactionAsync();
|
||||
|
||||
_unitOfWork.InstructionTestQuestionRepository.DeleteRange(existingInstructionTest.Questions);
|
||||
|
||||
if (await _unitOfWork.SaveAsync() == false)
|
||||
{
|
||||
_logger.LogError($"Failure to create existing questions for instruction test {instructionTest.Id} during update");
|
||||
throw new InstructionTestCreationException();
|
||||
}
|
||||
|
||||
_unitOfWork.InstructionTestRepository.Update(instructionTest);
|
||||
|
||||
if (await _unitOfWork.SaveAsync() == false)
|
||||
{
|
||||
_logger.LogError($"Failure to update instruction test {instructionTest.Id}");
|
||||
}
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
21
Services/Instructions/IInstructionService.cs
Normal file
21
Services/Instructions/IInstructionService.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using GamificationService.Models.Database;
|
||||
using GamificationService.Models.DTO;
|
||||
using GamificationService.Models.Messages.Instructions;
|
||||
|
||||
namespace GamificationService.Services.Instructions;
|
||||
|
||||
public interface IInstructionService
|
||||
{
|
||||
public Task<Instruction> CreateInstruction(Instruction model);
|
||||
public Task<bool> UpdateInstructionById(Instruction model);
|
||||
public Task<bool> DeleteInstructionById(long instructionId);
|
||||
|
||||
public Task<InstructionDTO> CreateInstruction(CreateInstructionRequest model);
|
||||
public Task<bool> UpdateInstructionById(UpdateInstructionRequest model);
|
||||
|
||||
public List<InstructionDTO> GetAllInstructions(long userId);
|
||||
public List<InstructionDTO> GetInstructionsByCategoryId(long userId, long categoryId);
|
||||
public List<InstructionDTO> GetCompletedInstructions(long userId);
|
||||
public List<InstructionDTO> GetUnfinishedInstructions(long userId);
|
||||
public List<InstructionDTO> GetInstructionById(long userId, long instructionId);
|
||||
}
|
||||
174
Services/Instructions/InstructionService.cs
Normal file
174
Services/Instructions/InstructionService.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using AutoMapper;
|
||||
using GamificationService.Database.Repositories;
|
||||
using GamificationService.Exceptions.Services.Instruction;
|
||||
using GamificationService.Models.Database;
|
||||
using GamificationService.Models.DTO;
|
||||
using GamificationService.Models.Messages.Instructions;
|
||||
using GamificationService.Services.InstructionTests;
|
||||
|
||||
namespace GamificationService.Services.Instructions;
|
||||
|
||||
public class InstructionService : IInstructionService
|
||||
{
|
||||
private readonly UnitOfWork _unitOfWork;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly ILogger<InstructionService> _logger;
|
||||
private readonly IInstructionTestsService _instructionTestService;
|
||||
|
||||
public InstructionService(UnitOfWork unitOfWork, IMapper mapper, ILogger<InstructionService> logger, IInstructionTestsService instructionTestService)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_mapper = mapper;
|
||||
_logger = logger;
|
||||
_instructionTestService = instructionTestService;
|
||||
}
|
||||
|
||||
public async Task<Instruction> CreateInstruction(Instruction model)
|
||||
{
|
||||
model.Id = 0;
|
||||
|
||||
await _unitOfWork.BeginTransactionAsync();
|
||||
|
||||
await _unitOfWork.InstructionRepository.InsertAsync(model);
|
||||
|
||||
if(await _unitOfWork.SaveAsync() == false)
|
||||
{
|
||||
_logger.LogError($"Instruction {model.Id} could not be created");
|
||||
throw new InstructionCreationException();
|
||||
}
|
||||
|
||||
await _unitOfWork.CommitAsync();
|
||||
|
||||
_logger.LogInformation($"Instruction {model.Id} created");
|
||||
return model;
|
||||
}
|
||||
|
||||
public async Task<InstructionDTO> CreateInstruction(CreateInstructionRequest model)
|
||||
{
|
||||
Instruction newInstruction = new Instruction()
|
||||
{
|
||||
Title = model.Title,
|
||||
Description = model.Description,
|
||||
CategoryId = model.CategoryId,
|
||||
Paragraphs = _mapper.Map<List<InstructionParagraph>>(model.Paragraphs),
|
||||
AssignDate = model.AssignDate,
|
||||
DeadlineDate = model.DeadlineDate,
|
||||
IsEnabled = model.IsEnabled
|
||||
};
|
||||
|
||||
return _mapper.Map<InstructionDTO>(await CreateInstruction(newInstruction));
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteInstructionById(long instructionId)
|
||||
{
|
||||
Instruction? instruction = _unitOfWork.InstructionRepository.GetByID(instructionId);
|
||||
if (instruction == null)
|
||||
{
|
||||
throw new InstructionNotFoundException();
|
||||
}
|
||||
_unitOfWork.InstructionRepository.Delete(instruction);
|
||||
|
||||
if (await _unitOfWork.SaveAsync() == false)
|
||||
{
|
||||
_logger.LogError($"Instruction {instructionId} could not be deleted");
|
||||
throw new InstructionDeletionException();
|
||||
}
|
||||
|
||||
_logger.LogInformation($"Instruction {instructionId} deleted");
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<InstructionDTO> GetAllInstructions(long userId)
|
||||
{
|
||||
// TODO: select accessible only
|
||||
var instructions = _unitOfWork.InstructionRepository.Get().ToList();
|
||||
return _mapper.Map<List<InstructionDTO>>(instructions);
|
||||
}
|
||||
|
||||
public List<InstructionDTO> GetCompletedInstructions(long userId)
|
||||
{
|
||||
var completedTests = _instructionTestService.GetCompletedInstructionTestsByUserId(userId);
|
||||
var instructions = _unitOfWork.InstructionRepository.Get()
|
||||
.Where(i => completedTests.Any(t => i.InstructionTestId == t.Id));
|
||||
|
||||
return _mapper.Map<List<InstructionDTO>>(instructions);
|
||||
}
|
||||
|
||||
public List<InstructionDTO> GetInstructionById(long userId, long instructionId)
|
||||
{
|
||||
// TODO: select accessible only
|
||||
var instruction = _unitOfWork.InstructionRepository.GetByID(instructionId);
|
||||
return _mapper.Map<List<InstructionDTO>>(instruction);
|
||||
}
|
||||
|
||||
public List<InstructionDTO> GetInstructionsByCategoryId(long userId, long categoryId)
|
||||
{
|
||||
var category = _unitOfWork.InstructionCategoryRepository.GetByID(categoryId);
|
||||
if (category == null)
|
||||
{
|
||||
throw new CategoryNotFoundException();
|
||||
}
|
||||
|
||||
var instructions = _unitOfWork.InstructionRepository.Get().Where(i => i.CategoryId == categoryId);
|
||||
return _mapper.Map<List<InstructionDTO>>(instructions);
|
||||
}
|
||||
|
||||
public List<InstructionDTO> GetUnfinishedInstructions(long userId)
|
||||
{
|
||||
// TODO: only show accessible
|
||||
var completedTests = _instructionTestService.GetCompletedInstructionTestsByUserId(userId);
|
||||
|
||||
var instructions = _unitOfWork.InstructionRepository.Get()
|
||||
.Where(i => !completedTests.Any(t => i.InstructionTestId == t.Id));
|
||||
|
||||
return _mapper.Map<List<InstructionDTO>>(instructions);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateInstructionById(Instruction model)
|
||||
{
|
||||
var existingInstruction = _unitOfWork.InstructionRepository.GetByID(model.Id);
|
||||
if (existingInstruction == null)
|
||||
{
|
||||
throw new InstructionNotFoundException();
|
||||
}
|
||||
|
||||
await _unitOfWork.BeginTransactionAsync();
|
||||
|
||||
_unitOfWork.InstructionParagraphRepository.DeleteRange(existingInstruction.Paragraphs);
|
||||
|
||||
if (await _unitOfWork.SaveAsync() == false)
|
||||
{
|
||||
_logger.LogError($"Failure to delete older paragraphs for instruction {model.Id} during update");
|
||||
throw new InstructionUpdateException();
|
||||
}
|
||||
|
||||
_unitOfWork.InstructionRepository.Update(model);
|
||||
if (await _unitOfWork.SaveAsync() == false)
|
||||
{
|
||||
_logger.LogError($"Instruction {model.Id} could not be updated");
|
||||
throw new InstructionUpdateException();
|
||||
}
|
||||
_logger.LogInformation($"Instruction {model.Id} updated");
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateInstructionById(UpdateInstructionRequest model)
|
||||
{
|
||||
Instruction? existingInstruction = _unitOfWork.InstructionRepository.GetByID(model.Id);
|
||||
|
||||
if (existingInstruction == null)
|
||||
{
|
||||
throw new InstructionNotFoundException();
|
||||
}
|
||||
|
||||
existingInstruction.Title = model.Title;
|
||||
existingInstruction.Description = model.Description;
|
||||
existingInstruction.CategoryId = model.CategoryId;
|
||||
existingInstruction.Paragraphs = _mapper.Map<List<InstructionParagraph>>(model.Paragraphs);
|
||||
existingInstruction.AssignDate = model.AssignDate;
|
||||
existingInstruction.DeadlineDate = model.DeadlineDate;
|
||||
existingInstruction.IsEnabled = model.IsEnabled;
|
||||
|
||||
return await UpdateInstructionById(_mapper.Map<Instruction>(model));
|
||||
}
|
||||
}
|
||||
13
Services/JWT/IJWTService.cs
Executable file
13
Services/JWT/IJWTService.cs
Executable file
@@ -0,0 +1,13 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using GamificationService.Models.Database;
|
||||
using GamificationService.Models.DTO;
|
||||
|
||||
namespace GamificationService.Services.JWT;
|
||||
|
||||
public interface IJwtService
|
||||
{
|
||||
string GenerateAccessToken(ApplicationUser user);
|
||||
JwtSecurityToken ValidateAccessToken(string token);
|
||||
Task<RefreshToken> GenerateRefreshTokenAsync(ApplicationUser user);
|
||||
Task RevokeRefreshTokenAsync(long userId, string refreshToken, string remoteIpAddress);
|
||||
}
|
||||
144
Services/JWT/JWTService.cs
Executable file
144
Services/JWT/JWTService.cs
Executable file
@@ -0,0 +1,144 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using GamificationService.Database.Repositories;
|
||||
using GamificationService.Exceptions.Services.JwtService;
|
||||
using GamificationService.Exceptions.UtilServices.JWT;
|
||||
using GamificationService.Models.Database;
|
||||
using GamificationService.Models.DTO;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace GamificationService.Services.JWT;
|
||||
|
||||
public class JwtService : IJwtService
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILogger<JwtService> _logger;
|
||||
private readonly UnitOfWork _unitOfWork;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public JwtService(IConfiguration configuration, ILogger<JwtService> logger, UnitOfWork unitOfWork)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_logger = logger;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
public string GenerateAccessToken(ApplicationUser user)
|
||||
{
|
||||
var jwtSettings = _configuration.GetSection("JwtSettings");
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["Key"]));
|
||||
var issuer = jwtSettings["Issuer"];
|
||||
var audience = jwtSettings["Audience"];
|
||||
|
||||
var claims = new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
||||
new Claim(ClaimTypes.Name, user.UserName),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
|
||||
};
|
||||
|
||||
var userRoles = _unitOfWork.UserRoleRepository.Get()
|
||||
.Where(ur => ur.UserId == user.Id)
|
||||
.Select(ur => ur.Role)
|
||||
.Include(rr => rr.RoleRights)
|
||||
.ThenInclude(rr=>rr.Right)
|
||||
.ToList();
|
||||
|
||||
foreach (var role in userRoles)
|
||||
{
|
||||
claims.Add(new Claim(ClaimTypes.Role, role.Name));
|
||||
|
||||
foreach (var right in role.RoleRights.Select(rr => rr.Right))
|
||||
{
|
||||
claims.Add(new Claim("Right", right.Name));
|
||||
}
|
||||
}
|
||||
|
||||
var expires = DateTime.UtcNow.AddMinutes(double.Parse(jwtSettings["AccessTokenExpirationMinutes"]));
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: issuer,
|
||||
audience: audience,
|
||||
claims: claims,
|
||||
expires: expires,
|
||||
signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
|
||||
);
|
||||
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
|
||||
private string GenerateRefreshToken()
|
||||
{
|
||||
var randomNumber = new byte[32];
|
||||
using var rng = RandomNumberGenerator.Create();
|
||||
rng.GetBytes(randomNumber);
|
||||
return Convert.ToBase64String(randomNumber);
|
||||
}
|
||||
|
||||
public JwtSecurityToken ValidateAccessToken(string token)
|
||||
{
|
||||
var jwtSettings = _configuration.GetSection("JwtSettings");
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings["Key"]));
|
||||
var issuer = jwtSettings["Issuer"];
|
||||
var audience = jwtSettings["Audience"];
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var validationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = key,
|
||||
ValidateIssuer = true,
|
||||
ValidIssuer = issuer,
|
||||
ValidateAudience = true,
|
||||
ValidAudience = audience,
|
||||
ValidateLifetime = true,
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
|
||||
SecurityToken validatedToken;
|
||||
var principal = tokenHandler.ValidateToken(token, validationParameters, out validatedToken);
|
||||
|
||||
return validatedToken as JwtSecurityToken;
|
||||
}
|
||||
|
||||
public async Task<RefreshToken> GenerateRefreshTokenAsync(ApplicationUser user)
|
||||
{
|
||||
var dbRefreshToken = new RefreshToken
|
||||
{
|
||||
UserId = user.Id,
|
||||
Token = GenerateRefreshToken(),
|
||||
Expires = DateTime.UtcNow.AddDays(double.Parse(_configuration["JwtSettings:RefreshTokenExpirationDays"])),
|
||||
Created = DateTime.UtcNow
|
||||
};
|
||||
|
||||
await _unitOfWork.RefreshTokenRepository.InsertAsync(dbRefreshToken);
|
||||
if (!await _unitOfWork.SaveAsync())
|
||||
{
|
||||
throw new GenerateRefreshTokenException("Failed to generate refresh token");
|
||||
}
|
||||
|
||||
return dbRefreshToken;
|
||||
}
|
||||
|
||||
public async Task RevokeRefreshTokenAsync(long userId, string refreshToken, string remoteIpAddress)
|
||||
{
|
||||
var token = await _unitOfWork.RefreshTokenRepository.Get()
|
||||
.FirstOrDefaultAsync(x => x.UserId == userId && x.Token == refreshToken);
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
token.IsRevoked = true;
|
||||
token.RevokedByIp = remoteIpAddress;
|
||||
token.RevokedOn = DateTime.UtcNow;
|
||||
|
||||
await _unitOfWork.SaveAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Services/Notification/INotificationService.cs
Executable file
11
Services/Notification/INotificationService.cs
Executable file
@@ -0,0 +1,11 @@
|
||||
using System.Net.Mail;
|
||||
using GamificationService.Models.Database;
|
||||
using GamificationService.Utils;
|
||||
|
||||
namespace GamificationService.Services.NotificationService;
|
||||
|
||||
public interface INotificationService
|
||||
{
|
||||
public Task SendMailNotificationAsync(ApplicationUser user, Notification notification);
|
||||
public Task SendPushNotificationAsync(ApplicationUser user, Notification notification);
|
||||
}
|
||||
56
Services/Notification/NotificationService.cs
Executable file
56
Services/Notification/NotificationService.cs
Executable file
@@ -0,0 +1,56 @@
|
||||
using System.Net.Mail;
|
||||
using GamificationService.Models.Database;
|
||||
using GamificationService.Utils;
|
||||
using GamificationService.Utils.Factory;
|
||||
|
||||
namespace GamificationService.Services.NotificationService;
|
||||
|
||||
public class NotificationService : INotificationService
|
||||
{
|
||||
#region Services
|
||||
|
||||
private readonly EmailClient _emailClient;
|
||||
private readonly ILogger<NotificationService> _logger;
|
||||
private readonly PushNotificationsClient _pushNotificationsClient;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
public NotificationService(EmailClient emailClient, PushNotificationsClient pushNotificationsClient, ILogger<NotificationService> logger)
|
||||
{
|
||||
_emailClient = emailClient;
|
||||
_pushNotificationsClient = pushNotificationsClient;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
public async Task SendMailNotificationAsync(ApplicationUser user, Notification notification)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _emailClient.SendEmail(((MailNotification)notification).ConvertToMailMessage(), user.Email);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e,e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Refactor, add reg.ru notifications
|
||||
public async Task SendPushNotificationAsync(ApplicationUser user, Notification notification)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _emailClient.SendEmail(((MailNotification)notification).ConvertToMailMessage(), user.Email);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e,e.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Services/Rights/IRightsService.cs
Executable file
12
Services/Rights/IRightsService.cs
Executable file
@@ -0,0 +1,12 @@
|
||||
using GamificationService.Models.Database;
|
||||
|
||||
namespace GamificationService.Services.Rights;
|
||||
|
||||
public interface IRightsService
|
||||
{
|
||||
Task<Right?> CreateRightAsync(string rightName, string description);
|
||||
Task<bool> UpdateRightAsync(long rightId, string newRightName, string newDescription);
|
||||
Task<bool> DeleteRightAsync(long rightId);
|
||||
Task<Right?> GetRightByIdAsync(long rightId);
|
||||
Task<(List<Right> Rights, int TotalCount)> GetAllRightsAsync(int pageNumber = 1, int pageSize = 10);
|
||||
}
|
||||
103
Services/Rights/RightsService.cs
Executable file
103
Services/Rights/RightsService.cs
Executable file
@@ -0,0 +1,103 @@
|
||||
using GamificationService.Database.Repositories;
|
||||
using GamificationService.Models.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace GamificationService.Services.Rights;
|
||||
|
||||
public class RightsService : IRightsService
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly UnitOfWork _unitOfWork;
|
||||
private readonly ILogger<IRightsService> _logger;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
public RightsService(UnitOfWork unitOfWork, ILogger<IRightsService> logger)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
public async Task<Right?> CreateRightAsync(string rightName, string description)
|
||||
{
|
||||
var right = new Right
|
||||
{
|
||||
Name = rightName,
|
||||
Description = description
|
||||
};
|
||||
|
||||
await _unitOfWork.RightRepository.InsertAsync(right);
|
||||
if (await _unitOfWork.SaveAsync())
|
||||
{
|
||||
return right;
|
||||
}
|
||||
|
||||
throw new Exception($"Unable to create right for {rightName}");
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateRightAsync(long rightId, string newRightName, string newDescription)
|
||||
{
|
||||
var right = await _unitOfWork.RightRepository.GetByIDAsync(rightId);
|
||||
|
||||
if (right == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"Right with ID {rightId} not found");
|
||||
}
|
||||
|
||||
right.Name = newRightName;
|
||||
right.Description = newDescription;
|
||||
|
||||
if (!await _unitOfWork.SaveAsync())
|
||||
{
|
||||
throw new Exception($"Unable to create right for {rightId}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteRightAsync(long rightId)
|
||||
{
|
||||
var right = await _unitOfWork.RightRepository.GetByIDAsync(rightId);
|
||||
|
||||
if (right == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"Right with ID {rightId} not found");
|
||||
}
|
||||
|
||||
_unitOfWork.RightRepository.Delete(right);
|
||||
if (!await _unitOfWork.SaveAsync())
|
||||
{
|
||||
throw new Exception($"Unable to delete right for {rightId}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<Right?> GetRightByIdAsync(long rightId)
|
||||
{
|
||||
return await _unitOfWork.RightRepository.GetByIDAsync(rightId);
|
||||
}
|
||||
|
||||
public async Task<(List<Right> Rights, int TotalCount)> GetAllRightsAsync(int pageNumber = 1, int pageSize = 10)
|
||||
{
|
||||
var query = _unitOfWork.RightRepository.Get();
|
||||
|
||||
var totalItems = await query.CountAsync();
|
||||
|
||||
var pagedRights = await query
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
return (pagedRights, totalItems);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
14
Services/Roles/IRolesService.cs
Executable file
14
Services/Roles/IRolesService.cs
Executable file
@@ -0,0 +1,14 @@
|
||||
using GamificationService.Models.Database;
|
||||
|
||||
namespace GamificationService.Services.Roles;
|
||||
|
||||
public interface IRolesService
|
||||
{
|
||||
Task<ApplicationRole> CreateRoleAsync(string roleName, string description);
|
||||
Task<bool> UpdateRoleAsync(long roleId, string newRoleName, string newDescription);
|
||||
Task<bool> DeleteRoleAsync(long roleId);
|
||||
Task<bool> AddRightToRoleAsync(long roleId, long rightId);
|
||||
Task<bool> RemoveRightFromRoleAsync(long roleId, long rightId);
|
||||
Task<ApplicationRole> GetRoleByIdAsync(long roleId);
|
||||
Task<(List<ApplicationRole> Roles, int TotalCount)> GetAllRolesAsync(int pageNumber = 1, int pageSize = 10);
|
||||
}
|
||||
162
Services/Roles/RolesService.cs
Executable file
162
Services/Roles/RolesService.cs
Executable file
@@ -0,0 +1,162 @@
|
||||
using GamificationService.Database.Repositories;
|
||||
using GamificationService.Models.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace GamificationService.Services.Roles;
|
||||
|
||||
public class RolesService : IRolesService
|
||||
{
|
||||
#region Services
|
||||
|
||||
private readonly ILogger<IRolesService> _logger;
|
||||
private readonly UnitOfWork _unitOfWork;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
public RolesService(ILogger<IRolesService> logger, UnitOfWork unitOfWork)
|
||||
{
|
||||
_logger = logger;
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
//TODO: refactor database work, to be more beautiful
|
||||
//ToDo: make better exception handling
|
||||
public async Task<ApplicationRole> CreateRoleAsync(string roleName, string description)
|
||||
{
|
||||
var role = new ApplicationRole(roleName)
|
||||
{
|
||||
Description = description
|
||||
};
|
||||
|
||||
await _unitOfWork.RoleRepository.InsertAsync(role);
|
||||
if (await _unitOfWork.SaveAsync())
|
||||
{
|
||||
return role;
|
||||
}
|
||||
throw new Exception("Unable to create role");
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateRoleAsync(long roleId, string newRoleName, string newDescription)
|
||||
{
|
||||
var role = await _unitOfWork.RoleRepository.GetByIDAsync(roleId);
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"Role with ID {roleId} not found");
|
||||
}
|
||||
|
||||
role.Name = newRoleName;
|
||||
role.Description = newDescription;
|
||||
|
||||
if (!await _unitOfWork.SaveAsync())
|
||||
{
|
||||
throw new Exception("Unable to create role");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteRoleAsync(long roleId)
|
||||
{
|
||||
var role = await _unitOfWork.RoleRepository.GetByIDAsync(roleId);
|
||||
|
||||
if (role == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"Role with ID {roleId} not found");
|
||||
}
|
||||
|
||||
_unitOfWork.RoleRepository.Delete(role);
|
||||
if (!await _unitOfWork.SaveAsync())
|
||||
{
|
||||
throw new Exception("Unable to delete role");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> AddRightToRoleAsync(long roleId, long rightId)
|
||||
{
|
||||
var role = await _unitOfWork.RoleRepository.Get()
|
||||
.Include(r => r.RoleRights)
|
||||
.FirstOrDefaultAsync(r => r.Id == roleId);
|
||||
|
||||
var right = await _unitOfWork.RightRepository.GetByIDAsync(rightId);
|
||||
|
||||
if (role == null || right == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"Role or Right not found");
|
||||
}
|
||||
|
||||
var existingRight = role.RoleRights.FirstOrDefault(rr => rr.RightId == rightId);
|
||||
|
||||
if (existingRight == null)
|
||||
{
|
||||
role.RoleRights.Add(new RoleRight { RoleId = roleId, RightId = rightId });
|
||||
if (!await _unitOfWork.SaveAsync())
|
||||
{
|
||||
throw new Exception("Unable to add role right");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public async Task<bool> RemoveRightFromRoleAsync(long roleId, long rightId)
|
||||
{
|
||||
var roleRight = await _unitOfWork.RoleRightRepository.Get()
|
||||
.FirstOrDefaultAsync(rr => rr.RoleId == roleId && rr.RightId == rightId);
|
||||
|
||||
if (roleRight == null)
|
||||
{
|
||||
throw new KeyNotFoundException($"Right not found for role");
|
||||
}
|
||||
|
||||
_unitOfWork.RoleRightRepository.Delete(roleRight);
|
||||
if (!await _unitOfWork.SaveAsync())
|
||||
{
|
||||
throw new Exception("Unable to remove role right");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<ApplicationRole> GetRoleByIdAsync(long roleId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _unitOfWork.RoleRepository.Get()
|
||||
.Include(r => r.RoleRights)
|
||||
.ThenInclude(rr => rr.Right)
|
||||
.FirstOrDefaultAsync(r => r.Id == roleId);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.LogError(e, e.Message);
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public async Task<(List<ApplicationRole> Roles, int TotalCount)> GetAllRolesAsync(int pageNumber = 1, int pageSize = 10)
|
||||
{
|
||||
var query = _unitOfWork.RoleRepository.Get()
|
||||
.Include(r => r.RoleRights)
|
||||
.ThenInclude(rr => rr.Right);
|
||||
|
||||
var totalItems = await query.CountAsync();
|
||||
var pagedRoles = await query
|
||||
.Skip((pageNumber - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
|
||||
return (pagedRoles, totalItems);
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
15
Services/UsersProfile/IUserProfileService.cs
Executable file
15
Services/UsersProfile/IUserProfileService.cs
Executable file
@@ -0,0 +1,15 @@
|
||||
using GamificationService.Models.Database;
|
||||
using GamificationService.Models.DTO;
|
||||
|
||||
namespace GamificationService.Services.UsersProfile;
|
||||
|
||||
public interface IUserProfileService
|
||||
{
|
||||
public Task<UserProfileDTO> AddUserProfile(long userId, UserProfileCreateDTO userProfile);
|
||||
public Task<UserProfile> AddUserProfile(UserProfile userProfile);
|
||||
public UserProfile? GetUserProfileByUserId(long id);
|
||||
public UserProfile? GetUserProfileById(long id);
|
||||
public Task<bool> UpdateUserProfileByUserId(long userId, UserProfileCreateDTO userProfile);
|
||||
public Task<bool> UpdateUserProfile(UserProfile userProfile);
|
||||
public bool DeleteUserProfile(long id);
|
||||
}
|
||||
121
Services/UsersProfile/UserProfileService.cs
Executable file
121
Services/UsersProfile/UserProfileService.cs
Executable file
@@ -0,0 +1,121 @@
|
||||
using AutoMapper;
|
||||
using GamificationService.Database.Repositories;
|
||||
using GamificationService.Exceptions.Services.ProfileService;
|
||||
using GamificationService.Models.Database;
|
||||
using GamificationService.Models.DTO;
|
||||
|
||||
namespace GamificationService.Services.UsersProfile;
|
||||
|
||||
public class UserProfileService : IUserProfileService
|
||||
{
|
||||
private readonly UnitOfWork _unitOfWork;
|
||||
|
||||
# region Services
|
||||
|
||||
private readonly ILogger<UserProfileService> _logger;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Constructor
|
||||
|
||||
public UserProfileService(UnitOfWork unitOfWork, ILogger<UserProfileService> logger, IMapper mapper)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
_logger = logger;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
# region Methods
|
||||
|
||||
public async Task<UserProfileDTO> AddUserProfile(long userId, UserProfileCreateDTO userProfile)
|
||||
{
|
||||
UserProfile userProfileEntity = _mapper.Map<UserProfile>(userProfile);
|
||||
userProfileEntity.UserId = userId;
|
||||
return _mapper.Map<UserProfileDTO>(await AddUserProfile(userProfileEntity));
|
||||
}
|
||||
|
||||
public async Task<UserProfile> AddUserProfile(UserProfile userProfile)
|
||||
{
|
||||
UserProfile userProfileEntity = userProfile;
|
||||
|
||||
// Make sure a user profile for the given user does not exist yet
|
||||
if (_unitOfWork.UserProfileRepository.Get(x => x.UserId == userProfile.UserId).Any())
|
||||
{
|
||||
_logger.LogWarning("A user profile already exists for the given user id: {UserId}", userProfile.UserId);
|
||||
throw new ProfileExistsException($"{userProfile.UserId}");
|
||||
}
|
||||
|
||||
await _unitOfWork.UserProfileRepository.InsertAsync(userProfileEntity);
|
||||
if (await _unitOfWork.SaveAsync())
|
||||
{
|
||||
_logger.LogInformation("User profile added for user id: {UserId}", userProfile.UserId);
|
||||
return userProfileEntity;
|
||||
}
|
||||
|
||||
_logger.LogError("Failed to add user profile for user id: {UserId}", userProfile.UserId);
|
||||
throw new ProfileCreationException();
|
||||
}
|
||||
|
||||
public UserProfile? GetUserProfileByUserId(long id)
|
||||
{
|
||||
return _unitOfWork.UserProfileRepository.Get(x => x.UserId == id).FirstOrDefault();
|
||||
}
|
||||
|
||||
public UserProfile? GetUserProfileById(long id)
|
||||
{
|
||||
return _unitOfWork.UserProfileRepository.GetByID(id);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateUserProfileByUserId(long userId, UserProfileCreateDTO userProfile)
|
||||
{
|
||||
var userProfileEntityUpdated = _mapper.Map<UserProfile>(userProfile);
|
||||
var profile = _unitOfWork.UserProfileRepository
|
||||
.Get(x => x.UserId == userId).FirstOrDefault() ?? throw new ProfileNotFoundException($"{userId}");
|
||||
userProfileEntityUpdated.Id = profile.Id;
|
||||
return await UpdateUserProfile(userProfileEntityUpdated);
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateUserProfile(UserProfile userProfile)
|
||||
{
|
||||
var userProfileEntityUpdated = userProfile;
|
||||
var userProfileEntity = await _unitOfWork.UserProfileRepository.GetByIDAsync(userProfileEntityUpdated.Id);
|
||||
|
||||
if (userProfileEntity == null)
|
||||
{
|
||||
throw new ProfileNotFoundException($"{userProfileEntityUpdated.Id}");
|
||||
}
|
||||
|
||||
_mapper.Map(userProfileEntityUpdated, userProfileEntity);
|
||||
|
||||
if (!await _unitOfWork.SaveAsync())
|
||||
{
|
||||
throw new ProfileUpdateException($"Failed to update user profile {userProfileEntityUpdated.Id}");
|
||||
}
|
||||
|
||||
_logger.LogInformation("User profile updated for user id: {UserId}", userProfile.UserId);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool DeleteUserProfile(long id)
|
||||
{
|
||||
var profile = _unitOfWork.UserProfileRepository.GetByID(id);
|
||||
if (profile == null)
|
||||
{
|
||||
throw new ProfileNotFoundException($"{id}");
|
||||
}
|
||||
|
||||
_unitOfWork.UserProfileRepository.Delete(id);
|
||||
if (_unitOfWork.Save())
|
||||
{
|
||||
_logger.LogInformation("User profile deleted: {UserId}", id);
|
||||
return true;
|
||||
}
|
||||
throw new ProfileDeletionException($"Failed to delete user profile {id}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
Reference in New Issue
Block a user