Merge branch 'unstable' into 'main'

A lot of API fixes + added Completions and Answers

See merge request internship-2025/survey-webapp/survey-webapp!25
This commit is contained in:
Вячеслав 2025-05-30 20:42:41 +00:00
commit 47467f0fa3
28 changed files with 425 additions and 61 deletions

View file

@ -24,8 +24,19 @@ public class UserContext : IUserContext
/// Возвращает UserId из токена, при отсуствии кидает Unauthorized /// Возвращает UserId из токена, при отсуствии кидает Unauthorized
/// </summary> /// </summary>
/// <exception cref="UnauthorizedAccessException"></exception> /// <exception cref="UnauthorizedAccessException"></exception>
public int UserId => //public int UserId =>
int.Parse( // int.Parse(
_httpContextAccessor.HttpContext?.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier) // _httpContextAccessor.HttpContext?.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value ?? throw new UnauthorizedException("Where's your token mister"));
?.Value ?? throw new UnauthorizedException("Where's your token mister")); private string? ClaimValue
=> _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
public int UserId
=> int.TryParse(ClaimValue, out var id)
? id
: throw new UnauthorizedException("User ID claim missing or malformed");
public int? NullableUserId
=> int.TryParse(ClaimValue, out var id)
? id
: null;
} }

View file

@ -0,0 +1,37 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SurveyBackend.Mappers;
using SurveyLib.Core.Services;
namespace SurveyBackend.Controllers;
[ApiController]
public class AnswerController : ControllerBase
{
private readonly IAnswerService _answerService;
public AnswerController(IAnswerService answerService)
{
_answerService = answerService;
}
[Authorize]
[HttpGet]
[Route("api/questions/{id:int}/answers")]
public async Task<IActionResult> GetAnswersByQuestionId(int id)
{
var models = await _answerService.GetAnswersByQuestionIdAsync(id);
var result = models.Select(AnswerMapper.ModelToOutputDto);
return Ok(result);
}
[Authorize]
[HttpGet]
[Route("api/completions/{id:int}/answers")]
public async Task<IActionResult> GetAnswersByCompletionId(int id)
{
var models = await _answerService.GetAnswersByCompletionIdAsync(id);
var result = models.Select(AnswerMapper.ModelToOutputDto);
return Ok(result);
}
}

View file

@ -52,16 +52,15 @@ public class AnswerVariantsController : ControllerBase
/// <summary> /// <summary>
/// Обновить вариант ответа на вопрос /// Обновить вариант ответа на вопрос
/// </summary> /// </summary>
/// <param name="surveyId">Идентификатор опроса</param>
/// <param name="questionId">Идентификатор вопроса</param>
/// <param name="id">Идентификатор варианта ответа</param> /// <param name="id">Идентификатор варианта ответа</param>
/// <param name="dto">Объект с данными для обновления варианта ответа</param> /// <param name="dto">Объект с данными для обновления варианта ответа</param>
/// <returns>Результат обновленного варианта ответа</returns> /// <returns>Результат обновленного варианта ответа</returns>
[Authorize] [Authorize]
[HttpPut("{id}")] [HttpPut]
public async Task<IActionResult> Update(int surveyId, int questionId, int id, [FromBody] AnswerVariantUpdateDto dto) [Route("/api/answerVariants/{id:int}")]
public async Task<IActionResult> Update(int id, [FromBody] AnswerVariantUpdateDto dto)
{ {
var model = AnswerVariantMapper.UpdateDtoToModel(dto, questionId, id); var model = AnswerVariantMapper.UpdateDtoToModel(dto, id);
await _answerVariantsService.UpdateAnswerVariantAsync(model); await _answerVariantsService.UpdateAnswerVariantAsync(model);
var result = AnswerVariantMapper.ModelToOutputDto(model); var result = AnswerVariantMapper.ModelToOutputDto(model);
return Ok(result); return Ok(result);
@ -70,13 +69,12 @@ public class AnswerVariantsController : ControllerBase
/// <summary> /// <summary>
/// Удалить вариант ответа на вопрос /// Удалить вариант ответа на вопрос
/// </summary> /// </summary>
/// <param name="surveyId">Идентификатор опроса</param>
/// <param name="questionId">Идентификатор вопроса</param>
/// <param name="id">Идентификатор варианта ответа</param> /// <param name="id">Идентификатор варианта ответа</param>
/// <returns>Результат операции удаления</returns> /// <returns>Результат операции удаления</returns>
[Authorize] [Authorize]
[HttpDelete("{id}")] [HttpDelete]
public async Task<IActionResult> Delete(int surveyId, int questionId, int id) [Route("/api/answerVariants/{id:int}")]
public async Task<IActionResult> Delete(int id)
{ {
await _answerVariantsService.DeleteAnswerVariantAsync(id); await _answerVariantsService.DeleteAnswerVariantAsync(id);
return Ok(); return Ok();

View file

@ -2,7 +2,6 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SurveyBackend.Core.Contexts; using SurveyBackend.Core.Contexts;
using SurveyBackend.Core.Services; using SurveyBackend.Core.Services;
using SurveyBackend.DTOs;
using SurveyBackend.DTOs.User; using SurveyBackend.DTOs.User;
using SurveyBackend.Mappers; using SurveyBackend.Mappers;
using IAuthorizationService = SurveyBackend.Core.Services.IAuthorizationService; using IAuthorizationService = SurveyBackend.Core.Services.IAuthorizationService;

View file

@ -0,0 +1,51 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using SurveyBackend.Core.Contexts;
using SurveyBackend.DTOs.Completion;
using SurveyBackend.Mappers;
using SurveyLib.Core.Services;
namespace SurveyBackend.Controllers;
[ApiController]
[Route("api/surveys/{surveyId:int}/completions")]
public class CompletionController : ControllerBase
{
private readonly ICompletionService _completionService;
private readonly IUserContext _userContext;
public CompletionController(ICompletionService completionService, IUserContext userContext)
{
_completionService = completionService;
_userContext = userContext;
}
[HttpGet]
[Authorize]
public async Task<IActionResult> GetCompletionsAsync(int surveyId)
{
var models = await _completionService.GetCompletionsBySurveyIdAsync(surveyId);
var result = models.Select(CompletionMapper.ModelToOutputDto);
return Ok(result);
}
[HttpGet]
[Route("/api/completions/{id:int}")]
[Authorize]
public async Task<IActionResult> GetCompletionAsync(int id)
{
var model = await _completionService.GetCompletionByIdAsync(id);
var result = CompletionMapper.ModelToOutputDto(model);
return Ok(result);
}
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> PostCompletionAsync([FromBody] CompletionCreateDto dto, [FromRoute] int surveyId)
{
var userId = _userContext.NullableUserId;
var model = CompletionMapper.CreateDtoToModel(dto, surveyId, userId);
await _completionService.AddCompletionAsync(model);
return Ok();
}
}

View file

@ -64,11 +64,11 @@ public class QuestionController : ControllerBase
/// <param name="surveyId"></param> /// <param name="surveyId"></param>
/// <returns></returns> /// <returns></returns>
[Authorize] [Authorize]
[HttpPut("{id}")] [HttpPut]
public async Task<IActionResult> UpdateQuestion([FromBody] QuestionUpdateDto dto, [FromRoute] int id, [Route("/api/questions/{id:int}")]
[FromRoute] int surveyId) public async Task<IActionResult> UpdateQuestion([FromBody] QuestionUpdateDto dto, [FromRoute] int id)
{ {
var question = QuestionMapper.QuestionUpdateToModel(dto, surveyId, id); var question = QuestionMapper.QuestionUpdateToModel(dto, id);
await _questionService.UpdateQuestionAsync(question); await _questionService.UpdateQuestionAsync(question);
var result = QuestionMapper.ModelToQuestionDto(question); var result = QuestionMapper.ModelToQuestionDto(question);
return Ok(result); return Ok(result);
@ -81,8 +81,9 @@ public class QuestionController : ControllerBase
/// <param name="surveyId"></param> /// <param name="surveyId"></param>
/// <returns></returns> /// <returns></returns>
[Authorize] [Authorize]
[HttpDelete("{id}")] [HttpDelete]
public async Task<IActionResult> DeleteQuestion([FromRoute] int id, [FromRoute] int surveyId) [Route("/api/questions/{id:int}")]
public async Task<IActionResult> DeleteQuestion([FromRoute] int id)
{ {
await _questionService.DeleteQuestionAsync(id); await _questionService.DeleteQuestionAsync(id);
return Ok(); return Ok();

View file

@ -1,11 +1,8 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using SurveyBackend.Core.Contexts; using SurveyBackend.Core.Contexts;
using SurveyBackend.DTOs.Survey; using SurveyBackend.DTOs.Survey;
using SurveyBackend.Mappers; using SurveyBackend.Mappers;
using SurveyBackend.Services.Exceptions;
using SurveyLib.Core.Models;
using SurveyLib.Core.Services; using SurveyLib.Core.Services;
namespace SurveyBackend.Controllers; namespace SurveyBackend.Controllers;

View file

@ -0,0 +1,7 @@
namespace SurveyBackend.DTOs.Answer;
public class AnswerCreateDto
{
public int QuestionId { get; set; }
public required string AnswerText { get; set; }
}

View file

@ -0,0 +1,8 @@
namespace SurveyBackend.DTOs.Answer;
public class AnswerOutputDto
{
public int QuestionId { get; set; }
public int CompletionId { get; set; }
public required string AnswerText { get; set; }
}

View file

@ -0,0 +1,8 @@
using SurveyBackend.DTOs.Answer;
namespace SurveyBackend.DTOs.Completion;
public class CompletionCreateDto
{
public List<AnswerCreateDto> Answers { get; set; }
}

View file

@ -0,0 +1,9 @@
namespace SurveyBackend.DTOs.Completion;
public class CompletionOutputDto
{
public int Id { get; set; }
public int SurveyId { get; set; }
public int? CompletedBy { get; set; }
public DateTime FinishedAt { get; set; }
}

View file

@ -0,0 +1,20 @@
using SurveyBackend.DTOs.Answer;
using SurveyLib.Core.Models;
namespace SurveyBackend.Mappers;
public static class AnswerMapper
{
public static Answer CreateDtoToModel(AnswerCreateDto dto) => new Answer
{
QuestionId = dto.QuestionId,
AnswerText = dto.AnswerText,
};
public static AnswerOutputDto ModelToOutputDto(Answer model) => new AnswerOutputDto
{
QuestionId = model.QuestionId,
AnswerText = model.AnswerText,
CompletionId = model.CompletionId,
};
}

View file

@ -11,10 +11,9 @@ public static class AnswerVariantMapper
Text = dto.Text Text = dto.Text
}; };
public static AnswerVariant UpdateDtoToModel(AnswerVariantUpdateDto dto, int questionId, int answerVariantId) => public static AnswerVariant UpdateDtoToModel(AnswerVariantUpdateDto dto, int answerVariantId) =>
new AnswerVariant new AnswerVariant
{ {
QuestionId = questionId,
Id = answerVariantId, Id = answerVariantId,
Text = dto.Text Text = dto.Text
}; };

View file

@ -0,0 +1,22 @@
using SurveyBackend.DTOs.Completion;
using SurveyLib.Core.Models;
namespace SurveyBackend.Mappers;
public static class CompletionMapper
{
public static Completion CreateDtoToModel(CompletionCreateDto dto, int surveyId, int? performerId) => new Completion
{
SurveyId = surveyId,
CompletedBy = performerId,
Answers = dto.Answers.Select(AnswerMapper.CreateDtoToModel).ToList(),
};
public static CompletionOutputDto ModelToOutputDto(Completion model) => new CompletionOutputDto
{
Id = model.Id,
SurveyId = model.SurveyId,
CompletedBy = model.CompletedBy,
FinishedAt = model.FinishedAt
};
}

View file

@ -61,7 +61,7 @@ public static class QuestionMapper
}; };
} }
public static QuestionBase QuestionUpdateToModel(QuestionCreateDto dto, int surveyId, int questionId) public static QuestionBase QuestionUpdateToModel(QuestionCreateDto dto, int questionId)
{ {
return dto.QuestionType.ToLower() switch return dto.QuestionType.ToLower() switch
{ {
@ -69,21 +69,16 @@ public static class QuestionMapper
{ {
Id = questionId, Id = questionId,
Title = dto.Title, Title = dto.Title,
SurveyId = surveyId,
}, },
"singleanswerquestion" => new SingleAnswerQuestion "singleanswerquestion" => new SingleAnswerQuestion
{ {
Id = questionId, Id = questionId,
Title = dto.Title, Title = dto.Title,
SurveyId = surveyId,
AnswerVariants = [],
}, },
"multipleanswerquestion" => new MultipleAnswerQuestion "multipleanswerquestion" => new MultipleAnswerQuestion
{ {
Id = questionId, Id = questionId,
Title = dto.Title, Title = dto.Title,
SurveyId = surveyId,
AnswerVariants = []
}, },
_ => throw new BadRequestException("Unknown question type") _ => throw new BadRequestException("Unknown question type")
}; };

View file

@ -1,5 +1,4 @@
using SurveyBackend.Core.Models; using SurveyBackend.Core.Models;
using SurveyBackend.DTOs;
using SurveyBackend.DTOs.User; using SurveyBackend.DTOs.User;
namespace SurveyBackend.Mappers; namespace SurveyBackend.Mappers;

View file

@ -5,18 +5,13 @@ using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using SurveyBackend.Contexts; using SurveyBackend.Contexts;
using SurveyBackend.Core.Contexts; using SurveyBackend.Core.Contexts;
using SurveyBackend.Core.Repositories;
using SurveyBackend.Core.Services;
using SurveyBackend.Filters; using SurveyBackend.Filters;
using SurveyBackend.Infrastructure;
using SurveyBackend.Infrastructure.Data; using SurveyBackend.Infrastructure.Data;
using SurveyBackend.Infrastructure.Repositories;
using SurveyBackend.Middlewares; using SurveyBackend.Middlewares;
using SurveyBackend.Services; using SurveyBackend.Services;
using SurveyBackend.Services.Services; using SurveyLib.Infrastructure.EFCore;
using SurveyLib.Core.Repositories;
using SurveyLib.Core.Services;
using SurveyLib.Infrastructure.EFCore.Data; using SurveyLib.Infrastructure.EFCore.Data;
using SurveyLib.Infrastructure.EFCore.Repositories;
namespace SurveyBackend; namespace SurveyBackend;
@ -39,21 +34,9 @@ public class Program
builder.Services.AddScoped<IUserContext, UserContext>(); builder.Services.AddScoped<IUserContext, UserContext>();
builder.Services.AddScoped<IUserRepository, UserRepository>(); builder.Services.AddSurveyBackendInfrastructure();
builder.Services.AddScoped<IUserService, UserService>(); builder.Services.AddSurveyLibInfrastructure();
builder.Services.AddSurveyBackendServices();
builder.Services.AddScoped<IPasswordHasher, Sha256PasswordHasher>();
builder.Services.AddScoped<IAuthorizationService, AuthorizationService>();
builder.Services.AddScoped<ISurveyRepository, SurveyRepository>();
builder.Services.AddScoped<IQuestionRepository, QuestionRepository>();
builder.Services.AddScoped<IAnswerVariantsRepository, AnswerVariantsRepository>();
builder.Services.AddScoped<ISurveyService, SurveyService>();
builder.Services.AddScoped<IQuestionService, QuestionService>();
builder.Services.AddScoped<IAnswerVariantsService, AnswerVariantsService>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => .AddJwtBearer(options =>

View file

@ -3,4 +3,5 @@ namespace SurveyBackend.Core.Contexts;
public interface IUserContext public interface IUserContext
{ {
int UserId { get; } int UserId { get; }
int? NullableUserId { get; }
} }

View file

@ -1,5 +1,3 @@
using Microsoft.AspNetCore.Identity;
namespace SurveyBackend.Core.Models; namespace SurveyBackend.Core.Models;
public class User public class User

View file

@ -0,0 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
using SurveyBackend.Core.Repositories;
using SurveyBackend.Infrastructure.Repositories;
namespace SurveyBackend.Infrastructure;
public static class DependencyInjection
{
public static IServiceCollection AddSurveyBackendInfrastructure(this IServiceCollection services)
{
services.AddScoped<IUserRepository, UserRepository>();
return services;
}
}

View file

@ -0,0 +1,27 @@
using Microsoft.Extensions.DependencyInjection;
using SurveyBackend.Core.Services;
using SurveyBackend.Services.Helpers;
using SurveyBackend.Services.Services;
using SurveyLib.Core.Services;
namespace SurveyBackend.Services;
public static class DependencyInjection
{
public static IServiceCollection AddSurveyBackendServices(this IServiceCollection services)
{
services.AddScoped<IUserService, UserService>();
services.AddScoped<IPasswordHasher, Sha256PasswordHasher>();
services.AddScoped<IAuthorizationService, AuthorizationService>();
services.AddScoped<ISurveyService, SurveyService>();
services.AddScoped<IQuestionService, QuestionService>();
services.AddScoped<IAnswerVariantsService, AnswerVariantsService>();
services.AddScoped<ICompletionService, CompletionService>();
services.AddScoped<IAnswerService, AnswerService>();
return services;
}
}

View file

@ -1,7 +1,7 @@
using System.Security.Cryptography; using System.Security.Cryptography;
using SurveyBackend.Core.Services; using SurveyBackend.Core.Services;
namespace SurveyBackend.Services.Services; namespace SurveyBackend.Services.Helpers;
public class Sha256PasswordHasher : IPasswordHasher public class Sha256PasswordHasher : IPasswordHasher
{ {

View file

@ -0,0 +1,58 @@
using SurveyBackend.Services.Exceptions;
using SurveyLib.Core.Models;
using SurveyLib.Core.Repositories;
using SurveyLib.Core.Services;
namespace SurveyBackend.Services.Services;
public class AnswerService : IAnswerService
{
private readonly IAnswerRepository _answerRepository;
private readonly IQuestionRepository _questionRepository;
private readonly ICompletionRepository _completionRepository;
public AnswerService(IAnswerRepository answerRepository, IQuestionRepository questionRepository,
ICompletionRepository completionRepository)
{
_answerRepository = answerRepository;
_questionRepository = questionRepository;
_completionRepository = completionRepository;
}
public async Task AddAnswerAsync(Answer answer)
{
await _answerRepository.AddAsync(answer);
}
public async Task UpdateAnswerAsync(Answer answer)
{
await _answerRepository.UpdateAsync(answer);
}
public async Task DeleteAnswerAsync(int id)
{
await _answerRepository.DeleteAsync(id);
}
public async Task<IEnumerable<Answer>> GetAnswersByCompletionIdAsync(int completionId)
{
var completion = await _completionRepository.GetByIdAsync(completionId);
if (completion is null)
{
throw new NotFoundException("Completion not found");
}
return await _answerRepository.GetAnswersByCompletionIdAsync(completionId);
}
public async Task<IEnumerable<Answer>> GetAnswersByQuestionIdAsync(int questionId)
{
var question = await _questionRepository.GetByIdAsync(questionId);
if (question is null)
{
throw new NotFoundException("Question not found");
}
return await _answerRepository.GetAnswersByQuestionIdAsync(questionId);
}
}

View file

@ -1,3 +1,4 @@
using SurveyBackend.Services.Exceptions;
using SurveyLib.Core.Models; using SurveyLib.Core.Models;
using SurveyLib.Core.Repositories; using SurveyLib.Core.Repositories;
using SurveyLib.Core.Services; using SurveyLib.Core.Services;
@ -7,34 +8,76 @@ namespace SurveyBackend.Services.Services;
public class AnswerVariantsService : IAnswerVariantsService public class AnswerVariantsService : IAnswerVariantsService
{ {
private readonly IAnswerVariantsRepository _answerVariantsRepository; private readonly IAnswerVariantsRepository _answerVariantsRepository;
private readonly IQuestionRepository _questionRepository;
public AnswerVariantsService(IAnswerVariantsRepository answerVariantsRepository) public AnswerVariantsService(IAnswerVariantsRepository answerVariantsRepository,
IQuestionRepository questionRepository)
{ {
_answerVariantsRepository = answerVariantsRepository; _answerVariantsRepository = answerVariantsRepository;
_questionRepository = questionRepository;
} }
// TODO: любой кто будет читать этот код, я понимаю проблему дублирования, не злитесь пожалуйста, я потом пишу на адекватные валидаторы и не будет дублирования
public async Task AddAnswerVariantAsync(AnswerVariant answerVariant) public async Task AddAnswerVariantAsync(AnswerVariant answerVariant)
{ {
var question = await _questionRepository.GetByIdAsync(answerVariant.QuestionId);
if (question is null)
{
throw new NotFoundException("Question not found");
}
await _answerVariantsRepository.AddAsync(answerVariant); await _answerVariantsRepository.AddAsync(answerVariant);
} }
public async Task UpdateAnswerVariantAsync(AnswerVariant answerVariant) public async Task UpdateAnswerVariantAsync(AnswerVariant answerVariant)
{ {
var question = await _questionRepository.GetByIdAsync(answerVariant.QuestionId);
if (question is null)
{
throw new NotFoundException("Question not found");
}
var answerVariantBase = await _answerVariantsRepository.GetByIdAsNoTrackingAsync(answerVariant.Id);
if (answerVariantBase is null)
{
throw new NotFoundException("Answer Variant not found");
}
answerVariant.QuestionId = answerVariantBase.QuestionId;
await _answerVariantsRepository.UpdateAsync(answerVariant); await _answerVariantsRepository.UpdateAsync(answerVariant);
} }
public async Task DeleteAnswerVariantAsync(int id) public async Task DeleteAnswerVariantAsync(int id)
{ {
var answerVariantBase = await _answerVariantsRepository.GetByIdAsNoTrackingAsync(id);
if (answerVariantBase is null)
{
throw new NotFoundException("Answer Variant not found");
}
await _answerVariantsRepository.DeleteAsync(id); await _answerVariantsRepository.DeleteAsync(id);
} }
public async Task<AnswerVariant> GetAnswerVariantByIdAsync(int id) public async Task<AnswerVariant> GetAnswerVariantByIdAsync(int id)
{ {
return await _answerVariantsRepository.GetByIdAsync(id); var answerVariant = await _answerVariantsRepository.GetByIdAsync(id);
if (answerVariant is null)
{
throw new NotFoundException("Answer Variant not found");
}
return answerVariant;
} }
public async Task<IEnumerable<AnswerVariant>> GetAnswerVariantsByQuestionIdAsync(int questionId) public async Task<IEnumerable<AnswerVariant>> GetAnswerVariantsByQuestionIdAsync(int questionId)
{ {
var question = await _questionRepository.GetByIdAsync(questionId);
if (question is null)
{
throw new NotFoundException("Question not found");
}
return await _answerVariantsRepository.GetAnswerVariantsByQuestionIdAsync(questionId); return await _answerVariantsRepository.GetAnswerVariantsByQuestionIdAsync(questionId);
} }
} }

View file

@ -0,0 +1,64 @@
using SurveyBackend.Services.Exceptions;
using SurveyLib.Core.Models;
using SurveyLib.Core.Repositories;
using SurveyLib.Core.Services;
namespace SurveyBackend.Services.Services;
public class CompletionService : ICompletionService
{
private readonly ICompletionRepository _completionRepository;
private readonly ISurveyRepository _surveyRepository;
public CompletionService(ICompletionRepository completionRepository, ISurveyRepository surveyRepository)
{
_completionRepository = completionRepository;
_surveyRepository = surveyRepository;
}
public async Task AddCompletionAsync(Completion completion)
{
var survey = await _surveyRepository.GetByIdAsync(completion.SurveyId);
if (survey is null)
{
throw new NotFoundException("Survey not found");
}
await _completionRepository.AddAsync(completion);
}
public async Task UpdateCompletionAsync(Completion completion)
{
// TODO: лол а что вообще значит ОбновитьВыполнение, надо выпилить из SurveyLib
await _completionRepository.UpdateAsync(completion);
}
public async Task DeleteCompletionAsync(int id)
{
// TODO: да и удалять их как-то бессмысленно
await _completionRepository.DeleteAsync(id);
}
public async Task<Completion> GetCompletionByIdAsync(int id)
{
var completion = await _completionRepository.GetByIdAsync(id);
if (completion is null)
{
throw new NotFoundException("Completion not found");
}
return completion;
}
public async Task<IEnumerable<Completion>> GetCompletionsBySurveyIdAsync(int surveyId)
{
var survey = await _surveyRepository.GetByIdAsync(surveyId);
if (survey is null)
{
throw new NotFoundException("Survey not found");
}
// TODO: проверить что запрашивает создатель (хз как)
return await _completionRepository.GetCompletionsBySurveyIdAsync(surveyId);
}
}

View file

@ -22,11 +22,25 @@ public class QuestionService : IQuestionService
public async Task AddQuestionAsync(QuestionBase question) public async Task AddQuestionAsync(QuestionBase question)
{ {
var survey = await _surveyRepository.GetByIdAsync(question.SurveyId);
if (survey is null)
{
throw new NotFoundException("Survey not found");
}
await _questionRepository.AddAsync(question); await _questionRepository.AddAsync(question);
} }
public async Task UpdateQuestionAsync(QuestionBase question) public async Task UpdateQuestionAsync(QuestionBase question)
{ {
var questionBase = await _questionRepository.GetByIdAsNoTrackingAsync(question.Id);
if (questionBase is null)
{
throw new NotFoundException("Question not found");
}
question.SurveyId = questionBase.SurveyId;
await _questionRepository.UpdateAsync(question); await _questionRepository.UpdateAsync(question);
} }

View file

@ -1,5 +1,4 @@
using SurveyBackend.Core.Contexts; using SurveyBackend.Core.Contexts;
using SurveyBackend.Core.Repositories;
using SurveyBackend.Services.Exceptions; using SurveyBackend.Services.Exceptions;
using SurveyLib.Core.Models; using SurveyLib.Core.Models;
using SurveyLib.Core.Repositories; using SurveyLib.Core.Repositories;
@ -64,6 +63,7 @@ public class SurveyService : ISurveyService
public async Task<IEnumerable<Survey>> GetSurveysByUserIdAsync(int userId) public async Task<IEnumerable<Survey>> GetSurveysByUserIdAsync(int userId)
{ {
// TODO: проверить существование юзера
return await _surveyRepository.GetSurveysByUserIdAsync(userId); return await _surveyRepository.GetSurveysByUserIdAsync(userId);
} }
} }

@ -1 +1 @@
Subproject commit 7d47ab9e60645032bc41daee5535aba2b7eeebdf Subproject commit d9f16ee761e31bb7af7a067c38f5fa02083f9d6c