diff --git a/gms-ui/.env.development b/gms-ui/.env.development index 047b5e40ec26fa5ca2586d5358942d11cafeef97..50ae1fd484dd4309452661c0062b6c50eaedadcc 100644 --- a/gms-ui/.env.development +++ b/gms-ui/.env.development @@ -1 +1,2 @@ VUE_APP_API_CLIENT = 'mock' +VUE_APP_API_BASE_URL = '' diff --git a/gms-ui/src/components/GenericSearchResults.vue b/gms-ui/src/components/GenericSearchResults.vue index d7fdade6782cd596eaf4fb0cdbb4c3b6988e1e2f..27d9199a627a9fa0d2fdfb176e06fc2eb69797d1 100644 --- a/gms-ui/src/components/GenericSearchResults.vue +++ b/gms-ui/src/components/GenericSearchResults.vue @@ -11,7 +11,7 @@ </span> </b-list-group-item> </b-list-group> - <Paginator :paginatedPanel="model.genericSearchResults" :onUpdate="updateSearchResults" /> + <Paginator :paginatedPanel="model.genericSearchResults" :onUpdate="updateSearchResults" :paginatorInput="input.genericSearch" /> </div> </div> </template> @@ -47,7 +47,10 @@ export default { } }, updateSearchResults: function() { - + client.search(this.input) + .then(results => { + this.$store.commit('displaySearchResults', results); + }); } } } diff --git a/gms-ui/src/components/GroupsPanel.vue b/gms-ui/src/components/GroupsPanel.vue index a30ab4dd2b8dea6ae2a8f86c43cf08af21bd6d87..038978e5fddadfa95f6e1d69ab5379f962b1fd05 100644 --- a/gms-ui/src/components/GroupsPanel.vue +++ b/gms-ui/src/components/GroupsPanel.vue @@ -22,7 +22,7 @@ </b-list-group> <p v-if="model.groupsPanel.items.length === 0">No groups</p> </div> - <Paginator :paginatedPanel="model.groupsPanel" :onUpdate="updatePagination" /> + <Paginator :paginatedPanel="model.groupsPanel" :onUpdate="updatePagination" :paginatorInput="input" /> <RenameGroupModal ref="renameGroupModal" /> <ConfirmRemoveGroupModal ref="confirmRemoveGroupModal" /> </b-tab> diff --git a/gms-ui/src/components/MembersPanel.vue b/gms-ui/src/components/MembersPanel.vue index c014be0bce6490c94acde6b4a579ad0e52f20638..b8d27362d57c4cac9fb2390d5e52f6660af030dc 100644 --- a/gms-ui/src/components/MembersPanel.vue +++ b/gms-ui/src/components/MembersPanel.vue @@ -15,7 +15,7 @@ </b-list-group> <p v-if="model.membersPanel.items.length === 0">No direct members</p> </div> - <Paginator :paginatedPanel="model.membersPanel" :onUpdate="updatePagination" /> + <Paginator :paginatedPanel="model.membersPanel" :onUpdate="updatePagination" :paginatorInput="input" /> <ConfirmRemoveMemberModal ref="confirmRemoveMemberModal" /> </b-tab> </template> diff --git a/gms-ui/src/components/Paginator.vue b/gms-ui/src/components/Paginator.vue index 09513c8362cd30c03a40b3cce3b84da7f3287afa..988e340d033c50ceb8777562df29462f076c51ea 100644 --- a/gms-ui/src/components/Paginator.vue +++ b/gms-ui/src/components/Paginator.vue @@ -10,7 +10,7 @@ <label for="page-size">Page size:</label> </b-col> <b-col sm="6"> - <b-form-select id="page-size" v-model="input.paginatorPageSize" :options="pageSizeOptions" v-on:change="changePageSize"></b-form-select> + <b-form-select id="page-size" v-model="paginatorInput.paginatorPageSize" :options="pageSizeOptions" v-on:change="changePageSize"></b-form-select> </b-col> </b-row> </div> @@ -26,11 +26,12 @@ export default { name: 'Paginator', props: { paginatedPanel: Object, - onUpdate: Function + onUpdate: Function, + paginatorInput: Object }, - computed: mapState({ + /*computed: mapState({ input: state => state.input - }), + }),*/ data: function() { return { pageSizeOptions: [{ @@ -54,11 +55,11 @@ export default { }, methods: { setPage: function(page) { - this.input.paginatorPage = page; + this.paginatorInput.paginatorPage = page; this.onUpdate(); }, changePageSize: function(pageSize) { - this.input.paginatorPageSize = pageSize; + this.paginatorInput.paginatorPageSize = pageSize; this.onUpdate(); } } diff --git a/gms-ui/src/components/PermissionsPanel.vue b/gms-ui/src/components/PermissionsPanel.vue index 12b3d4e2631136b4a4e317308b318ee007236531..95f67a85482b26af71217444972ff91fcbe136c0 100644 --- a/gms-ui/src/components/PermissionsPanel.vue +++ b/gms-ui/src/components/PermissionsPanel.vue @@ -25,7 +25,7 @@ </table> <p v-if="model.permissionsPanel.items.length === 0">No direct permissions</p> </div> - <Paginator :paginatedPanel="model.permissionsPanel" :onUpdate="updatePagination" /> + <Paginator :paginatedPanel="model.permissionsPanel" :onUpdate="updatePagination" :paginatorInput="input" /> <ConfirmRemovePermissionModal ref="confirmRemovePermissionModal" /> </b-tab> </template> diff --git a/gms-ui/src/components/TopMenu.vue b/gms-ui/src/components/TopMenu.vue index 8a6677d7c2c3a6a47a5a2501dce0a42c6b0e4d57..fc6e5bce2cc68c906952d5ee17245fecf5867987 100644 --- a/gms-ui/src/components/TopMenu.vue +++ b/gms-ui/src/components/TopMenu.vue @@ -10,7 +10,7 @@ <!-- Right aligned nav items --> <b-navbar-nav class="ml-auto"> <b-nav-form> - <b-form-input size="sm" class="mr-sm-2" placeholder="Search" v-model="input.genericSearch.filter"></b-form-input> + <b-form-input size="sm" class="mr-sm-2" placeholder="Search" v-model="input.genericSearch.filter" @keydown.native.enter="genericSearch"></b-form-input> <b-button size="sm" class="my-2 my-sm-0" type="button" v-on:click="genericSearch()">Search</b-button> </b-nav-form> <b-nav-item-dropdown :text="user" right v-if="user"> @@ -40,7 +40,12 @@ export default { showMainPage() { this.$store.commit('showMainPage'); }, - genericSearch() { + genericSearch(event) { + // prevent the page reload if enter is pressed + if (event) { + event.preventDefault(); + } + this.input.genericSearch.page = 1; this.input.genericSearch.pageSize = 20; client.search(this.input) diff --git a/gms-ui/src/store.js b/gms-ui/src/store.js index 06f5ed39541a4d2cd73b185890165c696ceb7f96..a6d1c9100490a4ebf758f9bf824445717320df51 100644 --- a/gms-ui/src/store.js +++ b/gms-ui/src/store.js @@ -30,9 +30,9 @@ export default new Vuex.Store({ paginatorPage: 1, selectedTab: 'groups', tabIndex: 0, - searchFilter: null, + searchFilter: '', genericSearch: { - filter: null, + filter: '', paginatorPage: 1, paginatorPageSize: 20 } diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/response/PaginatedData.java b/gms/src/main/java/it/inaf/ia2/gms/model/response/PaginatedData.java index 78f917a6bf76b98b8615a91706534f0014bf6892..d41f5510e1ffc20531af2a3f9d91673e3764f89c 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/model/response/PaginatedData.java +++ b/gms/src/main/java/it/inaf/ia2/gms/model/response/PaginatedData.java @@ -19,11 +19,21 @@ public class PaginatedData<T> { public PaginatedData(List<T> allItems, int page, int pageSize) { + if (page < 1) { + throw new IllegalArgumentException("Page numbers start from 1"); + } + totalItems = allItems.size(); - this.currentPage = page; this.pageSize = pageSize; - totalPages = (int) Math.ceil((double) totalItems / pageSize); + totalPages = totalItems == 0 ? 1 : (int) Math.ceil((double) totalItems / pageSize); + + if (page <= totalPages) { + this.currentPage = page; + } else { + // back to last available page + this.currentPage = totalPages; + } if (allItems.isEmpty()) { items = allItems; diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java b/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java index 7aa9a38d19664e3b7f8a729e83d386ddb12932d3..0bfe396a1f835bd878c5f28ed9c4660cdec6f283 100644 --- a/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java +++ b/gms/src/main/java/it/inaf/ia2/gms/service/SearchService.java @@ -75,18 +75,25 @@ public class SearchService { // Select only the groups visible to the user List<PermissionEntity> permissions = permissionsDAO.findUserPermissions(userId); - List<SearchResponseItem> items = new ArrayList<>(); + List<Map.Entry<String, String>> groupsIdPath = new ArrayList<>(); for (GroupEntity group : allGroups) { - PermissionUtils.getGroupPermission(group, permissions).ifPresent(permission -> { - SearchResponseItem item = new SearchResponseItem(); - item.setType(SearchResponseType.GROUP); - item.setId(group.getId()); - item.setLabel(group.getName()); - items.add(item); + groupsIdPath.add(new SimpleEntry<>(group.getId(), group.getPath())); }); } + List<SearchResponseItem> items = new ArrayList<>(); + Map<String, List<String>> groupNames = groupNameService.getNames(groupsIdPath); + for (Map.Entry<String, String> entry : groupsIdPath) { + String groupId = entry.getKey(); + SearchResponseItem item = new SearchResponseItem(); + item.setType(SearchResponseType.GROUP); + item.setId(groupId); + List<String> names = groupNames.get(groupId); + item.setLabel(String.join(" / ", names)); + items.add(item); + } + return items; } diff --git a/gms/src/test/java/it/inaf/ia2/gms/model/PaginatedDataTest.java b/gms/src/test/java/it/inaf/ia2/gms/model/PaginatedDataTest.java index 173f7e0e6fc0c3eee21bc6257f5da6d083ecd107..15e92fae44d846c415c675cbbea2b3e18d11179a 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/model/PaginatedDataTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/model/PaginatedDataTest.java @@ -56,4 +56,35 @@ public class PaginatedDataTest { assertEquals(6, (int) paginatedData.getLinks().get(0)); assertEquals(10, (int) paginatedData.getLinks().get(4)); } + + /** + * When the page size change (from a smaller size to a bigger size) and we + * are in the last page it could be that we need to set a lower page number + * because the selected page is empty. + */ + @Test + public void testGoToLastPageIfPageOverflow() { + + List<Integer> allItems = new ArrayList<>(); + for (int i = 1; i <= 6; i++) { + allItems.add(i); + } + + PaginatedData<Integer> paginatedData = new PaginatedData<>(allItems, 2, 20); + + assertEquals(1, paginatedData.getCurrentPage()); // back to last page + assertEquals(6, paginatedData.getItems().size()); + assertEquals(6, paginatedData.getTotalItems()); + } + + @Test(expected = IllegalArgumentException.class) + public void testIllegalPageNumber() { + new PaginatedData<>(new ArrayList<>(), 0, 20); + } + + @Test + public void testPageOneForEmptyList() { + PaginatedData<Integer> paginatedData = new PaginatedData<>(new ArrayList<>(), 1, 20); + assertEquals(1, paginatedData.getCurrentPage()); + } } diff --git a/gms/src/test/java/it/inaf/ia2/gms/service/SearchServiceTest.java b/gms/src/test/java/it/inaf/ia2/gms/service/SearchServiceTest.java index b05d53982fc57e0d8c5f01e638bbc2481c01e670..56d0d9cd39bb4e9ead2f4a700235418723a9e231 100644 --- a/gms/src/test/java/it/inaf/ia2/gms/service/SearchServiceTest.java +++ b/gms/src/test/java/it/inaf/ia2/gms/service/SearchServiceTest.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import static org.junit.Assert.assertEquals; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import static org.mockito.ArgumentMatchers.any; @@ -54,6 +55,28 @@ public class SearchServiceTest { @InjectMocks private SearchService searchService; + @Before + public void mockGroupNameService() { + + when(groupNameService.getNames(any())).then(invocation -> { + Map<String, List<String>> result = new HashMap<>(); + List<Map.Entry<String, String>> arg = invocation.getArgument(0); + for (Entry<String, String> entry : arg) { + List<String> names = new ArrayList<>(); + switch (entry.getKey()) { + case "ROOT": + names.add("Root"); + break; + case "group1_id": + names.add("Group 1"); + break; + } + result.put(entry.getKey(), names); + } + return result; + }); + } + @Test public void testGenericSearch() { @@ -140,24 +163,6 @@ public class SearchServiceTest { root.setPath(""); when(groupsService.getRoot()).thenReturn(root); - when(groupNameService.getNames(any())).then(invocation -> { - Map<String, List<String>> result = new HashMap<>(); - List<Map.Entry<String, String>> arg = invocation.getArgument(0); - for (Entry<String, String> entry : arg) { - List<String> names = new ArrayList<>(); - switch (entry.getKey()) { - case "ROOT": - names.add("Root"); - break; - case "group1_id": - names.add("Group 1"); - break; - } - result.put(entry.getKey(), names); - } - return result; - }); - UserSearchResponse response = searchService.getUserSearchResult("admin_id", "target_id"); assertEquals(1, response.getGroups().size()); assertEquals(1, response.getPermissions().size());