Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • xpokorn8/sprachschulsystem
1 result
Show changes
Commits on Source (10)
Showing
with 249 additions and 216 deletions
package org.fuseri.model.dto.common;
import lombok.Getter;
import lombok.Setter;
import java.util.List;
@Getter
@Setter
public class Result<T extends DomainObjectDto> {
private long total;
private int page;
private int pageSize;
private List<T> items;
}
......@@ -8,15 +8,16 @@ import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import org.fuseri.model.dto.exercise.AnswerCreateDto;
import org.fuseri.model.dto.exercise.AnswerDto;
import org.fuseri.model.dto.exercise.AnswersCreateDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
/**
* Represent a REST API controller for answers
* Handle HTTP requests related to answers
......@@ -32,27 +33,6 @@ public class AnswerController {
this.facade = facade;
}
/**
* Create a new answer for the given question ID
*
* @param dto the AnswerCreateDto object containing information about the answer to create
* @return a ResponseEntity containing an AnswerDto object representing the newly created answer, or a 404 Not Found response
* if the question with the specified ID in dto was not found
*/
@Operation(summary = "Create new answers for question", description = "Creates new answers for question.")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "Answers created successfully."),
@ApiResponse(responseCode = "400", description = "Invalid input.")
})
@PostMapping
public ResponseEntity<List<AnswerDto>> createMultiple(@Valid @RequestBody AnswersCreateDto dto) {
try {
return ResponseEntity.status(HttpStatus.CREATED).body(facade.createMultiple(dto));
} catch (EntityNotFoundException e) {
return ResponseEntity.notFound().build();
}
}
/**
* Update an answer
*
......
......@@ -3,15 +3,8 @@ package org.fuseri.moduleexercise.answer;
import jakarta.transaction.Transactional;
import org.fuseri.model.dto.exercise.AnswerCreateDto;
import org.fuseri.model.dto.exercise.AnswerDto;
import org.fuseri.model.dto.exercise.AnswersCreateDto;
import org.fuseri.moduleexercise.question.Question;
import org.fuseri.moduleexercise.question.QuestionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
* Represent facade for managing answers
......@@ -21,67 +14,29 @@ import java.util.List;
@Transactional
public class AnswerFacade {
private final AnswerService answerService;
private final QuestionService questionService;
private final AnswerMapper mapper;
/**
* Constructor for AnswerFacade
*
* @param answerService the service responsible for handling answer-related logic
* @param questionService the service responsible for handling question-related logic
* @param mapper the mapper responsible for converting between DTOs and entities
* @param answerService the service responsible for handling answer-related logic
* @param mapper the mapper responsible for converting between DTOs and entities
*/
@Autowired
public AnswerFacade(AnswerService answerService, QuestionService questionService, AnswerMapper mapper) {
public AnswerFacade(AnswerService answerService, AnswerMapper mapper) {
this.answerService = answerService;
this.questionService = questionService;
this.mapper = mapper;
}
/**
* Create a new answer for the given question ID
*
* @param dto the AnswerCreateDto object containing information about the answer to create
* @return an AnswerDto object representing the newly created answer
*/
public List<AnswerDto> createMultiple(@RequestBody AnswersCreateDto dto) {
List<Answer> createdAnswers = new ArrayList<>();
for (var answerDto : dto.getAnswers()) {
Question question;
question = questionService.find(dto.getQuestionId());
Answer answer = mapper.fromCreateDto(answerDto);
answer.setQuestion(question);
var createdAnswer = answerService.create(answer);
question.getAnswers().add(answer);
createdAnswers.add(createdAnswer);
}
return mapper.toDtoList(createdAnswers);
}
/**
* Update an answer
* Update answer
*
* @param id of answer to update
* @param dto dto with updated answer information
*/
public AnswerDto update(long id, AnswerCreateDto dto) {
var updatedAnswer = mapper.fromCreateDto(dto);
updatedAnswer.setId(id);
answerService.update(updatedAnswer);
Question question;
question = questionService.find(dto.getQuestionId());
var questionAnswers = question.getAnswers();
questionAnswers.removeIf(a -> a.getId() == id);
questionAnswers.add(updatedAnswer);
question.setAnswers(questionAnswers);
questionService.update(question);
return mapper.toDto(updatedAnswer);
return mapper.toDto(answerService.update(id, mapper.fromCreateDto(dto)));
}
/**
......@@ -90,15 +45,6 @@ public class AnswerFacade {
* @param id of answer to delete
*/
public void delete(long id) {
var answer = answerService.find(id);
Question question;
question = questionService.find(answer.getQuestion().getId());
var questionAnswers = question.getAnswers();
questionAnswers.removeIf(a -> a.getId() == answer.getId());
question.setAnswers(questionAnswers);
answerService.delete(id);
}
}
......@@ -4,13 +4,29 @@ import org.fuseri.model.dto.exercise.AnswerCreateDto;
import org.fuseri.model.dto.exercise.AnswerDto;
import org.fuseri.model.dto.exercise.AnswerInQuestionCreateDto;
import org.fuseri.moduleexercise.common.DomainMapper;
import org.fuseri.moduleexercise.question.Question;
import org.fuseri.moduleexercise.question.QuestionService;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Set;
/**
* Mapper between Answers and their corresponding DTOs
*/
@Mapper
public interface AnswerMapper extends DomainMapper<Answer, AnswerDto> {
@Mapper(componentModel = "spring", uses = {QuestionService.class})
public abstract class AnswerMapper implements DomainMapper<Answer, AnswerDto> {
@Autowired
private QuestionService questionService;
@Named("mapIdToQuestion")
public Question mapIdToQuestion(Long id) {
return questionService.find(id);
}
/**
* Convert DTO of type AnswerCreateDto to Answer
......@@ -18,12 +34,22 @@ public interface AnswerMapper extends DomainMapper<Answer, AnswerDto> {
* @param dto DTO to be converted
* @return corresponding Answer entity created from DTO
*/
Answer fromCreateDto(AnswerCreateDto dto);
@Mapping(target = "question", source = "dto.questionId", qualifiedByName = "mapIdToQuestion")
public abstract Answer fromCreateDto(AnswerCreateDto dto);
/**
* Convert Set of AnswerInQuestionCreateDto to List of corresponding Answers
*
* @param dtos to be converted
* @return corresponding list of Answers
*/
public abstract Set<Answer> fromCreateDtoList(List<AnswerInQuestionCreateDto> dtos);
/**
* Convert DTO of type AnswerInQuestionCreateDto to Answer
*
* @param dto DTO to be converted
* @return corresponding Answer entity created from DTO
*/
Answer fromCreateDto(AnswerInQuestionCreateDto dto);
public abstract Answer fromCreateDto(AnswerInQuestionCreateDto dto);
}
package org.fuseri.moduleexercise.common;
import org.fuseri.model.dto.common.DomainObjectDto;
import org.fuseri.model.dto.common.Result;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import java.util.List;
......@@ -42,18 +40,13 @@ public interface DomainMapper<T extends DomainObject, S extends DomainObjectDto>
List<S> toDtoList(List<T> entities);
/**
* Convert a {@link org.springframework.data.domain.Page} containing entities to a
* {@link Result} object containing a list of their corresponding DTO objects, along with
* pagination information
* Convert a {@link org.springframework.data.domain.Page} containing entities to a page containing DTOs
*
* @param source the Page of entities to be converted
* @return a Result object containing a list of DTO objects and pagination information
* @param entities entities to be converted to DTOs
* @return a Page object containing a list of DTO objects and pagination information
*/
@Mappings({
@Mapping(target = "total", expression = "java(source.getTotalElements())"),
@Mapping(target = "page", expression = "java(source.getNumber())"),
@Mapping(target = "pageSize", expression = "java(source.getSize())"),
@Mapping(target = "items", expression = "java(toDtoList(source.getContent()))")
})
Result<S> toResult(Page<T> source);
default Page<S> toDtoPage(Page<T> entities) {
return new PageImpl<>(toDtoList(entities.getContent()), entities.getPageable(), entities.getTotalPages());
}
}
......@@ -35,13 +35,15 @@ public abstract class DomainService<T extends DomainObject> {
/**
* Update an entity
*
* @param id the entity ID
* @param entity the entity to update
* @return the updated entity
*/
public T update(T entity) {
if (!getRepository().existsById(entity.getId())) {
public T update(long id, T entity) {
if (!getRepository().existsById(id)) {
throw new EntityNotFoundException("Entity with id " + entity.getId() + " not found.");
}
entity.setId(id);
return getRepository().save(entity);
}
......
......@@ -7,14 +7,22 @@ import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;
import org.fuseri.model.dto.common.Result;
import org.fuseri.model.dto.exercise.ExerciseCreateDto;
import org.fuseri.model.dto.exercise.ExerciseDto;
import org.fuseri.model.dto.exercise.QuestionDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* Represent a REST API controller for exercises
......@@ -78,7 +86,7 @@ public class ExerciseController {
* Find exercises and return them in paginated format
*
* @param page the page number of the exercises to retrieve
* @return A ResponseEntity containing a Result object with a paginated list of exercises and metadata.
* @return A ResponseEntity containing paginated ExerciseDTOs.
*/
@Operation(summary = "Get exercises in paginated format", description = "Returns exercises in paginated format.")
@ApiResponses(value = {
......@@ -86,27 +94,27 @@ public class ExerciseController {
@ApiResponse(responseCode = "400", description = "Invalid page number supplied"),
})
@GetMapping
public ResponseEntity<Result<ExerciseDto>> findAll(@PositiveOrZero @RequestParam int page) {
public ResponseEntity<Page<ExerciseDto>> findAll(@PositiveOrZero @RequestParam int page) {
return ResponseEntity.ok(facade.findAll(page));
}
/**
* Find exercises that mach filters and return them in paginated format
*
* @param page the page number of the exercises to retrieve
* @param courseId the id of the course to filter by
* @param difficulty the difficulty level to filter by
* @return A ResponseEntity containing a Result object with a paginated list of filtered ExerciseDto objects
* @param page the page number of the exercises to retrieve
* @return A ResponseEntity containing filtered and paginated ExerciseDTOs
*/
@Operation(summary = "Filter exercises per difficulty and per course", description = "Returns exercises which belong to specified course and have specified difficulty.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Successfully retrieved filtered paginated exercises."),
})
@GetMapping("filter")
public ResponseEntity<Result<ExerciseDto>> findPerDifficultyPerCourse(
public ResponseEntity<Page<ExerciseDto>> findPerDifficultyPerCourse(
@PositiveOrZero @RequestParam int page, @NotNull @RequestParam long courseId,
@PositiveOrZero @RequestParam int difficulty) {
Result<ExerciseDto> exercises = facade.findPerDifficultyPerCourse(page, courseId, difficulty);
Page<ExerciseDto> exercises = facade.findByCourseIdAndDifficulty(courseId, difficulty, page);
return ResponseEntity.ok(exercises);
}
......@@ -115,7 +123,7 @@ public class ExerciseController {
*
* @param exerciseId the ID of the exercise to find questions for
* @param page the page number of the questions to retrieve
* @return a ResponseEntity containing a Result object with a list of QuestionDto objects and pagination information,
* @return a ResponseEntity containing paginated QuestionDTOs which belong to an exercise with exerciseId
* or a NOT_FOUND response if the exercise ID is invalid
*/
@Operation(summary = "Find questions belonging to exercise by exercise ID",
......@@ -125,10 +133,10 @@ public class ExerciseController {
@ApiResponse(responseCode = "404", description = "Exercise with the specified ID was not found.")
})
@GetMapping("/{exercise-id}/questions")
public ResponseEntity<Result<QuestionDto>> findQuestions(@NotNull @PathVariable("exercise-id") long exerciseId,
@PositiveOrZero @RequestParam int page) {
public ResponseEntity<Page<QuestionDto>> findQuestions(@NotNull @PathVariable("exercise-id") long exerciseId,
@PositiveOrZero @RequestParam int page) {
try {
Result<QuestionDto> questions = facade.getQuestions(exerciseId, page);
Page<QuestionDto> questions = facade.getQuestions(exerciseId, page);
return ResponseEntity.ok(questions);
} catch (EntityNotFoundException e) {
return ResponseEntity.notFound().build();
......
......@@ -2,7 +2,6 @@ package org.fuseri.moduleexercise.exercise;
import jakarta.persistence.EntityNotFoundException;
import jakarta.transaction.Transactional;
import org.fuseri.model.dto.common.Result;
import org.fuseri.model.dto.exercise.ExerciseCreateDto;
import org.fuseri.model.dto.exercise.ExerciseDto;
import org.fuseri.model.dto.exercise.QuestionDto;
......@@ -26,8 +25,8 @@ public class ExerciseFacade {
/**
* Constructor for AnswerFacade
*
* @param exerciseService the service responsible for handling answer-related logic
* @param exerciseMapper the mapper responsible for converting between DTOs and entities
* @param exerciseService the service responsible for handling answer-related logic
* @param exerciseMapper the mapper responsible for converting between DTOs and entities
*/
@Autowired
public ExerciseFacade(ExerciseService exerciseService, ExerciseMapper exerciseMapper, QuestionMapper questionMapper) {
......@@ -64,11 +63,11 @@ public class ExerciseFacade {
* Retrieve a page of Exercise entities
*
* @param page the page number to retrieve (0-indexed)
* @return a page of Exercise entities
* @return paginated exerciseDTOs
*/
@org.springframework.transaction.annotation.Transactional(readOnly = true)
public Result<ExerciseDto> findAll(int page) {
return exerciseMapper.toResult(exerciseService.findAll(page));
public Page<ExerciseDto> findAll(int page) {
return exerciseMapper.toDtoPage(exerciseService.findAll(page));
}
/**
......@@ -77,11 +76,11 @@ public class ExerciseFacade {
* @param page the page number to retrieve
* @param courseId the id of the course to filter by
* @param difficulty the difficulty level to filter by
* @return a {@link Page} of {@link Exercise} objects filtered by the specified course id and difficulty level
* @return paginated exerciseDTOs filtered by the specified course ID and difficulty level
*/
public Result<ExerciseDto> findPerDifficultyPerCourse(int page, long courseId, int difficulty) {
Page<Exercise> exercises = exerciseService.findPerDifficultyPerCourse(page, courseId, difficulty);
return exerciseMapper.toResult(exercises);
public Page<ExerciseDto> findByCourseIdAndDifficulty(long courseId, int difficulty, int page) {
Page<Exercise> exercises = exerciseService.findByCourseIdAndDifficulty(courseId, difficulty, page);
return exerciseMapper.toDtoPage(exercises);
}
/**
......@@ -89,12 +88,12 @@ public class ExerciseFacade {
*
* @param exerciseId the ID of the exercise to retrieve questions for
* @param page the page number to retrieve (0-indexed)
* @return a page of Question entities associated with the specified exercise ID
* @return paginated questionDTOs associated with the specified exercise ID
*/
@org.springframework.transaction.annotation.Transactional(readOnly = true)
public Result<QuestionDto> getQuestions(long exerciseId, int page) {
public Page<QuestionDto> getQuestions(long exerciseId, int page) {
Page<Question> questions = exerciseService.getQuestions(exerciseId, page);
return questionMapper.toResult(questions);
return questionMapper.toDtoPage(questions);
}
/**
......@@ -106,8 +105,7 @@ public class ExerciseFacade {
*/
public ExerciseDto update(long id, ExerciseCreateDto dto) {
Exercise exercise = exerciseMapper.fromCreateDto(dto);
exercise.setId(id);
Exercise updatedExercise = exerciseService.update(exercise);
Exercise updatedExercise = exerciseService.update(id, exercise);
return exerciseMapper.toDto(updatedExercise);
}
......
package org.fuseri.moduleexercise.exercise;
import org.fuseri.model.dto.common.Result;
import org.fuseri.moduleexercise.question.Question;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
......@@ -17,16 +17,16 @@ public interface ExerciseRepository extends JpaRepository<Exercise, Long> {
/**
* Filters the exercises by the specified difficulty level and course id,
* and returns a {@link Result} object containing these filtered exercises
* and returns an object containing these filtered exercises
* along with pagination information
*
* @param pageRequest the pagination settings for the result
* @param courseId the id of the course to filter by
* @param difficulty the difficulty level to filter by
* @return a {@link Result} object containing a list of paginated exercises that match the filter criteria
* @param courseId the id of the course to filter by
* @param difficulty the difficulty level to filter by
* @param pageable the pagination settings
* @return object containing a list of paginated exercises that match the filter criteria
*/
@Query("SELECT e FROM Exercise e WHERE e.courseId = :courseId AND e.difficulty = :difficulty")
Page<Exercise> filterPerDifficultyPerCourse(PageRequest pageRequest, long courseId, int difficulty);
Page<Exercise> findByCourseIdAndDifficulty(long courseId, int difficulty, Pageable pageable);
@Query("SELECT q FROM Exercise e JOIN e.questions q WHERE e.id = :exerciseId")
Page<Question> getQuestions(PageRequest pageRequest, @Param("exerciseId") Long exerciseId);
......
......@@ -59,14 +59,13 @@ public class ExerciseService extends DomainService<Exercise> {
/**
* Retrieve a page of exercises filtered by the specified course id and difficulty level
*
* @param page the page number to retrieve
* @param courseId the id of the course to filter by
* @param difficulty the difficulty level to filter by
* @return a {@link Page} of {@link Exercise} objects filtered by the specified course id and difficulty level
* @param page the page number to retrieve
* @return paginated exercises filtered by the specified course ID and difficulty level
*/
public Page<Exercise> findPerDifficultyPerCourse(int page, long courseId, int difficulty) {
return repository.filterPerDifficultyPerCourse(
PageRequest.of(page, DEFAULT_PAGE_SIZE), courseId, difficulty);
public Page<Exercise> findByCourseIdAndDifficulty(long courseId, int difficulty, int page) {
return repository.findByCourseIdAndDifficulty(courseId, difficulty, PageRequest.of(page, DEFAULT_PAGE_SIZE));
}
/**
......
package org.fuseri.moduleexercise.question;
import jakarta.persistence.*;
import lombok.*;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.fuseri.moduleexercise.answer.Answer;
import org.fuseri.moduleexercise.common.DomainObject;
import org.fuseri.moduleexercise.exercise.Exercise;
......@@ -27,7 +36,15 @@ public class Question extends DomainObject {
private Set<Answer> answers = new HashSet<>();
@ManyToOne
@JoinColumn(name = "exercise_id", nullable=false)
@JoinColumn(name = "exercise_id", nullable = false)
private Exercise exercise;
/**
* Add answers to question
*
* @param answersToAdd to add
*/
public void addAnswers(Set<Answer> answersToAdd) {
answers.addAll(answersToAdd);
}
}
......@@ -9,13 +9,22 @@ import jakarta.persistence.EntityNotFoundException;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import org.fuseri.model.dto.exercise.AnswerDto;
import org.fuseri.model.dto.exercise.AnswerInQuestionCreateDto;
import org.fuseri.model.dto.exercise.QuestionCreateDto;
import org.fuseri.model.dto.exercise.QuestionDto;
import org.fuseri.model.dto.exercise.QuestionUpdateDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
......@@ -119,7 +128,7 @@ public class QuestionController {
@PutMapping("/{id}")
public ResponseEntity<QuestionDto> updateQuestion(@NotNull @PathVariable long id, @Valid @RequestBody QuestionUpdateDto dto) {
try {
return ResponseEntity.ok(questionFacade.update(id, dto));
return ResponseEntity.ok(questionFacade.patchUpdate(id, dto));
} catch (EntityNotFoundException e) {
return ResponseEntity.notFound().build();
}
......@@ -139,4 +148,21 @@ public class QuestionController {
questionFacade.delete(id);
return ResponseEntity.noContent().build();
}
/**
* Adds answers to the existing question resource
*
* @param id id of question to update
* @return the LectureDto representing the updated lecture
*/
@Operation(summary = "Add answers to the existing question.")
@PatchMapping("/{id}/answers")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "The question has been successfully updated"),
@ApiResponse(responseCode = "400", description = "The request body is invalid"),
@ApiResponse(responseCode = "404", description = "The question with the specified ID does not exist")
})
public ResponseEntity<QuestionDto> addAnswers(@PathVariable Long id, @RequestBody List<AnswerInQuestionCreateDto> answerDtoList) {
return ResponseEntity.ok(questionFacade.addAnswers(id, answerDtoList));
}
}
\ No newline at end of file
......@@ -2,18 +2,15 @@ package org.fuseri.moduleexercise.question;
import jakarta.transaction.Transactional;
import org.fuseri.model.dto.exercise.AnswerDto;
import org.fuseri.model.dto.exercise.AnswerInQuestionCreateDto;
import org.fuseri.model.dto.exercise.QuestionCreateDto;
import org.fuseri.model.dto.exercise.QuestionDto;
import org.fuseri.model.dto.exercise.QuestionUpdateDto;
import org.fuseri.moduleexercise.answer.Answer;
import org.fuseri.moduleexercise.answer.AnswerMapper;
import org.fuseri.moduleexercise.answer.AnswerService;
import org.fuseri.moduleexercise.exercise.Exercise;
import org.fuseri.moduleexercise.exercise.ExerciseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashSet;
import java.util.List;
/**
......@@ -24,18 +21,16 @@ import java.util.List;
@Service
public class QuestionFacade {
private final QuestionService questionService;
private final ExerciseService exerciseService;
private final AnswerService answerService;
private final QuestionMapper questionMapper;
private final AnswerMapper answerMapper;
@Autowired
public QuestionFacade(
QuestionService questionService, ExerciseService exerciseService,
QuestionService questionService,
AnswerService answerService, QuestionMapper questionMapper,
AnswerMapper answerMapper) {
this.questionService = questionService;
this.exerciseService = exerciseService;
this.answerService = answerService;
this.questionMapper = questionMapper;
this.answerMapper = answerMapper;
......@@ -48,8 +43,7 @@ public class QuestionFacade {
* @return a QuestionDto object representing the found question
*/
public QuestionDto find(long id) {
var a = questionService.find(id);
return questionMapper.toDto(a);
return questionMapper.toDto(questionService.find(id));
}
/**
......@@ -62,6 +56,17 @@ public class QuestionFacade {
return answerMapper.toDtoList(answerService.findAllByQuestionId(questionId));
}
/**
* Add answers to question
*
* @param id question ID
* @param dto List with AnswerInQuestionCreateDto to add to question
* @return a QuestionDto object representing the updated question
*/
public QuestionDto addAnswers(Long id, List<AnswerInQuestionCreateDto> dto) {
return questionMapper.toDto(questionService.addAnswers(id, answerMapper.fromCreateDtoList(dto)));
}
/**
* Create a new question
*
......@@ -69,30 +74,7 @@ public class QuestionFacade {
* @return a QuestionDto object representing the added question
*/
public QuestionDto create(QuestionCreateDto dto) {
Question question = questionMapper.fromCreateDto(dto);
Exercise exercise;
exercise = exerciseService.find(dto.getExerciseId());
exercise.getQuestions().add(question);
question.setExercise(exercise);
var answerDtos = dto.getAnswers();
var answers = new HashSet<Answer>();
for (var answerDto : answerDtos) {
Answer answer = answerMapper.fromCreateDto(answerDto);
answer = answerService.create(answer);
answers.add(answer);
}
question.setAnswers(answers);
var createdQuestion = questionService.create(question);
for (var answer : answers) {
answer.setQuestion(createdQuestion);
}
return questionMapper.toDto(createdQuestion);
return questionMapper.toDto(questionService.create(questionMapper.fromCreateDto(dto)));
}
/**
......@@ -101,12 +83,8 @@ public class QuestionFacade {
* @param dto dto of updated question with correct id
* @return dto of updated question
*/
public QuestionDto update(long id, QuestionUpdateDto dto) {
Question question = questionMapper.fromUpdateDto(dto);
question.setId(id);
List<Answer> questionAnswers = answerService.findAllByQuestionId(id);
question.setAnswers(new HashSet<>(questionAnswers));
Question updatedQuestion = questionService.update(question);
public QuestionDto patchUpdate(long id, QuestionUpdateDto dto) {
var updatedQuestion = questionService.patchUpdateWithoutAnswers(id, questionMapper.fromUpdateDto(dto));
return questionMapper.toDto(updatedQuestion);
}
......@@ -116,10 +94,6 @@ public class QuestionFacade {
* @param id of qustion to delete
*/
public void delete(long id) {
var question = questionService.find(id);
for (var answer : question.getAnswers()) {
answerService.delete(answer.getId());
}
questionService.delete(id);
}
}
package org.fuseri.moduleexercise.question;
import org.fuseri.model.dto.exercise.AnswerInQuestionCreateDto;
import org.fuseri.model.dto.exercise.QuestionCreateDto;
import org.fuseri.model.dto.exercise.QuestionDto;
import org.fuseri.model.dto.exercise.QuestionUpdateDto;
import org.fuseri.moduleexercise.answer.Answer;
import org.fuseri.moduleexercise.answer.AnswerMapper;
import org.fuseri.moduleexercise.common.DomainMapper;
import org.fuseri.moduleexercise.exercise.Exercise;
import org.fuseri.moduleexercise.exercise.ExerciseService;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Named;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Set;
/**
* Mapper between Questions and their corresponding DTOs
*/
@Mapper
public interface QuestionMapper extends DomainMapper<Question, QuestionDto> {
@Mapper(componentModel = "spring", uses = {AnswerMapper.class, ExerciseService.class})
public abstract class QuestionMapper implements DomainMapper<Question, QuestionDto> {
@Autowired
private ExerciseService exerciseService;
@Autowired
private AnswerMapper answerMapper;
@Named("mapIdToExercise")
public Exercise mapIdToExercise(Long id) {
return exerciseService.find(id);
}
@Named("mapDtoAnswersToAnswers")
public Set<Answer> mapDtoAnswersToAnswers(List<AnswerInQuestionCreateDto> dtos) {
return answerMapper.fromCreateDtoList(dtos);
}
/**
* Convert entity to its corresponding DTO
*
* @param question to be converted
* @return corresponding DTO created from question
*/
@Override
@Mapping(target = "exerciseId", source = "question.exercise.id")
public abstract QuestionDto toDto(Question question);
/**
* Convert DTO of type QuestionCreateDto to Question
......@@ -19,8 +55,10 @@ public interface QuestionMapper extends DomainMapper<Question, QuestionDto> {
* @param dto DTO to be converted
* @return corresponding Question entity created from DTO
*/
@Mapping(target = "answers", ignore = true)
Question fromCreateDto(QuestionCreateDto dto);
@Mapping(target = "exercise", source = "dto.exerciseId", qualifiedByName = "mapIdToExercise")
@Mapping(target = "answers", source = "dto.answers", qualifiedByName = "mapDtoAnswersToAnswers")
public abstract Question fromCreateDto(QuestionCreateDto dto);
Question fromUpdateDto(QuestionUpdateDto dto);
@Mapping(target = "exercise", source = "dto.exerciseId", qualifiedByName = "mapIdToExercise")
public abstract Question fromUpdateDto(QuestionUpdateDto dto);
}
......@@ -2,13 +2,15 @@ package org.fuseri.moduleexercise.question;
import jakarta.persistence.EntityNotFoundException;
import lombok.Getter;
import org.fuseri.moduleexercise.answer.Answer;
import org.fuseri.moduleexercise.common.DomainService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Optional;
import java.util.Set;
/**
* Represent a service for managing Question entities
*/
......@@ -43,4 +45,44 @@ public class QuestionService extends DomainService<Question> {
return repository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("Question '" + id + "' not found."));
}
/**
* Patch update a question. Don't update question answers.
*
* @param id the question ID
* @param updatedQuestion the question to update
* @return the updated question
*/
@Transactional
public Question patchUpdateWithoutAnswers(Long id, Question updatedQuestion) {
Optional<Question> optionalQuestion = repository.findById(id);
if (optionalQuestion.isPresent()) {
Question question = optionalQuestion.get();
question.setText(updatedQuestion.getText());
question.setExercise(updatedQuestion.getExercise());
return repository.save(question);
} else {
throw new EntityNotFoundException("Question with id: " + id + " was not found.");
}
}
/**
* Add answers to question with question ID.
*
* @param id of question
* @param answers to add to question
* @return updated question
*/
public Question addAnswers(Long id, Set<Answer> answers) {
Optional<Question> optionalQuestion = repository.findById(id);
if (optionalQuestion.isPresent()) {
Question question = optionalQuestion.get();
question.addAnswers(answers);
return repository.save(question);
} else {
throw new EntityNotFoundException(
"Question with id: " + id + " was not found.");
}
}
}