From 27e41d2e2bc91a421e8fe34818f481fe8ad878c9 Mon Sep 17 00:00:00 2001 From: Dominika Zemanovicova <xzemanov@fi.muni.cz> Date: Mon, 10 Apr 2023 09:00:18 +0200 Subject: [PATCH] Add OpenApi documentation for QuestionController, add ResponseEntity --- .../moduleexercise/answer/AnswerService.java | 4 + .../exercise/ExerciseController.java | 9 +- .../question/QuestionController.java | 85 ++++++++++++++----- 3 files changed, 71 insertions(+), 27 deletions(-) diff --git a/application/module-exercise/src/main/java/org/fuseri/moduleexercise/answer/AnswerService.java b/application/module-exercise/src/main/java/org/fuseri/moduleexercise/answer/AnswerService.java index d204151e..f5346fc5 100644 --- a/application/module-exercise/src/main/java/org/fuseri/moduleexercise/answer/AnswerService.java +++ b/application/module-exercise/src/main/java/org/fuseri/moduleexercise/answer/AnswerService.java @@ -36,9 +36,13 @@ public class AnswerService extends DomainService<Answer> { * * @param questionId the ID of the question to retrieve answers for * @return a list of Answer entities with the specified question ID + * @throws EntityNotFoundException if question with questionId does not exist */ @Transactional(readOnly = true) public List<Answer> findAllByQuestionId(long questionId) { + if (!getRepository().existsById(questionId)) { + throw new EntityNotFoundException("Question with id " + questionId + " not found."); + } return repository.findByQuestionId(questionId); } diff --git a/application/module-exercise/src/main/java/org/fuseri/moduleexercise/exercise/ExerciseController.java b/application/module-exercise/src/main/java/org/fuseri/moduleexercise/exercise/ExerciseController.java index b29b2d14..46c0cb6a 100644 --- a/application/module-exercise/src/main/java/org/fuseri/moduleexercise/exercise/ExerciseController.java +++ b/application/module-exercise/src/main/java/org/fuseri/moduleexercise/exercise/ExerciseController.java @@ -58,7 +58,7 @@ public class ExerciseController { * * @param id the ID of the exercise to find * @return a ResponseEntity containing an ExerciseDto object representing the found exercise, or a 404 Not Found response - * if the exercise with the specified ID was not found + * if the exercise with the specified ID was not found */ @Operation(summary = "Get an exercise by ID", description = "Returns an exercise with the specified ID.") @ApiResponses(value = { @@ -116,7 +116,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, - * or a NOT_FOUND response if the exercise ID is invalid + * or a NOT_FOUND response if the exercise ID is invalid */ @Operation(summary = "Find questions belonging to exercise by exercise ID", description = "Returns a paginated list of questions for the specified exercise ID.") @@ -126,7 +126,7 @@ public class ExerciseController { }) @GetMapping("/{exercise-id}/questions") public ResponseEntity<Result<QuestionDto>> findQuestions(@NotNull @PathVariable("exercise-id") long exerciseId, - @PositiveOrZero @RequestParam int page) { + @PositiveOrZero @RequestParam int page) { try { Result<QuestionDto> questions = facade.getQuestions(exerciseId, page); return ResponseEntity.ok(questions); @@ -167,8 +167,9 @@ public class ExerciseController { @ApiResponse(responseCode = "204", description = "Exercise with the specified ID deleted successfully."), }) @DeleteMapping("/{id}") - public void delete(@NotNull @PathVariable long id) { + public ResponseEntity<Void> delete(@NotNull @PathVariable long id) { facade.delete(id); + return ResponseEntity.noContent().build(); } } diff --git a/application/module-exercise/src/main/java/org/fuseri/moduleexercise/question/QuestionController.java b/application/module-exercise/src/main/java/org/fuseri/moduleexercise/question/QuestionController.java index b9f42d03..1595f03f 100644 --- a/application/module-exercise/src/main/java/org/fuseri/moduleexercise/question/QuestionController.java +++ b/application/module-exercise/src/main/java/org/fuseri/moduleexercise/question/QuestionController.java @@ -1,5 +1,10 @@ package org.fuseri.moduleexercise.question; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.persistence.EntityNotFoundException; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -9,8 +14,8 @@ 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.server.ResponseStatusException; import java.util.List; @@ -38,38 +43,64 @@ public class QuestionController { * Find a question by ID. * * @param id the ID of the question to find - * @return a QuestionDto object representing the found question + * @return a ResponseEntity containing a QuestionDto object representing the found question, or a 404 Not Found response + * if the question with the specified ID was not found */ + @Operation(summary = "Get a question by ID", description = "Returns a question with the specified ID.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Question with the specified ID retrieved successfully.", + content = @Content(schema = @Schema(implementation = QuestionDto.class))), + @ApiResponse(responseCode = "404", description = "Question with the specified ID was not found.") + }) @GetMapping("/{id}") - public QuestionDto find(@NotNull @PathVariable long id) { - return questionFacade.find(id); + public ResponseEntity<QuestionDto> find(@NotNull @PathVariable long id) { + try { + return ResponseEntity.ok(questionFacade.find(id)); + } catch (EntityNotFoundException e) { + return ResponseEntity.notFound().build(); + } } /** - * Retrieve a list of AnswerDto objects which belong to question with id + * Retrieve a list of AnswerDto objects which belong to the question with ID * * @param id the ID of the question for which to retrieve answers - * @return a List of AnswerDto objects + * @return a ResponseEntity containing a List of AnswerDto objects, or a 404 Not Found response + * if the question with the specified ID was not found */ + @Operation(summary = "Retrieve answers for a specific question") + @ApiResponse(responseCode = "200", description = "Successfully retrieved answers", + content = @Content(schema = @Schema(implementation = AnswerDto.class))) + @ApiResponse(responseCode = "404", description = "Question not found") @GetMapping("/{id}/answers") - public List<AnswerDto> getQuestionAnswers(@NotNull @PathVariable long id) { - return questionFacade.getQuestionAnswers(id); + public ResponseEntity<List<AnswerDto>> getQuestionAnswers(@NotNull @PathVariable long id) { + try { + return ResponseEntity.ok(questionFacade.getQuestionAnswers(id)); + } catch (EntityNotFoundException e) { + return ResponseEntity.notFound().build(); + } } /** * Add a new question to an exercise * * @param dto a QuestionCreateDto object representing the new question to add - * @return a QuestionDto object representing the added question - * @throws ResponseStatusException if the exercise with exerciseId from QuestionCreateDto does not exist + * @return a ResponseEntity containing a QuestionDto object representing the posted question, or a 404 Not Found response + * if the exercise with the specified ID in dto was not found */ + @Operation(summary = "Add a new question with answers to an exercise", description = "Creates a new question with answers and associates it with the specified exercise.") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "Question with answers created and added to the exercise successfully.", + content = @Content(schema = @Schema(implementation = QuestionDto.class))), + @ApiResponse(responseCode = "404", description = "Exercise with the specified ID was not found.") + }) @PostMapping - public QuestionDto addQuestion(@Valid @RequestBody QuestionCreateDto dto) { + public ResponseEntity<QuestionDto> addQuestion(@Valid @RequestBody QuestionCreateDto dto) { try { - return questionFacade.create(dto); + QuestionDto createdQuestionDto = questionFacade.create(dto); + return ResponseEntity.status(HttpStatus.CREATED).body(createdQuestionDto); } catch (EntityNotFoundException e) { - throw new ResponseStatusException(HttpStatus.NOT_FOUND, - e.getMessage()); + return ResponseEntity.notFound().build(); } } @@ -77,27 +108,35 @@ public class QuestionController { * Update question * * @param dto a QuestionDto object representing the updated question with correct id - * @return a QuestionUpdateDto object representing the updated question - * @throws ResponseStatusException if the question with id doesn't exist or its exercise doesn't exist + * @return a ResponseEntity containing a QuestionUpdateDto object representing the updated question, + * or a 404 Not Found response if the question with the specified ID was not found */ + @Operation(summary = "Update a question by ID", description = "Updates a question with the specified ID.") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Question with the specified ID updated successfully."), + @ApiResponse(responseCode = "404", description = "Question with the specified ID was not found.") + }) @PutMapping("/{id}") - public QuestionDto updateQuestion(@NotNull @PathVariable long id, @Valid @RequestBody QuestionUpdateDto dto) { + public ResponseEntity<QuestionDto> updateQuestion(@NotNull @PathVariable long id, @Valid @RequestBody QuestionUpdateDto dto) { try { - return questionFacade.update(id, dto); - } catch (IllegalArgumentException e) { - throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + return ResponseEntity.ok(questionFacade.update(id, dto)); } catch (EntityNotFoundException e) { - throw new ResponseStatusException(HttpStatus.NOT_FOUND, e.getMessage()); + return ResponseEntity.notFound().build(); } } /** - * Add a new question to an exercise + * Delete question with ID from exercise * * @param id of question to delete */ + @Operation(summary = "Delete a question with specified ID", description = "Deletes a question with the specified ID.") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Question with the specified ID deleted successfully."), + }) @DeleteMapping("/{id}") - public void deleteQuestion(@NotNull @PathVariable long id) { + public ResponseEntity<Void> deleteQuestion(@NotNull @PathVariable long id) { questionFacade.delete(id); + return ResponseEntity.noContent().build(); } } \ No newline at end of file -- GitLab