From d4cb983822cfedfd32925b9d031fcd2598bacbb6 Mon Sep 17 00:00:00 2001
From: Sonia Zorba <sonia.zorba@inaf.it>
Date: Wed, 17 Jul 2019 16:58:17 +0200
Subject: [PATCH] Included FE dist files inside Spring application; Groups
 model logic

---
 .gitignore                                    |  2 +
 gms-ui/.env.production                        |  1 +
 gms-ui/src/api/server/index.js                | 18 ++++--
 gms/pom.xml                                   | 21 ++++++
 .../it/inaf/ia2/gms/authn/OAuth2Config.java   |  6 ++
 .../it/inaf/ia2/gms/authn/SessionData.java    |  8 ++-
 .../ia2/gms/controller/GroupsController.java  |  2 +-
 .../ia2/gms/model/GroupsModelRequest.java     | 17 +++++
 .../it/inaf/ia2/gms/model/IdentityType.java   | 24 +++++--
 .../gms/persistence/MembershipRepository.java |  3 +
 .../persistence/PermissionsRepository.java    |  3 +
 .../java/it/inaf/ia2/gms/rap/RapClient.java   | 42 ++++++++++++
 .../ia2/gms/service/GroupsModelService.java   | 38 +++++++++--
 .../inaf/ia2/gms/service/MembersService.java  | 32 ++++++++++
 .../ia2/gms/service/PermissionsService.java   | 52 +++++++++++++++
 gms/src/main/resources/application.properties |  2 +
 .../gms/controller/GroupsControllerTest.java  | 64 +++++++++++++++++++
 17 files changed, 315 insertions(+), 20 deletions(-)
 create mode 100644 gms/src/main/java/it/inaf/ia2/gms/service/MembersService.java
 create mode 100644 gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java
 create mode 100644 gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java

diff --git a/.gitignore b/.gitignore
index ed9e360..005e1f7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 .DS_Store
 node_modules
 gms-ui/dist
+gms-ui/node/
 
 # local env files
 .env.local
@@ -53,3 +54,4 @@ nbactions.xml
 ### VS Code ###
 .vscode/
 
+/gms-ui/target/
diff --git a/gms-ui/.env.production b/gms-ui/.env.production
index 8ef8cb1..e86ff0c 100644
--- a/gms-ui/.env.production
+++ b/gms-ui/.env.production
@@ -1 +1,2 @@
 VUE_APP_API_CLIENT = 'server'
+BASE_API_URL = 'http://localhost:8081/'
diff --git a/gms-ui/src/api/server/index.js b/gms-ui/src/api/server/index.js
index c44f2b8..c78982a 100644
--- a/gms-ui/src/api/server/index.js
+++ b/gms-ui/src/api/server/index.js
@@ -1,9 +1,15 @@
-//import axios from 'axios'
+const BASE_API_URL = "http://localhost:8081/"
 
 export default {
-  fetchPosts () {
-    return null;//axios
-      //.get('https://jsonplaceholder.typicode.com/posts')
-      //.then(response => response.data)
+  fetchMainModel () {
+    return fetch(BASE_API_URL + 'groups?groupId=ROOT&tab=groups&paginatorPageSize=20&paginatorPage=1', {
+      method: 'GET',
+      mode: 'cors',
+      cache: 'no-cache',
+      credentials: 'include',
+      headers: {
+        'Content-Type': 'application/json'
+      }
+    });
   }
-}
+};
diff --git a/gms/pom.xml b/gms/pom.xml
index 3042e69..a24ffb0 100644
--- a/gms/pom.xml
+++ b/gms/pom.xml
@@ -67,6 +67,27 @@
 
     <build>
         <plugins>
+            <plugin>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>3.1.0</version>
+                <executions>
+                    <execution>
+                        <id>copy-resources</id>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>copy-resources</goal>
+                        </goals>
+                        <configuration>
+                            <outputDirectory>${basedir}/target/classes/static</outputDirectory>
+                            <resources>
+                                <resource>
+                                    <directory>../gms-ui/dist</directory>
+                                </resource>
+                            </resources>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
             <plugin>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-maven-plugin</artifactId>
diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/OAuth2Config.java b/gms/src/main/java/it/inaf/ia2/gms/authn/OAuth2Config.java
index 4fd68b5..81e0ec1 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/authn/OAuth2Config.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/authn/OAuth2Config.java
@@ -15,6 +15,7 @@ import org.springframework.security.oauth2.provider.ClientDetailsService;
 import org.springframework.security.oauth2.provider.client.InMemoryClientDetailsService;
 import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
 import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
+import org.springframework.web.client.RestTemplate;
 
 /**
  * Extending the AuthorizationServerEndpointsConfiguration disables the Spring
@@ -63,4 +64,9 @@ public class OAuth2Config extends AuthorizationServerEndpointsConfiguration {
         return new DefaultUserInfoRestTemplateFactory(customizers, details,
                 oauth2ClientContext);
     }
+
+    @Bean
+    public RestTemplate rapRestTemplate() {
+        return new RestTemplate();
+    }
 }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java b/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java
index 5a82085..92ddbc8 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/authn/SessionData.java
@@ -20,13 +20,13 @@ public class SessionData {
     private UsersRepository usersRepository;
 
     private String userId;
+    private String accessToken;
 
     @PostConstruct
     public void init() {
         CustomAuthenticationData authn = (CustomAuthenticationData) ((OAuth2Authentication) request.getUserPrincipal()).getUserAuthentication();
         userId = (String) authn.getPrincipal();
-        //accessToken = (String) ((CustomAuthenticationData) request.getUserPrincipal()).getAttributes().get("access_token");
-        //System.out.println("SessionData initialized: " + accessToken);
+        accessToken = (String) authn.getAttributes().get("access_token");
 
         if (!usersRepository.findById(userId).isPresent()) {
             User user = new User();
@@ -38,4 +38,8 @@ public class SessionData {
     public String getUserId() {
         return userId;
     }
+
+    public String getAccessToken() {
+        return accessToken;
+    }
 }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java
index c1fb662..4ee7683 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/controller/GroupsController.java
@@ -35,7 +35,7 @@ public class GroupsController {
     private GroupsService groupsService;
 
     @GetMapping(value = "/groups", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
-    public GroupsModelResponse getGroupsModelResponse(@Valid @RequestBody GroupsModelRequest groupsModelRequest) {
+    public GroupsModelResponse getGroupsModelResponse(@Valid GroupsModelRequest groupsModelRequest) {
         return groupsModelService.getGroupsModel(groupsModelRequest, session.getUserId());
     }
 
diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/GroupsModelRequest.java b/gms/src/main/java/it/inaf/ia2/gms/model/GroupsModelRequest.java
index 9127d87..79e22f4 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/model/GroupsModelRequest.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/model/GroupsModelRequest.java
@@ -1,8 +1,17 @@
 package it.inaf.ia2.gms.model;
 
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
 public class GroupsModelRequest extends BaseModelRequest {
 
+    @NotNull
     private String groupId;
+
+    @NotNull
+    private String tab;
+
+    @Min(1)
     private int paginatorPage;
     private int paginatorPageSize;
 
@@ -14,6 +23,14 @@ public class GroupsModelRequest extends BaseModelRequest {
         this.groupId = groupId;
     }
 
+    public String getTab() {
+        return tab;
+    }
+
+    public void setTab(String tab) {
+        this.tab = tab;
+    }
+
     public int getPaginatorPage() {
         return paginatorPage;
     }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/model/IdentityType.java b/gms/src/main/java/it/inaf/ia2/gms/model/IdentityType.java
index f2a5655..fe0c6f6 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/model/IdentityType.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/model/IdentityType.java
@@ -2,11 +2,21 @@ package it.inaf.ia2.gms.model;
 
 public enum IdentityType {
 
-    EDU_GAIN,
-    X509,
-    ORCID,
-    GOOGLE,
-    LINKEDIN,
-    FACEBOOK,
-    LOCAL_IDP
+    EDU_GAIN("EduGAIN"),
+    X509("X.509"),
+    ORCID("OrcID"),
+    GOOGLE("Google"),
+    LINKEDIN("LinkedIn"),
+    FACEBOOK("Facebook"),
+    LOCAL_IDP("LocalIdP");
+
+    private final String value;
+
+    IdentityType(String value) {
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
 }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipRepository.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipRepository.java
index cd33692..44bd7df 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipRepository.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/MembershipRepository.java
@@ -1,5 +1,6 @@
 package it.inaf.ia2.gms.persistence;
 
+import it.inaf.ia2.gms.persistence.model.Group;
 import it.inaf.ia2.gms.persistence.model.Membership;
 import it.inaf.ia2.gms.persistence.model.MembershipId;
 import it.inaf.ia2.gms.persistence.model.User;
@@ -13,4 +14,6 @@ import org.springframework.stereotype.Repository;
 public interface MembershipRepository extends JpaRepository<Membership, MembershipId> {
 
     List<Membership> findBy_user(User user);
+
+    List<Membership> findBy_group(Group group);
 }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsRepository.java b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsRepository.java
index f3e19f1..d43f293 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsRepository.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/persistence/PermissionsRepository.java
@@ -1,5 +1,6 @@
 package it.inaf.ia2.gms.persistence;
 
+import it.inaf.ia2.gms.persistence.model.Group;
 import it.inaf.ia2.gms.persistence.model.User;
 import it.inaf.ia2.gms.persistence.model.UserGroupPermission;
 import it.inaf.ia2.gms.persistence.model.UserGroupPermissionId;
@@ -13,4 +14,6 @@ import org.springframework.stereotype.Repository;
 public interface PermissionsRepository extends JpaRepository<UserGroupPermission, UserGroupPermissionId> {
 
     List<UserGroupPermission> findBy_user(User user);
+
+    List<UserGroupPermission> findBy_group(Group group);
 }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java b/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java
index a7eff0d..0dba6d8 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/rap/RapClient.java
@@ -1,8 +1,50 @@
 package it.inaf.ia2.gms.rap;
 
+import it.inaf.ia2.gms.authn.SessionData;
+import it.inaf.ia2.gms.model.RapUser;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.ParameterizedTypeReference;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.MediaType;
 import org.springframework.stereotype.Component;
+import org.springframework.web.client.RestTemplate;
 
 @Component
 public class RapClient {
 
+    @Value("${rap.ws-url}")
+    private String rapBaseUrl;
+
+    @Autowired
+    private SessionData sessionData;
+
+    @Autowired
+    private RestTemplate rapRestTemplate;
+
+    public List<RapUser> getUsers(Set<String> identifiers) {
+
+        String url = rapBaseUrl + "/user?identifiers=" + String.join(",", identifiers);
+        return rapRestTemplate.exchange(url, HttpMethod.GET, getEntity(), new ParameterizedTypeReference<List<RapUser>>() {
+        }).getBody();
+    }
+
+    private HttpEntity<?> getEntity() {
+        return getEntity(null);
+    }
+
+    private <T> HttpEntity<T> getEntity(T body) {
+
+        HttpHeaders headers = new HttpHeaders();
+        headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
+
+        headers.add("Authorization", "Bearer " + sessionData.getAccessToken());
+
+        return new HttpEntity<>(body, headers);
+    }
 }
diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/GroupsModelService.java b/gms/src/main/java/it/inaf/ia2/gms/service/GroupsModelService.java
index 1673fd6..813e171 100644
--- a/gms/src/main/java/it/inaf/ia2/gms/service/GroupsModelService.java
+++ b/gms/src/main/java/it/inaf/ia2/gms/service/GroupsModelService.java
@@ -5,9 +5,13 @@ import it.inaf.ia2.gms.model.GroupNode;
 import it.inaf.ia2.gms.model.GroupsModelRequest;
 import it.inaf.ia2.gms.model.GroupsModelResponse;
 import it.inaf.ia2.gms.model.PaginatedData;
+import it.inaf.ia2.gms.model.Permission;
+import it.inaf.ia2.gms.model.RapUser;
+import it.inaf.ia2.gms.model.UserPermission;
 import it.inaf.ia2.gms.persistence.model.Group;
 import it.inaf.ia2.gms.persistence.model.User;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -21,6 +25,12 @@ public class GroupsModelService {
     @Autowired
     private GroupsService groupsService;
 
+    @Autowired
+    private MembersService membersService;
+
+    @Autowired
+    private PermissionsService permissionsService;
+
     public GroupsModelResponse getGroupsModel(GroupsModelRequest request, String userId) {
 
         User user = usersService.getUserById(userId);
@@ -30,12 +40,31 @@ public class GroupsModelService {
 
         response.setPage("groups");
 
-        List<GroupNode> groupNodes = groupsService.getSubgroups(group, user);
+        response.setBreadcrumbs(buildBreadcrumbs(new ArrayList<>(), group));
 
-        response.setGroupsPanel(new PaginatedData<>(groupNodes, request.getPaginatorPage(), request.getPaginatorPageSize()));
+        List<Permission> currentNodePermissions = groupsService.getPermissions(group, user);
+        response.setPermissions(currentNodePermissions);
 
-        response.setPermissions(groupsService.getPermissions(group, user));
-        response.setBreadcrumbs(buildBreadcrumbs(new ArrayList<>(), group));
+        switch (request.getTab()) {
+            case "groups":
+                List<GroupNode> groupNodes = groupsService.getSubgroups(group, user);
+                response.setGroupsPanel(new PaginatedData<>(groupNodes, request.getPaginatorPage(), request.getPaginatorPageSize()));
+                break;
+            case "members":
+                if (currentNodePermissions.contains(Permission.ADMIN)
+                        || currentNodePermissions.contains(Permission.MANAGE_MEMBERS)
+                        || currentNodePermissions.contains(Permission.VIEW_MEMBERS)) {
+                    List<RapUser> members = membersService.getMembers(group);
+                    response.setMembersPanel(new PaginatedData<>(members, request.getPaginatorPage(), request.getPaginatorPageSize()));
+                }
+                break;
+            case "permissions":
+                if (currentNodePermissions.contains(Permission.ADMIN)) {
+                    List<UserPermission> permissions = permissionsService.getUserPermissions(group);
+                    response.setPermissionsPanel(new PaginatedData<>(permissions, request.getPaginatorPage(), request.getPaginatorPageSize()));
+                }
+                break;
+        }
 
         return response;
     }
@@ -48,6 +77,7 @@ public class GroupsModelService {
         breadcrumbs.add(bc);
 
         if (group.getParentGroup() == null) {
+            Collections.reverse(breadcrumbs);
             return breadcrumbs;
         } else {
             return buildBreadcrumbs(breadcrumbs, group.getParentGroup());
diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/MembersService.java b/gms/src/main/java/it/inaf/ia2/gms/service/MembersService.java
new file mode 100644
index 0000000..5e1b5a0
--- /dev/null
+++ b/gms/src/main/java/it/inaf/ia2/gms/service/MembersService.java
@@ -0,0 +1,32 @@
+package it.inaf.ia2.gms.service;
+
+import it.inaf.ia2.gms.model.RapUser;
+import it.inaf.ia2.gms.persistence.MembershipRepository;
+import it.inaf.ia2.gms.persistence.model.Group;
+import it.inaf.ia2.gms.persistence.model.Membership;
+import it.inaf.ia2.gms.rap.RapClient;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class MembersService {
+
+    @Autowired
+    private MembershipRepository membershipRepository;
+
+    @Autowired
+    private RapClient rapClient;
+
+    public List<RapUser> getMembers(Group group) {
+        List<Membership> memberships = membershipRepository.findBy_group(group);
+
+        Set<String> userIdentifiers = memberships.stream()
+                .map(m -> m.getUser().getId())
+                .collect(Collectors.toSet());
+
+        return rapClient.getUsers(userIdentifiers);
+    }
+}
diff --git a/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java b/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java
new file mode 100644
index 0000000..7f7d3e3
--- /dev/null
+++ b/gms/src/main/java/it/inaf/ia2/gms/service/PermissionsService.java
@@ -0,0 +1,52 @@
+package it.inaf.ia2.gms.service;
+
+import it.inaf.ia2.gms.model.RapUser;
+import it.inaf.ia2.gms.model.UserPermission;
+import it.inaf.ia2.gms.persistence.PermissionsRepository;
+import it.inaf.ia2.gms.persistence.model.Group;
+import it.inaf.ia2.gms.persistence.model.UserGroupPermission;
+import it.inaf.ia2.gms.rap.RapClient;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class PermissionsService {
+
+    @Autowired
+    private PermissionsRepository permissionsRepository;
+
+    @Autowired
+    private RapClient rapClient;
+
+    public List<UserPermission> getUserPermissions(Group group) {
+
+        List<UserGroupPermission> permissions = permissionsRepository.findBy_group(group);
+
+        Set<String> userIdentifiers = permissions.stream()
+                .map(p -> p.getUser().getId())
+                .collect(Collectors.toSet());
+
+        Map<String, RapUser> users = rapClient.getUsers(userIdentifiers).stream()
+                .collect(Collectors.toMap(RapUser::getId, Function.identity()));
+
+        List<UserPermission> result = new ArrayList<>();
+
+        for (UserGroupPermission p : permissions) {
+            RapUser rapUser = users.get(p.getUser().getId());
+            if (rapUser != null) {
+                UserPermission permission = new UserPermission();
+                permission.setPermission(p.getPermission());
+                permission.setUser(rapUser);
+                result.add(permission);
+            }
+        }
+
+        return result;
+    }
+}
diff --git a/gms/src/main/resources/application.properties b/gms/src/main/resources/application.properties
index 0893bb1..683fc40 100644
--- a/gms/src/main/resources/application.properties
+++ b/gms/src/main/resources/application.properties
@@ -16,3 +16,5 @@ spring.datasource.url=jdbc:mysql://localhost:3306/gms?useJDBCCompliantTimezoneSh
 spring.datasource.username=gms
 spring.datasource.password=gms
 #spring.jpa.open-in-view=false
+
+rap.ws-url=http://localhost/rap-ia2/ws
\ No newline at end of file
diff --git a/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java b/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java
new file mode 100644
index 0000000..fc12cfc
--- /dev/null
+++ b/gms/src/test/java/it/inaf/ia2/gms/controller/GroupsControllerTest.java
@@ -0,0 +1,64 @@
+package it.inaf.ia2.gms.controller;
+
+import it.inaf.ia2.gms.authn.SessionData;
+import it.inaf.ia2.gms.model.GroupsModelRequest;
+import it.inaf.ia2.gms.model.GroupsModelResponse;
+import it.inaf.ia2.gms.service.GroupsModelService;
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import static org.mockito.ArgumentMatchers.any;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.web.servlet.MockMvc;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+@RunWith(MockitoJUnitRunner.class)
+public class GroupsControllerTest {
+
+    @Mock
+    private SessionData session;
+
+    @Mock
+    private GroupsModelService groupsModelService;
+
+    @InjectMocks
+    private GroupsController controller;
+
+    @Autowired
+    private MockMvc mockMvc;
+
+    @Before
+    public void init() {
+        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
+    }
+
+    @Test
+    public void testGetGroups() throws Exception {
+
+        GroupsModelResponse response = new GroupsModelResponse();
+
+        when(groupsModelService.getGroupsModel(any(), any()))
+                .thenReturn(response);
+
+        mockMvc.perform(get("/groups?groupId=ROOT&tab=groups&paginatorPageSize=20&paginatorPage=1"))
+                .andExpect(status().isOk());
+
+        ArgumentCaptor<GroupsModelRequest> requestCaptor = ArgumentCaptor.forClass(GroupsModelRequest.class);
+        verify(groupsModelService).getGroupsModel(requestCaptor.capture(), any());
+        GroupsModelRequest request = requestCaptor.getValue();
+
+        assertEquals("ROOT", request.getGroupId());
+        assertEquals("groups", request.getTab());
+        assertEquals(1, request.getPaginatorPage());
+        assertEquals(20, request.getPaginatorPageSize());
+    }
+}
-- 
GitLab