diff --git a/webapp/src/main/java/com/box/l10n/mojito/rest/security/UserWS.java b/webapp/src/main/java/com/box/l10n/mojito/rest/security/UserWS.java index 2089f8db6a..5076b02a2e 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/rest/security/UserWS.java +++ b/webapp/src/main/java/com/box/l10n/mojito/rest/security/UserWS.java @@ -1,10 +1,6 @@ package com.box.l10n.mojito.rest.security; -import static com.box.l10n.mojito.rest.security.UserSpecification.enabledEquals; -import static com.box.l10n.mojito.rest.security.UserSpecification.usernameEquals; -import static com.box.l10n.mojito.specification.Specifications.ifParamNotNull; import static org.slf4j.LoggerFactory.getLogger; -import static org.springframework.data.jpa.domain.Specification.where; import com.box.l10n.mojito.entity.security.user.Authority; import com.box.l10n.mojito.entity.security.user.User; @@ -48,11 +44,9 @@ public class UserWS { @RequestMapping(value = "/api/users", method = RequestMethod.GET) public Page getUsers( @RequestParam(value = "username", required = false) String username, + @RequestParam(value = "search", required = false) String search, @PageableDefault(sort = "username", direction = Sort.Direction.ASC) Pageable pageable) { - Page users = - userService.findAll( - where(ifParamNotNull(usernameEquals(username))).and(enabledEquals(true)), pageable); - return users; + return userService.findByUsernameOrName(username, search, pageable); } /** diff --git a/webapp/src/main/java/com/box/l10n/mojito/service/security/user/UserRepository.java b/webapp/src/main/java/com/box/l10n/mojito/service/security/user/UserRepository.java index e1a7193e9f..b8761bb023 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/service/security/user/UserRepository.java +++ b/webapp/src/main/java/com/box/l10n/mojito/service/security/user/UserRepository.java @@ -11,6 +11,8 @@ import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import org.springframework.data.rest.core.annotation.RepositoryRestResource; /** @@ -37,4 +39,21 @@ public interface UserRepository extends JpaRepository, JpaSpecificat @Override @EntityGraph(value = "User.legacy", type = EntityGraphType.FETCH) Optional findById(Long aLong); + + @Query( + """ + select u + from User u + where (:username is null or u.username = :username) + and ((:search is null or :search = '') + or (lower(u.username) like lower(concat('%', :search, '%')) + or (u.commonName is not null + and u.commonName <> '' + and lower(u.commonName) like lower(concat('%', :search, '%'))) + or ((u.commonName is null or u.commonName = '') + and lower(concat(u.givenName, ' ', u.surname)) like lower(concat('%', :search, '%'))))) + and u.enabled = true + """) + Page findByUsernameOrName( + @Param("username") String username, @Param("search") String search, Pageable pageable); } diff --git a/webapp/src/main/java/com/box/l10n/mojito/service/security/user/UserService.java b/webapp/src/main/java/com/box/l10n/mojito/service/security/user/UserService.java index 177ee0938b..592ffbcc3c 100644 --- a/webapp/src/main/java/com/box/l10n/mojito/service/security/user/UserService.java +++ b/webapp/src/main/java/com/box/l10n/mojito/service/security/user/UserService.java @@ -18,7 +18,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import org.springframework.data.jpa.domain.Specification; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Component; @@ -367,11 +366,12 @@ public User getOrCreateOrUpdateBasicUser( * Cannot use an EntityGraph with pagination as it triggers the following warning: HHH90003004: * firstResult/maxResults specified with collection fetch; applying in memory * - * @param spec + * @param username + * @param search * @param pageable */ - public Page findAll(Specification spec, Pageable pageable) { - final Page users = userRepository.findAll(spec, pageable); + public Page findByUsernameOrName(String username, String search, Pageable pageable) { + final Page users = userRepository.findByUsernameOrName(username, search, pageable); users.forEach( u -> { Hibernate.initialize(u.getAuthorities()); diff --git a/webapp/src/main/resources/properties/en.properties b/webapp/src/main/resources/properties/en.properties index 3831b9b9eb..c267fe1882 100644 --- a/webapp/src/main/resources/properties/en.properties +++ b/webapp/src/main/resources/properties/en.properties @@ -64,6 +64,9 @@ branches.searchstatusDropdown.owner.onlyMyBranches=Only my branches # Branches search input placeholder, shown when no text is entered in the search field. To search branches by name or by username branches.searchtext=Branch name or username... +# User search input placeholder, shown when no text is entered in the search field. To search users by username or by name +user.searchtext=Username or name... + # Label displayed as the title of the modal popup used to upload a screenshot branches.screenshotUploadModal.title=Upload screenshot for selected textunits diff --git a/webapp/src/main/resources/public/js/actions/users/UserActions.js b/webapp/src/main/resources/public/js/actions/users/UserActions.js index 0d5aa26ed0..3001170c19 100644 --- a/webapp/src/main/resources/public/js/actions/users/UserActions.js +++ b/webapp/src/main/resources/public/js/actions/users/UserActions.js @@ -14,9 +14,6 @@ class UserActions { "checkUsernameTaken", "checkUsernameTakenSuccess", "checkUsernameTakenError", - "getAllUsers", - "getAllUsersSuccess", - "getAllUsersError", "deleteRequest", "deleteRequestSuccess", "deleteRequestError", @@ -26,6 +23,9 @@ class UserActions { "saveEditRequest", "saveEditRequestSuccess", "saveEditRequestError", + "getUsers", + "getUsersSuccess", + "getUsersError", ); } } diff --git a/webapp/src/main/resources/public/js/actions/users/UserDataSource.js b/webapp/src/main/resources/public/js/actions/users/UserDataSource.js index 8c787763b1..4446250092 100644 --- a/webapp/src/main/resources/public/js/actions/users/UserDataSource.js +++ b/webapp/src/main/resources/public/js/actions/users/UserDataSource.js @@ -1,15 +1,20 @@ import UserClient from "../../sdk/UserClient"; import UserActions from "./UserActions"; import UserModalActions from "./UserModalActions"; +import UserSearcherParameters from "../../sdk/UserSearcherParameters"; +import UserSearchParamStore from "../../stores/users/UserSearchParamStore"; const UserDataSource = { - getAllUsers: { + getUsers: { remote(userStoreState, pageRequestParams) { - return UserClient.getUsers(pageRequestParams.page, pageRequestParams.size); + const userSearcherParameters = new UserSearcherParameters(); + const { searchText } = UserSearchParamStore.getState(); + const { page, size } = pageRequestParams; + userSearcherParameters.search(searchText).page(page).size(size); + return UserClient.getUsers(userSearcherParameters); }, - - success: UserActions.getAllUsersSuccess, - error: UserActions.getAllUsersError + success: UserActions.getUsersSuccess, + error: UserActions.getUsersError }, checkUsernameTakenRequest: { diff --git a/webapp/src/main/resources/public/js/actions/users/UserSearchParamActions.js b/webapp/src/main/resources/public/js/actions/users/UserSearchParamActions.js new file mode 100644 index 0000000000..91d3db5f66 --- /dev/null +++ b/webapp/src/main/resources/public/js/actions/users/UserSearchParamActions.js @@ -0,0 +1,12 @@ +import alt from "../../alt"; + +class UserSearchParamActions { + constructor() { + this.generateActions( + "changeSearchText", + "resetUserSearchParams", + ); + } +} + +export default alt.createActions(UserSearchParamActions); diff --git a/webapp/src/main/resources/public/js/components/common/SearchText.js b/webapp/src/main/resources/public/js/components/common/SearchText.js new file mode 100644 index 0000000000..030577853b --- /dev/null +++ b/webapp/src/main/resources/public/js/components/common/SearchText.js @@ -0,0 +1,54 @@ +import React from "react"; +import {injectIntl} from "react-intl"; +import PropTypes from "prop-types"; +import keycode from "keycode"; +import {Button, FormControl, FormGroup, Glyphicon, InputGroup} from "react-bootstrap"; + +class SearchText extends React.Component { + static propTypes = { + "searchText": PropTypes.string.isRequired, + "isSpinnerShown": PropTypes.bool.isRequired, + "onSearchTextChanged": PropTypes.func.isRequired, + "onPerformSearch": PropTypes.func.isRequired, + "placeholderTextId": PropTypes.string.isRequired + } + + onKeyDownOnSearchText(e) { + e.stopPropagation(); + if (e.keyCode === keycode("enter")) { + this.props.onPerformSearch(); + } + } + + onSearchButtonClicked() { + this.props.onPerformSearch(); + } + + renderSearchButton() { + return ( + + ); + } + + render() { + return ( +
+ + + this.props.onSearchTextChanged(e.target.value)} + placeholder={this.props.intl.formatMessage({id: this.props.placeholderTextId})} + onKeyDown={(e) => this.onKeyDownOnSearchText(e)}/> + + {this.props.isSpinnerShown && ()} + + {this.renderSearchButton()} + + +
+ ); + } +} +export default injectIntl(SearchText); \ No newline at end of file diff --git a/webapp/src/main/resources/public/js/components/users/UserMainPage.js b/webapp/src/main/resources/public/js/components/users/UserMainPage.js index 22965f057c..341719f7cd 100644 --- a/webapp/src/main/resources/public/js/components/users/UserMainPage.js +++ b/webapp/src/main/resources/public/js/components/users/UserMainPage.js @@ -1,9 +1,8 @@ import React from "react"; -import {FormattedMessage, FormattedDate, FormattedTime, injectIntl, __esModule} from "react-intl"; +import {FormattedMessage, FormattedDate, FormattedTime, injectIntl} from "react-intl"; import {Table, Button, OverlayTrigger, Popover, DropdownButton, MenuItem} from "react-bootstrap"; import UserActions from "../../actions/users/UserActions"; import UserStore from "../../stores/users/UserStore"; -import User from "../../sdk/entity/User"; import UserModal from "./UserModal"; import UserDeleteModal from "./UserDeleteModal"; import { UserRole } from "./UserRole"; @@ -11,6 +10,9 @@ import UserErrorModal from "./UserErrorModal"; import AltContainer from "alt-container"; import UserModalStore from "../../stores/users/UserModalStore"; import UserModalActions from "../../actions/users/UserModalActions"; +import SearchText from "../common/SearchText"; +import UserSearchParamActions from "../../actions/users/UserSearchParamActions"; +import UserSearchParamStore from "../../stores/users/UserSearchParamStore"; class UserMainPage extends React.Component { @@ -168,6 +170,13 @@ class UserMainPage extends React.Component {

+
+ + UserSearchParamActions.changeSearchText(text)} + onPerformSearch={() => UserActions.getUsers()} + placeholderTextId="user.searchtext" /> + +
{this.renderPageBar()} {this.renderUsersTable()} diff --git a/webapp/src/main/resources/public/js/sdk/UserClient.js b/webapp/src/main/resources/public/js/sdk/UserClient.js index 626883a2fa..2a08d41a4d 100644 --- a/webapp/src/main/resources/public/js/sdk/UserClient.js +++ b/webapp/src/main/resources/public/js/sdk/UserClient.js @@ -3,21 +3,8 @@ import User from "./entity/User"; import UserPage from "./UsersPage"; class UserClient extends BaseClient { - - /** - * @param {Number} page - * @param {Number} size - * @returns {UserPage} - */ - getUsers(page = 0, size = 5) { - let promise = this.get(this.getUrl(), { - "page": page, - "size": size - }); - - return promise.then(function (result) { - return UserPage.toUserPage(result); - }); + getUsers(userSearcherParameters) { + return this.get(this.getUrl(), userSearcherParameters.getParams()); } checkUsernameTaken(username) { diff --git a/webapp/src/main/resources/public/js/sdk/UserSearcherParameters.js b/webapp/src/main/resources/public/js/sdk/UserSearcherParameters.js new file mode 100644 index 0000000000..cf62ab7b93 --- /dev/null +++ b/webapp/src/main/resources/public/js/sdk/UserSearcherParameters.js @@ -0,0 +1,24 @@ +export default class UserSearcherParameters { + constructor() { + this.params = {}; + } + + search(searchText) { + this.params.search = searchText; + return this; + } + + page(page) { + this.params.page = page; + return this; + } + + size(size) { + this.params.size = size; + return this; + } + + getParams() { + return this.params; + } +} diff --git a/webapp/src/main/resources/public/js/stores/users/UserSearchParamStore.js b/webapp/src/main/resources/public/js/stores/users/UserSearchParamStore.js new file mode 100644 index 0000000000..d77f14cf67 --- /dev/null +++ b/webapp/src/main/resources/public/js/stores/users/UserSearchParamStore.js @@ -0,0 +1,39 @@ +import alt from "../../alt"; +import UserSearchParamActions from "../../actions/users/UserSearchParamActions"; +import UserActions from "../../actions/users/UserActions"; + +class UserSearchParamStore { + constructor() { + this.setDefaultState(); + + this.bindActions(UserSearchParamActions); + this.bindActions(UserActions); + } + + setDefaultState() { + this.searchText = ""; + this.isSpinnerShown = false; + } + + changeSearchText(text) { + this.searchText = text; + } + + resetUserSearchParams() { + this.setDefaultState(); + } + + getUsers() { + this.isSpinnerShown = true; + } + + getUsersSuccess() { + this.isSpinnerShown = false; + } + + getUsersError() { + this.isSpinnerShown = false; + } +} + +export default alt.createStore(UserSearchParamStore, "UserSearchParamStore"); diff --git a/webapp/src/main/resources/public/js/stores/users/UserStore.js b/webapp/src/main/resources/public/js/stores/users/UserStore.js index ad67ae6b25..2b7d2148b3 100644 --- a/webapp/src/main/resources/public/js/stores/users/UserStore.js +++ b/webapp/src/main/resources/public/js/stores/users/UserStore.js @@ -5,6 +5,7 @@ import User from "../../sdk/entity/User"; import UserPage from "../../sdk/UsersPage"; import UserStatics from "../../utils/UserStatics"; import PageRequestParams from "../../sdk/PageRequestParams"; +import UserSearchParamStore from "./UserSearchParamStore"; class UserStore { constructor() { @@ -26,6 +27,8 @@ class UserStore { /** @type {Number} */ this.lastErrorCode = 0; + this.isSearching = false; + this.bindActions(UserActions); this.registerAsync(UserDataSource); } @@ -61,7 +64,7 @@ class UserStore { const pageSize = this.userPage ? this.userPage.size : 10; const pageRequestParams = new PageRequestParams(prevPage, pageSize); - this.getInstance().getAllUsers(pageRequestParams); + this.getInstance().getUsers(pageRequestParams); } nextPage() { @@ -69,14 +72,14 @@ class UserStore { const pageSize = this.userPage ? this.userPage.size : 10; const pageRequestParams = new PageRequestParams(nextPage, pageSize); - this.getInstance().getAllUsers(pageRequestParams); + this.getInstance().getUsers(pageRequestParams); } updatePageSize(newSize) { const currPage = this.userPage ? this.userPage.number : 0; const pageRequestParams = new PageRequestParams(currPage, newSize); - this.getInstance().getAllUsers(pageRequestParams); + this.getInstance().getUsers(pageRequestParams); } @@ -85,27 +88,9 @@ class UserStore { const pageSize = this.userPage ? this.userPage.size : 10; const pageRequestParams = new PageRequestParams(currPage, pageSize); - this.getInstance().getAllUsers(pageRequestParams); - } - - onGetAllUsers(pageRequestParams) { - this.getInstance().getAllUsers(pageRequestParams); - } - - /** - * @param {UserPage} userPage - */ - onGetAllUsersSuccess(userPage) { - this.userPage = userPage; - } - - onGetAllUsersError(err) { - this.lastErrorKey = "userErrorModal.load"; - this.lastErrorCode = err.response.status; - console.log("error fetching users", err); + this.getInstance().getUsers(pageRequestParams); } - onDeleteRequest(id) { this.getInstance().deleteRequest(id); } @@ -165,6 +150,28 @@ class UserStore { onSavePasswordError(err) { console.log("error changing password", err); } + + getUsers() { + this.waitFor(UserSearchParamStore); + + const currPage = 0; + const pageSize = 10; + + const pageRequestParams = new PageRequestParams(currPage, pageSize); + + this.getInstance().getUsers(pageRequestParams); + this.isSearching = true; + } + + getUsersSuccess(users) { + this.userPage = UserPage.toUserPage(users) + this.isSearching = false; + } + + getUsersError() { + this.lastErrorKey = "userErrorModal.load"; + this.lastErrorCode = err.response.status; + } } export default alt.createStore(UserStore, 'UserStore'); diff --git a/webapp/src/main/resources/sass/mojito.scss b/webapp/src/main/resources/sass/mojito.scss index e0be296f2c..3d55fcc41c 100644 --- a/webapp/src/main/resources/sass/mojito.scss +++ b/webapp/src/main/resources/sass/mojito.scss @@ -1003,10 +1003,10 @@ label.current-pageNumber { .users-root { display: grid; - grid-template-columns: 1fr 1fr 1fr; + grid-template-columns: 1fr 2fr 1fr; grid-template-rows: min-content min-content min-content; grid-template-areas: - "count . pages" + "count search pages" "main main main" "newUser . ." ; diff --git a/webapp/src/test/java/com/box/l10n/mojito/service/security/user/UserServiceTest.java b/webapp/src/test/java/com/box/l10n/mojito/service/security/user/UserServiceTest.java index 4169b892b3..97a5858d63 100644 --- a/webapp/src/test/java/com/box/l10n/mojito/service/security/user/UserServiceTest.java +++ b/webapp/src/test/java/com/box/l10n/mojito/service/security/user/UserServiceTest.java @@ -4,6 +4,8 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import com.box.l10n.mojito.entity.security.user.User; import com.box.l10n.mojito.security.Role; @@ -12,6 +14,9 @@ import org.junit.Rule; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.transaction.annotation.Transactional; public class UserServiceTest extends ServiceTestBase { @@ -71,4 +76,213 @@ public void testSystemUserExist() { assertNotNull("System user has a createdByUser", systemUser.getCreatedByUser()); assertFalse("System user should be disabled", systemUser.getEnabled()); } + + @Test + @Transactional + public void testFindAllWithoutCommonName() { + this.userService.createUserWithRole( + "username", "password", Role.ROLE_USER, "givenName", "surname", null, false); + Page usersPage = + this.userService.findByUsernameOrName("username", null, Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("username", usersPage.getContent().getFirst().getUsername()); + assertEquals("givenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("surname", usersPage.getContent().getFirst().getSurname()); + assertNull(usersPage.getContent().getFirst().getCommonName()); + + usersPage = + this.userService.findByUsernameOrName(null, "givenname surname", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("username", usersPage.getContent().getFirst().getUsername()); + assertEquals("givenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("surname", usersPage.getContent().getFirst().getSurname()); + assertNull(usersPage.getContent().getFirst().getCommonName()); + + usersPage = + this.userService.findByUsernameOrName(null, "givenname surnam", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("username", usersPage.getContent().getFirst().getUsername()); + assertEquals("givenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("surname", usersPage.getContent().getFirst().getSurname()); + assertNull(usersPage.getContent().getFirst().getCommonName()); + + usersPage = + this.userService.findByUsernameOrName(null, "ivenName surname", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("username", usersPage.getContent().getFirst().getUsername()); + assertEquals("givenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("surname", usersPage.getContent().getFirst().getSurname()); + assertNull(usersPage.getContent().getFirst().getCommonName()); + + usersPage = + this.userService.findByUsernameOrName(null, "GIVENNAME SURNAME", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("username", usersPage.getContent().getFirst().getUsername()); + assertEquals("givenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("surname", usersPage.getContent().getFirst().getSurname()); + assertNull(usersPage.getContent().getFirst().getCommonName()); + + usersPage = this.userService.findByUsernameOrName(null, "username", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("username", usersPage.getContent().getFirst().getUsername()); + assertEquals("givenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("surname", usersPage.getContent().getFirst().getSurname()); + assertNull(usersPage.getContent().getFirst().getCommonName()); + + usersPage = this.userService.findByUsernameOrName(null, "usernam", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("username", usersPage.getContent().getFirst().getUsername()); + assertEquals("givenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("surname", usersPage.getContent().getFirst().getSurname()); + assertNull(usersPage.getContent().getFirst().getCommonName()); + + usersPage = this.userService.findByUsernameOrName(null, "sername", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("username", usersPage.getContent().getFirst().getUsername()); + assertEquals("givenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("surname", usersPage.getContent().getFirst().getSurname()); + assertNull(usersPage.getContent().getFirst().getCommonName()); + + usersPage = this.userService.findByUsernameOrName(null, "USERNAME", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("username", usersPage.getContent().getFirst().getUsername()); + assertEquals("givenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("surname", usersPage.getContent().getFirst().getSurname()); + assertNull(usersPage.getContent().getFirst().getCommonName()); + + usersPage = + this.userService.findByUsernameOrName("usernameWithoutMatch", null, Pageable.ofSize(10)); + assertEquals(0, usersPage.getContent().size()); + + usersPage = this.userService.findByUsernameOrName(null, "noMatch", Pageable.ofSize(10)); + assertEquals(0, usersPage.getContent().size()); + } + + @Test + @Transactional + public void testFindAllWithCommonName() { + this.userService.createUserWithRole( + "test_username", + "password", + Role.ROLE_USER, + "testGivenName", + "testSurname", + "commonName", + false); + Page usersPage = + this.userService.findByUsernameOrName("test_username", null, Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("test_username", usersPage.getContent().getFirst().getUsername()); + assertEquals("testGivenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("testSurname", usersPage.getContent().getFirst().getSurname()); + assertEquals("commonName", usersPage.getContent().getFirst().getCommonName()); + + usersPage = this.userService.findByUsernameOrName(null, "commonName", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("test_username", usersPage.getContent().getFirst().getUsername()); + assertEquals("testGivenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("testSurname", usersPage.getContent().getFirst().getSurname()); + assertEquals("commonName", usersPage.getContent().getFirst().getCommonName()); + + usersPage = this.userService.findByUsernameOrName(null, "commonNam", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("test_username", usersPage.getContent().getFirst().getUsername()); + assertEquals("testGivenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("testSurname", usersPage.getContent().getFirst().getSurname()); + assertEquals("commonName", usersPage.getContent().getFirst().getCommonName()); + + usersPage = this.userService.findByUsernameOrName(null, "ommonName", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("test_username", usersPage.getContent().getFirst().getUsername()); + assertEquals("testGivenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("testSurname", usersPage.getContent().getFirst().getSurname()); + assertEquals("commonName", usersPage.getContent().getFirst().getCommonName()); + + usersPage = this.userService.findByUsernameOrName(null, "COMMONNAME", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("test_username", usersPage.getContent().getFirst().getUsername()); + assertEquals("testGivenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("testSurname", usersPage.getContent().getFirst().getSurname()); + assertEquals("commonName", usersPage.getContent().getFirst().getCommonName()); + + usersPage = + this.userService.findByUsernameOrName(null, "no commonName match", Pageable.ofSize(10)); + assertEquals(0, usersPage.getContent().size()); + + usersPage = + this.userService.findByUsernameOrName( + null, "testGivenName testSurname", Pageable.ofSize(10)); + assertEquals(0, usersPage.getContent().size()); + } + + @Test + @Transactional + public void testFindAllWithBlankSearch() { + this.userService.createUserWithRole( + "blank_username", + "password", + Role.ROLE_USER, + "BlankGivenName", + "BlankSurname", + null, + false); + Page usersPage = + this.userService.findByUsernameOrName("blank_username", "", Pageable.ofSize(10)); + assertEquals(1, usersPage.getContent().size()); + assertEquals("blank_username", usersPage.getContent().getFirst().getUsername()); + assertEquals("BlankGivenName", usersPage.getContent().getFirst().getGivenName()); + assertEquals("BlankSurname", usersPage.getContent().getFirst().getSurname()); + assertNull(usersPage.getContent().getFirst().getCommonName()); + + usersPage = this.userService.findByUsernameOrName(null, "", Pageable.ofSize(10)); + assertFalse(usersPage.getContent().isEmpty()); + assertTrue( + usersPage.getContent().stream() + .anyMatch(user -> user.getUsername().equals("blank_username"))); + } + + @Test + @Transactional + public void testFindAllForMultipleMatches() { + this.userService.createUserWithRole( + "first_username", + "password", + Role.ROLE_USER, + "FirstGivenName", + "FirstSurname", + null, + false); + this.userService.createUserWithRole( + "second_username", + "password", + Role.ROLE_USER, + "SecondGivenName", + "FirstSurname", + null, + false); + this.userService.createUserWithRole( + "third_username", + "password", + Role.ROLE_USER, + "SecondGivenName", + "ThirdSurname", + "SecondGivenName", + false); + Page usersPage = + this.userService.findByUsernameOrName(null, "username", Pageable.ofSize(10)); + assertEquals(3, usersPage.getContent().size()); + assertEquals("first_username", usersPage.getContent().getFirst().getUsername()); + assertEquals("second_username", usersPage.getContent().get(1).getUsername()); + assertEquals("third_username", usersPage.getContent().getLast().getUsername()); + + usersPage = this.userService.findByUsernameOrName(null, "FirstSurname", Pageable.ofSize(10)); + assertEquals(2, usersPage.getContent().size()); + assertEquals("first_username", usersPage.getContent().getFirst().getUsername()); + assertEquals("second_username", usersPage.getContent().getLast().getUsername()); + + usersPage = this.userService.findByUsernameOrName(null, "SecondGivenName", Pageable.ofSize(10)); + assertEquals(2, usersPage.getContent().size()); + assertEquals("second_username", usersPage.getContent().getFirst().getUsername()); + assertEquals("third_username", usersPage.getContent().getLast().getUsername()); + } }