From 5aac9c43d6153953676b9098fb57579d7653eb29 Mon Sep 17 00:00:00 2001 From: Ricardo Campos Date: Wed, 18 Sep 2024 11:45:20 -0300 Subject: [PATCH] docs: add open api docs and javadoc issue #46 --- .../controller/AuthenticationController.java | 34 +++++- .../java_api/controller/SomeController.java | 15 --- .../java_api/controller/TaskController.java | 110 +++++++++++++++++- .../java_api/request/TaskPatchRequest.java | 8 +- .../java_api/request/TaskRequest.java | 7 +- .../java_api/response/TaskResponse.java | 16 ++- .../java_api/response/TaskUrlResponse.java | 8 +- .../java_api/service/AuthService.java | 1 + .../java_api/service/JwtService.java | 1 + .../java_api/service/TaskService.java | 1 + .../java_api/service/UserService.java | 1 + .../service/impl/AuthServiceImpl.java | 1 + .../java_api/service/impl/JwtServiceImpl.java | 3 +- .../service/impl/TaskServiceImpl.java | 1 + .../service/impl/UserServiceImpl.java | 3 +- 15 files changed, 181 insertions(+), 29 deletions(-) delete mode 100644 java-api/src/main/java/br/com/tasknoteapp/java_api/controller/SomeController.java diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/AuthenticationController.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/AuthenticationController.java index d9eb15c..95bf1a0 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/AuthenticationController.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/AuthenticationController.java @@ -1,10 +1,13 @@ package br.com.tasknoteapp.java_api.controller; import br.com.tasknoteapp.java_api.exception.UserAlreadyExistsException; +import br.com.tasknoteapp.java_api.exception.UserNotFoundException; import br.com.tasknoteapp.java_api.request.LoginRequest; import br.com.tasknoteapp.java_api.response.JwtAuthenticationResponse; import br.com.tasknoteapp.java_api.service.AuthService; 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.tags.Tag; import jakarta.validation.Valid; @@ -17,9 +20,12 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +/** This class contains resources for handling authentication. */ @RestController @RequestMapping("/auth") -@Tag(name = "Authentication", description = "Authentication controller.") +@Tag( + name = "Authentication", + description = "Authentication resources to handle user authentication.") @AllArgsConstructor public class AuthenticationController { @@ -29,7 +35,8 @@ public class AuthenticationController { * Authenticate a user given his email and password. * * @param loginRequest User data containing email and password. - * @return OK if authenticated, 401 - Unauthorized otherwise + * @return JwtAuthenticationResponse with token created. + * @throws UserNotFoundException if user not found */ @PostMapping(path = "/signin", consumes = "application/json", produces = "application/json") @Operation( @@ -37,7 +44,18 @@ public class AuthenticationController { description = "SigIn an existing user given his email and password", responses = { @ApiResponse(responseCode = "200", description = "User successfully logged in"), - @ApiResponse(responseCode = "400", description = "Wrong or missing information"), + @ApiResponse( + responseCode = "400", + description = "Wrong or missing information", + content = @Content(schema = @Schema(implementation = Void.class))), + @ApiResponse( + responseCode = "403", + description = "Forbidden. Access Denied", + content = @Content(schema = @Schema(implementation = Void.class))), + @ApiResponse( + responseCode = "404", + description = "User not found", + content = @Content(schema = @Schema(implementation = Void.class))) }) public JwtAuthenticationResponse signin(@RequestBody @Valid LoginRequest loginRequest) { String token = authService.signin(loginRequest); @@ -57,8 +75,14 @@ public class AuthenticationController { description = "Signup a new user given his email and password", responses = { @ApiResponse(responseCode = "201", description = "User successfully created and saved"), - @ApiResponse(responseCode = "400", description = "Wrong or missing information"), - @ApiResponse(responseCode = "409", description = "User already exists") + @ApiResponse( + responseCode = "400", + description = "Wrong or missing information", + content = @Content(schema = @Schema(implementation = Void.class))), + @ApiResponse( + responseCode = "409", + description = "User already exists", + content = @Content(schema = @Schema(implementation = Void.class))) }) public ResponseEntity signup( @RequestBody @Valid LoginRequest loginRequest) { diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/SomeController.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/SomeController.java deleted file mode 100644 index 4294e91..0000000 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/SomeController.java +++ /dev/null @@ -1,15 +0,0 @@ -package br.com.tasknoteapp.java_api.controller; - -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/rest/some") -public class SomeController { - - @GetMapping - public String some() { - return "Some"; - } -} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/TaskController.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/TaskController.java index 77626af..178fb02 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/TaskController.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/controller/TaskController.java @@ -1,10 +1,18 @@ package br.com.tasknoteapp.java_api.controller; import br.com.tasknoteapp.java_api.entity.TaskEntity; +import br.com.tasknoteapp.java_api.exception.TaskNotFoundException; import br.com.tasknoteapp.java_api.request.TaskPatchRequest; import br.com.tasknoteapp.java_api.request.TaskRequest; import br.com.tasknoteapp.java_api.response.TaskResponse; import br.com.tasknoteapp.java_api.service.TaskService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +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.tags.Tag; import jakarta.validation.Valid; import java.util.List; import lombok.AllArgsConstructor; @@ -19,27 +27,123 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +/** This class contains resources for handling tasks. */ @RestController @RequestMapping("/rest/tasks") +@Tag(name = "Tasks", description = "Tasks resources to handle user tasks and urls.") @AllArgsConstructor public class TaskController { private final TaskService taskService; + /** + * Get all tasks. + * + * @return List of TaskResponse with all found tasks and its urls, if any. + */ @GetMapping + @Operation( + summary = "Get all tasks", + description = "Get all tasks for the current user and its urls, if any", + responses = { + @ApiResponse( + responseCode = "200", + description = "Tasks successfully retrieved", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = TaskResponse.class, type = "array"))), + @ApiResponse( + responseCode = "403", + description = "Forbidden. Access Denied", + content = @Content(schema = @Schema(implementation = Void.class))) + }) public List getAllTasks() { return taskService.getAllTasks(); } + /** + * Patch a task. + * + * @param id The task id to be patched. + * @param taskRequest Task data to be patched, including optionally its urls. + * @return TaskResponse containing data that was updated. + * @throws TaskNotFoundException when task not found. + */ @PatchMapping("/{id}") + @Operation( + summary = "Patch a task", + description = "Patch a task and all its urls. Option to patch only the urls.", + responses = { + @ApiResponse( + responseCode = "200", + description = "Task successfully patched", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = TaskResponse.class))), + @ApiResponse( + responseCode = "403", + description = "Forbidden. Access Denied", + content = @Content(schema = @Schema(implementation = Void.class))), + @ApiResponse( + responseCode = "404", + description = "Task not found", + content = @Content(schema = @Schema(implementation = Void.class))) + }) public ResponseEntity putTask( - @PathVariable Long id, @RequestBody @Valid TaskPatchRequest taskRequest) { + @Parameter( + name = "id", + in = ParameterIn.PATH, + description = "Task id to be patched.", + required = true, + schema = @Schema(type = "integer", format = "int64")) + @PathVariable + Long id, + @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "Task data to be patched, including optionally its urls.", + required = true) + @RequestBody + @Valid + TaskPatchRequest taskRequest) { return ResponseEntity.ok(taskService.patchTask(id, taskRequest)); } - // https://restfulapi.net/rest-put-vs-post/ + /** + * Create a task. + * + * @param taskRequest Task data to be created, including optionally its urls. Following RESTful + * API pattern from https://restfulapi.net/rest-put-vs-post/. + * @return TaskResponse containing data that was created. + */ @PostMapping - public ResponseEntity postTasks(@RequestBody @Valid TaskRequest taskRequest) { + @Operation( + summary = "Create a task", + description = "Create a task and all its urls.", + responses = { + @ApiResponse( + responseCode = "201", + description = "Task successfully crated.", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = TaskResponse.class))), + @ApiResponse( + responseCode = "400", + description = "Wrong or missing information", + content = @Content(schema = @Schema(implementation = Void.class))), + @ApiResponse( + responseCode = "403", + description = "Forbidden. Access Denied", + content = @Content(schema = @Schema(implementation = Void.class))), + }) + public ResponseEntity postTasks( + @io.swagger.v3.oas.annotations.parameters.RequestBody( + description = "Task data to be created, including optionally its urls.", + required = true) + @RequestBody + @Valid + TaskRequest taskRequest) { TaskEntity createdTask = taskService.createTask(taskRequest); return ResponseEntity.status(HttpStatus.CREATED).body(TaskResponse.fromEntity(createdTask)); } diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskPatchRequest.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskPatchRequest.java index b05fa84..ed3073d 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskPatchRequest.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskPatchRequest.java @@ -1,5 +1,11 @@ package br.com.tasknoteapp.java_api.request; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; -public record TaskPatchRequest(String description, Boolean done, List urls) {} +/** This record represents a task patch payload. */ +@Schema(description = "Task patch payload.") +public record TaskPatchRequest( + @Schema(description = "Task description. Optional.") String description, + @Schema(description = "Task done definition. Optional.") Boolean done, + @Schema(description = "Task urls. Optional.") List urls) {} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskRequest.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskRequest.java index ae1284f..1e6abcc 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskRequest.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/request/TaskRequest.java @@ -1,6 +1,11 @@ package br.com.tasknoteapp.java_api.request; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; import java.util.List; -public record TaskRequest(@NotNull String description, List urls) {} +/** This record represents a task request to be created. */ +@Schema(description = "Task request to be created.") +public record TaskRequest( + @Schema(description = "Task description.") @NotNull String description, + @Schema(description = "Task urls. Optional.") List urls) {} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskResponse.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskResponse.java index b9c9e36..1cceef5 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskResponse.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskResponse.java @@ -2,12 +2,26 @@ package br.com.tasknoteapp.java_api.response; import br.com.tasknoteapp.java_api.entity.TaskEntity; import br.com.tasknoteapp.java_api.entity.TaskUrlEntity; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.ArrayList; import java.util.List; import java.util.Objects; -public record TaskResponse(Long id, String description, Boolean done, List urls) { +/** This record represents a task and its urls object to be returned. */ +@Schema(description = "This record represents a task and its urls object to be returned.") +public record TaskResponse( + @Schema(description = "The id of the task", example = "1") Long id, + @Schema(description = "The description of the task", example = "Task 1") String description, + @Schema(description = "The done status of the task", example = "false") Boolean done, + @Schema(description = "The urls of the task, zero, one or more.", example = "[]") + List urls) { + /** + * Creates a TaskResponse given a TaskEntity and its Urls. + * + * @param entity The TaskEntity source data. + * @return TaskResponse instance with all task data and urls, if any. + */ public static TaskResponse fromEntity(TaskEntity entity) { List urls = entity.getUrls(); List urlsResponse = new ArrayList<>(); diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskUrlResponse.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskUrlResponse.java index 169e08f..6dc8435 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskUrlResponse.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/response/TaskUrlResponse.java @@ -1,3 +1,9 @@ package br.com.tasknoteapp.java_api.response; -public record TaskUrlResponse(Long id, String url) {} +import io.swagger.v3.oas.annotations.media.Schema; + +/** This record represents a task url object. */ +@Schema(description = "This record represents a task url object.") +public record TaskUrlResponse( + @Schema(description = "Task url id", example = "1") Long id, + @Schema(description = "Task url link", example = "http://duckduckgo.com") String url) {} diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/AuthService.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/AuthService.java index 749bca1..0e8c5e7 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/AuthService.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/AuthService.java @@ -5,6 +5,7 @@ import br.com.tasknoteapp.java_api.request.LoginRequest; import java.util.Optional; import org.springframework.security.core.userdetails.User; +/** This interface contains methods for handling user Authentication. */ public interface AuthService { /** diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/JwtService.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/JwtService.java index 7246f78..9aa3a08 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/JwtService.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/JwtService.java @@ -4,6 +4,7 @@ import java.time.LocalDateTime; import java.util.Map; import org.springframework.security.core.userdetails.UserDetails; +/** This interface contains methods for handling user JWT tokens. */ public interface JwtService { public String getEmailFromToken(String token); diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/TaskService.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/TaskService.java index 2498fc3..991ed5d 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/TaskService.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/TaskService.java @@ -6,6 +6,7 @@ import br.com.tasknoteapp.java_api.request.TaskRequest; import br.com.tasknoteapp.java_api.response.TaskResponse; import java.util.List; +/** This interface contains methods for handling user user Tasks. */ public interface TaskService { public List getAllTasks(); diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/UserService.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/UserService.java index 1bb628b..9208f9d 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/UserService.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/UserService.java @@ -2,6 +2,7 @@ package br.com.tasknoteapp.java_api.service; import org.springframework.security.core.userdetails.UserDetailsService; +/** This interface contains methods for handling user details withing Spring Auth. */ public interface UserService { UserDetailsService userDetailsService(); } diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/AuthServiceImpl.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/AuthServiceImpl.java index 3f23a8c..8c30ed8 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/AuthServiceImpl.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/AuthServiceImpl.java @@ -18,6 +18,7 @@ import org.springframework.security.core.userdetails.User; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; +/** This class contains the implementation for the Auth Service class. */ @Slf4j @Service @AllArgsConstructor diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/JwtServiceImpl.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/JwtServiceImpl.java index acfc44d..f85c8bb 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/JwtServiceImpl.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/JwtServiceImpl.java @@ -12,8 +12,9 @@ import javax.crypto.SecretKey; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Service; +/** This class contains the implementation for the Jwt Service class. */ @Service -public class JwtServiceImpl implements JwtService { +class JwtServiceImpl implements JwtService { private final long SECOND = 1000; private final long MINUTE = SECOND * 60; diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/TaskServiceImpl.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/TaskServiceImpl.java index ef79705..0fc677e 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/TaskServiceImpl.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/TaskServiceImpl.java @@ -22,6 +22,7 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +/** This class contains the implementation for the Task Service class. */ @Slf4j @Service @AllArgsConstructor diff --git a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/UserServiceImpl.java b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/UserServiceImpl.java index b485b22..740dbf9 100644 --- a/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/UserServiceImpl.java +++ b/java-api/src/main/java/br/com/tasknoteapp/java_api/service/impl/UserServiceImpl.java @@ -9,9 +9,10 @@ import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.stereotype.Service; +/** This class contains the implementation for the User Service class. */ @Service @RequiredArgsConstructor -public class UserServiceImpl implements UserService { +class UserServiceImpl implements UserService { private final UserRepository userRepository;