diff --git a/api/src/main/java/com/exelaration/abstractmemery/controllers/UserController.java b/api/src/main/java/com/exelaration/abstractmemery/controllers/UserController.java index 1fe6458..e2c5d14 100644 --- a/api/src/main/java/com/exelaration/abstractmemery/controllers/UserController.java +++ b/api/src/main/java/com/exelaration/abstractmemery/controllers/UserController.java @@ -1,18 +1,23 @@ package com.exelaration.abstractmemery.controllers; import com.exelaration.abstractmemery.domains.ApplicationUser; +import com.exelaration.abstractmemery.services.UserService; import com.exelaration.abstractmemery.services.implementations.UserDetailsServiceImpl; +import java.util.ArrayList; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/users") public class UserController { + @Autowired private UserService userService; @Autowired private UserDetailsServiceImpl userDetailsService; @PostMapping("/sign-up") @@ -32,4 +37,9 @@ public String updateSettings(@RequestBody ApplicationUser user) { } return "User already exists"; } + + @GetMapping(value = "/", params = "text") + public ArrayList getUsersForSearch(@RequestParam String text) throws Exception { + return userService.getUsersWithText(text); + } } diff --git a/api/src/main/java/com/exelaration/abstractmemery/projections/UserProjection.java b/api/src/main/java/com/exelaration/abstractmemery/projections/UserProjection.java new file mode 100644 index 0000000..0b8595d --- /dev/null +++ b/api/src/main/java/com/exelaration/abstractmemery/projections/UserProjection.java @@ -0,0 +1,7 @@ +package com.exelaration.abstractmemery.projections; + +public interface UserProjection { + String getUsername(); + + void setUsername(String username); +} diff --git a/api/src/main/java/com/exelaration/abstractmemery/repositories/UserRepository.java b/api/src/main/java/com/exelaration/abstractmemery/repositories/UserRepository.java index 496f597..df60f70 100644 --- a/api/src/main/java/com/exelaration/abstractmemery/repositories/UserRepository.java +++ b/api/src/main/java/com/exelaration/abstractmemery/repositories/UserRepository.java @@ -1,8 +1,12 @@ package com.exelaration.abstractmemery.repositories; import com.exelaration.abstractmemery.domains.ApplicationUser; +import com.exelaration.abstractmemery.projections.UserProjection; +import java.util.ArrayList; import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository { ApplicationUser findByUsername(String username); + + ArrayList findByUsernameIgnoreCaseContains(String username); } diff --git a/api/src/main/java/com/exelaration/abstractmemery/services/UserService.java b/api/src/main/java/com/exelaration/abstractmemery/services/UserService.java new file mode 100644 index 0000000..ce47c18 --- /dev/null +++ b/api/src/main/java/com/exelaration/abstractmemery/services/UserService.java @@ -0,0 +1,10 @@ +package com.exelaration.abstractmemery.services; + +import java.util.ArrayList; +import javax.transaction.Transactional; + +@Transactional +public interface UserService { + + public ArrayList getUsersWithText(String text); +} diff --git a/api/src/main/java/com/exelaration/abstractmemery/services/implementations/UserServiceImpl.java b/api/src/main/java/com/exelaration/abstractmemery/services/implementations/UserServiceImpl.java new file mode 100644 index 0000000..1d7a1f1 --- /dev/null +++ b/api/src/main/java/com/exelaration/abstractmemery/services/implementations/UserServiceImpl.java @@ -0,0 +1,24 @@ +package com.exelaration.abstractmemery.services.implementations; + +import com.exelaration.abstractmemery.projections.UserProjection; +import com.exelaration.abstractmemery.repositories.UserRepository; +import com.exelaration.abstractmemery.services.UserService; +import java.util.ArrayList; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service("userService") +public class UserServiceImpl implements UserService { + + @Autowired private UserRepository userRepository; + + public ArrayList getUsersWithText(String username) { + ArrayList usernamesProjections = + userRepository.findByUsernameIgnoreCaseContains(username); + ArrayList usernames = new ArrayList(); + for (UserProjection name : usernamesProjections) { + usernames.add(name.getUsername()); + } + return usernames; + } +} diff --git a/api/src/test/java/com/exelaration/abstractmemery/controllers/UserControllerTest.java b/api/src/test/java/com/exelaration/abstractmemery/controllers/UserControllerTest.java index 8db0a99..7d2073d 100644 --- a/api/src/test/java/com/exelaration/abstractmemery/controllers/UserControllerTest.java +++ b/api/src/test/java/com/exelaration/abstractmemery/controllers/UserControllerTest.java @@ -1,12 +1,16 @@ package com.exelaration.abstractmemery.controllers; +import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import com.exelaration.abstractmemery.domains.ApplicationUser; +import com.exelaration.abstractmemery.services.UserService; import com.exelaration.abstractmemery.services.implementations.UserDetailsServiceImpl; import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.ArrayList; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -27,6 +31,7 @@ public class UserControllerTest { private MockMvc mockMvc; @Autowired private UserController userController; @MockBean private UserDetailsServiceImpl userDetailsService; + @MockBean private UserService userService; @BeforeEach public void setUp() { @@ -93,4 +98,36 @@ public void UserControllerTest_WhenRegisterFails_ExpectStatus400() throws Except .content(new ObjectMapper().writeValueAsString(testUser))) .andExpect(badRequest); } + + @Test + public void getUsersForSearch_WhenUsersExist_Expect200andArray() throws Exception { + ResultMatcher ok = MockMvcResultMatchers.status().isOk(); + String url = "/users/?text=test"; + + ArrayList expectedUsernames = new ArrayList(); + expectedUsernames.add("test@gmail.com"); + when(userService.getUsersWithText(any())).thenReturn(expectedUsernames); + + mockMvc + .perform(MockMvcRequestBuilders.get(url)) + .andExpect(ok) + .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$", hasSize(1))) + .andExpect(jsonPath("$[0]", is("test@gmail.com"))); + } + + @Test + public void getUsersForSearch_WhenUsersDoesNotExist_Expect200andEmptyArray() throws Exception { + ResultMatcher ok = MockMvcResultMatchers.status().isOk(); + String url = "/users/?text=test"; + + ArrayList expectedUsernames = new ArrayList(); + when(userService.getUsersWithText(any())).thenReturn(expectedUsernames); + + mockMvc + .perform(MockMvcRequestBuilders.get(url)) + .andExpect(ok) + .andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$", hasSize(0))); + } } diff --git a/api/src/test/java/com/exelaration/abstractmemery/services/UserServiceTest.java b/api/src/test/java/com/exelaration/abstractmemery/services/UserServiceTest.java new file mode 100644 index 0000000..8b40a5f --- /dev/null +++ b/api/src/test/java/com/exelaration/abstractmemery/services/UserServiceTest.java @@ -0,0 +1,53 @@ +package com.exelaration.abstractmemery.services; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.exelaration.abstractmemery.projections.UserProjection; +import com.exelaration.abstractmemery.repositories.UserRepository; +import com.exelaration.abstractmemery.services.implementations.UserServiceImpl; +import java.util.ArrayList; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.springframework.data.projection.ProjectionFactory; +import org.springframework.data.projection.SpelAwareProxyProjectionFactory; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +@ExtendWith(SpringExtension.class) +public class UserServiceTest { + @Mock private UserRepository userRepository; + @InjectMocks private UserServiceImpl userService; + + @BeforeEach + public void init() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void getUsersWithText_WhenUsersExist_ExpectArray() { + ProjectionFactory factory = new SpelAwareProxyProjectionFactory(); + UserProjection userProjection = factory.createProjection(UserProjection.class); + userProjection.setUsername("test@gmail.com"); + ArrayList returnedUsernames = new ArrayList(); + returnedUsernames.add(userProjection); + Mockito.when(userRepository.findByUsernameIgnoreCaseContains("test")) + .thenReturn(returnedUsernames); + + ArrayList expectedUsernames = new ArrayList(); + expectedUsernames.add("test@gmail.com"); + assertEquals(expectedUsernames, userService.getUsersWithText("test")); + } + + @Test + public void getUsersWithText_WhenUsersDoesNotExist_ExpectArray() { + ArrayList returnedUsernames = new ArrayList(); + Mockito.when(userRepository.findByUsernameIgnoreCaseContains("test")) + .thenReturn(returnedUsernames); + ArrayList expectedUsernames = new ArrayList(); + assertEquals(expectedUsernames, userService.getUsersWithText("test")); + } +} diff --git a/ui/src/SearchResults.tsx b/ui/src/SearchResults.tsx index 93ad25d..7dbeab3 100644 --- a/ui/src/SearchResults.tsx +++ b/ui/src/SearchResults.tsx @@ -7,6 +7,7 @@ function SearchPage(props: any) { const [memeResponses, setMemeResponses] = useState([]); const [displayMessage, setDisplayMessage] = useState(""); const [isValid, setIsValid] = useState(true); + const [usernamesResponse, setUsernames] = useState([]); useEffect(() => { function getSearchURLs() { @@ -29,6 +30,22 @@ function SearchPage(props: any) { getSearchURLs(); }, [props.match.params.text]); + useEffect(() => { + function getUsernames() { + const response = axios + .get("http://localhost:8080/users/?text=" + props.match.params.text) + .then((response) => { + const usernamesResponse = response.data; + setUsernames(usernamesResponse); + }) + .catch((error) => { + setDisplayMessage("There are no Users with this search criteria"); + }); + return response; + } + getUsernames(); + }, [props.match.params.text]); + if (!isValid) { return (
@@ -39,18 +56,12 @@ function SearchPage(props: any) {
); - } else if (memeResponses.length === 0) { - return ( -
- -
-

Search Results

-
There are no images with this search criteria
-
-
- ); + } + let memes; + if (memeResponses.length === 0) { + memes =
No image exists with this search criteria
; } else { - const memes = memeResponses.map(function (meme) { + memes = memeResponses.map(function (meme) { return (
{" "} @@ -63,16 +74,33 @@ function SearchPage(props: any) {
); }); + } - return ( -
- -
-

Search Results

-
    {memes}
-
-
- ); + let usernames; + if (usernamesResponse.length === 0) { + usernames =
No users exist with this search criteria
; + } else { + usernames = usernamesResponse.map(function (username) { + return ( +
+ {" "} +
{username}
+
+ ); + }); } + + return ( +
+ +
+

Search Results

+

Users

+
    {usernames}
+

Memes

+
    {memes}
+
+
+ ); } export default SearchPage;