Commit b1c044c0 authored by Viacheslev Kabanovich's avatar Viacheslev Kabanovich

Improved controller to return 403 and error message instead of 500.

parent e8b7dac5
...@@ -24,33 +24,33 @@ public class FriendsController { ...@@ -24,33 +24,33 @@ public class FriendsController {
private FriendsService friendsService; private FriendsService friendsService;
@PostMapping(value = "/request", headers = "Authorization") @PostMapping(value = "/request", headers = "Authorization")
public ResponseEntity<Boolean> request(@RequestParam String friendname, public ResponseEntity<String> request(@RequestParam String friendname,
@AuthenticationPrincipal UserDetails signedUser) { @AuthenticationPrincipal UserDetails signedUser) {
return new ResponseEntity<Boolean>(friendsService.request(friendname, signedUser), HttpStatus.OK); return friendsService.request(friendname, signedUser);
} }
@PostMapping(value = "/reject", headers = "Authorization") @PostMapping(value = "/reject", headers = "Authorization")
public ResponseEntity<Boolean> reject(@RequestParam String friendname, public ResponseEntity<String> reject(@RequestParam String friendname,
@AuthenticationPrincipal UserDetails signedUser) { @AuthenticationPrincipal UserDetails signedUser) {
return new ResponseEntity<Boolean>(friendsService.reject(friendname, signedUser), HttpStatus.OK); return friendsService.reject(friendname, signedUser);
} }
@PostMapping(value = "/accept", headers = "Authorization") @PostMapping(value = "/accept", headers = "Authorization")
public ResponseEntity<Boolean> accept(@RequestParam String friendname, public ResponseEntity<String> accept(@RequestParam String friendname,
@AuthenticationPrincipal UserDetails signedUser) { @AuthenticationPrincipal UserDetails signedUser) {
return new ResponseEntity<Boolean>(friendsService.accept(friendname, signedUser), HttpStatus.OK); return friendsService.accept(friendname, signedUser);
} }
@PostMapping(value = "/makeBest", headers = "Authorization") @PostMapping(value = "/makeBest", headers = "Authorization")
public ResponseEntity<Boolean> makeBest(@RequestParam String friendname, public ResponseEntity<String> makeBest(@RequestParam String friendname,
@AuthenticationPrincipal UserDetails signedUser) { @AuthenticationPrincipal UserDetails signedUser) {
return new ResponseEntity<Boolean>(friendsService.makeBest(friendname, signedUser), HttpStatus.OK); return friendsService.makeBest(friendname, signedUser);
} }
@PostMapping(value = "/undoBest", headers = "Authorization") @PostMapping(value = "/undoBest", headers = "Authorization")
public ResponseEntity<Boolean> undoBest(@RequestParam String friendname, public ResponseEntity<String> undoBest(@RequestParam String friendname,
@AuthenticationPrincipal UserDetails signedUser) { @AuthenticationPrincipal UserDetails signedUser) {
return new ResponseEntity<Boolean>(friendsService.undoBest(friendname, signedUser), HttpStatus.OK); return friendsService.undoBest(friendname, signedUser);
} }
@GetMapping(value = "/all", headers = "Authorization") @GetMapping(value = "/all", headers = "Authorization")
......
...@@ -7,6 +7,8 @@ import javax.validation.Valid; ...@@ -7,6 +7,8 @@ import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.DelegatingMessageSource; import org.springframework.context.support.DelegatingMessageSource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
...@@ -17,7 +19,6 @@ import org.springframework.web.bind.annotation.RequestMapping; ...@@ -17,7 +19,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import com.microsocialnetwork.users.dto.SignupRequest; import com.microsocialnetwork.users.dto.SignupRequest;
import com.microsocialnetwork.users.exceptions.MicroSocialNetworkException;
import com.microsocialnetwork.users.model.User; import com.microsocialnetwork.users.model.User;
import com.microsocialnetwork.users.repository.UserRepository; import com.microsocialnetwork.users.repository.UserRepository;
...@@ -33,18 +34,19 @@ public class UserController { ...@@ -33,18 +34,19 @@ public class UserController {
@PostMapping("/sign-up") @PostMapping("/sign-up")
@Validated @Validated
public void signUp(@RequestBody @Valid SignupRequest request, public ResponseEntity<String> signUp(@RequestBody @Valid SignupRequest request,
BindingResult bindingResult) { BindingResult bindingResult) {
if (bindingResult.hasErrors()) { if (bindingResult.hasErrors()) {
DelegatingMessageSource d = new DelegatingMessageSource(); DelegatingMessageSource d = new DelegatingMessageSource();
String message = bindingResult.getAllErrors().stream() String message = bindingResult.getAllErrors().stream()
.map(error -> d.getMessage(error, Locale.getDefault())) .map(error -> d.getMessage(error, Locale.getDefault()))
.collect(Collectors.joining(", ")); .collect(Collectors.joining(", "));
throw new MicroSocialNetworkException(message); return ResponseEntity.status(HttpStatus.FORBIDDEN).body(message);
} }
User user = new User(request.getUsername(), request.getEmail(), User user = new User(request.getUsername(), request.getEmail(),
bCryptPasswordEncoder.encode(request.getPassword())); bCryptPasswordEncoder.encode(request.getPassword()));
userRepository.save(user); userRepository.save(user);
return new ResponseEntity<>("Signed up as " + request.getUsername(), HttpStatus.OK);
} }
@GetMapping(headers = {"Authorization"}) @GetMapping(headers = {"Authorization"})
......
package com.microsocialnetwork.users.service; package com.microsocialnetwork.users.service;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.content;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -11,6 +11,8 @@ import java.util.stream.Collectors; ...@@ -11,6 +11,8 @@ import java.util.stream.Collectors;
import javax.transaction.Transactional; import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -32,8 +34,11 @@ public class FriendsService { ...@@ -32,8 +34,11 @@ public class FriendsService {
private FriendLinkRepository linkRepository; private FriendLinkRepository linkRepository;
@Transactional @Transactional
public Boolean request(String friendname, UserDetails signedUser) { public ResponseEntity<String> request(String friendname, UserDetails signedUser) {
Context ctx = new Context(friendname, signedUser); Context ctx = new Context(friendname, signedUser);
if (ctx.error != null) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ctx.error);
}
if (ctx.direct == null) { if (ctx.direct == null) {
// 1. There were no prior requests // 1. There were no prior requests
ctx.createNewLink(); ctx.createNewLink();
...@@ -42,8 +47,9 @@ public class FriendsService { ...@@ -42,8 +47,9 @@ public class FriendsService {
// 2. There was a rejection, try new request. // 2. There was a rejection, try new request.
if (ctx.reverse.getRejectCount() >= 3) { if (ctx.reverse.getRejectCount() >= 3) {
//However, if the target side rejected source repeatedly, deny. //However, if the target side rejected source repeatedly, deny.
throw new MicroSocialNetworkException("Request denied: You have bean rejected by " String message = "Request denied: You have bean rejected by "
+ friendname + " " + ctx.reverse.getRejectCount() + " times."); + friendname + " " + ctx.reverse.getRejectCount() + " times.";
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(message);
} }
ctx.direct.setStatus(FriendStatusEnum.REQUEST_TO_THE_OTHER); ctx.direct.setStatus(FriendStatusEnum.REQUEST_TO_THE_OTHER);
ctx.direct.setRejectCount(0); ctx.direct.setRejectCount(0);
...@@ -53,48 +59,46 @@ public class FriendsService { ...@@ -53,48 +59,46 @@ public class FriendsService {
ctx.direct.onAccept(); ctx.direct.onAccept();
ctx.reverse.onAccepted(); ctx.reverse.onAccepted();
} else { } else {
return false; return ResponseEntity.status(HttpStatus.FORBIDDEN).body("You have already sent the request.");
} }
ctx.save(); ctx.save();
return true; return ResponseEntity.status(HttpStatus.OK).body("Request is sent to " + friendname);
} }
@Transactional @Transactional
public Boolean reject(String friendname, UserDetails signedUser) { public ResponseEntity<String> reject(String friendname, UserDetails signedUser) {
Context ctx = new Context(friendname, signedUser); Context ctx = new Context(friendname, signedUser);
if (ctx.direct != null && (ctx.direct.getStatus().isFriend() || ctx.direct.getStatus() == FriendStatusEnum.REQUEST_BY_THE_OTHER)) { if (ctx.error == null && ctx.direct != null && (ctx.direct.getStatus().isFriend() || ctx.direct.getStatus() == FriendStatusEnum.REQUEST_BY_THE_OTHER)) {
//If you are requested, you may reject the request; //If you are requested, you may reject the request;
//If you two are friends, you may reject the friendship. //If you two are friends, you may reject the friendship.
ctx.direct.onReject(); ctx.direct.onReject();
ctx.reverse.onRejected(); ctx.reverse.onRejected();
ctx.save(); ctx.save();
return true; return ResponseEntity.status(HttpStatus.OK).body("You rejected friendship of " + friendname + ".");
} else { } else {
//You cannot reject a person who is not your friend, nor requested you. return ResponseEntity.status(HttpStatus.FORBIDDEN).body("You cannot reject a person who is not your friend, nor requested you.");
return false;
} }
} }
@Transactional @Transactional
public Boolean accept(String friendname, UserDetails signedUser) { public ResponseEntity<String> accept(String friendname, UserDetails signedUser) {
Context ctx = new Context(friendname, signedUser); Context ctx = new Context(friendname, signedUser);
if (ctx.direct != null && (ctx.direct.getStatus() == FriendStatusEnum.REQUEST_BY_THE_OTHER)) { if (ctx.error == null && ctx.direct != null && (ctx.direct.getStatus() == FriendStatusEnum.REQUEST_BY_THE_OTHER)) {
//If you are requested, you may reject the request; //If you are requested, you may reject the request;
//If you two are friends, you may reject the friendship. //If you two are friends, you may reject the friendship.
ctx.direct.onAccept(); ctx.direct.onAccept();
ctx.reverse.onAccepted(); ctx.reverse.onAccepted();
ctx.save(); ctx.save();
return true; return ResponseEntity.status(HttpStatus.OK).body("You named " + friendname + " your friend!");
} else { } else {
//You cannot reject a person who is not your friend, nor requested you. return ResponseEntity.status(HttpStatus.FORBIDDEN).body(friendname + " did not request for your friennship, but may extends your request.");
return false;
} }
} }
@Transactional @Transactional
public Boolean makeBest(String friendname, UserDetails signedUser) { public ResponseEntity<String> makeBest(String friendname, UserDetails signedUser) {
Context ctx = new Context(friendname, signedUser); Context ctx = new Context(friendname, signedUser);
if (ctx.direct != null && (ctx.direct.getStatus().isFriend() && !ctx.direct.getStatus().isBestFriend())) { if (ctx.error == null && ctx.direct != null && (ctx.direct.getStatus().isFriend() && !ctx.direct.getStatus().isBestFriend())) {
if (ctx.reverse.getStatus().isBestFriend()) { if (ctx.reverse.getStatus().isBestFriend()) {
ctx.direct.setStatus(FriendStatusEnum.MUTUAL_BEST_FRIENDSHIP); ctx.direct.setStatus(FriendStatusEnum.MUTUAL_BEST_FRIENDSHIP);
ctx.reverse.setStatus(FriendStatusEnum.MUTUAL_BEST_FRIENDSHIP); ctx.reverse.setStatus(FriendStatusEnum.MUTUAL_BEST_FRIENDSHIP);
...@@ -103,17 +107,16 @@ public class FriendsService { ...@@ -103,17 +107,16 @@ public class FriendsService {
ctx.reverse.setStatus(FriendStatusEnum.BEST_FRIENDSHIP_BY_THE_OTHER); ctx.reverse.setStatus(FriendStatusEnum.BEST_FRIENDSHIP_BY_THE_OTHER);
} }
ctx.save(); ctx.save();
return true; return ResponseEntity.status(HttpStatus.OK).body("You named " + friendname + " your best friend!");
} else { } else {
//You must be a friend to choose him the best friend. return ResponseEntity.status(HttpStatus.FORBIDDEN).body("You can only select the best friend of your friends.");
return false;
} }
} }
@Transactional @Transactional
public Boolean undoBest(String friendname, UserDetails signedUser) { public ResponseEntity<String> undoBest(String friendname, UserDetails signedUser) {
Context ctx = new Context(friendname, signedUser); Context ctx = new Context(friendname, signedUser);
if (ctx.direct != null && (ctx.direct.getStatus().isBestFriend())) { if (ctx.error == null && ctx.direct != null && (ctx.direct.getStatus().isBestFriend())) {
if (!ctx.reverse.getStatus().isBestFriend()) { if (!ctx.reverse.getStatus().isBestFriend()) {
ctx.direct.setStatus(FriendStatusEnum.FRIENDSHIP); ctx.direct.setStatus(FriendStatusEnum.FRIENDSHIP);
ctx.reverse.setStatus(FriendStatusEnum.FRIENDSHIP); ctx.reverse.setStatus(FriendStatusEnum.FRIENDSHIP);
...@@ -122,10 +125,9 @@ public class FriendsService { ...@@ -122,10 +125,9 @@ public class FriendsService {
ctx.reverse.setStatus(FriendStatusEnum.BEST_FRIENDSHIP_TO_THE_OTHER); ctx.reverse.setStatus(FriendStatusEnum.BEST_FRIENDSHIP_TO_THE_OTHER);
} }
ctx.save(); ctx.save();
return true; return ResponseEntity.status(HttpStatus.OK).body("You excluded " + friendname + " from your best friends. He is still your friend, though...");
} else { } else {
//You did not set him a best friend. return ResponseEntity.status(HttpStatus.FORBIDDEN).body(friendname + "was not your the best friend.");
return false;
} }
} }
...@@ -168,16 +170,21 @@ public class FriendsService { ...@@ -168,16 +170,21 @@ public class FriendsService {
public class Context { public class Context {
String error = null;
User source; User source;
User target; User target;
FriendLink direct; FriendLink direct;
FriendLink reverse; FriendLink reverse;
Context(String friendname, UserDetails signedUser) { Context(String friendname, UserDetails signedUser) {
source = userRepository.findByUsername(signedUser.getUsername()).get(); target = userRepository.findByUsername(friendname).orElse(null);
target = userRepository.findByUsername(friendname).orElseThrow(() -> new MicroSocialNetworkException("User " + friendname + " not found.")); if (target == null) {
direct = linkRepository.findByUserAndFriend(source, target).orElse(null); error = "User " + friendname + " not found.";
reverse = linkRepository.findByUserAndFriend(target, source).orElse(null); } else {
source = userRepository.findByUsername(signedUser.getUsername()).get();
direct = linkRepository.findByUserAndFriend(source, target).orElse(null);
reverse = linkRepository.findByUserAndFriend(target, source).orElse(null);
}
} }
void createNewLink() { void createNewLink() {
......
...@@ -4,6 +4,7 @@ import static org.junit.Assert.assertTrue; ...@@ -4,6 +4,7 @@ import static org.junit.Assert.assertTrue;
import java.util.Base64; import java.util.Base64;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -29,7 +30,8 @@ public class SignUpTest { ...@@ -29,7 +30,8 @@ public class SignUpTest {
@Autowired @Autowired
WebApplicationContext webApplicationContext; WebApplicationContext webApplicationContext;
void init() { @BeforeEach
public void init() {
if (mvc == null) { if (mvc == null) {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
} }
...@@ -37,7 +39,6 @@ public class SignUpTest { ...@@ -37,7 +39,6 @@ public class SignUpTest {
@Test @Test
public void signUp() throws Exception { public void signUp() throws Exception {
init();
String uri = "/users/sign-up"; String uri = "/users/sign-up";
MvcResult mvcResult = mvc.perform( MvcResult mvcResult = mvc.perform(
MockMvcRequestBuilders.post(uri) MockMvcRequestBuilders.post(uri)
...@@ -46,28 +47,25 @@ public class SignUpTest { ...@@ -46,28 +47,25 @@ public class SignUpTest {
.accept(MediaType.APPLICATION_JSON_VALUE) .accept(MediaType.APPLICATION_JSON_VALUE)
).andExpect(MockMvcResultMatchers.status().isOk()) ).andExpect(MockMvcResultMatchers.status().isOk())
.andReturn(); .andReturn();
assertTrue("Expected name of signed up user in the response body.", mvcResult.getResponse().getContentAsString().indexOf("user_1") >= 0);
} }
@Test @Test
public void signUpWithUnacceptedUserName() throws Exception { public void signUpWithUnacceptedUserName() throws Exception {
init();
String uri = "/users/sign-up"; String uri = "/users/sign-up";
try { MvcResult mvcResult = mvc.perform(
mvc.perform( MockMvcRequestBuilders.post(uri)
MockMvcRequestBuilders.post(uri) .content("{\"username\":\"user 2\",\"password\":\"p\",\"email\":\"user_2@best.com\"}")
.content("{\"username\":\"user 2\",\"password\":\"p\",\"email\":\"user_2@best.com\"}") .header("Content-Type", "application/json")
.header("Content-Type", "application/json") .accept(MediaType.APPLICATION_JSON_VALUE)
.accept(MediaType.APPLICATION_JSON_VALUE) ).andExpect(MockMvcResultMatchers.status().isForbidden())
).andExpect(MockMvcResultMatchers.status().isInternalServerError()); .andReturn();
} catch (NestedServletException e) { assertTrue("Expected description of valid username.", mvcResult.getResponse().getContentAsString().indexOf("must use only letters") >= 0);
assertTrue(e.getCause().getMessage().startsWith("Username must use only letters"));
}
} }
@Test @Test
public void getUsers() throws Exception { public void getUsers() throws Exception {
init(); // signUp(); //do not need it as database is not reset.
// signUp();
String uri = "/users"; String uri = "/users";
String auth = Base64.getEncoder().encodeToString("user_1:p".getBytes()); String auth = Base64.getEncoder().encodeToString("user_1:p".getBytes());
MvcResult mvcResult = mvc.perform( MvcResult mvcResult = mvc.perform(
...@@ -77,7 +75,6 @@ public class SignUpTest { ...@@ -77,7 +75,6 @@ public class SignUpTest {
).andExpect(MockMvcResultMatchers.status().isOk()) ).andExpect(MockMvcResultMatchers.status().isOk())
.andReturn(); .andReturn();
String body = mvcResult.getResponse().getContentAsString(); String body = mvcResult.getResponse().getContentAsString();
System.out.println(body);
assertTrue(body.indexOf("user_1@best.com") >= 0); assertTrue(body.indexOf("user_1@best.com") >= 0);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment