Skip to content

Commit 6c23102

Browse files
authored
Merge pull request #183 from nanotaboada/feature/validation
refactor: migrate validations from services to controllers
2 parents 193d218 + 2b44db7 commit 6c23102

File tree

9 files changed

+191
-333
lines changed

9 files changed

+191
-333
lines changed

.vscode/settings.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"editor.detectIndentation": false,
66
"editor.formatOnSave": true,
77
"[java]": {
8-
"editor.defaultFormatter": "redhat.java"
8+
"editor.defaultFormatter": "redhat.java",
9+
"editor.inlayHints.enabled": "off"
910
},
1011
"java.configuration.updateBuildConfiguration": "automatic",
1112
"java.compile.nullAnalysis.mode": "automatic",

assets/images/structure.svg

Lines changed: 1 addition & 1 deletion
Loading
Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package ar.com.nanotaboada.java.samples.spring.boot.controllers;
22

3+
import static org.springframework.http.HttpHeaders.LOCATION;
4+
35
import java.net.URI;
46
import java.util.List;
57

6-
import org.springframework.http.HttpHeaders;
8+
import jakarta.validation.Valid;
9+
10+
import org.hibernate.validator.constraints.ISBN;
711
import org.springframework.http.HttpStatus;
812
import org.springframework.http.ResponseEntity;
913
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -25,6 +29,7 @@
2529
import io.swagger.v3.oas.annotations.responses.ApiResponse;
2630
import io.swagger.v3.oas.annotations.responses.ApiResponses;
2731
import io.swagger.v3.oas.annotations.tags.Tag;
32+
2833
import ar.com.nanotaboada.java.samples.spring.boot.models.BookDTO;
2934
import ar.com.nanotaboada.java.samples.spring.boot.services.BooksService;
3035

@@ -52,22 +57,18 @@ public BooksController(BooksService booksService) {
5257
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content),
5358
@ApiResponse(responseCode = "409", description = "Conflict", content = @Content)
5459
})
55-
public ResponseEntity<String> post(@RequestBody BookDTO bookDTO) {
56-
if (booksService.retrieveByIsbn(bookDTO.getIsbn()) != null) {
57-
return new ResponseEntity<>(HttpStatus.CONFLICT);
58-
} else {
59-
if (booksService.create(bookDTO)) {
60-
URI location = MvcUriComponentsBuilder
61-
.fromMethodName(BooksController.class, "getByIsbn", bookDTO.getIsbn())
62-
.build()
63-
.toUri();
64-
HttpHeaders httpHeaders = new HttpHeaders();
65-
httpHeaders.setLocation(location);
66-
return new ResponseEntity<>(httpHeaders, HttpStatus.CREATED);
67-
} else {
68-
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
69-
}
60+
public ResponseEntity<Void> post(@RequestBody @Valid BookDTO bookDTO) {
61+
boolean created = booksService.create(bookDTO);
62+
if (!created) {
63+
return ResponseEntity.status(HttpStatus.CONFLICT).build();
7064
}
65+
URI location = MvcUriComponentsBuilder
66+
.fromMethodCall(MvcUriComponentsBuilder.on(BooksController.class).getByIsbn(bookDTO.getIsbn()))
67+
.build()
68+
.toUri();
69+
return ResponseEntity.status(HttpStatus.CREATED)
70+
.header(LOCATION, location.toString())
71+
.build();
7172
}
7273

7374
/*
@@ -77,18 +78,16 @@ public ResponseEntity<String> post(@RequestBody BookDTO bookDTO) {
7778
*/
7879

7980
@GetMapping("/books/{isbn}")
80-
@Operation(summary = "Retrieves a book by its ID")
81+
@Operation(summary = "Retrieves a book by its ISBN")
8182
@ApiResponses(value = {
8283
@ApiResponse(responseCode = "200", description = "OK", content = @Content(mediaType = "application/json", schema = @Schema(implementation = BookDTO.class))),
8384
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content)
8485
})
8586
public ResponseEntity<BookDTO> getByIsbn(@PathVariable String isbn) {
8687
BookDTO bookDTO = booksService.retrieveByIsbn(isbn);
87-
if (bookDTO != null) {
88-
return new ResponseEntity<>(bookDTO, HttpStatus.OK);
89-
} else {
90-
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
91-
}
88+
return (bookDTO != null)
89+
? ResponseEntity.status(HttpStatus.OK).body(bookDTO)
90+
: ResponseEntity.status(HttpStatus.NOT_FOUND).build();
9291
}
9392

9493
@GetMapping("/books")
@@ -98,7 +97,7 @@ public ResponseEntity<BookDTO> getByIsbn(@PathVariable String isbn) {
9897
})
9998
public ResponseEntity<List<BookDTO>> getAll() {
10099
List<BookDTO> books = booksService.retrieveAll();
101-
return new ResponseEntity<>(books, HttpStatus.OK);
100+
return ResponseEntity.status(HttpStatus.OK).body(books);
102101
}
103102

104103
/*
@@ -108,22 +107,17 @@ public ResponseEntity<List<BookDTO>> getAll() {
108107
*/
109108

110109
@PutMapping("/books")
111-
@Operation(summary = "Updates (entirely) a book by its ID")
110+
@Operation(summary = "Updates (entirely) a book by its ISBN")
112111
@ApiResponses(value = {
113112
@ApiResponse(responseCode = "204", description = "No Content", content = @Content),
114113
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content),
115114
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content)
116115
})
117-
public ResponseEntity<String> put(@RequestBody BookDTO bookDTO) {
118-
if (booksService.retrieveByIsbn(bookDTO.getIsbn()) != null) {
119-
if (booksService.update(bookDTO)) {
120-
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
121-
} else {
122-
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
123-
}
124-
} else {
125-
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
126-
}
116+
public ResponseEntity<Void> put(@RequestBody @Valid BookDTO bookDTO) {
117+
boolean updated = booksService.update(bookDTO);
118+
return (updated)
119+
? ResponseEntity.status(HttpStatus.NO_CONTENT).build()
120+
: ResponseEntity.status(HttpStatus.NOT_FOUND).build();
127121
}
128122

129123
/*
@@ -133,21 +127,16 @@ public ResponseEntity<String> put(@RequestBody BookDTO bookDTO) {
133127
*/
134128

135129
@DeleteMapping("/books/{isbn}")
136-
@Operation(summary = "Deletes a book by its ID")
130+
@Operation(summary = "Deletes a book by its ISBN")
137131
@ApiResponses(value = {
138132
@ApiResponse(responseCode = "204", description = "No Content", content = @Content),
139133
@ApiResponse(responseCode = "400", description = "Bad Request", content = @Content),
140134
@ApiResponse(responseCode = "404", description = "Not Found", content = @Content)
141135
})
142-
public ResponseEntity<String> delete(@PathVariable String isbn) {
143-
if (booksService.retrieveByIsbn(isbn) != null) {
144-
if (booksService.delete(isbn)) {
145-
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
146-
} else {
147-
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
148-
}
149-
} else {
150-
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
151-
}
136+
public ResponseEntity<Void> delete(@PathVariable @ISBN String isbn) {
137+
boolean deleted = booksService.delete(isbn);
138+
return (deleted)
139+
? ResponseEntity.status(HttpStatus.NO_CONTENT).build()
140+
: ResponseEntity.status(HttpStatus.NOT_FOUND).build();
152141
}
153142
}

src/main/java/ar/com/nanotaboada/java/samples/spring/boot/services/BooksService.java

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package ar.com.nanotaboada.java.samples.spring.boot.services;
22

3-
import jakarta.validation.Validator;
4-
import lombok.RequiredArgsConstructor;
5-
63
import java.util.List;
74
import java.util.stream.StreamSupport;
85

6+
import lombok.RequiredArgsConstructor;
7+
98
import org.modelmapper.ModelMapper;
109
import org.springframework.cache.annotation.CacheEvict;
1110
import org.springframework.cache.annotation.CachePut;
@@ -21,7 +20,6 @@
2120
public class BooksService {
2221

2322
private final BooksRepository booksRepository;
24-
private final Validator validator;
2523
private final ModelMapper modelMapper;
2624

2725
/*
@@ -32,14 +30,14 @@ public class BooksService {
3230

3331
@CachePut(value = "books", key = "#bookDTO.isbn")
3432
public boolean create(BookDTO bookDTO) {
35-
boolean notExists = !booksRepository.existsById(bookDTO.getIsbn());
36-
boolean valid = validator.validate(bookDTO).isEmpty();
37-
if (notExists && valid) {
33+
if (booksRepository.existsById(bookDTO.getIsbn())) {
34+
return false;
35+
} else {
3836
Book book = mapFrom(bookDTO);
3937
booksRepository.save(book);
4038
return true;
4139
}
42-
return false;
40+
4341
}
4442

4543
/*
@@ -70,14 +68,13 @@ public List<BookDTO> retrieveAll() {
7068

7169
@CachePut(value = "books", key = "#bookDTO.isbn")
7270
public boolean update(BookDTO bookDTO) {
73-
boolean exists = booksRepository.existsById(bookDTO.getIsbn());
74-
boolean valid = validator.validate(bookDTO).isEmpty();
75-
if (exists && valid) {
71+
if (booksRepository.existsById(bookDTO.getIsbn())) {
7672
Book book = mapFrom(bookDTO);
7773
booksRepository.save(book);
7874
return true;
75+
} else {
76+
return false;
7977
}
80-
return false;
8178
}
8279

8380
/*
@@ -91,8 +88,9 @@ public boolean delete(String isbn) {
9188
if (booksRepository.existsById(isbn)) {
9289
booksRepository.deleteById(isbn);
9390
return true;
91+
} else {
92+
return false;
9493
}
95-
return false;
9694
}
9795

9896
private BookDTO mapFrom(Book book) {

src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOsBuilder.java renamed to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookDTOFakes.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@
66

77
import ar.com.nanotaboada.java.samples.spring.boot.models.BookDTO;
88

9-
public class BookDTOsBuilder {
9+
public final class BookDTOFakes {
1010

11-
public static BookDTO buildOneValid() {
11+
private BookDTOFakes() {
12+
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
13+
}
14+
15+
public static BookDTO createOneValid() {
1216
BookDTO bookDTO = new BookDTO();
1317
bookDTO.setIsbn("978-1484200773");
1418
bookDTO.setTitle("Pro Git");
@@ -28,7 +32,7 @@ Pro Git (Second Edition) is your fully-updated guide to Git and its \
2832
return bookDTO;
2933
}
3034

31-
public static BookDTO buildOneInvalid() {
35+
public static BookDTO createOneInvalid() {
3236
BookDTO bookDTO = new BookDTO();
3337
bookDTO.setIsbn("978-1234567890"); // Invalid (invalid ISBN)
3438
bookDTO.setTitle("Title");
@@ -42,7 +46,7 @@ public static BookDTO buildOneInvalid() {
4246
return bookDTO;
4347
}
4448

45-
public static List<BookDTO> buildManyValid() {
49+
public static List<BookDTO> createManyValid() {
4650
ArrayList<BookDTO> bookDTOs = new ArrayList<>();
4751
BookDTO bookDTO9781838986698 = new BookDTO();
4852
bookDTO9781838986698.setIsbn("9781838986698");

src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BooksBuilder.java renamed to src/test/java/ar/com/nanotaboada/java/samples/spring/boot/test/BookFakes.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,13 @@
66

77
import ar.com.nanotaboada.java.samples.spring.boot.models.Book;
88

9-
public class BooksBuilder {
9+
public final class BookFakes {
1010

11-
public static Book buildOneValid() {
11+
private BookFakes() {
12+
throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
13+
}
14+
15+
public static Book createOneValid() {
1216
Book book = new Book();
1317
book.setIsbn("9781484200773");
1418
book.setTitle("Pro Git");
@@ -28,7 +32,7 @@ Pro Git (Second Edition) is your fully-updated guide to Git and its \
2832
return book;
2933
}
3034

31-
public static Book buildOneInvalid() {
35+
public static Book createOneInvalid() {
3236
Book book = new Book();
3337
book.setIsbn("9781234567890"); // Invalid (invalid ISBN)
3438
book.setTitle("Title");
@@ -42,7 +46,7 @@ public static Book buildOneInvalid() {
4246
return book;
4347
}
4448

45-
public static List<Book> buildManyValid() {
49+
public static List<Book> createManyValid() {
4650
ArrayList<Book> books = new ArrayList<>();
4751
Book book9781838986698 = new Book();
4852
book9781838986698.setIsbn("9781838986698");

0 commit comments

Comments
 (0)