Skip to content

Commit 9497e25

Browse files
committed
docs(swagger): clean up schema names and standardize API documentation
- Add @Schema(name = "PageResponseDTO") to fix generated names like PageResponseDTOBookResponseDTO caused by generic type erasure - Add @Schema(hidden = true) to BookLoanDTO — internal DTO, should not appear as a top-level schema - Add @operation and @apiresponse to BookController.uploadCover which was undocumented - Translate CategoryController @operation descriptions to Portuguese for consistency with the rest of the controllers - Fix typo in OpenApiConfig: bearerFormat "JWTd" → "JWT" - Add persist-authorization: true to application-dev.yml so token survives page refresh in Swagger UI
1 parent 6b30e50 commit 9497e25

File tree

8 files changed

+80
-47
lines changed

8 files changed

+80
-47
lines changed

src/main/java/com/example/library/auth/AuthController.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import io.swagger.v3.oas.annotations.tags.Tag;
1818

1919
import com.example.library.auth.dto.LoginRequestDTO;
20-
import com.example.library.auth.dto.LogoutRequest;
20+
import com.example.library.auth.dto.LogoutRequestDTO;
2121
import com.example.library.auth.dto.RefreshTokenDTO;
2222
import com.example.library.auth.dto.TokenResponseDTO;
2323
import com.example.library.refresh_token.RefreshToken;
@@ -38,9 +38,9 @@ public class AuthController {
3838
private final RefreshTokenService refreshTokenService;
3939

4040
@Operation(
41-
summary = "Autenticar usuário e obter tokens JWT",
42-
description = "Autentica o usuário usando username e password, e retorna um access token JWT e um refresh token."
43-
)
41+
summary = "Autenticar usuário e obter tokens JWT",
42+
description = "Autentica o usuário usando username e password, e retorna um access token JWT e um refresh token."
43+
)
4444
@PostMapping("/login")
4545
public TokenResponseDTO login(@RequestBody LoginRequestDTO request) {
4646

@@ -58,9 +58,9 @@ public TokenResponseDTO login(@RequestBody LoginRequestDTO request) {
5858
}
5959

6060
@Operation(
61-
summary = "Renovar tokens JWT usando refresh token",
62-
description = "Valida o refresh token e, se válido, gera um novo access token e um novo refresh token (token rotation)."
63-
)
61+
summary = "Renovar tokens JWT usando refresh token",
62+
description = "Valida o refresh token e, se válido, gera um novo access token e um novo refresh token (token rotation)."
63+
)
6464
@PostMapping("/refresh")
6565
public ResponseEntity<TokenResponseDTO> refresh(@RequestBody RefreshTokenDTO refreshToken) {
6666

@@ -83,11 +83,11 @@ public ResponseEntity<TokenResponseDTO> refresh(@RequestBody RefreshTokenDTO ref
8383
}
8484

8585
@Operation(
86-
summary = "Realizar o logout usando refresh token",
87-
description = "Deleta refresh token do bd."
88-
)
86+
summary = "Realizar o logout usando refresh token",
87+
description = "Deleta refresh token do bd."
88+
)
8989
@PostMapping("/logout")
90-
public ResponseEntity<Void> logout(@RequestBody LogoutRequest request) {
90+
public ResponseEntity<Void> logout(@RequestBody LogoutRequestDTO request) {
9191
refreshTokenService.invalidate(request.refreshToken());
9292
return ResponseEntity.noContent().build();
9393
}

src/main/java/com/example/library/auth/dto/LogoutRequest.java renamed to src/main/java/com/example/library/auth/dto/LogoutRequestDTO.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22

33
import com.fasterxml.jackson.annotation.JsonAlias;
44

5-
public record LogoutRequest(
6-
7-
@JsonAlias("refresh_token")
5+
public record LogoutRequestDTO(
6+
@JsonAlias("refresh_token")
87
String refreshToken
9-
10-
) {
11-
12-
}
8+
) {}

src/main/java/com/example/library/book/BookController.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,12 @@ public ResponseEntity<Void> deleteById(@PathVariable Long id) {
8888
return ResponseEntity.noContent().build();
8989
}
9090

91+
@Operation(
92+
summary = "Upload de capa do livro",
93+
description = "Faz upload de uma imagem de capa para um livro existente e salva a URL no S3"
94+
)
95+
@ApiResponse(responseCode = "200", description = "Capa enviada com sucesso")
96+
@ApiResponse(responseCode = "404", description = "Livro não encontrado")
9197
@PostMapping("/{id}/cover")
9298
public ResponseEntity<URI> uploadCover(@PathVariable Long id, @RequestPart ("file") MultipartFile file) {
9399
URI uri = bookMediaService.uploadCover(id, file);

src/main/java/com/example/library/category/CategoryController.java

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,21 @@
2323
import com.example.library.common.dto.PageResponseDTO;
2424

2525
import jakarta.validation.Valid;
26+
import lombok.RequiredArgsConstructor;
2627

2728
@Tag(name = "Categories", description = "Endpoints para gerenciamento de categorias de livros")
29+
@RequiredArgsConstructor
2830
@RestController
2931
@RequestMapping("/api/v1/categories")
3032
public class CategoryController {
3133

3234
private final CategoryService service;
3335

34-
public CategoryController(CategoryService service) {
35-
this.service = service;
36-
}
37-
38-
@Operation(summary = "Create a new category")
36+
@Operation(
37+
summary = "Criar nova categoria",
38+
description = "Cria uma nova categoria de livros. Requer permissão de administrador"
39+
)
40+
@ApiResponse(responseCode = "201", description = "Categoria criada com sucesso")
3941
@PostMapping
4042
@PreAuthorize("hasRole('ADMIN')")
4143
public ResponseEntity<CategoryResponseDTO> create(@RequestBody @Valid CategoryCreateDTO dto) {
@@ -44,24 +46,34 @@ public ResponseEntity<CategoryResponseDTO> create(@RequestBody @Valid CategoryCr
4446
return ResponseEntity.created(uri).body(response);
4547
}
4648

47-
@Operation(summary = "Find category by id")
48-
@ApiResponse(responseCode = "200", description = "Category found")
49-
@ApiResponse(responseCode = "404", description = "Category not found")
50-
@GetMapping("/{id}")
49+
@Operation(
50+
summary = "Buscar categoria por ID",
51+
description = "Retorna os detalhes de uma categoria específica usando seu ID"
52+
)
53+
@ApiResponse(responseCode = "200", description = "Categoria encontrada")
54+
@ApiResponse(responseCode = "404", description = "Categoria não encontrada")
55+
@GetMapping("/{id}")
5156
public ResponseEntity<CategoryResponseDTO> findById(@PathVariable Long id) {
5257
return ResponseEntity.ok(service.findById(id));
5358
}
5459

55-
@Operation(summary = "List all categories")
56-
@GetMapping
60+
@Operation(
61+
summary = "Listar categorias com paginação",
62+
description = "Retorna uma lista paginada de categorias"
63+
)
64+
@ApiResponse(responseCode = "200", description = "Lista de categorias retornada com sucesso")
65+
@GetMapping
5766
public ResponseEntity<PageResponseDTO<CategoryResponseDTO>> findAll(Pageable pageable) {
5867
return ResponseEntity.ok(service.findAll(pageable));
5968
}
6069

61-
@Operation(summary = "Delete category by id")
62-
@ApiResponse(responseCode = "204", description = "Category deleted")
63-
@ApiResponse(responseCode = "404", description = "Category not found")
64-
@DeleteMapping("/{id}")
70+
@Operation(
71+
summary = "Deletar categoria por ID",
72+
description = "Remove uma categoria existente do sistema. Requer permissão de administrador"
73+
)
74+
@ApiResponse(responseCode = "204", description = "Categoria deletada com sucesso")
75+
@ApiResponse(responseCode = "404", description = "Categoria não encontrada")
76+
@DeleteMapping("/{id}")
6577
@PreAuthorize("hasRole('ADMIN')")
6678
public ResponseEntity<Void> deleteById(@PathVariable Long id) {
6779
service.deleteById(id);

src/main/java/com/example/library/common/dto/PageResponseDTO.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
import java.io.Serializable;
44
import java.util.List;
55

6+
import io.swagger.v3.oas.annotations.media.Schema;
7+
8+
@Schema(name = "PageResponseDTO", description = "Resposta paginada genérica")
69
public record PageResponseDTO<T>(
710
List<T> content,
811
int page,
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.example.library.loan.dto;
22

3-
public record BookLoanDTO(
3+
import io.swagger.v3.oas.annotations.media.Schema;
44

5+
@Schema(hidden = true)
6+
public record BookLoanDTO(
57
String title
6-
7-
) {
8-
}
8+
) {}

src/main/java/com/example/library/swagger/OpenApiConfig.java

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ OpenAPI libraryOpenAPI() {
2323
return new OpenAPI()
2424
.info(apiInfo())
2525
.addSecurityItem(new SecurityRequirement().addList("Bearer Authentication"))
26-
.components(components()
27-
)
26+
.components(components())
2827
.externalDocs(new ExternalDocumentation()
2928
.description("GitHub Repository")
3029
.url("https://github.com/erichiroshi/library-api"))
@@ -35,12 +34,20 @@ private Components components() {
3534
return new Components().addSecuritySchemes("Bearer Authentication",
3635
new SecurityScheme()
3736
.description("""
38-
Insira o token JWT gerado após o login:
39-
access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
37+
1. Execute POST /auth/login com suas credenciais
38+
2. Copie o valor de access_token da resposta
39+
3. Clique em "Authorize" (🔒) no topo da página
40+
4. Cole o token no campo e clique em "Authorize"
41+
42+
O token ficará salvo para todas as chamadas da sessão.
43+
44+
Credenciais de teste:
45+
- ADMIN → admin@admin.com / admin123
46+
- USER → user@user.com / user123
4047
""")
4148
.type(SecurityScheme.Type.HTTP)
4249
.scheme("bearer")
43-
.bearerFormat("JWTd"));
50+
.bearerFormat("JWT"));
4451
}
4552

4653
private Info apiInfo() {
@@ -66,10 +73,12 @@ private Info apiInfo() {
6673
}
6774

6875
private List<Tag> apiTags() {
69-
return List.of(new Tag().name("Auth").description("Endpoints para autenticação e renovação de tokens JWT"),
70-
new Tag().name("Authors").description("Endpoints para gerenciamento de autores"),
71-
new Tag().name("Books").description("Endpoints para gerenciamento de livros"),
72-
new Tag().name("Loans").description("Endpoints para gerenciamento de categorias de livros"),
73-
new Tag().name("Categories").description("Endpoints para gerenciamento de empréstimos de livros"));
76+
return List.of(
77+
new Tag().name("Auth").description("Endpoints para autenticação e renovação de tokens JWT"),
78+
new Tag().name("Authors").description("Endpoints para gerenciamento de autores"),
79+
new Tag().name("Books").description("Endpoints para gerenciamento de livros"),
80+
new Tag().name("Categories").description("Endpoints para gerenciamento de categorias de livros"),
81+
new Tag().name("Loans").description("Endpoints para gerenciamento de empréstimos de livros")
82+
);
7483
}
7584
}

src/main/resources/application-dev.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ spring:
2424
pool:
2525
min-idle: 0
2626

27+
# SWAGGER UI
28+
springdoc:
29+
swagger-ui:
30+
persist-authorization: true # token sobrevive ao F5
31+
tags-sorter: alpha # remove — deixa na ordem do OpenApiConfig
32+
2733
# ═══════════════════════════════════════════════════════════════════
2834
# TRACING — Micrometer + OpenTelemetry
2935
# ═══════════════════════════════════════════════════════════════════
@@ -43,6 +49,7 @@ management:
4349
health:
4450
show-details: always
4551

52+
# LOG
4653
logging:
4754
level:
4855
'[com.example.library]': DEBUG

0 commit comments

Comments
 (0)