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