Added custom exception handlers
This commit is contained in:
parent
813b38aed1
commit
0757c02db0
@ -14,6 +14,14 @@ import java.util.HashMap;
|
|||||||
|
|
||||||
@ControllerAdvice
|
@ControllerAdvice
|
||||||
public class CustomExceptionsHandler {
|
public class CustomExceptionsHandler {
|
||||||
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
|
@ExceptionHandler(GenericErrorException.class)
|
||||||
|
public ResponseEntity<String> genericErrorException(GenericErrorException ex) {
|
||||||
|
var error = new JsonEmitter<>(ex.getMessage()).emitJsonKey(ex.getKey());
|
||||||
|
|
||||||
|
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
@ResponseStatus(HttpStatus.BAD_REQUEST)
|
||||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||||
public ResponseEntity<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
|
public ResponseEntity<String> handleValidationExceptions(MethodArgumentNotValidException ex) {
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package com.ceticamarco.bits.exception;
|
||||||
|
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
|
@ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "Bad Request")
|
||||||
|
public class GenericErrorException extends RuntimeException {
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
public GenericErrorException(String message, String key) {
|
||||||
|
super(message);
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GenericErrorException() {
|
||||||
|
super("Bad Request");
|
||||||
|
this.key = "error";
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() { return key; }
|
||||||
|
}
|
@ -3,7 +3,7 @@ package com.ceticamarco.bits.exception;
|
|||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||||
|
|
||||||
@ResponseStatus(value= HttpStatus.UNAUTHORIZED, reason="Unauthorized user")
|
@ResponseStatus(value = HttpStatus.UNAUTHORIZED, reason = "Unauthorized User")
|
||||||
public class UnauthorizedUserException extends RuntimeException {
|
public class UnauthorizedUserException extends RuntimeException {
|
||||||
public UnauthorizedUserException(String message) {
|
public UnauthorizedUserException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.ceticamarco.bits.post;
|
package com.ceticamarco.bits.post;
|
||||||
|
|
||||||
|
import com.ceticamarco.bits.exception.GenericErrorException;
|
||||||
import com.ceticamarco.bits.exception.UnauthorizedUserException;
|
import com.ceticamarco.bits.exception.UnauthorizedUserException;
|
||||||
import com.ceticamarco.bits.json.JsonEmitter;
|
import com.ceticamarco.bits.json.JsonEmitter;
|
||||||
import com.ceticamarco.bits.user.User;
|
import com.ceticamarco.bits.user.User;
|
||||||
@ -28,14 +29,14 @@ public class PostController {
|
|||||||
public ResponseEntity<List<Post>> getPosts(@RequestBody User user) {
|
public ResponseEntity<List<Post>> getPosts(@RequestBody User user) {
|
||||||
// Check if email and password are specified
|
// Check if email and password are specified
|
||||||
if(user.getPassword() == null || user.getEmail() == null) {
|
if(user.getPassword() == null || user.getEmail() == null) {
|
||||||
throw new UnauthorizedUserException("Specify both email and password");
|
throw new GenericErrorException("Specify both email and password", "error");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get post list
|
// Get post list
|
||||||
var res = postService.getPosts(user);
|
var res = postService.getPosts(user);
|
||||||
|
|
||||||
// Check if user is authorized
|
// Check if user is authorized
|
||||||
if(res.isLeft()) { // TODO: implement proper generic exception handler
|
if(res.isLeft()) {
|
||||||
throw new UnauthorizedUserException(res.getLeft().getMessage());
|
throw new UnauthorizedUserException(res.getLeft().getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,15 +51,13 @@ public class PostController {
|
|||||||
*/
|
*/
|
||||||
@GetMapping("/api/posts/{postId}")
|
@GetMapping("/api/posts/{postId}")
|
||||||
public ResponseEntity<String> getPostById(@PathVariable("postId") String postId) {
|
public ResponseEntity<String> getPostById(@PathVariable("postId") String postId) {
|
||||||
var res = postService.getPostById(postId)
|
var res = postService.getPostById(postId);
|
||||||
.map(post -> new JsonEmitter<>(post).emitJsonKey())
|
if(res.isLeft()) {
|
||||||
.swap()
|
throw new GenericErrorException(res.getLeft().getMessage(), "error");
|
||||||
.map(error -> new JsonEmitter<>(error.getMessage()).emitJsonKey("error"))
|
}
|
||||||
.swap();
|
|
||||||
|
|
||||||
return res.isRight()
|
var jsonOutput = new JsonEmitter<>(res.get()).emitJsonKey();
|
||||||
? new ResponseEntity<>(res.get(), HttpStatus.OK)
|
return new ResponseEntity<>(jsonOutput, HttpStatus.OK);
|
||||||
: new ResponseEntity<>(res.getLeft(), HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,14 +71,14 @@ public class PostController {
|
|||||||
public ResponseEntity<List<Post>> getPostByTitle(@RequestBody Post req) {
|
public ResponseEntity<List<Post>> getPostByTitle(@RequestBody Post req) {
|
||||||
// Check if email and password are specified
|
// Check if email and password are specified
|
||||||
if(req.getUser() == null || req.getUser().getPassword() == null || req.getUser().getEmail() == null) {
|
if(req.getUser() == null || req.getUser().getPassword() == null || req.getUser().getEmail() == null) {
|
||||||
throw new UnauthorizedUserException("Specify both email and password");
|
throw new GenericErrorException("Specify both email and password", "error");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get post by title
|
// Get post by title
|
||||||
var res = postService.getPostByTitle(req);
|
var res = postService.getPostByTitle(req);
|
||||||
|
|
||||||
// Check if user is authorized
|
// Check if user is authorized
|
||||||
if(res.isLeft()) { // TODO: implement proper generic exception handler
|
if(res.isLeft()) {
|
||||||
throw new UnauthorizedUserException(res.getLeft().getMessage());
|
throw new UnauthorizedUserException(res.getLeft().getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,15 +93,13 @@ public class PostController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/api/posts/new")
|
@PostMapping("/api/posts/new")
|
||||||
public ResponseEntity<String> submitPost(@Valid @RequestBody Post post) {
|
public ResponseEntity<String> submitPost(@Valid @RequestBody Post post) {
|
||||||
var res = postService.addNewPost(post)
|
var res =postService.addNewPost(post);
|
||||||
.map(postId -> new JsonEmitter<>(postId).emitJsonKey("post_id"))
|
if(res.isLeft()) {
|
||||||
.swap()
|
throw new GenericErrorException(res.getLeft().getMessage(), "error");
|
||||||
.map(error -> new JsonEmitter<>(error.getMessage()).emitJsonKey("error"))
|
}
|
||||||
.swap();
|
|
||||||
|
|
||||||
return res.isRight()
|
var jsonOutput = new JsonEmitter<>(res.get()).emitJsonKey("post_id");
|
||||||
? new ResponseEntity<>(res.get(), HttpStatus.OK)
|
return new ResponseEntity<>(jsonOutput, HttpStatus.OK);
|
||||||
: new ResponseEntity<>(res.getLeft(), HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -114,15 +111,14 @@ public class PostController {
|
|||||||
*/
|
*/
|
||||||
@PutMapping("/api/posts/{postId}")
|
@PutMapping("/api/posts/{postId}")
|
||||||
public ResponseEntity<String> updatePost(@Valid @RequestBody Post post, @PathVariable("postId") String postId) {
|
public ResponseEntity<String> updatePost(@Valid @RequestBody Post post, @PathVariable("postId") String postId) {
|
||||||
|
// Update post
|
||||||
var res = postService.updatePost(post, postId);
|
var res = postService.updatePost(post, postId);
|
||||||
|
if(res.isPresent()) {
|
||||||
|
throw new GenericErrorException(res.get().getMessage(), "error");
|
||||||
|
}
|
||||||
|
|
||||||
return res.map(error -> {
|
var jsonOutput = new JsonEmitter<>("OK").emitJsonKey("status");
|
||||||
var jsonOutput = new JsonEmitter<>(res.get().getMessage()).emitJsonKey("error");
|
return new ResponseEntity<>(jsonOutput, HttpStatus.OK);
|
||||||
return new ResponseEntity<>(jsonOutput, HttpStatus.BAD_REQUEST);
|
|
||||||
}).orElseGet(() -> {
|
|
||||||
var jsonOutput = new JsonEmitter<String>("OK").emitJsonKey("status");
|
|
||||||
return new ResponseEntity<>(jsonOutput, HttpStatus.OK);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -142,12 +138,11 @@ public class PostController {
|
|||||||
|
|
||||||
// Delete the post
|
// Delete the post
|
||||||
var res = postService.deletePost(user, postId);
|
var res = postService.deletePost(user, postId);
|
||||||
return res.map(error -> {
|
if(res.isPresent()) {
|
||||||
var jsonOutput = new JsonEmitter<>(error.getMessage()).emitJsonKey("error");
|
throw new GenericErrorException(res.get().getMessage(), "error");
|
||||||
return new ResponseEntity<>(jsonOutput, HttpStatus.BAD_REQUEST);
|
}
|
||||||
}).orElseGet(() -> {
|
|
||||||
var jsonOutput = new JsonEmitter<>("OK").emitJsonKey("status");
|
var jsonOutput = new JsonEmitter<>("OK").emitJsonKey("status");
|
||||||
return new ResponseEntity<>(jsonOutput, HttpStatus.OK);
|
return new ResponseEntity<>(jsonOutput, HttpStatus.OK);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.ceticamarco.bits.user;
|
package com.ceticamarco.bits.user;
|
||||||
|
|
||||||
|
import com.ceticamarco.bits.exception.GenericErrorException;
|
||||||
import com.ceticamarco.bits.exception.UnauthorizedUserException;
|
import com.ceticamarco.bits.exception.UnauthorizedUserException;
|
||||||
import com.ceticamarco.bits.json.JsonEmitter;
|
import com.ceticamarco.bits.json.JsonEmitter;
|
||||||
import jakarta.validation.Valid;
|
import jakarta.validation.Valid;
|
||||||
@ -29,14 +30,14 @@ public class UserController {
|
|||||||
public ResponseEntity<List<User>> getUsers(@RequestBody User user) {
|
public ResponseEntity<List<User>> getUsers(@RequestBody User user) {
|
||||||
// Check if email and password are specified
|
// Check if email and password are specified
|
||||||
if(user.getPassword() == null || user.getEmail() == null) {
|
if(user.getPassword() == null || user.getEmail() == null) {
|
||||||
throw new UnauthorizedUserException("Specify both email and password");
|
throw new GenericErrorException("Specify both email and password", "error");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get post list
|
// Get post list
|
||||||
var res = userService.getUsers(user);
|
var res = userService.getUsers(user);
|
||||||
|
|
||||||
// Check if user is authorized
|
// Check if user is authorized
|
||||||
if(res.isLeft()) { // TODO: implement proper generic exception handler
|
if(res.isLeft()) {
|
||||||
throw new UnauthorizedUserException(res.getLeft().getMessage());
|
throw new UnauthorizedUserException(res.getLeft().getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,15 +52,13 @@ public class UserController {
|
|||||||
*/
|
*/
|
||||||
@PostMapping("/api/users/new")
|
@PostMapping("/api/users/new")
|
||||||
public ResponseEntity<String> submitUser(@Valid @RequestBody User user) {
|
public ResponseEntity<String> submitUser(@Valid @RequestBody User user) {
|
||||||
var res = userService.addNewUser(user)
|
var res = userService.addNewUser(user);
|
||||||
.map(userId -> new JsonEmitter<>(userId).emitJsonKey("user_id"))
|
if(res.isLeft()) {
|
||||||
.swap()
|
throw new GenericErrorException(res.getLeft().getMessage(), "error");
|
||||||
.map(error -> new JsonEmitter<>(error.getMessage()).emitJsonKey("error"))
|
}
|
||||||
.swap();
|
|
||||||
|
|
||||||
return res.isRight()
|
var jsonOutput = new JsonEmitter<>(res.get()).emitJsonKey("user_id");
|
||||||
? new ResponseEntity<>(res.get(), HttpStatus.OK)
|
return new ResponseEntity<>(jsonOutput, HttpStatus.OK);
|
||||||
: new ResponseEntity<>(res.getLeft(), HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,17 +71,15 @@ public class UserController {
|
|||||||
public ResponseEntity<String> deleteUser(@RequestBody User user) {
|
public ResponseEntity<String> deleteUser(@RequestBody User user) {
|
||||||
// Check if email and password are specified
|
// Check if email and password are specified
|
||||||
if(user.getPassword() == null || user.getEmail() == null) {
|
if(user.getPassword() == null || user.getEmail() == null) {
|
||||||
var res = new JsonEmitter<>("Specify both email and password").emitJsonKey("error");
|
throw new GenericErrorException("Specify both email and password", "error");
|
||||||
return new ResponseEntity<>(res, HttpStatus.BAD_REQUEST);
|
|
||||||
}
|
}
|
||||||
// Delete the user
|
// Delete the user
|
||||||
var res = userService.deleteUser(user);
|
var res = userService.deleteUser(user);
|
||||||
return res.map(error -> {
|
if(res.isPresent()) {
|
||||||
var jsonOutput = new JsonEmitter<>(error.getMessage()).emitJsonKey("error");
|
throw new GenericErrorException(res.get().getMessage(), "error");
|
||||||
return new ResponseEntity<>(jsonOutput, HttpStatus.BAD_REQUEST);
|
}
|
||||||
}).orElseGet(() -> {
|
|
||||||
var jsonOutput = new JsonEmitter<>("OK").emitJsonKey("status");
|
var jsonOutput = new JsonEmitter<>("OK").emitJsonKey("status");
|
||||||
return new ResponseEntity<>(jsonOutput, HttpStatus.OK);
|
return new ResponseEntity<>(jsonOutput, HttpStatus.OK);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
spring.jpa.hibernate.ddl-auto=update
|
spring.jpa.hibernate.ddl-auto=create-drop
|
||||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
|
||||||
spring.jpa.properties.hibernate.format_sql=true
|
spring.jpa.properties.hibernate.format_sql=true
|
||||||
|
|
||||||
# Adjust these values in production
|
# Adjust these values in production
|
||||||
#server.port=3000
|
server.port=3000
|
||||||
#spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/bit
|
spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/bit
|
||||||
#spring.datasource.username=postgres
|
spring.datasource.username=postgres
|
||||||
#spring.datasource.password=toor
|
spring.datasource.password=toor
|
||||||
#spring.security.user.name=admin
|
spring.security.user.name=admin
|
||||||
#spring.security.user.password=admin
|
spring.security.user.password=admin
|
||||||
|
|
||||||
# For unit tests only
|
# For unit tests only
|
||||||
#spring.datasource.url=jdbc:h2:mem:testdb
|
#spring.datasource.url=jdbc:h2:mem:testdb
|
||||||
|
@ -41,14 +41,18 @@ public class PostControllerTests {
|
|||||||
post.setTitle("test");
|
post.setTitle("test");
|
||||||
post.setContent("This is a test");
|
post.setContent("This is a test");
|
||||||
|
|
||||||
when(postService.getPosts()).thenReturn(List.of(post));
|
var user = new User();
|
||||||
|
user.setEmail("john@example.com");
|
||||||
|
user.setPassword("qwerty");
|
||||||
|
|
||||||
|
when(postService.getPosts(any(User.class))).thenReturn(Either.right(List.of(post)));
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/api/posts")
|
mockMvc.perform(MockMvcRequestBuilders.get("/api/posts")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(post)))
|
.content(objectMapper.writeValueAsString(user)))
|
||||||
.andExpect(MockMvcResultMatchers.status().isOk());
|
.andExpect(MockMvcResultMatchers.status().isOk());
|
||||||
|
|
||||||
Mockito.verify(postService, Mockito.times(1)).getPosts();
|
Mockito.verify(postService, Mockito.times(1)).getPosts(any(User.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -71,18 +75,22 @@ public class PostControllerTests {
|
|||||||
@Test
|
@Test
|
||||||
public void getPostByTitle() throws Exception {
|
public void getPostByTitle() throws Exception {
|
||||||
var post = new Post();
|
var post = new Post();
|
||||||
|
var user = new User();
|
||||||
|
user.setEmail("john@example.com");
|
||||||
|
user.setPassword("qwerty");
|
||||||
post.setId("abc123");
|
post.setId("abc123");
|
||||||
post.setTitle("test");
|
post.setTitle("test");
|
||||||
post.setContent("This is a test");
|
post.setContent("This is a test");
|
||||||
|
post.setUser(user);
|
||||||
|
|
||||||
when(postService.getPostByTitle(anyString())).thenReturn(List.of(post));
|
when(postService.getPostByTitle(any(Post.class))).thenReturn(Either.right(List.of(post)));
|
||||||
|
|
||||||
mockMvc.perform(MockMvcRequestBuilders.get("/api/posts/bytitle")
|
mockMvc.perform(MockMvcRequestBuilders.get("/api/posts/bytitle")
|
||||||
.contentType(MediaType.APPLICATION_JSON)
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
.content(objectMapper.writeValueAsString(post)))
|
.content(objectMapper.writeValueAsString(post)))
|
||||||
.andExpect(MockMvcResultMatchers.status().isOk());
|
.andExpect(MockMvcResultMatchers.status().isOk());
|
||||||
|
|
||||||
Mockito.verify(postService, Mockito.times(1)).getPostByTitle(anyString());
|
Mockito.verify(postService, Mockito.times(1)).getPostByTitle(any(Post.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -15,6 +15,7 @@ import org.springframework.test.web.servlet.MockMvc;
|
|||||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
@ -31,6 +32,23 @@ class UserControllerTest {
|
|||||||
@MockBean
|
@MockBean
|
||||||
private UserService userService;
|
private UserService userService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getUsers() throws Exception {
|
||||||
|
var user = new User();
|
||||||
|
user.setUsername("john");
|
||||||
|
user.setEmail("john@example.com");
|
||||||
|
user.setPassword("qwerty");
|
||||||
|
|
||||||
|
when(userService.getUsers(any(User.class))).thenReturn(Either.right(List.of(user)));
|
||||||
|
|
||||||
|
mockMvc.perform(MockMvcRequestBuilders.get("/api/users")
|
||||||
|
.contentType(MediaType.APPLICATION_JSON)
|
||||||
|
.content(objectMapper.writeValueAsString(user)))
|
||||||
|
.andExpect(MockMvcResultMatchers.status().isOk());
|
||||||
|
|
||||||
|
Mockito.verify(userService, Mockito.times(1)).getUsers(any(User.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addNewUser() throws Exception {
|
public void addNewUser() throws Exception {
|
||||||
var user = new User();
|
var user = new User();
|
||||||
|
Loading…
Reference in New Issue
Block a user