From 6692a918213798b8bdd132ee0370d910a78b2b65 Mon Sep 17 00:00:00 2001 From: shept Date: Sun, 20 Apr 2025 23:12:20 +0500 Subject: [PATCH] life quality changes - refactored SurveyController.cs - added ProducesResponseType to every endpoint in every controller - remade mappers --- .../Controllers/AuthController.cs | 8 ++- .../Controllers/QuestionController.cs | 22 ++++--- .../Controllers/SurveyController.cs | 33 +++++++---- .../DTOs/Question/CreateQuestionDTO.cs | 1 - .../DTOs/Survey/OutputSurveyDto.cs | 9 +++ ...serRegistrationMapper.cs => AuthMapper.cs} | 5 +- .../QuestionDTOs/QuestionCreationMapper.cs | 34 ----------- .../QuestionDTOs/QuestionOutputMapper.cs | 31 ---------- .../Mappers/QuestionMapper.cs | 57 +++++++++++++++++++ .../SurveyBackend.API/Mappers/SurveyMapper.cs | 28 +++++++++ .../Services/QuestionService.cs | 11 +++- 11 files changed, 146 insertions(+), 93 deletions(-) create mode 100644 SurveyBackend/SurveyBackend.API/DTOs/Survey/OutputSurveyDto.cs rename SurveyBackend/SurveyBackend.API/Mappers/{UserDTOs/UserRegistrationMapper.cs => AuthMapper.cs} (70%) delete mode 100644 SurveyBackend/SurveyBackend.API/Mappers/QuestionDTOs/QuestionCreationMapper.cs delete mode 100644 SurveyBackend/SurveyBackend.API/Mappers/QuestionDTOs/QuestionOutputMapper.cs create mode 100644 SurveyBackend/SurveyBackend.API/Mappers/QuestionMapper.cs create mode 100644 SurveyBackend/SurveyBackend.API/Mappers/SurveyMapper.cs diff --git a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs index 06bfddc..efa8023 100644 --- a/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs +++ b/SurveyBackend/SurveyBackend.API/Controllers/AuthController.cs @@ -1,7 +1,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using SurveyBackend.DTOs; -using SurveyBackend.Mappers.UserDTOs; +using SurveyBackend.Mappers; using IAuthorizationService = SurveyBackend.Core.Services.IAuthorizationService; namespace SurveyBackend.Controllers; @@ -19,6 +19,8 @@ public class AuthController : ControllerBase [AllowAnonymous] [HttpPost("login")] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status200OK)] public async Task LogIn([FromBody] UserLoginDto loginData) { var token = await _authorizationService.LogInUser(loginData.Email, loginData.Password); @@ -27,10 +29,12 @@ public class AuthController : ControllerBase [AllowAnonymous] [HttpPost("register")] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status200OK)] public async Task Register([FromBody] UserRegistrationDto registerData) { var token = await _authorizationService.RegisterUser( - UserRegistrationMapper.UserRegistrationToModel(registerData)); + AuthMapper.UserRegistrationToModel(registerData)); return Ok(new { token = token }); } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/Controllers/QuestionController.cs b/SurveyBackend/SurveyBackend.API/Controllers/QuestionController.cs index 0a522c5..fcaabf2 100644 --- a/SurveyBackend/SurveyBackend.API/Controllers/QuestionController.cs +++ b/SurveyBackend/SurveyBackend.API/Controllers/QuestionController.cs @@ -2,13 +2,13 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using SurveyBackend.Core.Contexts; using SurveyBackend.DTOs.Question; -using SurveyBackend.Mappers.QuestionDTOs; +using SurveyBackend.Mappers; using SurveyLib.Core.Services; namespace SurveyBackend.Controllers; [ApiController] -[Route("api/questions")] +[Route("surveys/{surveyId}/questions")] public class QuestionController : ControllerBase { private readonly IQuestionService _questionService; @@ -19,20 +19,24 @@ public class QuestionController : ControllerBase } [AllowAnonymous] - [HttpGet("by_survey/{id}")] - public async Task GetBySurveyId(int id) + [HttpGet] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] + public async Task GetBySurveyId([FromRoute] int surveyId) { - var questions = await _questionService.GetQuestionsBySurveyIdAsync(id); - var result = questions.Select(QuestionOutputMapper.ModelToQuestionDTO).ToList(); + var questions = await _questionService.GetQuestionsBySurveyIdAsync(surveyId); + var result = questions.Select(QuestionMapper.ModelToQuestionDto).ToList(); return Ok(result); } [Authorize] [HttpPost] - public async Task AddQuestion(CreateQuestionDto dto) + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status201Created)] + public async Task AddQuestion(CreateQuestionDto dto, [FromRoute] int surveyId) { - var model = QuestionCreationMapper.QuestionCreationToModel(dto); + var model = QuestionMapper.QuestionCreationToModel(dto, surveyId); await _questionService.AddQuestionAsync(model); - return Ok(); + return Created(); } } \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/Controllers/SurveyController.cs b/SurveyBackend/SurveyBackend.API/Controllers/SurveyController.cs index 7b2379e..6db2d78 100644 --- a/SurveyBackend/SurveyBackend.API/Controllers/SurveyController.cs +++ b/SurveyBackend/SurveyBackend.API/Controllers/SurveyController.cs @@ -3,13 +3,15 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using SurveyBackend.Core.Contexts; using SurveyBackend.DTOs.Survey; +using SurveyBackend.Mappers; +using SurveyBackend.Services.Exceptions; using SurveyLib.Core.Models; using SurveyLib.Core.Services; namespace SurveyBackend.Controllers; [ApiController] -[Route("api/surveys")] +[Route("surveys")] public class SurveyController : ControllerBase { private readonly ISurveyService _surveyService; @@ -23,38 +25,42 @@ public class SurveyController : ControllerBase [AllowAnonymous] [HttpGet] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] public async Task Get() { - var result = await _surveyService.GetSurveysAsync(); + var surveys = await _surveyService.GetSurveysAsync(); + var result = surveys.Select(SurveyMapper.ModelToOutputDto); return Ok(result); } [AllowAnonymous] [HttpGet("{id}")] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(typeof(OutputSurveyDto), StatusCodes.Status200OK)] public async Task Get(int id) { - var result = await _surveyService.GetSurveyAsync(id); - return result is not null ? Ok(result) : NotFound(); + var survey = await _surveyService.GetSurveyAsync(id); + var result = SurveyMapper.ModelToOutputDto(survey); + return Ok(result); } [Authorize] [HttpPost] + [ProducesResponseType(StatusCodes.Status201Created)] public async Task Post([FromBody] CreateSurveyDto dto) { var userId = _userContext.UserId; - var survey = new Survey - { - Title = dto.Title, - Description = dto.Description, - CreatedBy = userId, - }; + var survey = SurveyMapper.CreateDtoToModel(dto, userId); await _surveyService.AddSurveyAsync(survey); - return Ok(); + return Created(); } [Authorize] [HttpDelete("{id}")] + [ProducesResponseType(StatusCodes.Status404NotFound)] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(StatusCodes.Status200OK)] public async Task Delete(int id) { await _surveyService.DeleteSurveyAsync(id); @@ -62,10 +68,13 @@ public class SurveyController : ControllerBase } [Authorize] - [HttpGet("my_surveys")] + [HttpGet("my")] + [ProducesResponseType(StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(List), StatusCodes.Status200OK)] public async Task GetMySurveys() { var userId = _userContext.UserId; + var result = await _surveyService.GetSurveysByUserIdAsync(userId); return Ok(result); } diff --git a/SurveyBackend/SurveyBackend.API/DTOs/Question/CreateQuestionDTO.cs b/SurveyBackend/SurveyBackend.API/DTOs/Question/CreateQuestionDTO.cs index e295fc2..2379b73 100644 --- a/SurveyBackend/SurveyBackend.API/DTOs/Question/CreateQuestionDTO.cs +++ b/SurveyBackend/SurveyBackend.API/DTOs/Question/CreateQuestionDTO.cs @@ -3,7 +3,6 @@ namespace SurveyBackend.DTOs.Question; public class CreateQuestionDto { public required string Title { get; set; } - public required int SurveyId { get; set; } public required string QuestionType { get; set; } public List? AnswerVariants { get; set; } diff --git a/SurveyBackend/SurveyBackend.API/DTOs/Survey/OutputSurveyDto.cs b/SurveyBackend/SurveyBackend.API/DTOs/Survey/OutputSurveyDto.cs new file mode 100644 index 0000000..faec104 --- /dev/null +++ b/SurveyBackend/SurveyBackend.API/DTOs/Survey/OutputSurveyDto.cs @@ -0,0 +1,9 @@ +namespace SurveyBackend.DTOs.Survey; + +public class OutputSurveyDto +{ + public required int Id { get; set; } + public required string Title { get; set; } + public required string Description { get; set; } + public int? CreatedBy { get; set; } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/Mappers/UserDTOs/UserRegistrationMapper.cs b/SurveyBackend/SurveyBackend.API/Mappers/AuthMapper.cs similarity index 70% rename from SurveyBackend/SurveyBackend.API/Mappers/UserDTOs/UserRegistrationMapper.cs rename to SurveyBackend/SurveyBackend.API/Mappers/AuthMapper.cs index 5d98322..50b1980 100644 --- a/SurveyBackend/SurveyBackend.API/Mappers/UserDTOs/UserRegistrationMapper.cs +++ b/SurveyBackend/SurveyBackend.API/Mappers/AuthMapper.cs @@ -1,10 +1,9 @@ using SurveyBackend.Core.Models; -using SurveyBackend.Core.Services; using SurveyBackend.DTOs; -namespace SurveyBackend.Mappers.UserDTOs; +namespace SurveyBackend.Mappers; -public static class UserRegistrationMapper +public static class AuthMapper { public static User UserRegistrationToModel(UserRegistrationDto dto) => new User { diff --git a/SurveyBackend/SurveyBackend.API/Mappers/QuestionDTOs/QuestionCreationMapper.cs b/SurveyBackend/SurveyBackend.API/Mappers/QuestionDTOs/QuestionCreationMapper.cs deleted file mode 100644 index aeacd4f..0000000 --- a/SurveyBackend/SurveyBackend.API/Mappers/QuestionDTOs/QuestionCreationMapper.cs +++ /dev/null @@ -1,34 +0,0 @@ -using SurveyBackend.DTOs.Question; -using SurveyBackend.Services.Exceptions; -using SurveyLib.Core.Models; -using SurveyLib.Core.Models.QuestionVariants; - -namespace SurveyBackend.Mappers.QuestionDTOs; - -public static class QuestionCreationMapper -{ - public static QuestionBase QuestionCreationToModel(CreateQuestionDto dto) - { - return dto.QuestionType.ToLower() switch - { - "textquestion" => new TextQuestion - { - Title = dto.Title, - SurveyId = dto.SurveyId, - }, - "singleanswerquestion" => new SingleAnswerQuestion - { - Title = dto.Title, - SurveyId = dto.SurveyId, - AnswerVariants = dto.AnswerVariants?.Select(v => new AnswerVariant { Text = v }).ToList() ?? [], - }, - "multipleanswerquestion" => new MultipleAnswerQuestion - { - Title = dto.Title, - SurveyId = dto.SurveyId, - AnswerVariants = dto.AnswerVariants?.Select(v => new AnswerVariant { Text = v }).ToList() ?? [] - }, - _ => throw new BadRequestException("Unknown question type") - }; - } -} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/Mappers/QuestionDTOs/QuestionOutputMapper.cs b/SurveyBackend/SurveyBackend.API/Mappers/QuestionDTOs/QuestionOutputMapper.cs deleted file mode 100644 index a647851..0000000 --- a/SurveyBackend/SurveyBackend.API/Mappers/QuestionDTOs/QuestionOutputMapper.cs +++ /dev/null @@ -1,31 +0,0 @@ -using SurveyBackend.DTOs.Question; -using SurveyLib.Core.Models; -using SurveyLib.Core.Models.QuestionVariants; - -namespace SurveyBackend.Mappers.QuestionDTOs; - -public static class QuestionOutputMapper -{ - public static OutputQuestionDto ModelToQuestionDTO(QuestionBase question) - { - var withAnswerVariants = question.GetType() != typeof(TextQuestion); - return new OutputQuestionDto - { - Id = question.Id, - SurveyId = question.SurveyId, - Title = question.Title, - QuestionType = question.Discriminator, - AnswerVariants = withAnswerVariants ? GetAnswerVariants(question.AnswerVariants) : null, - }; - } - - private static List GetAnswerVariants(IEnumerable answerVariants) - { - return answerVariants.Select(av => new OutputAnswerVariantDto - { - Id = av.Id, - QuestionId = av.QuestionId, - Text = av.Text, - }).ToList(); - } -} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/Mappers/QuestionMapper.cs b/SurveyBackend/SurveyBackend.API/Mappers/QuestionMapper.cs new file mode 100644 index 0000000..b6fd399 --- /dev/null +++ b/SurveyBackend/SurveyBackend.API/Mappers/QuestionMapper.cs @@ -0,0 +1,57 @@ +using SurveyBackend.DTOs.Question; +using SurveyBackend.Services.Exceptions; +using SurveyLib.Core.Models; +using SurveyLib.Core.Models.QuestionVariants; + +namespace SurveyBackend.Mappers; + +public static class QuestionMapper +{ + public static QuestionBase QuestionCreationToModel(CreateQuestionDto dto, int surveyId) + { + return dto.QuestionType.ToLower() switch + { + "textquestion" => new TextQuestion + { + Title = dto.Title, + SurveyId = surveyId, + }, + "singleanswerquestion" => new SingleAnswerQuestion + { + Title = dto.Title, + SurveyId = surveyId, + AnswerVariants = dto.AnswerVariants?.Select(v => new AnswerVariant { Text = v }).ToList() ?? [], + }, + "multipleanswerquestion" => new MultipleAnswerQuestion + { + Title = dto.Title, + SurveyId = surveyId, + AnswerVariants = dto.AnswerVariants?.Select(v => new AnswerVariant { Text = v }).ToList() ?? [] + }, + _ => throw new BadRequestException("Unknown question type") + }; + } + + public static OutputQuestionDto ModelToQuestionDto(QuestionBase question) + { + var withAnswerVariants = question.GetType() != typeof(TextQuestion); + return new OutputQuestionDto + { + Id = question.Id, + SurveyId = question.SurveyId, + Title = question.Title, + QuestionType = question.Discriminator, + AnswerVariants = withAnswerVariants ? AnswerVariantsToDto(question.AnswerVariants) : null, + }; + } + + private static List AnswerVariantsToDto(IEnumerable answerVariants) + { + return answerVariants.Select(av => new OutputAnswerVariantDto + { + Id = av.Id, + QuestionId = av.QuestionId, + Text = av.Text, + }).ToList(); + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.API/Mappers/SurveyMapper.cs b/SurveyBackend/SurveyBackend.API/Mappers/SurveyMapper.cs new file mode 100644 index 0000000..46f85cb --- /dev/null +++ b/SurveyBackend/SurveyBackend.API/Mappers/SurveyMapper.cs @@ -0,0 +1,28 @@ +using SurveyBackend.DTOs.Survey; +using SurveyLib.Core.Models; + +namespace SurveyBackend.Mappers; + +public static class SurveyMapper +{ + public static Survey CreateDtoToModel(CreateSurveyDto dto, int userId) + { + return new Survey + { + Title = dto.Title, + Description = dto.Description, + CreatedBy = userId + }; + } + + public static OutputSurveyDto ModelToOutputDto(Survey survey) + { + return new OutputSurveyDto + { + Id = survey.Id, + Title = survey.Title, + Description = survey.Description, + CreatedBy = survey.CreatedBy, + }; + } +} \ No newline at end of file diff --git a/SurveyBackend/SurveyBackend.Services/Services/QuestionService.cs b/SurveyBackend/SurveyBackend.Services/Services/QuestionService.cs index ce3e11e..d62a28f 100644 --- a/SurveyBackend/SurveyBackend.Services/Services/QuestionService.cs +++ b/SurveyBackend/SurveyBackend.Services/Services/QuestionService.cs @@ -9,10 +9,13 @@ namespace SurveyBackend.Services.Services; public class QuestionService : IQuestionService { private readonly IQuestionRepository _questionRepository; + private readonly ISurveyRepository _surveyRepository; - public QuestionService(IQuestionRepository questionRepository, IUserContext userContext) + public QuestionService(IQuestionRepository questionRepository, ISurveyRepository surveyRepository, + IUserContext userContext) { _questionRepository = questionRepository; + _surveyRepository = surveyRepository; } public async Task AddQuestionAsync(QuestionBase question) @@ -49,6 +52,12 @@ public class QuestionService : IQuestionService public async Task> GetQuestionsBySurveyIdAsync(int surveyId) { + var survey = await _surveyRepository.GetByIdAsync(surveyId); + if (survey is null) + { + throw new NotFoundException("Survey not found"); + } + return await _questionRepository.GetQuestionsBySurveyId(surveyId); } } \ No newline at end of file