From af5194132494201e64f42dd4d1c9d87214e15773 Mon Sep 17 00:00:00 2001
From: Jozef Daxner <daxner.dodo@gmail.com>
Date: Fri, 13 Jun 2025 12:07:08 +0200
Subject: [PATCH 1/9] [NAE-2116] Frontend remote configuration

- Refactor repositories and services to support pagination
- Updated repository and service layer methods to utilize pagination where applicable, improving scalability and performance.
- Replaced unpaged results with pageable implementations across multiple data queries and ensured method signatures reflect these changes.
---
 .gitignore                                    |  1 +
 .../auth/service/RegistrationService.java     |  8 ++-
 .../engine/auth/web/UserController.java       |  2 +-
 .../ImpersonationAuthorizationService.java    |  2 +-
 .../petrinet/service/PetriNetService.java     |  3 +-
 .../startup/runner/DefaultFiltersRunner.java  | 19 ++---
 .../startup/runner/SuperCreatorRunner.java    |  2 +-
 .../auth/service/AuthorityServiceImpl.java    | 40 +++--------
 .../engine/auth/service/GroupServiceImpl.java | 25 ++++---
 .../engine/auth/service/UserServiceImpl.java  |  9 +--
 .../auth/repository/AuthorityRepository.java  |  7 +-
 .../auth/repository/GroupRepository.java      |  5 +-
 .../auth/repository/UserRepository.java       | 69 +++++++++----------
 .../engine/auth/service/AuthorityService.java | 14 ++--
 .../engine/auth/service/GroupService.java     |  8 +--
 .../engine/auth/service/UserService.java      |  8 +--
 16 files changed, 104 insertions(+), 118 deletions(-)

diff --git a/.gitignore b/.gitignore
index 450790f1fd..a20b5cf8c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -208,6 +208,7 @@ buildNumber.properties
 *.iml
 out
 gen
+/storage/
 ### Groovy template
 # .gitignore created from Groovy contributors in https://github.com/apache/groovy/blob/master/.gitignore
 
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/RegistrationService.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/RegistrationService.java
index 9d56ca0ce0..fa1baeb34f 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/auth/service/RegistrationService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/service/RegistrationService.java
@@ -14,6 +14,8 @@
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 import org.springframework.scheduling.annotation.Scheduled;
 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
 import org.springframework.stereotype.Service;
@@ -59,7 +61,7 @@ public void removeExpiredUsers() {
     @Scheduled(cron = "0 0 1 * * *")
     public void resetExpiredToken() {
         log.info("Resetting expired user tokens");
-        List<User> users = userService.findAllByStateAndExpirationDateBefore(UserState.BLOCKED, LocalDateTime.now(), null);
+        Page<User> users = userService.findAllByStateAndExpirationDateBefore(UserState.BLOCKED, LocalDateTime.now(), null, Pageable.unpaged());
         if (users == null || users.isEmpty()) {
             log.info("There are none expired tokens. Everything is awesome.");
             return;
@@ -69,8 +71,8 @@ public void resetExpiredToken() {
             user.setToken(null);
             user.setExpirationDate(null);
         });
-        users = userService.saveUsers(users.stream().map(u -> (IUser) u).collect(Collectors.toList()));
-        log.info("Reset " + users.size() + " expired user tokens");
+        userService.saveUsers(users.stream().map(u -> (IUser) u).collect(Collectors.toList()));
+        log.info("Reset " + users.getContent().size() + " expired user tokens");
     }
 
     @Override
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java
index d5dda0d26b..fc1ac2661c 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/auth/web/UserController.java
@@ -260,7 +260,7 @@ public ResponseEntity<ResponseMessage> assignNegativeRolesToUser(@PathVariable("
             @ApiResponse(responseCode = "500", description = "Internal server error")
     })
     public ResponseEntity<List<Authority>> getAllAuthorities() {
-        return ResponseEntity.ok(authorityService.findAll());
+        return ResponseEntity.ok(authorityService.findAll(Pageable.unpaged()).stream().toList());
     }
 
     @PreAuthorize("@authorizationService.hasAuthority('ADMIN')")
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/impersonation/service/ImpersonationAuthorizationService.java b/application-engine/src/main/java/com/netgrif/application/engine/impersonation/service/ImpersonationAuthorizationService.java
index 344897081a..9a996e420c 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/impersonation/service/ImpersonationAuthorizationService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/impersonation/service/ImpersonationAuthorizationService.java
@@ -96,7 +96,7 @@ public List<Authority> getAuthorities(List<Case> configs, IUser impersonated) {
             return new ArrayList<>();
         }
         Set<String> authIds = extractSetFromField(configs, "impersonated_authorities");
-        return authorityService.findAllByIds(new ArrayList<>(authIds)).stream()
+        return authorityService.findAllByIds(new ArrayList<>(authIds), Pageable.unpaged()).stream()
                 .filter(configAuth -> impersonated.getAuthorities().stream().anyMatch(userAuth -> userAuth.getStringId().equals(configAuth.getStringId())))
                 .collect(Collectors.toList());
     }
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
index e6f3702ce5..01c79619b2 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
@@ -484,7 +484,8 @@ public Page<PetriNetReference> search(PetriNetSearch criteriaClass, LoggedUser u
             if (criteriaClass.getGroup().size() == 1) {
                 this.addValueCriteria(query, queryTotal, Criteria.where("author.email").is(groupService.getGroupOwnerEmail(criteriaClass.getGroup().get(0))));
             } else {
-                this.addValueCriteria(query, queryTotal, Criteria.where("author.email").in(groupService.getGroupsOwnerEmails(criteriaClass.getGroup())));
+                // TODO: pagination?
+                this.addValueCriteria(query, queryTotal, Criteria.where("author.email").in(groupService.getGroupsOwnerEmails(criteriaClass.getGroup(), Pageable.unpaged())));
             }
         }
         if (criteriaClass.getVersion() != null) {
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java
index 16a16658e3..1d43324aa7 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java
@@ -60,16 +60,17 @@ public void run(ApplicationArguments args) throws Exception {
         if (!createDefaultFilters) return;
         // All cases
         createCaseFilter("All cases", "assignment", FILTER_VISIBILITY_PUBLIC, "", new ArrayList<String>(),
-                Map.of(
+                new HashMap<>(Map.of(
                         "predicateMetadata", new ArrayList<>(),
-                        "searchCategories", new ArrayList<>()),
+                        "searchCategories", new ArrayList<>())
+                ),
                 Map.of(
                         GERMAN_ISO_3166_CODE, "Alle Fälle",
                         SLOVAK_ISO_3166_CODE, "Všetky prípady"));
 
         // My cases
         createCaseFilter("My cases", "assignment_ind", FILTER_VISIBILITY_PUBLIC, "(author:<<me>>)", new ArrayList<String>(),
-                Map.of(
+                new HashMap<>(Map.of(
                         "predicateMetadata", List.of(List.of(Map.of(
                                 "category", "case_author",
                                 "configuration", Map.of("operator", "equals"),
@@ -79,17 +80,18 @@ public void run(ApplicationArguments args) throws Exception {
                                 ))
                         ))),
                         "searchCategories", List.of("case_author")
-                ), Map.of(
+                )),
+                Map.of(
                         GERMAN_ISO_3166_CODE, "Meine Fälle",
                         SLOVAK_ISO_3166_CODE, "Moje prípady"
                 ));
 
         // All tasks
         createTaskFilter("All tasks", "library_add_check", FILTER_VISIBILITY_PUBLIC, "", new ArrayList<String>(),
-                Map.of(
+                new HashMap<>(Map.of(
                         "predicateMetadata", List.of(),
                         "searchCategories", List.of()
-                ),
+                )),
                 Map.of(
                         GERMAN_ISO_3166_CODE, "Alle Aufgaben",
                         SLOVAK_ISO_3166_CODE, "Všetky úlohy"
@@ -97,7 +99,7 @@ public void run(ApplicationArguments args) throws Exception {
 
         // My tasks
         createTaskFilter("My tasks", "account_box", FILTER_VISIBILITY_PUBLIC, "(userId:<<me>>)", new ArrayList<String>(),
-                Map.of(
+                new HashMap<>(Map.of(
                         "predicateMetadata", List.of(List.of(Map.of(
                                 "category", "task_assignee",
                                 "configuration", Map.of("operator", "equals"),
@@ -107,7 +109,8 @@ public void run(ApplicationArguments args) throws Exception {
                                 ))
                         ))),
                         "searchCategories", List.of("task_assignee")
-                ), Map.of(
+                )),
+                Map.of(
                         GERMAN_ISO_3166_CODE, "Meine Aufgaben",
                         SLOVAK_ISO_3166_CODE, "Moje úlohy"
                 ));
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java
index 3aaa7a03b5..1a5560726f 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java
@@ -93,7 +93,7 @@ public void setAllProcessRoles() {
     }
 
     public void setAllAuthorities() {
-        superUser.setAuthorities(Set.copyOf(authorityService.findAll()));
+        superUser.setAuthorities(Set.copyOf(authorityService.findAll(Pageable.unpaged()).stream().toList()));
         superUser = userService.saveUser(superUser, null);
     }
 
diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AuthorityServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AuthorityServiceImpl.java
index 528946c1f4..8d66e5007c 100644
--- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AuthorityServiceImpl.java
+++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/AuthorityServiceImpl.java
@@ -6,6 +6,8 @@
 import lombok.extern.slf4j.Slf4j;
 import org.bson.types.ObjectId;
 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;
 
 import java.util.List;
@@ -23,39 +25,15 @@ public void setAuthorityRepository(AuthorityRepository authorityRepository) {
     }
 
     @Override
-    public List<Authority> findAll() {
-        return authorityRepository.findAll();
+    public Page<Authority> findAll(Pageable pageable) {
+        return authorityRepository.findAll(pageable);
     }
 
     @Override
     @Transactional
-    public Authority getOrCreate(String s) {
-        Optional<Authority> authority = authorityRepository.findById(s);
-        return authority.orElseGet(() -> authorityRepository.save(new AuthorityImpl(s)));
-    }
-
-    @Override
-    @Transactional
-    public Authority getOrCreatePermission(String s) {
-        return getOrCreate(s);
-    }
-
-    @Override
-    @Transactional
-    public Authority getOrCreateRole(String s) {
-        return getOrCreate(s);
-    }
-
-    //TODO: this was never used
-    @Override
-    public List<Authority> getAllPermissions() {
-        return List.of();
-    }
-
-    //TODO: this was never used
-    @Override
-    public List<Authority> getAllRoles() {
-        return List.of();
+    public Authority getOrCreate(String name) {
+        Optional<Authority> authority = authorityRepository.findByName(name);
+        return authority.orElseGet(() -> authorityRepository.save(new AuthorityImpl(name)));
     }
 
     @Override
@@ -64,7 +42,7 @@ public Authority getOne(String s) {
     }
 
     @Override
-    public List<Authority> findAllByIds(List<String> ids) {
-        return authorityRepository.findAllBy_idIn(ids.stream().map(ObjectId::new).collect(Collectors.toList()));
+    public Page<Authority> findAllByIds(List<String> ids, Pageable pageable) {
+        return authorityRepository.findAllBy_idIn(ids.stream().map(ObjectId::new).collect(Collectors.toList()), pageable);
     }
 }
diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
index dcad18d724..86a8377e41 100644
--- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
+++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
@@ -1,5 +1,6 @@
 package com.netgrif.application.engine.auth.service;
 
+import com.netgrif.application.engine.adapter.spring.utils.PageableUtils;
 import com.netgrif.application.engine.auth.config.GroupConfigurationProperties;
 import com.netgrif.application.engine.auth.repository.GroupRepository;
 import com.netgrif.application.engine.objects.auth.domain.Authority;
@@ -75,7 +76,7 @@ public Group findById(String id) {
     }
 
     @Override
-    public Page<Group> findAllByIds(Set<String> ids, Pageable pageable) {
+    public Page<Group> findAllByIds(Collection<String> ids, Pageable pageable) {
         Page<Group> groups = groupRepository.findAllByIdIn(ids, pageable);
         groups.getContent().forEach(this::populateMembers);
         return groups;
@@ -185,13 +186,15 @@ public void populateMembers(Group group) {
     }
 
     @Override
-    public Set<String> getAllCoMembers(IUser user) {
-        Set<Group> userMembershipGroups = groupRepository.findAllByMemberIdsContains(user.getStringId());
+    public Page<String> getAllCoMembers(IUser user, Pageable pageable) {
+        Page<Group> userMembershipGroups = groupRepository.findAllByMemberIdsContains(user.getStringId(), pageable);
         IUser system = userService.getSystem();
-        return userMembershipGroups.stream().map(Group::getMemberIds).flatMap(Set::stream)
-                .filter(id -> !id.equals(user.getStringId()))
-                .filter(id -> !id.equals(system.getStringId()))
-                .collect(Collectors.toSet());
+        return PageableUtils.listToPage(
+                userMembershipGroups.stream().map(Group::getMemberIds).flatMap(Set::stream)
+                        .filter(id -> !id.equals(user.getStringId()) && !id.equals(system.getStringId()))
+                        .collect(Collectors.toList()),
+                userMembershipGroups.getPageable()
+        ) ;
     }
 
     @Override
@@ -219,13 +222,13 @@ public Group assignAuthority(String groupId, String authorityId) {
     }
 
     @Override
-    public List<Group> findByIds(Collection<String> ids) {
-        return groupRepository.findAllById(ids);
+    public Page<Group> findByIds(Collection<String> ids, Pageable pageable) {
+        return groupRepository.findAllByIdIn(ids, pageable);
     }
 
     @Override
-    public Collection<String> getGroupsOwnerEmails(Collection<String> groupIds) {
-        return this.findByIds(groupIds).stream().map(this::getGroupOwnerEmail).toList();
+    public Page<String> getGroupsOwnerEmails(Collection<String> groupIds, Pageable pageable) {
+        return this.findByIds(groupIds, pageable).map(this::getGroupOwnerEmail);
     }
 
     @Override
diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
index 21e1334c01..842ac5a02f 100644
--- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
+++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
@@ -201,7 +201,7 @@ public void removeAllByStateAndExpirationDateBefore(UserState state, LocalDateTi
     }
 
     @Override
-    public List<User> findAllByStateAndExpirationDateBefore(UserState state, LocalDateTime expirationDate, Collection<String> realmIds) {
+    public Page<User> findAllByStateAndExpirationDateBefore(UserState state, LocalDateTime expirationDate, Collection<String> realmIds, Pageable pageable) {
         Set<String> collectionNames = collectionNameProvider.getCollectionNamesForRealms(realmIds);
         return userRepository.findAllByStateAndExpirationDateBefore(state, expirationDate, mongoTemplate, collectionNames);
     }
@@ -301,7 +301,7 @@ public IUser findByEmail(String email, String realmId) {
 
     @Override
     public Page<IUser> findAllCoMembers(com.netgrif.application.engine.objects.auth.domain.LoggedUser loggedUser, Pageable pageable) {
-        Set<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser());
+        Set<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser(), Pageable.unpaged()).stream().collect(Collectors.toSet());
         members.add(loggedUser.getSelfOrImpersonated().getId());
         Set<ObjectId> objMembers = members.stream().map(ObjectId::new).collect(Collectors.toSet());
         Set<String> collectionNames = collectionNameProvider.getCollectionNamesForAllRealm();
@@ -310,7 +310,8 @@ public Page<IUser> findAllCoMembers(com.netgrif.application.engine.objects.auth.
 
     @Override
     public Page<IUser> searchAllCoMembers(String query, com.netgrif.application.engine.objects.auth.domain.LoggedUser loggedUser, Pageable pageable) {
-        Set<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser());
+        // TODO: how to send pageable to group service????
+        Set<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser(), Pageable.unpaged()).stream().collect(Collectors.toSet());
         members.add(loggedUser.getSelfOrImpersonated().getId());
         Set<String> collectionNames = collectionNameProvider.getCollectionNamesForAllRealm();
         return changeType(userRepository.findAll(buildPredicate(members.stream().map(ObjectId::new)
@@ -328,7 +329,7 @@ public Page<IUser> searchAllCoMembers(String query, List<ProcessResourceId> role
         }
 
 
-        Set<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser());
+        Set<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser(), Pageable.unpaged()).stream().collect(Collectors.toSet());
         members.add(loggedUser.getSelfOrImpersonated().getId());
         BooleanExpression predicate = buildPredicate(members.stream().map(ObjectId::new).collect(Collectors.toSet()), query);
         if (!(roleIds == null || roleIds.isEmpty())) {
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/AuthorityRepository.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/AuthorityRepository.java
index 0f3d4916d3..79252267f1 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/AuthorityRepository.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/AuthorityRepository.java
@@ -2,15 +2,18 @@
 
 import com.netgrif.application.engine.objects.auth.domain.Authority;
 import org.bson.types.ObjectId;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 import org.springframework.data.mongodb.repository.MongoRepository;
 import org.springframework.stereotype.Repository;
 import java.util.List;
+import java.util.Optional;
 
 @Repository
 public interface AuthorityRepository extends MongoRepository<Authority, String> {
-    Authority findByName(String name);
+    Optional<Authority> findByName(String name);
 
     List<Authority> findAllByNameStartsWith(String prefix);
 
-    List<Authority> findAllBy_idIn(List<ObjectId> ids);
+    Page<Authority> findAllBy_idIn(List<ObjectId> ids, Pageable pageable);
 }
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java
index 17ec03be0e..ad5150fe87 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java
@@ -7,6 +7,7 @@
 import org.springframework.data.querydsl.QuerydslPredicateExecutor;
 import org.springframework.stereotype.Repository;
 
+import java.util.Collection;
 import java.util.Optional;
 import java.util.Set;
 
@@ -16,9 +17,9 @@ public interface GroupRepository extends MongoRepository<Group, String>, Queryds
 
     Optional<Group> findByIdentifier(String identifier);
 
-    Set<Group> findAllByMemberIdsContains(String memberId);
+    Page<Group> findAllByMemberIdsContains(String memberId, Pageable pageable);
 
-    Page<Group> findAllByIdIn(Set<String> ids, Pageable pageable);
+    Page<Group> findAllByIdIn(Collection<String> ids, Pageable pageable);
 
     Page<Group> findAllByRealmId(String realmId, Pageable pageable);
 }
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java
index f5b39054dd..f885c8a422 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java
@@ -43,14 +43,12 @@ default void deleteAll() {
         throw new UnsupportedOperationException("This method is not supported. Use 'UserRepository.deleteAll(MongoTemplate, Collection<String>)' instead.'");
     }
 
-    default Page<User> findAll(Predicate predicate, Pageable pageable, MongoTemplate mongoTemplate, Collection<String> collectionNames) {
+    default Page<User> findAll(Predicate predicate, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
         Assert.notNull(predicate, "Predicate must not be null");
         Assert.notNull(pageable, "Pageable must not be null");
 
-        return PageableUtils.listToPage(collectionNames.stream().map(collection -> {
-            SpringDataMongodbQuery<com.netgrif.application.engine.adapter.spring.auth.domain.User> query = createQuery(predicate, mongoTemplate, collection);
-            return query.fetch().stream().map(User.class::cast).toList();
-        }).flatMap(List::stream).toList(), pageable);
+        SpringDataMongodbQuery<com.netgrif.application.engine.adapter.spring.auth.domain.User> query = createQuery(predicate, mongoTemplate, collection);
+        return query.fetchPage(pageable).map(User.class::cast);
     }
 
     default Optional<User> findById(ObjectId objectId, MongoTemplate mongoTemplate, String collectionName) {
@@ -79,23 +77,17 @@ default void deleteAll(MongoTemplate mongoTemplate, Collection<String> collectio
         collectionName.forEach(collection -> mongoTemplate.remove(new Query(), collection));
     }
 
-    default Page<User> findDistinctByStateAndProcessRoles__idIn(UserState state, List<ProcessResourceId> roleId, Pageable pageable, MongoTemplate mongoTemplate, Set<String> collectionNames) {
-        Set<User> resultUserSet = collectionNames.stream().map(collectionName ->
-            mongoTemplate.find(
-                Query.query(
-                    Criteria.where("state").is(state)
-                            .and("processRoles._id").in(roleId))
-                .with(pageable),
-                com.netgrif.application.engine.adapter.spring.auth.domain.User.class,
-                collectionName)
-        ).flatMap(List::stream).collect(Collectors.toSet());
-        return new PageImpl<>(resultUserSet.stream().toList(), pageable, resultUserSet.size());
+    default Page<User> findDistinctByStateAndProcessRoles__idIn(UserState state, List<ProcessResourceId> roleId, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
+        Query query = Query.query(
+                Criteria.where("state").is(state)
+                        .and("processRoles._id").in(roleId));
+        return resolveUserPage(pageable, mongoTemplate, collection, query);
     }
 
-    default List<User> findAllByProcessRoles__idIn(List<ProcessResourceId> rolesId, MongoTemplate mongoTemplate, Set<String> collectionNames) {
-        return collectionNames.stream().map(collectionName ->
-           mongoTemplate.find(Query.query(Criteria.where("processRoles._id").in(rolesId)), com.netgrif.application.engine.adapter.spring.auth.domain.User.class, collectionName)
-        ).flatMap(List::stream).map(User.class::cast).toList();
+    default Page<User> findAllByProcessRoles__idIn(List<ProcessResourceId> rolesId, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
+        Query query = Query.query(
+                Criteria.where("processRoles._id").in(rolesId));
+        return resolveUserPage(pageable, mongoTemplate, collection, query);
     }
 
     default void removeAllByStateAndExpirationDateBefore(UserState state, LocalDateTime dateTime, MongoTemplate mongoTemplate, Set<String> collectionNames) {
@@ -104,26 +96,33 @@ default void removeAllByStateAndExpirationDateBefore(UserState state, LocalDateT
         );
     }
 
-    default List<User> findAllByStateAndExpirationDateBefore(UserState state, LocalDateTime dateTime, MongoTemplate mongoTemplate, Set<String> collectionNames) {
-        return collectionNames.stream().map(collectionName ->
-                mongoTemplate.find(Query.query(Criteria.where("state").is(state).and("credentials.token.properties.expirationDate").lt(dateTime)), com.netgrif.application.engine.adapter.spring.auth.domain.User.class, collectionName)
-        ).flatMap(List::stream).map(User.class::cast).toList();
+    default Page<User> findAllByStateAndExpirationDateBefore(UserState state, LocalDateTime dateTime, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
+        Query query = Query.query(
+                Criteria.where("state").is(state)
+                        .and("credentials.token.properties.expirationDate").lt(dateTime));
+        return resolveUserPage(pageable, mongoTemplate, collection, query);
     }
 
-    default Page<User> findAllByIdInAndState(Set<ObjectId> ids, UserState state, Pageable pageable, MongoTemplate mongoTemplate, Set<String> collectionNames) {
-        Set<User> resultUserSet = collectionNames.stream().map(collectionName ->
-            mongoTemplate.find(
-                Query.query(
-                    Criteria.where("id").in(ids)
-                            .and("state").is(state))
-                    .with(pageable),
-                com.netgrif.application.engine.adapter.spring.auth.domain.User.class,
-                collectionName)
-        ).flatMap(List::stream).collect(Collectors.toSet());
-        return new PageImpl<>(resultUserSet.stream().toList(), pageable, resultUserSet.size());
+    default Page<User> findAllByIdInAndState(Set<ObjectId> ids, UserState state, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
+        Query query = Query.query(
+                        Criteria.where("id").in(ids)
+                                .and("state").is(state));
+        return resolveUserPage(pageable, mongoTemplate, collection, query);
     }
 
     private SpringDataMongodbQuery<com.netgrif.application.engine.adapter.spring.auth.domain.User> createQuery(Predicate predicate, MongoTemplate mongoTemplate, String collectionName) {
         return new SpringDataMongodbQuery<>(mongoTemplate, com.netgrif.application.engine.adapter.spring.auth.domain.User.class, collectionName).where(predicate);
     }
+
+    private static PageImpl<User> resolveUserPage(Pageable pageable, MongoTemplate mongoTemplate, String collection, Query query) {
+        List<User> resultUserList = mongoTemplate.find(
+                        query.with(pageable),
+                        com.netgrif.application.engine.adapter.spring.auth.domain.User.class,
+                        collection)
+                .stream()
+                .map(User.class::cast)
+                .collect(Collectors.toList());
+        long total = mongoTemplate.count(query.limit(-1).skip(-1), com.netgrif.application.engine.adapter.spring.auth.domain.User.class);
+        return new PageImpl<>(resultUserList, pageable, total);
+    }
 }
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/AuthorityService.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/AuthorityService.java
index 7080961470..13860f6ca1 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/AuthorityService.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/AuthorityService.java
@@ -1,24 +1,18 @@
 package com.netgrif.application.engine.auth.service;
 
 import com.netgrif.application.engine.objects.auth.domain.Authority;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 
 import java.util.List;
 
 public interface AuthorityService {
 
-    List<Authority> findAll();
+    Page<Authority> findAll(Pageable pageable);
 
     Authority getOrCreate(String name);
 
-    Authority getOrCreatePermission(String name);
-
-    Authority getOrCreateRole(String name);
-
-    List<Authority> getAllPermissions();
-
-    List<Authority> getAllRoles();
-
     Authority getOne(String id);
 
-    List<Authority> findAllByIds(List<String> ids);
+    Page<Authority> findAllByIds(List<String> ids, Pageable pageable);
 }
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java
index 8c89ce45f0..351f853b7c 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java
@@ -26,9 +26,9 @@ public interface GroupService {
 
     Group findById(String id);
 
-    List<Group> findByIds(Collection<String> ids);
+    Page<Group> findByIds(Collection<String> ids, Pageable pageable);
 
-    Page<Group> findAllByIds(Set<String> ids, Pageable pageable);
+    Page<Group> findAllByIds(Collection<String> ids, Pageable pageable);
 
     Page<Group> findAll(Pageable pageable);
 
@@ -48,13 +48,13 @@ public interface GroupService {
 
     void populateMembers(Group group);
 
-    Set<String> getAllCoMembers(IUser user);
+    Page<String> getAllCoMembers(IUser user, Pageable pageable);
 
     Page<Group> findByPredicate(Predicate predicate, Pageable pageable);
 
     Group assignAuthority(String groupId, String authorityId);
 
-    Collection<String> getGroupsOwnerEmails(Collection<String> groupIds);
+    Page<String> getGroupsOwnerEmails(Collection<String> groupIds, Pageable pageable);
 
     String getGroupOwnerEmail(String groupId);
 }
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserService.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserService.java
index 5a8509f4d0..eba9c73bbc 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserService.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserService.java
@@ -58,15 +58,15 @@ public interface UserService {
 
     Page<IUser> findAllCoMembers(com.netgrif.application.engine.objects.auth.domain.LoggedUser loggedUser, Pageable pageable);
 
-    List<IUser> findAllByIds(Collection<String> ids, String realmId);
+    Page<IUser> findAllByIds(Collection<String> ids, String realmId, Pageable pageable);
 
     Page<IUser> findAllActiveByProcessRoles(Set<ProcessResourceId> roleIds, Pageable pageable);
 
     Page<IUser> findAllActiveByProcessRoles(Set<ProcessResourceId> roleIds, Pageable pageable, Collection<String> realmIds);
 
-    List<IUser> findAllByProcessRoles(Set<ProcessResourceId> roleIds, Collection<String> realmIds);
+    Page<IUser> findAllByProcessRoles(Set<ProcessResourceId> roleIds, Collection<String> realmIds, Pageable pageable);
 
-    List<IUser> findAllByProcessRoles(Set<ProcessResourceId> roleIds);
+    Page<IUser> findAllByProcessRoles(Set<ProcessResourceId> roleIds, Pageable pageable);
 
     void addDefaultAuthorities(IUser user);
 
@@ -114,7 +114,7 @@ public interface UserService {
 
    void removeAllByStateAndExpirationDateBefore(UserState state, LocalDateTime expirationDate, Collection<String> realmIds);
 
-    List<User> findAllByStateAndExpirationDateBefore(UserState state, LocalDateTime expirationDate, Collection<String> realmIds);
+    Page<User> findAllByStateAndExpirationDateBefore(UserState state, LocalDateTime expirationDate, Collection<String> realmIds, Pageable pageable);
 
     IUser transformToUser(Author author);
 

From 292dff18bc229e575a7e1457d92e25026cce6d7f Mon Sep 17 00:00:00 2001
From: Jozef Daxner <daxner.dodo@gmail.com>
Date: Wed, 25 Jun 2025 16:33:25 +0200
Subject: [PATCH 2/9] [NAE-2122] Implement Structured and Efficient Pagination
 in gRPC

- Refactor pagination support across repositories and services
- Updated methods to support `Pageable` for paginated data retrieval and processing.
- Replaced bulk-loading calls with paginated queries to improve scalability and performance.
---
 .../engine/importer/service/Importer.java     |   4 +-
 .../config/ProcessBeansConfiguration.java     |  10 +-
 .../repositories/PetriNetRepository.java      |   4 +-
 .../domain/roles/ProcessRoleRepository.java   |  54 ++++---
 .../petrinet/service/PetriNetService.java     |  49 +++---
 .../petrinet/service/ProcessRoleService.java  | 107 ++++++++-----
 .../service/interfaces/IPetriNetService.java  |  10 +-
 .../petrinet/web/PetriNetController.java      |   9 +-
 .../startup/runner/AnonymousRoleRunner.java   |   4 +-
 .../startup/runner/DefaultRoleRunner.java     |   4 +-
 .../startup/runner/FunctionsCacheRunner.java  |   4 +-
 .../startup/runner/SuperCreatorRunner.java    |  24 ++-
 .../workflow/service/CaseSearchService.java   |   4 +-
 .../service/ConfigurableMenuService.java      |   3 +-
 .../service/ProcessRoleServiceTest.java       |  18 ++-
 .../petrinet/service/ProcessRoleService.java  |  14 +-
 .../spring/utils/PaginationProperties.java    |  15 ++
 .../engine/auth/service/GroupServiceImpl.java |   1 +
 .../engine/auth/service/UserServiceImpl.java  | 141 +++++++++---------
 .../auth/repository/UserRepository.java       |  22 ++-
 .../engine/auth/service/UserService.java      |  12 +-
 .../requestbodies/UserSearchRequestBody.java  |   2 +
 22 files changed, 320 insertions(+), 195 deletions(-)
 create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java

diff --git a/application-engine/src/main/java/com/netgrif/application/engine/importer/service/Importer.java b/application-engine/src/main/java/com/netgrif/application/engine/importer/service/Importer.java
index e6cb08bb2d..44db5a9c1e 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/importer/service/Importer.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/importer/service/Importer.java
@@ -1061,7 +1061,7 @@ protected void createRole(Role importRole) {
         if (shouldInitializeRole(importRole)) {
             role = initRole(importRole);
         } else {
-            role = new ArrayList<>(processRoleService.findAllByImportId(ProcessRole.GLOBAL + importRole.getId())).get(0);
+            role = processRoleService.findByImportId(ProcessRole.GLOBAL + importRole.getId());
         }
         role.set_id(new ProcessResourceId(new ObjectId(net.getStringId())));
 
@@ -1071,7 +1071,7 @@ protected void createRole(Role importRole) {
 
     protected boolean shouldInitializeRole(Role importRole) {
         return importRole.isGlobal() == null || !importRole.isGlobal() ||
-                (importRole.isGlobal() && processRoleService.findAllByImportId(ProcessRole.GLOBAL + importRole.getId()).isEmpty());
+                (importRole.isGlobal() && processRoleService.findByImportId(ProcessRole.GLOBAL + importRole.getId()) == null);
     }
 
     protected ProcessRole initRole(Role importRole) {
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/config/ProcessBeansConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/config/ProcessBeansConfiguration.java
index 185495800b..bd7f49907b 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/config/ProcessBeansConfiguration.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/config/ProcessBeansConfiguration.java
@@ -1,7 +1,9 @@
 package com.netgrif.application.engine.petrinet.config;
 
 import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService;
+import com.netgrif.application.engine.adapter.spring.utils.PaginationProperties;
 import com.netgrif.application.engine.auth.service.GroupService;
+import com.netgrif.application.engine.auth.service.RealmService;
 import com.netgrif.application.engine.petrinet.domain.dataset.logic.action.runner.RoleActionsRunner;
 import com.netgrif.application.engine.petrinet.domain.repositories.PetriNetRepository;
 import com.netgrif.application.engine.petrinet.domain.roles.ProcessRoleRepository;
@@ -27,7 +29,9 @@ public ProcessRoleService processRoleService(ProcessRoleRepository processRoleRe
                                                  @Lazy PetriNetService petriNetService,
                                                  @Lazy UserService userService,
                                                  ISecurityContextService securityContextService,
-                                                 @Lazy GroupService groupService
+                                                 @Lazy GroupService groupService,
+                                                 @Lazy RealmService realmService,
+                                                 @Lazy PaginationProperties paginationProperties
                                                  ) {
         return new com.netgrif.application.engine.petrinet.service.ProcessRoleService(
                 processRoleRepository,
@@ -37,7 +41,9 @@ public ProcessRoleService processRoleService(ProcessRoleRepository processRoleRe
                 petriNetService,
                 userService,
                 securityContextService,
-                groupService
+                groupService,
+                realmService,
+                paginationProperties
         );
     }
 
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java
index 3a133ee4bc..d4c81e0d46 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java
@@ -16,15 +16,13 @@ public interface PetriNetRepository extends MongoRepository<PetriNet, String>, Q
 
     PetriNet findByImportId(String id);
 
-    List<PetriNet> findAllByIdentifier(String identifier);
-
     PetriNet findByIdentifierAndVersion(String identifier, Version version);
 
     Page<PetriNet> findByIdentifier(String identifier, Pageable pageable);
 
     Page<PetriNet> findByIdentifierIn(List<String> identifier, Pageable pageable);
 
-    List<PetriNet> findAllByVersion(Version version);
+    Page<PetriNet> findAllByVersion(Version version, Pageable pageable);
 
     List<PetriNet> findAllByUriNodeId(String uri);
 
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java
index 0ba7793e71..5c2bb21fa7 100755
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java
@@ -3,10 +3,13 @@
 import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole;
 import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId;
 import org.bson.types.ObjectId;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 import org.springframework.data.mongodb.repository.MongoRepository;
 import org.springframework.data.mongodb.repository.Query;
 import org.springframework.data.querydsl.QuerydslPredicateExecutor;
 import org.springframework.stereotype.Repository;
+import org.springframework.data.util.Pair;
 
 import java.util.*;
 import java.util.stream.Collectors;
@@ -14,30 +17,46 @@
 @Repository
 public interface ProcessRoleRepository extends MongoRepository<ProcessRole, String>, QuerydslPredicateExecutor<ProcessRole> {
 
-    ProcessRole findByImportId(String importId);
+    Optional<ProcessRole> findByImportId(String importId);
 
-    Set<ProcessRole> findAllByProcessId(String netId);
+    Page<ProcessRole> findAllByProcessId(String netId, Pageable pageable);
 
-    Set<ProcessRole> findAllByImportIdIn(Set<String> importIds);
+    List<ProcessRole> findAllByImportIdIn(Set<String> importIds);
 
-    Set<ProcessRole> findAllByName_DefaultValue(String name);
+    Page<ProcessRole> findAllByName_DefaultValue(String name, Pageable pageable);
 
-    Set<ProcessRole> findAllByImportId(String importId);
+    Page<ProcessRole> findAllByImportId(String importId, Pageable pageable);
 
-    Set<ProcessRole> findAllByGlobalIsTrue();
+    Page<ProcessRole> findAllByGlobalIsTrue(Pageable pageable);
 
     @Query("{ '_id.objectId': ?0 }")
     Optional<ProcessRole> findByIdObjectId(ObjectId objectId);
 
-    void deleteAllBy_idIn(Collection<ProcessResourceId> ids);
+    @Query("{ '_id.objectId': { $in: ?0 } }")
+    List<ProcessRole> findByObjectIds(Collection<ObjectId> objectIds);
 
+    void deleteAllBy_idIn(Collection<ProcessResourceId> ids);
 
-        //TODO: It goes one at a time... make bulk
-    default Set<ProcessRole> findAllById(Set<String> ids) {
-        return ids.stream()
-                .map(this::findByCompositeId)
-                .flatMap(Optional::stream)
-                .collect(Collectors.toSet());
+    default Set<ProcessRole> findAllByIdsSet(Collection<String> ids) {
+        Map<Boolean, List<String>> partitionedIds = ids.stream()
+                .collect(Collectors.partitioningBy(id -> id.contains(ProcessResourceId.ID_SEPARATOR)));
+
+        List<ObjectId> forObjectIds = partitionedIds.get(false).stream()
+                .map(ObjectId::new)
+                .collect(Collectors.toList());
+
+        List<ObjectId> forNetworkObjectIds = new ArrayList<>();
+        List<String> forNetworkNetworkIds = new ArrayList<>();
+        partitionedIds.get(true).forEach(id -> {
+                    String[] parts = id.split(ProcessResourceId.ID_SEPARATOR);
+                    forNetworkNetworkIds.add(parts[0]);
+                    forNetworkObjectIds.add(new ObjectId(parts[1]));
+                });
+
+        List<ProcessRole> processRoles = new ArrayList<>();
+        processRoles.addAll(findByObjectIds(forObjectIds));
+        processRoles.addAll(findByNetworkIdsAndObjectIds(forNetworkNetworkIds, forNetworkObjectIds));
+        return new HashSet<>(processRoles);
     }
 
     default Optional<ProcessRole> findByCompositeId(String compositeId) {
@@ -51,10 +70,9 @@ default Optional<ProcessRole> findByCompositeId(String compositeId) {
         }
     }
 
-    default List<ProcessRole> findAllByCompositeId(Collection<String> compositeId) {
-        return compositeId.stream().map(this::findByCompositeId).map(optional -> optional.orElse(null)).filter(Objects::nonNull).collect(Collectors.toList());
-    }
-
     @Query("{ '_id.shortProcessId': ?0, '_id.objectId': ?1 }")
     Optional<ProcessRole> findByNetworkIdAndObjectId(String networkId, ObjectId objectId);
-}
+
+    @Query("{ $or: [ { '_id.shortProcessId': ?0, '_id.objectId': ?1 } ] }")
+    List<ProcessRole> findByNetworkIdsAndObjectIds(Collection<String> networkIds, Collection<ObjectId> objectIds);
+}
\ No newline at end of file
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
index 01c79619b2..b93d360de3 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
@@ -295,8 +295,8 @@ public PetriNet getPetriNet(String identifier, Version version) {
     }
 
     @Override
-    public List<PetriNet> getByIdentifier(String identifier) {
-        List<PetriNet> nets = repository.findAllByIdentifier(identifier);
+    public Page<PetriNet> getByIdentifier(String identifier, Pageable pageable) {
+        Page<PetriNet> nets = repository.findByIdentifier(identifier, pageable);
         nets.forEach(PetriNet::initializeArcs);
         return nets;
     }
@@ -360,8 +360,8 @@ public PetriNetImportReference getNetFromCase(String caseId) {
     }
 
     @Override
-    public List<PetriNet> getAll() {
-        List<PetriNet> nets = repository.findAll();
+    public Page<PetriNet> getAll(Pageable pageable) {
+        Page<PetriNet> nets = repository.findAll(pageable);
         nets.forEach(PetriNet::initializeArcs);
         return nets;
     }
@@ -380,35 +380,50 @@ public FileSystemResource getFile(String netId, String title) {
     }
 
     @Override
-    public List<PetriNetReference> getReferences(LoggedUser user, Locale locale) {
-        return getAll().stream().map(net -> transformToReference(net, locale)).collect(Collectors.toList());
+    public Page<PetriNetReference> getReferences(LoggedUser user, Locale locale, Pageable pageable) {
+        return getAll(pageable).map(net -> transformToReference(net, locale));
     }
 
     @Override
-    public List<PetriNetReference> getReferencesByIdentifier(String identifier, LoggedUser user, Locale locale) {
-        return getByIdentifier(identifier).stream().map(net -> transformToReference(net, locale)).collect(Collectors.toList());
+    public Page<PetriNetReference> getReferencesByIdentifier(String identifier, LoggedUser user, Locale locale, Pageable pageable) {
+        return getByIdentifier(identifier, pageable).map(net -> transformToReference(net, locale));
     }
 
     @Override
-    public List<PetriNetReference> getReferencesByVersion(Version version, LoggedUser user, Locale locale) {
-        List<PetriNetReference> references;
-
+    public Page<PetriNetReference> getReferencesByVersion(Version version, LoggedUser user, Locale locale, Pageable pageable) {
+        Page<PetriNetReference> references;
         if (version == null) {
             GroupOperation groupByIdentifier = Aggregation.group("identifier").max("version").as("version");
-            Aggregation aggregation = Aggregation.newAggregation(groupByIdentifier);
-            AggregationResults<Document> results = mongoTemplate.aggregate(aggregation, "petriNet", Document.class);
-            references = results.getMappedResults().stream()
+            Aggregation aggregation = Aggregation.newAggregation(
+                    groupByIdentifier,
+                    Aggregation.sort(pageable.getSort()),
+                    Aggregation.skip((long) pageable.getPageNumber() * pageable.getPageSize()),
+                    Aggregation.limit(pageable.getPageSize())
+            );
+            List<Document> results = mongoTemplate.aggregate(aggregation, "petriNet", Document.class).getMappedResults();
+            List<PetriNetReference> referenceList = results.stream()
                     .map(doc -> {
                         Document versionDoc = doc.get("version", Document.class);
                         Version refVersion = new Version(versionDoc.getLong("major"), versionDoc.getLong("minor"), versionDoc.getLong("patch"));
                         return getReference(doc.getString("_id"), refVersion, user, locale);
                     })
                     .collect(Collectors.toList());
+            Aggregation countAggregation = Aggregation.newAggregation(
+                    groupByIdentifier,
+                    Aggregation.count().as("total")
+            );
+            AggregationResults<Document> countResults = mongoTemplate.aggregate(
+                    countAggregation,
+                    "your_collection_name",
+                    Document.class
+            );
+            long total = countResults.getUniqueMappedResult() != null
+                    ? countResults.getUniqueMappedResult().getLong("total")
+                    : 0L;
+            references = new PageImpl<>(referenceList, pageable, total);
         } else {
-            references = repository.findAllByVersion(version).stream()
-                    .map(net -> transformToReference(net, locale)).collect(Collectors.toList());
+            references = repository.findAllByVersion(version, pageable).map(net -> transformToReference(net, locale));
         }
-
         return references;
     }
 
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java
index 77b7a583ef..b43fba519b 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java
@@ -1,10 +1,13 @@
 package com.netgrif.application.engine.petrinet.service;
 
+import com.netgrif.application.engine.adapter.spring.utils.PaginationProperties;
 import com.netgrif.application.engine.auth.service.GroupService;
+import com.netgrif.application.engine.auth.service.RealmService;
 import com.netgrif.application.engine.objects.auth.domain.Group;
 import com.netgrif.application.engine.objects.auth.domain.IUser;
 import com.netgrif.application.engine.objects.auth.domain.LoggedUser;
 import com.netgrif.application.engine.auth.service.UserService;
+import com.netgrif.application.engine.objects.auth.domain.Realm;
 import com.netgrif.application.engine.objects.event.events.user.UserRoleChangeEvent;
 import com.netgrif.application.engine.objects.importer.model.EventPhaseType;
 import com.netgrif.application.engine.objects.petrinet.domain.PetriNet;
@@ -24,7 +27,9 @@
 import org.slf4j.LoggerFactory;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.annotation.Lazy;
-import org.springframework.stereotype.Service;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
 
 import java.util.*;
 import java.util.stream.Collectors;
@@ -42,6 +47,8 @@ public class ProcessRoleService implements com.netgrif.application.engine.adapte
     private final IPetriNetService petriNetService;
     private final ISecurityContextService securityContextService;
     private final GroupService groupService;
+    private final RealmService realmService;
+    private final PaginationProperties paginationProperties;
 
     private ProcessRole defaultRole;
     private ProcessRole anonymousRole;
@@ -49,7 +56,8 @@ public class ProcessRoleService implements com.netgrif.application.engine.adapte
     public ProcessRoleService(ProcessRoleRepository processRoleRepository,
                               PetriNetRepository netRepository,
                               ApplicationEventPublisher publisher, RoleActionsRunner roleActionsRunner,
-                              @Lazy IPetriNetService petriNetService, @Lazy UserService userService, ISecurityContextService securityContextService, @Lazy GroupService groupService) {
+                              @Lazy IPetriNetService petriNetService, @Lazy UserService userService, ISecurityContextService securityContextService, @Lazy GroupService groupService,
+                              @Lazy RealmService realmService, @Lazy PaginationProperties paginationProperties) {
         this.processRoleRepository = processRoleRepository;
         this.netRepository = netRepository;
         this.publisher = publisher;
@@ -58,6 +66,8 @@ public ProcessRoleService(ProcessRoleRepository processRoleRepository,
         this.userService = userService;
         this.securityContextService = securityContextService;
         this.groupService = groupService;
+        this.realmService = realmService;
+        this.paginationProperties = paginationProperties;
     }
 
     @Override
@@ -66,13 +76,13 @@ public ProcessRole save(ProcessRole processRole) {
     }
 
     @Override
-    public List<ProcessRole> getAll() {
-        return processRoleRepository.findAll();
+    public Page<ProcessRole> getAll(Pageable pageable) {
+        return processRoleRepository.findAll(pageable);
     }
 
     @Override
-    public List<ProcessRole> findAllByNetId(String s) {
-        return new ArrayList<>(processRoleRepository.findAllByProcessId(s));
+    public Page<ProcessRole> findAllByNetId(String s, Pageable pageable) {
+        return processRoleRepository.findAllByProcessId(s, pageable);
     }
 
     @Override
@@ -88,7 +98,7 @@ public void delete(String s) {
 
     @Override
     public void deleteAll(Collection<String> collection) {
-        List<ProcessRole> processRoles = processRoleRepository.findAllByCompositeId(collection);
+        Set<ProcessRole> processRoles = processRoleRepository.findAllByIdsSet(collection);
         processRoleRepository.deleteAll(processRoles);
     }
 
@@ -204,17 +214,17 @@ protected String getProcessIdFromFirstRole(Set<ProcessRole> newRoles) {
 
     @Override
     public ProcessRole getDefaultRole() {
-        return processRoleRepository.findByImportId(ProcessRole.DEFAULT_ROLE);
+        return processRoleRepository.findByImportId(ProcessRole.DEFAULT_ROLE).orElse(null);
     }
 
     @Override
     public ProcessRole getAnonymousRole() {
-        return processRoleRepository.findByImportId(ProcessRole.ANONYMOUS_ROLE);
+        return processRoleRepository.findByImportId(ProcessRole.ANONYMOUS_ROLE).orElse(null);
     }
 
     @Override
     public Collection<ProcessRole> findAllByIds(Collection<ProcessResourceId> collection) {
-        return processRoleRepository.findAllByCompositeId(collection.stream().map(ProcessResourceId::getStringId).collect(Collectors.toList()));
+        return processRoleRepository.findAllByIdsSet(collection.stream().map(ProcessResourceId::getStringId).collect(Collectors.toList()));
     }
 
     @Override
@@ -225,7 +235,7 @@ public ProcessRole findById(ProcessResourceId processResourceId) {
     @Override
     public List<ProcessRole> saveAll(Iterable<ProcessRole> entities) {
         return StreamSupport.stream(entities.spliterator(), false).map(processRole -> {
-            if (!processRole.isGlobal() || processRoleRepository.findAllByImportId(processRole.getImportId()).isEmpty()) {
+            if (!processRole.isGlobal() || processRoleRepository.findByImportId(processRole.getImportId()) == null) {
                 return processRoleRepository.save(processRole);
             }
             return null;
@@ -234,7 +244,7 @@ public List<ProcessRole> saveAll(Iterable<ProcessRole> entities) {
 
     @Override
     public Set<ProcessRole> findByIds(Set<String> ids) {
-        return new HashSet<>(processRoleRepository.findAllById(ids));
+        return processRoleRepository.findAllByIdsSet(ids);
     }
 
     private Set<ProcessRole> updateRequestedRoles(IUser user, Set<ProcessRole> rolesNewToUser, Set<ProcessRole> rolesRemovedFromUser) {
@@ -349,13 +359,13 @@ private void removeOldAndAssignNewRolesToUser(IUser user, Set<ProcessRole> reque
     }
 
     @Override
-    public List<ProcessRole> findAll() {
-        return processRoleRepository.findAll();
+    public Page<ProcessRole> findAll(Pageable pageable) {
+        return processRoleRepository.findAll(pageable);
     }
 
     @Override
-    public Set<ProcessRole> findAllGlobalRoles() {
-        return processRoleRepository.findAllByGlobalIsTrue();
+    public Page<ProcessRole> findAllGlobalRoles(Pageable pageable) {
+        return processRoleRepository.findAllByGlobalIsTrue(pageable);
     }
 
     @Override
@@ -373,10 +383,10 @@ private List<ProcessRole> findAll(PetriNet net) {
     @Override
     public ProcessRole defaultRole() {
         if (defaultRole == null) {
-            Set<ProcessRole> roles = processRoleRepository.findAllByName_DefaultValue(ProcessRole.DEFAULT_ROLE);
+            Page<ProcessRole> roles = processRoleRepository.findAllByImportId(ProcessRole.DEFAULT_ROLE, Pageable.ofSize(2));
             if (roles.isEmpty())
                 throw new IllegalStateException("No default process role has been found!");
-            if (roles.size() > 1)
+            if (roles.getTotalElements() > 1)
                 throw new IllegalStateException("More than 1 default process role exists!");
             defaultRole = roles.stream().findFirst().orElse(null);
         }
@@ -386,10 +396,10 @@ public ProcessRole defaultRole() {
     @Override
     public ProcessRole anonymousRole() {
         if (anonymousRole == null) {
-            Set<ProcessRole> roles = processRoleRepository.findAllByImportId(ProcessRole.ANONYMOUS_ROLE);
+            Page<ProcessRole> roles = processRoleRepository.findAllByImportId(ProcessRole.ANONYMOUS_ROLE, Pageable.ofSize(2));
             if (roles.isEmpty())
                 throw new IllegalStateException("No anonymous process role has been found!");
-            if (roles.size() > 1)
+            if (roles.getTotalElements() > 1)
                 throw new IllegalStateException("More than 1 anonymous process role exists!");
             anonymousRole = roles.stream().findFirst().orElse(null);
         }
@@ -399,22 +409,22 @@ public ProcessRole anonymousRole() {
     /**
      * @param importId id from a process of a role
      * @return a process role object
-     * @deprecated use {@link ProcessRoleService#findAllByImportId(String)} instead
+     * @deprecated use {@link ProcessRoleService#findAllByImportId(String, Pageable)} instead
      */
     @Deprecated(forRemoval = true, since = "6.2.0")
     @Override
     public ProcessRole findByImportId(String importId) {
-        return processRoleRepository.findAllByImportId(importId).stream().findFirst().orElse(null);
+        return processRoleRepository.findByImportId(importId).orElse(null);
     }
 
     @Override
-    public Set<ProcessRole> findAllByImportId(String importId) {
-        return processRoleRepository.findAllByImportId(importId);
+    public Page<ProcessRole> findAllByImportId(String importId, Pageable pageable) {
+        return processRoleRepository.findAllByImportId(importId, pageable);
     }
 
     @Override
-    public Set<ProcessRole> findAllByDefaultName(String name) {
-        return processRoleRepository.findAllByName_DefaultValue(name);
+    public Page<ProcessRole> findAllByDefaultName(String name, Pageable pageable) {
+        return processRoleRepository.findAllByName_DefaultValue(name, pageable);
     }
 
     @Override
@@ -429,20 +439,37 @@ public void deleteRolesOfNet(PetriNet net, LoggedUser loggedUser) {
         List<ProcessResourceId> deletedRoleIds = this.findAll(net.getStringId()).stream().filter(processRole -> processRole.getProcessId() != null).map(ProcessRole::get_id).collect(Collectors.toList());
         Set<String> deletedRoleStringIds = deletedRoleIds.stream().map(ProcessResourceId::toString).collect(Collectors.toSet());
 
-        List<IUser> usersWithRemovedRoles = this.userService.findAllByProcessRoles(new HashSet<>(deletedRoleIds));
-        for (IUser user : usersWithRemovedRoles) {
-            log.info("[" + net.getStringId() + "]: Removing deleted roles of Petri net " + net.getIdentifier() + " version " + net.getVersion().toString() + " from user " + user.getFullName() + " with id " + user.getStringId());
-
-            if (user.getProcessRoles().isEmpty()) {
-                continue;
-            }
-
-            Set<ProcessResourceId> newRoles = user.getProcessRoles().stream()
-                    .filter(role -> !deletedRoleStringIds.contains(role.getStringId()))
-                    .map(ProcessRole::get_id)
-                    .collect(Collectors.toSet());
-            this.assignRolesToUser(user, newRoles, loggedUser);
-        }
+        Pageable realmPageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
+        Page<Realm> realms;
+        do {
+            realms = realmService.getSmallRealm(realmPageable);
+
+            realms.forEach(realm -> {
+                Pageable usersPageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
+                Page<IUser> users;
+                do {
+                    users = this.userService.findAllByProcessRoles(new HashSet<>(deletedRoleIds), realm.getId(), usersPageable);
+
+                    for (IUser user : users) {
+                        log.info("[" + net.getStringId() + "]: Removing deleted roles of Petri net " + net.getIdentifier() + " version " + net.getVersion().toString() + " from user " + user.getFullName() + " with id " + user.getStringId());
+
+                        if (user.getProcessRoles().isEmpty()) {
+                            continue;
+                        }
+
+                        Set<ProcessResourceId> newRoles = user.getProcessRoles().stream()
+                                .filter(role -> !deletedRoleStringIds.contains(role.getStringId()))
+                                .map(ProcessRole::get_id)
+                                .collect(Collectors.toSet());
+                        this.assignRolesToUser(user, newRoles, loggedUser);
+                    }
+
+                    usersPageable = usersPageable.next();
+                } while (users.hasNext());
+            });
+
+            realmPageable = realmPageable.next();
+        } while (realms.hasNext());
 
         log.info("[" + net.getStringId() + "]: Deleting all roles of Petri net " + net.getIdentifier() + " version " + net.getVersion().toString());
         this.processRoleRepository.deleteAllBy_idIn(deletedRoleIds);
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
index c11ff71c84..930d8c61a5 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
@@ -61,7 +61,7 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti
 
     PetriNet getPetriNet(String identifier, Version version);
 
-    List<PetriNet> getByIdentifier(String identifier);
+    Page<PetriNet> getByIdentifier(String identifier, Pageable pageable);
 
     List<PetriNet> findAllByUriNodeId(String uriNodeId);
 
@@ -69,15 +69,15 @@ static DataFieldReference transformToReference(PetriNet net, Transition transiti
 
     PetriNet getNewestVersionByIdentifier(String identifier);
 
-    List<PetriNet> getAll();
+    Page<PetriNet> getAll(Pageable pageable);
 
     FileSystemResource getFile(String netId, String title);
 
-    List<PetriNetReference> getReferences(LoggedUser user, Locale locale);
+    Page<PetriNetReference> getReferences(LoggedUser user, Locale locale, Pageable pageable);
 
-    List<PetriNetReference> getReferencesByIdentifier(String identifier, LoggedUser user, Locale locale);
+    Page<PetriNetReference> getReferencesByIdentifier(String identifier, LoggedUser user, Locale locale, Pageable pageable);
 
-    List<PetriNetReference> getReferencesByVersion(Version version, LoggedUser user, Locale locale);
+    Page<PetriNetReference> getReferencesByVersion(Version version, LoggedUser user, Locale locale, Pageable pageable);
 
     List<PetriNetReference> getReferencesByUsersProcessRoles(LoggedUser user, Locale locale);
 
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java
index 46e23c8a33..847278f34d 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/PetriNetController.java
@@ -127,13 +127,14 @@ public EntityModel<EventOutcomeWithMessage> importPetriNet(
     public ResponseEntity<Page<PetriNetReference>> getAll(@RequestParam(value = "indentifier", required = false) String identifier, @RequestParam(value = "version", required = false) String version, Pageable pageable, Authentication auth, Locale locale) {
         LoggedUser user = (LoggedUser) auth.getPrincipal();
         if (identifier != null && version == null) {
-            return ResponseEntity.ok(new PageImpl<>(service.getReferencesByIdentifier(identifier, user, locale), pageable, 0));
+            return ResponseEntity.ok(service.getReferencesByIdentifier(identifier, user, locale, pageable));
         } else if (identifier == null && version != null) {
-            return ResponseEntity.ok(new PageImpl<>(service.getReferencesByVersion(converter.convert(version), user, locale), pageable, 0));
+            return ResponseEntity.ok(service.getReferencesByVersion(converter.convert(version), user, locale, pageable));
         } else if (identifier != null) {
-            return ResponseEntity.ok(new PageImpl<>(Collections.singletonList(service.getReference(identifier, converter.convert(version), user, locale)), pageable, 0));
+            PetriNetReference reference = service.getReference(identifier, converter.convert(version), user, locale);
+            return ResponseEntity.ok(new PageImpl<>(Collections.singletonList(reference), pageable, reference.getIdentifier().isEmpty() ? 0 : 1));
         } else {
-            return ResponseEntity.ok(new PageImpl<>(service.getReferences(user, locale), pageable, 0));
+            return ResponseEntity.ok(service.getReferences(user, locale, pageable));
         }
     }
 
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/AnonymousRoleRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/AnonymousRoleRunner.java
index c6be15a8dc..fe462f806e 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/AnonymousRoleRunner.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/AnonymousRoleRunner.java
@@ -27,8 +27,8 @@ public class AnonymousRoleRunner implements ApplicationEngineStartupRunner {
     @Override
     public void run(ApplicationArguments args) throws Exception {
         log.info("Creating anonymous process role");
-        Set<ProcessRole> role = processRoleService.findAllByImportId(ProcessRole.ANONYMOUS_ROLE);
-        if (role != null && !role.isEmpty()) {
+        ProcessRole role = processRoleService.findByImportId(ProcessRole.ANONYMOUS_ROLE);
+        if (role != null) {
             log.info("Anonymous role already exists");
             return;
         }
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultRoleRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultRoleRunner.java
index b9e1cd5add..95909eb6fb 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultRoleRunner.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultRoleRunner.java
@@ -29,8 +29,8 @@ public class DefaultRoleRunner implements ApplicationEngineStartupRunner {
     @Override
     public void run(ApplicationArguments args) throws Exception {
         log.info("Creating default process role");
-        Set<ProcessRole> role = (Set<ProcessRole>) processRoleService.findAllByDefaultName(ProcessRole.DEFAULT_ROLE);
-        if (role != null && !role.isEmpty()) {
+        ProcessRole role = processRoleService.findByImportId(ProcessRole.DEFAULT_ROLE);
+        if (role != null) {
             log.info("Default role already exists");
             return;
         }
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/FunctionsCacheRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/FunctionsCacheRunner.java
index f1c6a60a75..358b90e1c4 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/FunctionsCacheRunner.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/FunctionsCacheRunner.java
@@ -7,6 +7,7 @@
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.boot.ApplicationArguments;
+import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Component;
 
 @Slf4j
@@ -21,7 +22,8 @@ public class FunctionsCacheRunner implements ApplicationEngineStartupRunner {
     @Override
     public void run(ApplicationArguments args) throws Exception {
         log.info("Namespace function caching started");
-        petriNetService.getAll().forEach(cacheService::cachePetriNetFunctions);
+        // TODO JOFO: unpaged necessary?
+        petriNetService.getAll(Pageable.unpaged()).forEach(cacheService::cachePetriNetFunctions);
     }
 
 }
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java
index 1a5560726f..ad3ec6d87c 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java
@@ -1,10 +1,12 @@
 package com.netgrif.application.engine.startup.runner;
 
+import com.netgrif.application.engine.adapter.spring.utils.PaginationProperties;
 import com.netgrif.application.engine.auth.service.AuthorityService;
 import com.netgrif.application.engine.objects.auth.domain.*;
 import com.netgrif.application.engine.auth.service.UserService;
 import com.netgrif.application.engine.auth.service.GroupService;
 import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService;
+import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole;
 import com.netgrif.application.engine.startup.ApplicationEngineStartupRunner;
 import com.netgrif.application.engine.startup.annotation.RunnerOrder;
 import com.netgrif.application.engine.objects.auth.domain.enums.UserState;
@@ -14,6 +16,8 @@
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.boot.ApplicationArguments;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Component;
 
@@ -38,6 +42,7 @@ public class SuperCreatorRunner implements ApplicationEngineStartupRunner {
     private final UserService userService;
     private final GroupService groupService;
     private final ProcessRoleService processRoleService;
+    private final PaginationProperties paginationProperties;
 
     @Getter
     private IUser superUser;
@@ -65,7 +70,15 @@ private IUser createSuperUser() {
             user.setPassword(superAdminPassword);
             user.setState(UserState.ACTIVE);
             user.setAuthorities(authorities);
-            user.setProcessRoles(new HashSet<>(processRoleService.findAll()));
+
+            Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
+            Page<ProcessRole> processRoles;
+            do {
+                processRoles = processRoleService.findAll(pageable);
+                user.getProcessRoles().addAll(processRoles.getContent());
+                pageable = pageable.next();
+            } while (processRoles.hasNext());
+
             this.superUser = userService.createUser(user, null);
             log.info("Super user created");
         } else {
@@ -88,7 +101,14 @@ public void setAllGroups() {
     }
 
     public void setAllProcessRoles() {
-        superUser.setProcessRoles(Set.copyOf(processRoleService.findAll()));
+        Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
+        Page<ProcessRole> processRoles;
+        do {
+            processRoles = processRoleService.findAll(pageable);
+            superUser.getProcessRoles().addAll(processRoles.getContent());
+            pageable = pageable.next();
+        } while (processRoles.hasNext());
+
         superUser = userService.saveUser(superUser, null);
     }
 
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java
index d9d4abfe4a..a22d8222da 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java
@@ -23,6 +23,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 
 import java.time.LocalDate;
@@ -267,7 +268,8 @@ public Predicate fullText(Object petriNetQuery, String searchPhrase) {
 
         List<PetriNet> petriNets;
         if (processes.isEmpty()) {
-            petriNets = petriNetService.getAll();
+            // TODO JOFO: unpaged necessary?
+            petriNets = petriNetService.getAll(Pageable.unpaged()).getContent();
         } else {
             petriNets = processes.stream().map(process -> petriNetService.getNewestVersionByIdentifier(process)).collect(Collectors.toList());
         }
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/ConfigurableMenuService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/ConfigurableMenuService.java
index 6ac3a4e7ca..2de3e2e1db 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/ConfigurableMenuService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/ConfigurableMenuService.java
@@ -18,6 +18,7 @@
 import com.netgrif.application.engine.utils.FullPageRequest;
 import com.netgrif.application.engine.workflow.service.interfaces.IConfigurableMenuService;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Pageable;
 import org.springframework.stereotype.Service;
 
 import java.util.*;
@@ -94,7 +95,7 @@ public Map<String, I18nString> getNetsByAuthorAsMapOptions(IUser author, Locale
     public Map<String, I18nString> getAvailableRolesFromNet(EnumerationMapField processField, MultichoiceMapField permittedRoles, MultichoiceMapField bannedRoles) {
 
         if (GLOBAL_ROLE.equals(processField.getValue())) {
-            return processRoleService.findAllGlobalRoles().stream()
+            return processRoleService.findAllGlobalRoles(Pageable.unpaged()).stream()
                     .filter(role -> !permittedRoles.getOptions().containsKey(role.getImportId() + ":" + GLOBAL_ROLE)
                             && !bannedRoles.getOptions().containsKey(role.getImportId() + ":" + GLOBAL_ROLE))
                     .collect(Collectors.toMap(
diff --git a/application-engine/src/test/java/com/netgrif/application/engine/petrinet/service/ProcessRoleServiceTest.java b/application-engine/src/test/java/com/netgrif/application/engine/petrinet/service/ProcessRoleServiceTest.java
index 6ed4609530..9a93297b72 100644
--- a/application-engine/src/test/java/com/netgrif/application/engine/petrinet/service/ProcessRoleServiceTest.java
+++ b/application-engine/src/test/java/com/netgrif/application/engine/petrinet/service/ProcessRoleServiceTest.java
@@ -13,6 +13,8 @@
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.junit.jupiter.SpringExtension;
 
@@ -53,14 +55,14 @@ public void before() {
 
     @Test
     void shouldFindAllProcessRoles() throws IOException, MissingPetriNetMetaDataException {
-        List<ProcessRole> roles = processRoleService.findAll();
-        int originalRoles = roles.size();
+        Page<ProcessRole> roles = processRoleService.findAll(Pageable.unpaged());
+        long originalRoles = roles.getTotalElements();
         petriNetService.importPetriNet(new FileInputStream("src/test/resources/all_data.xml"), VersionType.MAJOR, superCreator.getLoggedSuper());
         petriNetService.importPetriNet(new FileInputStream("src/test/resources/role_all_data.xml"), VersionType.MAJOR, superCreator.getLoggedSuper());
-        roles = processRoleService.findAll();
+        roles = processRoleService.findAll(Pageable.unpaged());
         assertNotNull(roles);
         assertFalse(roles.isEmpty());
-        assertEquals(originalRoles + 3, roles.size()); // + 2 roles from all_data and 1 role from role_all_data
+        assertEquals(originalRoles + 3, roles.getTotalElements()); // + 2 roles from all_data and 1 role from role_all_data
     }
 
     @Test
@@ -93,20 +95,20 @@ void shouldGetAnonymousRole() {
     @Test
     void shouldFindAllProcessRolesByImportId() throws IOException, MissingPetriNetMetaDataException {
         petriNetService.importPetriNet(new FileInputStream("src/test/resources/all_data.xml"), VersionType.MAJOR, superCreator.getLoggedSuper());
-        Set<ProcessRole> roles = processRoleService.findAllByImportId(ROLE_IMPORT_ID);
+        Page<ProcessRole> roles = processRoleService.findAllByImportId(ROLE_IMPORT_ID, Pageable.unpaged());
         assertNotNull(roles);
         assertFalse(roles.isEmpty());
-        assertEquals(1, roles.size());
+        assertEquals(1, roles.getTotalElements());
         assertEquals(ROLE_IMPORT_ID, roles.stream().findFirst().get().getImportId());
     }
 
     @Test
     void shouldFindAllProcessRolesByName() throws IOException, MissingPetriNetMetaDataException {
         petriNetService.importPetriNet(new FileInputStream("src/test/resources/all_data.xml"), VersionType.MAJOR, superCreator.getLoggedSuper());
-        Collection<ProcessRole> roles = processRoleService.findAllByDefaultName("Process role");
+        Page<ProcessRole> roles = processRoleService.findAllByDefaultName("Process role", Pageable.unpaged());
         assertNotNull(roles);
         assertFalse(roles.isEmpty());
-        assertEquals(1, roles.size());
+        assertEquals(1, roles.getTotalElements());
         assertEquals(ROLE_IMPORT_ID, roles.stream().findFirst().get().getImportId());
     }
 }
diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/service/ProcessRoleService.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/service/ProcessRoleService.java
index 5c6a448428..9736a0edba 100644
--- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/service/ProcessRoleService.java
+++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/service/ProcessRoleService.java
@@ -6,14 +6,16 @@
 import com.netgrif.application.engine.objects.auth.domain.LoggedUser;
 import com.netgrif.application.engine.objects.petrinet.domain.PetriNet;
 import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
 
 import java.util.*;
 
 public interface ProcessRoleService {
     ProcessRole save(ProcessRole processRole);
     List<ProcessRole> saveAll(Iterable<ProcessRole> processRoles);
-    List<ProcessRole> getAll();
-    List<ProcessRole> findAllByNetId(String netId);
+    Page<ProcessRole> getAll(Pageable pageable);
+    Page<ProcessRole> findAllByNetId(String netId, Pageable pageable);
     Optional<ProcessRole> get(ProcessResourceId id);
     void delete(String id);
     void deleteAll(Collection<String> ids);
@@ -28,13 +30,13 @@ public interface ProcessRoleService {
     ProcessRole getAnonymousRole();
     Collection<ProcessRole> findAllByIds(Collection<ProcessResourceId> roleIds);
     ProcessRole findById(ProcessResourceId id);
-    Collection<ProcessRole> findAllByDefaultName(String name);
-    Set<ProcessRole> findAllByImportId(String importId);
+    Page<ProcessRole> findAllByDefaultName(String name, Pageable pageable);
+    Page<ProcessRole> findAllByImportId(String importId, Pageable pageable);
     ProcessRole findById(String id);
     Set<ProcessRole> findByIds(Set<String> ids);
     ProcessRole findByImportId(String importId);
-    List<ProcessRole> findAll();
-    Set<ProcessRole> findAllGlobalRoles();
+    Page<ProcessRole> findAll(Pageable pageable);
+    Page<ProcessRole> findAllGlobalRoles(Pageable pageable);
     List<ProcessRole> findAll(String netId);
     ProcessRole defaultRole();
     ProcessRole anonymousRole();
diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java
new file mode 100644
index 0000000000..b39bb79382
--- /dev/null
+++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java
@@ -0,0 +1,15 @@
+package com.netgrif.application.engine.adapter.spring.utils;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "netgrif.pagination")
+public class PaginationProperties {
+
+    private int backendPageSize = 100;
+
+    private int frontendPageSize = 20;
+}
diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
index 86a8377e41..5fb939a42f 100644
--- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
+++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
@@ -187,6 +187,7 @@ public void populateMembers(Group group) {
 
     @Override
     public Page<String> getAllCoMembers(IUser user, Pageable pageable) {
+        // TODO JOFO: rework for pagination
         Page<Group> userMembershipGroups = groupRepository.findAllByMemberIdsContains(user.getStringId(), pageable);
         IUser system = userService.getSystem();
         return PageableUtils.listToPage(
diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
index 842ac5a02f..f76d369aa3 100644
--- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
+++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
@@ -2,6 +2,7 @@
 
 import com.netgrif.application.engine.adapter.spring.auth.domain.LoggedUserImpl;
 import com.netgrif.application.engine.adapter.spring.petrinet.service.ProcessRoleService;
+import com.netgrif.application.engine.adapter.spring.utils.PaginationProperties;
 import com.netgrif.application.engine.adapter.spring.workflow.service.FilterImportExportService;
 import com.netgrif.application.engine.auth.config.GroupConfigurationProperties;
 import com.netgrif.application.engine.auth.provider.CollectionNameProvider;
@@ -19,11 +20,10 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.data.domain.Page;
-import org.springframework.data.domain.PageImpl;
+import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.query.Query;
-import org.springframework.data.support.PageableExecutionUtils;
 import org.springframework.security.core.Authentication;
 import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.security.crypto.password.PasswordEncoder;
@@ -56,6 +56,8 @@ public class UserServiceImpl implements UserService {
 
     private IUser systemUser;
 
+    private PaginationProperties paginationProperties;
+
     @Autowired
     public void setUserRepository(UserRepository userRepository) {
         this.userRepository = userRepository;
@@ -103,6 +105,12 @@ public void setGroupService(GroupService groupService) {
         this.groupService = groupService;
     }
 
+    @Lazy
+    @Autowired
+    public void setPaginationProperties(PaginationProperties paginationProperties) {
+        this.paginationProperties = paginationProperties;
+    }
+
     @Override
     public IUser saveUser(IUser user, String realmId) {
         user.setRealmId(realmId);
@@ -140,13 +148,9 @@ public Optional<IUser> findUserByUsername(String username, String realmId) {
     public Page<IUser> findAllUsers(String realmName, Pageable pageable) {
         log.trace("Retrieving all users in realm [{}]", realmName);
         String collectionName = collectionNameProvider.getCollectionNameForRealm(realmName);
-        Page<IUser> page = PageableExecutionUtils.getPage(
-                mongoTemplate.findAll(IUser.class, collectionName),
-                pageable,
-                () -> mongoTemplate.count(new Query().with(pageable), IUser.class, collectionName)
-        );
-        log.debug("Found [{}] users in realm [{}]", page.getContent().size(), realmName);
-        return page;
+        Page<User> users = userRepository.findAllByQuery(new Query(), pageable, mongoTemplate, collectionName);
+        log.debug("Found [{}] users in realm [{}]", users.getContent().size(), realmName);
+        return changeType(users);
     }
 
     @Override
@@ -201,9 +205,9 @@ public void removeAllByStateAndExpirationDateBefore(UserState state, LocalDateTi
     }
 
     @Override
-    public Page<User> findAllByStateAndExpirationDateBefore(UserState state, LocalDateTime expirationDate, Collection<String> realmIds, Pageable pageable) {
-        Set<String> collectionNames = collectionNameProvider.getCollectionNamesForRealms(realmIds);
-        return userRepository.findAllByStateAndExpirationDateBefore(state, expirationDate, mongoTemplate, collectionNames);
+    public Page<User> findAllByStateAndExpirationDateBefore(UserState state, LocalDateTime expirationDate, String realmId, Pageable pageable) {
+        String collection = collectionNameProvider.getCollectionNameForRealm(realmId);
+        return userRepository.findAllByStateAndExpirationDateBefore(state, expirationDate, pageable, mongoTemplate, collection);
     }
 
     @Override
@@ -246,7 +250,15 @@ public void addAllRolesToAdminByUsername(String username) {
             throw new IllegalArgumentException("Admin user with username [%s] cannot be found.".formatted(username));
         }
         IUser user = userOptional.get();
-        user.setProcessRoles(new HashSet<>(processRoleService.findAll()));
+
+        Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
+        Page<ProcessRole> processRoles;
+        do {
+            processRoles = processRoleService.findAll(pageable);
+            user.getProcessRoles().addAll(processRoles.getContent());
+            pageable = pageable.next();
+        } while (processRoles.hasNext());
+
         saveUser(user, user.getRealmId());
     }
 
@@ -301,26 +313,28 @@ public IUser findByEmail(String email, String realmId) {
 
     @Override
     public Page<IUser> findAllCoMembers(com.netgrif.application.engine.objects.auth.domain.LoggedUser loggedUser, Pageable pageable) {
+        // TODO JOFO: rework for pagination
         Set<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser(), Pageable.unpaged()).stream().collect(Collectors.toSet());
         members.add(loggedUser.getSelfOrImpersonated().getId());
         Set<ObjectId> objMembers = members.stream().map(ObjectId::new).collect(Collectors.toSet());
-        Set<String> collectionNames = collectionNameProvider.getCollectionNamesForAllRealm();
-        return changeType(userRepository.findAllByIdInAndState(objMembers, UserState.ACTIVE, pageable, mongoTemplate, collectionNames), pageable);
+        String collectionName = collectionNameProvider.getCollectionNameForRealm(loggedUser.getRealmId());
+        return changeType(userRepository.findAllByIdInAndState(objMembers, UserState.ACTIVE, pageable, mongoTemplate, collectionName));
     }
 
     @Override
     public Page<IUser> searchAllCoMembers(String query, com.netgrif.application.engine.objects.auth.domain.LoggedUser loggedUser, Pageable pageable) {
-        // TODO: how to send pageable to group service????
-        Set<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser(), Pageable.unpaged()).stream().collect(Collectors.toSet());
-        members.add(loggedUser.getSelfOrImpersonated().getId());
-        Set<String> collectionNames = collectionNameProvider.getCollectionNamesForAllRealm();
-        return changeType(userRepository.findAll(buildPredicate(members.stream().map(ObjectId::new)
-                .collect(Collectors.toSet()), query), pageable, mongoTemplate, collectionNames), pageable);
+        // TODO JOFO: rework for pagination
+        Page<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser(), Pageable.unpaged());
+        members.getContent().add(loggedUser.getSelfOrImpersonated().getId());
+        String collectionName = collectionNameProvider.getCollectionNameForRealm(loggedUser.getRealmId());
+        return changeType(userRepository.findAllByQuery(buildPredicate(members.stream().map(ObjectId::new)
+                .collect(Collectors.toSet()), query), pageable, mongoTemplate, collectionName));
 
     }
 
     @Override
     public Page<IUser> searchAllCoMembers(String query, List<ProcessResourceId> roleIds, List<ProcessResourceId> negateRoleIds, com.netgrif.application.engine.objects.auth.domain.LoggedUser loggedUser, Pageable pageable) {
+        // TODO JOFO: rework for pagination!!!
         if ((roleIds == null || roleIds.isEmpty()) && (negateRoleIds == null || negateRoleIds.isEmpty()))
             return searchAllCoMembers(query, loggedUser, pageable);
 
@@ -329,61 +343,42 @@ public Page<IUser> searchAllCoMembers(String query, List<ProcessResourceId> role
         }
 
 
-        Set<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser(), Pageable.unpaged()).stream().collect(Collectors.toSet());
-        members.add(loggedUser.getSelfOrImpersonated().getId());
+        Page<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser(), Pageable.unpaged());
+        members.getContent().add(loggedUser.getSelfOrImpersonated().getId());
         BooleanExpression predicate = buildPredicate(members.stream().map(ObjectId::new).collect(Collectors.toSet()), query);
         if (!(roleIds == null || roleIds.isEmpty())) {
             predicate = predicate.and(QUser.user.processRoles.any()._id.in(roleIds));
         }
         predicate = predicate.and(QUser.user.processRoles.any()._id.in(negateRoleIds).not());
-        Set<String> collectionNames = collectionNameProvider.getCollectionNamesForAllRealm();
-        Page<User> users = userRepository.findAll(predicate, pageable, mongoTemplate, collectionNames);
+        String collection = collectionNameProvider.getCollectionNameForRealm(loggedUser.getRealmId());
+        Page<User> users = userRepository.findAllByQuery(predicate, pageable, mongoTemplate, collection);
 
-        return changeType(users, pageable);
+        return changeType(users);
     }
 
     @Override
-    public List<IUser> findAllByIds(Collection<String> ids, String realmId) {
+    public Page<IUser> findAllByIds(Collection<String> ids, String realmId, Pageable pageable) {
         log.debug("Finding users by collection of IDs [{}]", ids);
-        return ids.stream().map(id -> findById(id, realmId)).toList();
-    }
-
-    @Override
-    public Page<IUser> findAllActiveByProcessRoles(Set<ProcessResourceId> roleIds, Pageable pageable) {
-        return this.findAllActiveByProcessRoles(roleIds, pageable, List.of(getLoggedUser().getRealmId()));
-    }
-
-    @Override
-    public Page<IUser> findAllActiveByProcessRoles(Set<ProcessResourceId> roleIds, Pageable pageable, Collection<String> realmIds) {
-        Set<String> collectionNames = collectionNameProvider.getCollectionNamesForRealms(realmIds);
-        Page<User> users = userRepository.findDistinctByStateAndProcessRoles__idIn(
-                UserState.ACTIVE,
-                new ArrayList<>(roleIds),
-                pageable,
-                mongoTemplate,
-                collectionNames
-        );
-        return changeType(users, pageable);
+        String collection = collectionNameProvider.getCollectionNameForRealm(realmId);
+        Page<User> users = userRepository.findAllByIds(ids.stream().map(ObjectId::new).toList(), pageable, mongoTemplate, collection);
+        return changeType(users);
     }
 
     @Override
-    public List<IUser> findAllByProcessRoles(Set<ProcessResourceId> roleIds, Collection<String> realmIds) {
-        Set<String> collectionNames = collectionNameProvider.getCollectionNamesForRealms(realmIds);
-        return searchUsersByRoleIds(roleIds, collectionNames);
+    public Page<IUser> findAllActiveByProcessRoles(Set<ProcessResourceId> roleIds, Pageable pageable, String realmId) {
+        String collection = collectionNameProvider.getCollectionNameForRealm(realmId);
+        Page<User> users = userRepository.findDistinctByStateAndProcessRoles__idIn( UserState.ACTIVE, roleIds, pageable, mongoTemplate, collection);
+        return changeType(users);
     }
 
     @Override
-    public List<IUser> findAllByProcessRoles(Set<ProcessResourceId> roleIds) {
-        Set<String> collectionNames = collectionNameProvider.getCollectionNamesForAllRealm();
-        return searchUsersByRoleIds(roleIds, collectionNames);
+    public Page<IUser> findAllByProcessRoles(Set<ProcessResourceId> roleIds, String realmId, Pageable pageable) {
+        String collectionName = collectionNameProvider.getCollectionNameForRealm(realmId);
+        return searchUsersByRoleIds(roleIds, collectionName, pageable);
     }
 
-    protected List<IUser> searchUsersByRoleIds(Set<ProcessResourceId> roleIds, Set<String> collectionNames) {
-        List<User> users = userRepository.findAllByProcessRoles__idIn(
-                new ArrayList<>(roleIds),
-                mongoTemplate,
-                collectionNames
-        );
+    protected Page<IUser> searchUsersByRoleIds(Set<ProcessResourceId> roleIds, String collectionName, Pageable pageable) {
+        Page<User> users = userRepository.findAllByProcessRoles__idIn(roleIds, pageable, mongoTemplate, collectionName);
         return changeType(users);
     }
 
@@ -427,7 +422,14 @@ public IUser getSystem() {
         if (systemUser == null) {
             systemUser = createSystemUser();
         }
-        systemUser.setProcessRoles(new HashSet<>(processRoleService.getAll()));
+
+        Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
+        Page<ProcessRole> processRoles;
+        do {
+            processRoles = processRoleService.findAll(pageable);
+            systemUser.getProcessRoles().addAll(processRoles.getContent());
+            pageable = pageable.next();
+        } while (processRoles.hasNext());
         return systemUser;
     }
 
@@ -497,9 +499,16 @@ public IUser removeNegativeProcessRole(IUser user, String roleId) {
     @Override
     public void removeRoleOfDeletedPetriNet(PetriNet petriNet, Collection<String> realmIds) {
         Set<String> collectionNames = collectionNameProvider.getCollectionNamesForRealms(realmIds);
-        List<IUser> users = findAllByProcessRoles(petriNet.getRoles().values().stream().map(ProcessRole::get_id).collect(Collectors.toSet()), collectionNames);
-        users.forEach(u -> {
-            petriNet.getRoles().forEach((k, role) -> removeRole(u, role.get_id()));
+        collectionNames.forEach(collection -> {
+            Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
+            Page<IUser> users;
+            do {
+                users = findAllByProcessRoles(petriNet.getRoles().values().stream().map(ProcessRole::get_id).collect(Collectors.toSet()), collection, pageable);
+                users.forEach(u -> {
+                    petriNet.getRoles().forEach((k, role) -> removeRole(u, role.get_id()));
+                });
+                pageable = pageable.next();
+            } while (users.hasNext());
         });
     }
 
@@ -617,12 +626,8 @@ protected void setDisablePassword(User user) {
         log.debug("Password N/A set for user [{}]", user.getUsername());
     }
 
-    private <T> Page<IUser> changeType(Page<T> users, Pageable pageable) {
-        return new PageImpl<>(changeType(users.getContent()), pageable, users.getTotalElements());
-    }
-
-    private <T> List<IUser> changeType(List<T> users) {
-        return users.stream().map(IUser.class::cast).collect(Collectors.toList());
+    private <T> Page<IUser> changeType(Page<T> users) {
+        return users.map(IUser.class::cast);
     }
 
     private BooleanExpression buildPredicate(Set<ObjectId> members, String query) {
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java
index f885c8a422..3f557f0240 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java
@@ -1,6 +1,5 @@
 package com.netgrif.application.engine.auth.repository;
 
-import com.netgrif.application.engine.adapter.spring.utils.PageableUtils;
 import com.netgrif.application.engine.objects.auth.domain.User;
 import com.netgrif.application.engine.objects.auth.domain.enums.UserState;
 import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId;
@@ -43,7 +42,7 @@ default void deleteAll() {
         throw new UnsupportedOperationException("This method is not supported. Use 'UserRepository.deleteAll(MongoTemplate, Collection<String>)' instead.'");
     }
 
-    default Page<User> findAll(Predicate predicate, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
+    default Page<User> findAllByQuery(Predicate predicate, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
         Assert.notNull(predicate, "Predicate must not be null");
         Assert.notNull(pageable, "Pageable must not be null");
 
@@ -77,14 +76,21 @@ default void deleteAll(MongoTemplate mongoTemplate, Collection<String> collectio
         collectionName.forEach(collection -> mongoTemplate.remove(new Query(), collection));
     }
 
-    default Page<User> findDistinctByStateAndProcessRoles__idIn(UserState state, List<ProcessResourceId> roleId, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
+    default Page<User> findAllByQuery(Query query, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
+        if (query == null) {
+            query = new Query();
+        }
+        return resolveUserPage(pageable, mongoTemplate, collection, query);
+    }
+
+    default Page<User> findDistinctByStateAndProcessRoles__idIn(UserState state, Collection<ProcessResourceId> roleId, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
         Query query = Query.query(
                 Criteria.where("state").is(state)
                         .and("processRoles._id").in(roleId));
         return resolveUserPage(pageable, mongoTemplate, collection, query);
     }
 
-    default Page<User> findAllByProcessRoles__idIn(List<ProcessResourceId> rolesId, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
+    default Page<User> findAllByProcessRoles__idIn(Collection<ProcessResourceId> rolesId, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
         Query query = Query.query(
                 Criteria.where("processRoles._id").in(rolesId));
         return resolveUserPage(pageable, mongoTemplate, collection, query);
@@ -103,7 +109,13 @@ default Page<User> findAllByStateAndExpirationDateBefore(UserState state, LocalD
         return resolveUserPage(pageable, mongoTemplate, collection, query);
     }
 
-    default Page<User> findAllByIdInAndState(Set<ObjectId> ids, UserState state, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
+    default Page<User> findAllByIds(Collection<ObjectId> ids, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
+        Query query = Query.query(
+                Criteria.where("id").in(ids));
+        return resolveUserPage(pageable, mongoTemplate, collection, query);
+    }
+
+    default Page<User> findAllByIdInAndState(Collection<ObjectId> ids, UserState state, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
         Query query = Query.query(
                         Criteria.where("id").in(ids)
                                 .and("state").is(state));
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserService.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserService.java
index eba9c73bbc..02e0924fcb 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserService.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/UserService.java
@@ -60,13 +60,9 @@ public interface UserService {
 
     Page<IUser> findAllByIds(Collection<String> ids, String realmId, Pageable pageable);
 
-    Page<IUser> findAllActiveByProcessRoles(Set<ProcessResourceId> roleIds, Pageable pageable);
+    Page<IUser> findAllActiveByProcessRoles(Set<ProcessResourceId> roleIds, Pageable pageable, String realmId);
 
-    Page<IUser> findAllActiveByProcessRoles(Set<ProcessResourceId> roleIds, Pageable pageable, Collection<String> realmIds);
-
-    Page<IUser> findAllByProcessRoles(Set<ProcessResourceId> roleIds, Collection<String> realmIds, Pageable pageable);
-
-    Page<IUser> findAllByProcessRoles(Set<ProcessResourceId> roleIds, Pageable pageable);
+    Page<IUser> findAllByProcessRoles(Set<ProcessResourceId> roleIds, String realmId, Pageable pageable);
 
     void addDefaultAuthorities(IUser user);
 
@@ -112,9 +108,9 @@ public interface UserService {
 
     LoggedUserImpl transformToLoggedUser(IUser user);
 
-   void removeAllByStateAndExpirationDateBefore(UserState state, LocalDateTime expirationDate, Collection<String> realmIds);
+    void removeAllByStateAndExpirationDateBefore(UserState state, LocalDateTime expirationDate, Collection<String> realmIds);
 
-    Page<User> findAllByStateAndExpirationDateBefore(UserState state, LocalDateTime expirationDate, Collection<String> realmIds, Pageable pageable);
+    Page<User> findAllByStateAndExpirationDateBefore(UserState state, LocalDateTime expirationDate, String realmIds, Pageable pageable);
 
     IUser transformToUser(Author author);
 
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/requestbodies/UserSearchRequestBody.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/requestbodies/UserSearchRequestBody.java
index 1716dfdac2..f24856fb4e 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/requestbodies/UserSearchRequestBody.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/requestbodies/UserSearchRequestBody.java
@@ -7,6 +7,8 @@
 @Data
 public class UserSearchRequestBody {
 
+    private String realmId;
+
     private String fulltext;
 
     private List<String> roles;

From 26ed9dbdee1ded748ad0eaecbaf8b3d0ea35a57b Mon Sep 17 00:00:00 2001
From: Jozef Daxner <daxner.dodo@gmail.com>
Date: Wed, 2 Jul 2025 12:41:58 +0200
Subject: [PATCH 3/9] [NAE-2122] Implement Structured and Efficient Pagination
 in gRPC

- Refactor co-member search logic for better pagination support
- Replaced outdated co-member retrieval methods with a more efficient approach using predicates for filtering.
- Removed unused methods and redundant code from GroupService and GroupServiceImpl, simplifying the logic.
---
 .../engine/auth/service/GroupServiceImpl.java | 15 ------
 .../engine/auth/service/UserServiceImpl.java  | 51 +++++++++----------
 .../engine/auth/service/GroupService.java     |  2 -
 3 files changed, 23 insertions(+), 45 deletions(-)

diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
index 5fb939a42f..efdba39a8b 100644
--- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
+++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
@@ -1,6 +1,5 @@
 package com.netgrif.application.engine.auth.service;
 
-import com.netgrif.application.engine.adapter.spring.utils.PageableUtils;
 import com.netgrif.application.engine.auth.config.GroupConfigurationProperties;
 import com.netgrif.application.engine.auth.repository.GroupRepository;
 import com.netgrif.application.engine.objects.auth.domain.Authority;
@@ -16,7 +15,6 @@
 import org.springframework.data.domain.Pageable;
 
 import java.util.*;
-import java.util.stream.Collectors;
 
 @Slf4j
 @Getter
@@ -185,19 +183,6 @@ public void populateMembers(Group group) {
         });
     }
 
-    @Override
-    public Page<String> getAllCoMembers(IUser user, Pageable pageable) {
-        // TODO JOFO: rework for pagination
-        Page<Group> userMembershipGroups = groupRepository.findAllByMemberIdsContains(user.getStringId(), pageable);
-        IUser system = userService.getSystem();
-        return PageableUtils.listToPage(
-                userMembershipGroups.stream().map(Group::getMemberIds).flatMap(Set::stream)
-                        .filter(id -> !id.equals(user.getStringId()) && !id.equals(system.getStringId()))
-                        .collect(Collectors.toList()),
-                userMembershipGroups.getPageable()
-        ) ;
-    }
-
     @Override
     public Page<Group> findByPredicate(Predicate predicate, Pageable pageable) {
         return groupRepository.findAll(predicate, pageable);
diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
index f76d369aa3..63dfa7bcb2 100644
--- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
+++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
@@ -20,6 +20,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageImpl;
 import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.mongodb.core.MongoTemplate;
@@ -313,46 +314,35 @@ public IUser findByEmail(String email, String realmId) {
 
     @Override
     public Page<IUser> findAllCoMembers(com.netgrif.application.engine.objects.auth.domain.LoggedUser loggedUser, Pageable pageable) {
-        // TODO JOFO: rework for pagination
-        Set<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser(), Pageable.unpaged()).stream().collect(Collectors.toSet());
-        members.add(loggedUser.getSelfOrImpersonated().getId());
-        Set<ObjectId> objMembers = members.stream().map(ObjectId::new).collect(Collectors.toSet());
-        String collectionName = collectionNameProvider.getCollectionNameForRealm(loggedUser.getRealmId());
-        return changeType(userRepository.findAllByIdInAndState(objMembers, UserState.ACTIVE, pageable, mongoTemplate, collectionName));
+        return this.searchAllCoMembers(null, loggedUser, pageable);
     }
 
     @Override
     public Page<IUser> searchAllCoMembers(String query, com.netgrif.application.engine.objects.auth.domain.LoggedUser loggedUser, Pageable pageable) {
-        // TODO JOFO: rework for pagination
-        Page<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser(), Pageable.unpaged());
-        members.getContent().add(loggedUser.getSelfOrImpersonated().getId());
+        IUser user = this.findById(loggedUser.getSelfOrImpersonated().getId(), loggedUser.getSelfOrImpersonated().getRealmId());
+        BooleanExpression predicate = buildPredicate(user, query);
         String collectionName = collectionNameProvider.getCollectionNameForRealm(loggedUser.getRealmId());
-        return changeType(userRepository.findAllByQuery(buildPredicate(members.stream().map(ObjectId::new)
-                .collect(Collectors.toSet()), query), pageable, mongoTemplate, collectionName));
-
+        Page<User> users = userRepository.findAllByQuery(predicate, pageable, mongoTemplate, collectionName);
+        return changeType(users);
     }
 
     @Override
     public Page<IUser> searchAllCoMembers(String query, List<ProcessResourceId> roleIds, List<ProcessResourceId> negateRoleIds, com.netgrif.application.engine.objects.auth.domain.LoggedUser loggedUser, Pageable pageable) {
-        // TODO JOFO: rework for pagination!!!
-        if ((roleIds == null || roleIds.isEmpty()) && (negateRoleIds == null || negateRoleIds.isEmpty()))
+        if ((roleIds == null || roleIds.isEmpty()) && (negateRoleIds == null || negateRoleIds.isEmpty())) {
             return searchAllCoMembers(query, loggedUser, pageable);
-
-        if (negateRoleIds == null) {
-            negateRoleIds = new ArrayList<>();
         }
 
-
-        Page<String> members = groupService.getAllCoMembers(loggedUser.getSelfOrImpersonated().transformToUser(), Pageable.unpaged());
-        members.getContent().add(loggedUser.getSelfOrImpersonated().getId());
-        BooleanExpression predicate = buildPredicate(members.stream().map(ObjectId::new).collect(Collectors.toSet()), query);
-        if (!(roleIds == null || roleIds.isEmpty())) {
+        IUser user = this.findById(loggedUser.getSelfOrImpersonated().getId(), loggedUser.getSelfOrImpersonated().getRealmId());
+        BooleanExpression predicate = buildPredicate(user, query);
+        if (roleIds != null && !roleIds.isEmpty()) {
             predicate = predicate.and(QUser.user.processRoles.any()._id.in(roleIds));
         }
-        predicate = predicate.and(QUser.user.processRoles.any()._id.in(negateRoleIds).not());
-        String collection = collectionNameProvider.getCollectionNameForRealm(loggedUser.getRealmId());
-        Page<User> users = userRepository.findAllByQuery(predicate, pageable, mongoTemplate, collection);
+        if (negateRoleIds != null && !negateRoleIds.isEmpty()) {
+            predicate = predicate.and(QUser.user.processRoles.any()._id.in(negateRoleIds).not());
+        }
 
+        String collectionName = collectionNameProvider.getCollectionNameForRealm(loggedUser.getRealmId());
+        Page<User> users = userRepository.findAllByQuery(predicate, pageable, mongoTemplate, collectionName);
         return changeType(users);
     }
 
@@ -367,7 +357,7 @@ public Page<IUser> findAllByIds(Collection<String> ids, String realmId, Pageable
     @Override
     public Page<IUser> findAllActiveByProcessRoles(Set<ProcessResourceId> roleIds, Pageable pageable, String realmId) {
         String collection = collectionNameProvider.getCollectionNameForRealm(realmId);
-        Page<User> users = userRepository.findDistinctByStateAndProcessRoles__idIn( UserState.ACTIVE, roleIds, pageable, mongoTemplate, collection);
+        Page<User> users = userRepository.findDistinctByStateAndProcessRoles__idIn(UserState.ACTIVE, roleIds, pageable, mongoTemplate, collection);
         return changeType(users);
     }
 
@@ -630,10 +620,15 @@ private <T> Page<IUser> changeType(Page<T> users) {
         return users.map(IUser.class::cast);
     }
 
-    private BooleanExpression buildPredicate(Set<ObjectId> members, String query) {
+    private BooleanExpression buildPredicate(IUser user, String query) {
+        IUser system = this.getSystem();
         BooleanExpression predicate = QUser.user
-                .id.in(members)
+                .groupIds.any().in(user.getGroupIds())
+                .and(QUser.user.id.ne(new ObjectId(system.getStringId())))
                 .and(QUser.user.state.eq(UserState.ACTIVE));
+        if (query == null) {
+            return predicate;
+        }
         for (String word : query.split(" ")) {
             predicate = predicate
                     .andAnyOf(QUser.user.email.containsIgnoreCase(word),
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java
index 351f853b7c..3bbbe61a11 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/GroupService.java
@@ -48,8 +48,6 @@ public interface GroupService {
 
     void populateMembers(Group group);
 
-    Page<String> getAllCoMembers(IUser user, Pageable pageable);
-
     Page<Group> findByPredicate(Predicate predicate, Pageable pageable);
 
     Group assignAuthority(String groupId, String authorityId);

From 25a21db366aeb5be8060f82692790ab03126f8f3 Mon Sep 17 00:00:00 2001
From: Jozef Daxner <daxner.dodo@gmail.com>
Date: Wed, 2 Jul 2025 12:56:51 +0200
Subject: [PATCH 4/9] [NAE-2122] Implement Structured and Efficient Pagination
 in gRPC

- Fixed pagination response for WorkflowController where pageSize and pageNumber were flipped
---
 .../engine/workflow/web/WorkflowController.java        | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java
index 61a9b9c9e4..25c83ca385 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java
@@ -102,7 +102,7 @@ public PagedModel<CaseResource> getAll(Pageable pageable, PagedResourcesAssemble
                 .getAll(pageable, assembler)).withRel("all");
         PagedModel<CaseResource> resources = assembler.toModel(cases, new CaseResourceAssembler(), selfLink);
         ResourceLinkAssembler.addLinks(resources, Case.class, selfLink.getRel().toString());
-        return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageNumber(), pageable.getPageSize(), cases.getTotalElements()));
+        return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements()));
     }
 
     @Operation(summary = "Generic case search with QueryDSL predicate", security = {@SecurityRequirement(name = "BasicAuth")})
@@ -113,7 +113,7 @@ public PagedModel<CaseResource> search2(@QuerydslPredicate(root = Case.class) Pr
                 .search2(predicate, pageable, assembler)).withRel("search2");
         PagedModel<CaseResource> resources = assembler.toModel(cases, new CaseResourceAssembler(), selfLink);
         ResourceLinkAssembler.addLinks(resources, Case.class, selfLink.getRel().toString());
-        return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageNumber(), pageable.getPageSize(), cases.getTotalElements()));
+        return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements()));
     }
 
     @Operation(summary = "Generic case search on Elasticsearch database", security = {@SecurityRequirement(name = "BasicAuth")})
@@ -126,7 +126,7 @@ public PagedModel<CaseResource> search(@RequestBody SingleCaseSearchRequestAsLis
 
         PagedModel<CaseResource> resources = assembler.toModel(cases, new CaseResourceAssembler(), selfLink);
         ResourceLinkAssembler.addLinks(resources, ElasticCase.class, selfLink.getRel().toString());
-        return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageNumber(), pageable.getPageSize(), cases.getTotalElements()));
+        return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements()));
     }
 
     @Operation(summary = "Generic case search on Mongo database", security = {@SecurityRequirement(name = "BasicAuth")})
@@ -137,7 +137,7 @@ public PagedModel<CaseResource> searchMongo(@RequestBody Map<String, Object> sea
                 .searchMongo(searchBody, pageable, auth, assembler, locale)).withRel("search");
         PagedModel<CaseResource> resources = assembler.toModel(cases, new CaseResourceAssembler(), selfLink);
         ResourceLinkAssembler.addLinks(resources, Case.class, selfLink.getRel().toString());
-        return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageNumber(), pageable.getPageSize(), cases.getTotalElements()));
+        return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements()));
     }
 
 
@@ -165,7 +165,7 @@ public PagedModel<CaseResource> findAllByAuthor(@PathVariable("id") String autho
                 .findAllByAuthor(authorId, petriNet, assembler, pageable)).withRel("author");
         PagedModel<CaseResource> resources = assembler.toModel(cases, new CaseResourceAssembler(), selfLink);
         ResourceLinkAssembler.addLinks(resources, Case.class, selfLink.getRel().toString());
-        return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageNumber(), pageable.getPageSize(), cases.getTotalElements()));
+        return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements()));
     }
 
     @PreAuthorize("@authorizationService.hasAuthority('ADMIN')")

From df117d02bdd7cee6c2442c07d595c1669ea49aca Mon Sep 17 00:00:00 2001
From: Jozef Daxner <daxner.dodo@gmail.com>
Date: Thu, 3 Jul 2025 12:17:10 +0200
Subject: [PATCH 5/9] [NAE-2122] Implement Structured and Efficient Pagination
 in gRPC

- Fix serialization issue with Case Icon object
---
 .../application/engine/objects/petrinet/domain/Icon.java | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/Icon.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/Icon.java
index 5c371cf87c..3cc0068b48 100644
--- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/Icon.java
+++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/Icon.java
@@ -3,9 +3,16 @@
 import lombok.Getter;
 import lombok.Setter;
 
+import java.io.Serial;
+import java.io.Serializable;
+
 @Setter
 @Getter
-public class Icon {
+public class Icon implements Serializable {
+
+    @Serial
+    private static final long serialVersionUID = -140211037056216078L;
+
     private String key;
 
     private String value;

From c685b2c6dbe3a567a41b8b9f57348dfcda54ccd9 Mon Sep 17 00:00:00 2001
From: Jozef Daxner <daxner.dodo@gmail.com>
Date: Thu, 3 Jul 2025 15:34:33 +0200
Subject: [PATCH 6/9] [NAE-2122] Implement Structured and Efficient Pagination
 in gRPC

- Add comprehensive documentation and pagination to repositories
- Enhanced Javadoc documentation across repositories and services for clarity.
- Added methods supporting pagination in key repositories like UserRepository, AuthorityRepository, ProcessRoleRepository, and GroupRepository.
- Removed unused methods in ElasticPetriNetService and related interfaces for cleaner implementation.
---
 .../domain/ElasticPetriNetRepository.java     |  19 +-
 .../service/ElasticPetriNetService.java       |  37 +--
 .../interfaces/IElasticPetriNetService.java   |   8 -
 .../repositories/PetriNetRepository.java      |  47 +++-
 .../domain/roles/ProcessRoleRepository.java   |  95 ++++++-
 .../service/interfaces/IPetriNetService.java  | 240 +++++++++++++++++-
 .../service/interfaces/IWorkflowService.java  |   8 -
 .../workflow/web/WorkflowController.java      |  10 +-
 .../engine-processes/preference_item.xml      |   6 +-
 .../spring/utils/PaginationProperties.java    |  16 +-
 .../engine/auth/service/GroupServiceImpl.java |  30 ++-
 .../auth/repository/AuthorityRepository.java  |  23 +-
 .../auth/repository/GroupRepository.java      |  39 ++-
 .../auth/repository/UserRepository.java       | 157 +++++++++++-
 .../engine/auth/service/AuthorityService.java |  30 ++-
 .../requestbodies/UserSearchRequestBody.java  |  19 ++
 16 files changed, 679 insertions(+), 105 deletions(-)

diff --git a/application-engine/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticPetriNetRepository.java b/application-engine/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticPetriNetRepository.java
index 9a5da428d8..7f86ffa915 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticPetriNetRepository.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticPetriNetRepository.java
@@ -6,12 +6,25 @@
 
 import java.util.List;
 
+/**
+ * Repository interface for managing {@link ElasticPetriNet} entities in Elasticsearch.
+ * Extends {@link ElasticsearchRepository} to provide CRUD operations and additional query methods.
+ */
 @Repository
 public interface ElasticPetriNetRepository extends ElasticsearchRepository<ElasticPetriNet, String> {
 
+    /**
+     * Finds an {@link ElasticPetriNet} entity by its string ID.
+     *
+     * @param stringId the string ID of the {@link ElasticPetriNet} to find
+     * @return the {@link ElasticPetriNet} entity with the given string ID, or {@code null} if none found
+     */
     ElasticPetriNet findByStringId(String stringId);
 
-    List<ElasticPetriNet> findAllByUriNodeId(String uriNodeId);
-
+    /**
+     * Deletes all {@link ElasticPetriNet} entities with the given string ID.
+     *
+     * @param id the string ID of the {@link ElasticPetriNet} entities to delete
+     */
     void deleteAllByStringId(String id);
-}
+}
\ No newline at end of file
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java
index d9fc4b0c08..d5443d7e29 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java
@@ -1,20 +1,13 @@
 package com.netgrif.application.engine.elastic.service;
 
-import com.netgrif.application.engine.objects.elastic.domain.ElasticPetriNet;
 import com.netgrif.application.engine.elastic.domain.ElasticPetriNetRepository;
 import com.netgrif.application.engine.elastic.service.executors.Executor;
 import com.netgrif.application.engine.elastic.service.interfaces.IElasticPetriNetService;
-import com.netgrif.application.engine.objects.petrinet.domain.PetriNet;
-import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService;
+import com.netgrif.application.engine.objects.elastic.domain.ElasticPetriNet;
 import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.annotation.Lazy;
 import org.springframework.dao.InvalidDataAccessApiUsageException;
 import org.springframework.stereotype.Service;
 
-import java.util.List;
-import java.util.stream.Collectors;
-
 @Service
 @Slf4j
 public class ElasticPetriNetService implements IElasticPetriNetService {
@@ -23,19 +16,11 @@ public class ElasticPetriNetService implements IElasticPetriNetService {
 
     private final Executor executors;
 
-    private IPetriNetService petriNetService;
-
     public ElasticPetriNetService(ElasticPetriNetRepository repository, Executor executors) {
         this.repository = repository;
         this.executors = executors;
     }
 
-    @Lazy
-    @Autowired
-    public void setPetriNetService(IPetriNetService petriNetService) {
-        this.petriNetService = petriNetService;
-    }
-
     @Override
     public void index(ElasticPetriNet net) {
         executors.execute(net.getStringId(), () -> {
@@ -69,24 +54,4 @@ public void remove(String id) {
             log.info("[" + id + "]: PetriNet \"" + id + "\" deleted");
         });
     }
-
-    @Override
-    public String findUriNodeId(PetriNet net) {
-        if (net == null) {
-            return null;
-        }
-        ElasticPetriNet elasticPetriNet = repository.findByStringId(net.getStringId());
-        if (elasticPetriNet == null) {
-            log.warn("[" + net.getStringId() + "] PetriNet with id [" + net.getStringId() + "] is not indexed.");
-            return null;
-        }
-
-        return elasticPetriNet.getUriNodeId();
-    }
-
-    @Override
-    public List<PetriNet> findAllByUriNodeId(String uriNodeId) {
-        List<com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticPetriNet> elasticPetriNets = repository.findAllByUriNodeId(uriNodeId);
-        return petriNetService.findAllById(elasticPetriNets.stream().map(ElasticPetriNet::getStringId).collect(Collectors.toList()));
-    }
 }
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticPetriNetService.java
index 02a36e50a7..7cd005f6c6 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticPetriNetService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticPetriNetService.java
@@ -1,11 +1,8 @@
 package com.netgrif.application.engine.elastic.service.interfaces;
 
 import com.netgrif.application.engine.objects.elastic.domain.ElasticPetriNet;
-import com.netgrif.application.engine.objects.petrinet.domain.PetriNet;
 import org.springframework.scheduling.annotation.Async;
 
-import java.util.List;
-
 public interface IElasticPetriNetService {
 
     @Async
@@ -14,9 +11,4 @@ public interface IElasticPetriNetService {
     void indexNow(ElasticPetriNet net);
 
     void remove(String id);
-
-    String findUriNodeId(PetriNet net);
-
-    List<PetriNet> findAllByUriNodeId(String uriNodeId);
-
 }
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java
index d4c81e0d46..641a0c50af 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java
@@ -8,23 +8,52 @@
 import org.springframework.data.mongodb.repository.MongoRepository;
 import org.springframework.data.querydsl.QuerydslPredicateExecutor;
 
-import java.util.List;
-
+/**
+ * Repository interface for handling operations on {@link PetriNet} entities.
+ * Extends {@link MongoRepository} for standard CRUD operations and {@link QuerydslPredicateExecutor}
+ * to support dynamic queries.
+ */
 public interface PetriNetRepository extends MongoRepository<PetriNet, String>, QuerydslPredicateExecutor<PetriNet> {
 
-    List<PetriNet> findByTitle_DefaultValue(String title);
-
+    /**
+     * Finds a {@link PetriNet} entity by its import identifier.
+     *
+     * @param id the import identifier of the desired PetriNet.
+     * @return the {@link PetriNet} entity matching the given import identifier, or {@code null} if none found.
+     */
     PetriNet findByImportId(String id);
 
+    /**
+     * Finds a {@link PetriNet} entity by its identifier and version.
+     *
+     * @param identifier the unique identifier of the PetriNet.
+     * @param version    the version of the PetriNet.
+     * @return the {@link PetriNet} entity matching the given identifier and version, or {@code null} if none found.
+     */
     PetriNet findByIdentifierAndVersion(String identifier, Version version);
 
+    /**
+     * Finds a paginated list of {@link PetriNet} entities by their identifier.
+     *
+     * @param identifier the unique identifier of the PetriNet.
+     * @param pageable   the pagination details.
+     * @return a {@link Page} containing the list of matching {@link PetriNet} entities.
+     */
     Page<PetriNet> findByIdentifier(String identifier, Pageable pageable);
 
-    Page<PetriNet> findByIdentifierIn(List<String> identifier, Pageable pageable);
-
+    /**
+     * Finds a paginated list of {@link PetriNet} entities by their version.
+     *
+     * @param version  the version of the PetriNet.
+     * @param pageable the pagination details.
+     * @return a {@link Page} containing the list of matching {@link PetriNet} entities.
+     */
     Page<PetriNet> findAllByVersion(Version version, Pageable pageable);
 
-    List<PetriNet> findAllByUriNodeId(String uri);
-
+    /**
+     * Deletes a {@link PetriNet} entity by its unique object ID.
+     *
+     * @param id the unique ID of the PetriNet to delete.
+     */
     void deleteBy_id(ObjectId id);
-}
+}
\ No newline at end of file
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java
index 5c2bb21fa7..78b4835ab5 100755
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java
@@ -9,34 +9,94 @@
 import org.springframework.data.mongodb.repository.Query;
 import org.springframework.data.querydsl.QuerydslPredicateExecutor;
 import org.springframework.stereotype.Repository;
-import org.springframework.data.util.Pair;
 
 import java.util.*;
 import java.util.stream.Collectors;
 
+/**
+ * Repository interface for managing {@link ProcessRole} entities in a MongoDB database.
+ * Extends {@link MongoRepository} for standard CRUD operations and {@link QuerydslPredicateExecutor}
+ * to support dynamic queries.
+ */
 @Repository
 public interface ProcessRoleRepository extends MongoRepository<ProcessRole, String>, QuerydslPredicateExecutor<ProcessRole> {
 
+    /**
+     * Finds a {@link ProcessRole} by its import ID.
+     *
+     * @param importId the import ID to search for
+     * @return an {@link Optional} containing the found {@link ProcessRole}, if any
+     */
     Optional<ProcessRole> findByImportId(String importId);
 
-    Page<ProcessRole> findAllByProcessId(String netId, Pageable pageable);
 
-    List<ProcessRole> findAllByImportIdIn(Set<String> importIds);
+    /**
+     * Finds a paginated list of all {@link ProcessRole} entities associated with a specific process ID.
+     *
+     * @param netId    the process ID
+     * @param pageable the pageable object for pagination settings
+     * @return a {@link Page} of {@link ProcessRole} entities
+     */
+    Page<ProcessRole> findAllByProcessId(String netId, Pageable pageable);
 
+    /**
+     * Finds a paginated list of all {@link ProcessRole} entities by their default name value.
+     *
+     * @param name     the default name value
+     * @param pageable the pageable object for pagination settings
+     * @return a {@link Page} of {@link ProcessRole} entities
+     */
     Page<ProcessRole> findAllByName_DefaultValue(String name, Pageable pageable);
 
+    /**
+     * Finds a paginated list of all {@link ProcessRole} entities by their import ID with pagination.
+     *
+     * @param importId the import ID to filter
+     * @param pageable the pageable object for pagination settings
+     * @return a {@link Page} of {@link ProcessRole} entities
+     */
     Page<ProcessRole> findAllByImportId(String importId, Pageable pageable);
 
+    /**
+     * Finds a paginated list of all global {@link ProcessRole} entities where the global flag is set to true.
+     *
+     * @param pageable the pageable object for pagination settings
+     * @return a {@link Page} of {@link ProcessRole} entities
+     */
     Page<ProcessRole> findAllByGlobalIsTrue(Pageable pageable);
 
+    /**
+     * Finds a {@link ProcessRole} by its object ID.
+     *
+     * @param objectId the object ID to search for
+     * @return an {@link Optional} containing the found {@link ProcessRole}, if any
+     */
     @Query("{ '_id.objectId': ?0 }")
     Optional<ProcessRole> findByIdObjectId(ObjectId objectId);
 
+    /**
+     * Finds all {@link ProcessRole} entities by their object IDs.
+     *
+     * @param objectIds a {@link Collection} of object IDs
+     * @return a {@link List} of {@link ProcessRole} entities
+     */
     @Query("{ '_id.objectId': { $in: ?0 } }")
     List<ProcessRole> findByObjectIds(Collection<ObjectId> objectIds);
 
+    /**
+     * Deletes all {@link ProcessRole} entities with the provided collection of resource IDs.
+     *
+     * @param ids a collection of {@link ProcessResourceId} entities
+     */
     void deleteAllBy_idIn(Collection<ProcessResourceId> ids);
 
+    /**
+     * Finds all {@link ProcessRole} entities by a collection of IDs, where the IDs may
+     * represent either object IDs or composite IDs (containing a network ID and an object ID).
+     *
+     * @param ids a collection of process role IDs to filter
+     * @return a {@link Set} of {@link ProcessRole} entities
+     */
     default Set<ProcessRole> findAllByIdsSet(Collection<String> ids) {
         Map<Boolean, List<String>> partitionedIds = ids.stream()
                 .collect(Collectors.partitioningBy(id -> id.contains(ProcessResourceId.ID_SEPARATOR)));
@@ -48,10 +108,10 @@ default Set<ProcessRole> findAllByIdsSet(Collection<String> ids) {
         List<ObjectId> forNetworkObjectIds = new ArrayList<>();
         List<String> forNetworkNetworkIds = new ArrayList<>();
         partitionedIds.get(true).forEach(id -> {
-                    String[] parts = id.split(ProcessResourceId.ID_SEPARATOR);
-                    forNetworkNetworkIds.add(parts[0]);
-                    forNetworkObjectIds.add(new ObjectId(parts[1]));
-                });
+            String[] parts = id.split(ProcessResourceId.ID_SEPARATOR);
+            forNetworkNetworkIds.add(parts[0]);
+            forNetworkObjectIds.add(new ObjectId(parts[1]));
+        });
 
         List<ProcessRole> processRoles = new ArrayList<>();
         processRoles.addAll(findByObjectIds(forObjectIds));
@@ -59,6 +119,13 @@ default Set<ProcessRole> findAllByIdsSet(Collection<String> ids) {
         return new HashSet<>(processRoles);
     }
 
+    /**
+     * Finds a {@link ProcessRole} by its composite ID. The composite ID may represent either a single
+     * object ID or a combination of network ID and object ID.
+     *
+     * @param compositeId the composite ID to search for
+     * @return an {@link Optional} containing the found {@link ProcessRole}, if any
+     */
     default Optional<ProcessRole> findByCompositeId(String compositeId) {
         String[] parts = compositeId.split(ProcessResourceId.ID_SEPARATOR);
         if (parts.length == 2) {
@@ -70,9 +137,23 @@ default Optional<ProcessRole> findByCompositeId(String compositeId) {
         }
     }
 
+    /**
+     * Finds a {@link ProcessRole} by a network ID and object ID.
+     *
+     * @param networkId the short process ID
+     * @param objectId  the object ID
+     * @return an {@link Optional} containing the found {@link ProcessRole}, if any
+     */
     @Query("{ '_id.shortProcessId': ?0, '_id.objectId': ?1 }")
     Optional<ProcessRole> findByNetworkIdAndObjectId(String networkId, ObjectId objectId);
 
+    /**
+     * Finds all {@link ProcessRole} entities matching the specified network IDs and object IDs.
+     *
+     * @param networkIds a collection of short process IDs
+     * @param objectIds  a collection of object IDs
+     * @return a {@link List} of {@link ProcessRole} entities
+     */
     @Query("{ $or: [ { '_id.shortProcessId': ?0, '_id.objectId': ?1 } ] }")
     List<ProcessRole> findByNetworkIdsAndObjectIds(Collection<String> networkIds, Collection<ObjectId> objectIds);
 }
\ No newline at end of file
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
index c9cbf50bd9..73921f1e2f 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
@@ -24,79 +24,315 @@
 import java.io.InputStream;
 import java.util.*;
 
+/**
+ * Interface defining methods for managing {@link PetriNet} objects.
+ */
 public interface IPetriNetService {
 
+    /** 
+     * Transforms a {@link PetriNet} into a {@link PetriNetReference}.
+     * 
+     * @param net the PetriNet to transform
+     * @param locale the locale to use for translations
+     * @return a {@link PetriNetReference} representing the given PetriNet
+     */
     static PetriNetReference transformToReference(PetriNet net, Locale locale) {
-        //return new PetriNetReference(net.getStringId(), net.getIdentifier(), net.getVersion(), net.getTitle().getTranslation(locale), net.getInitials(), net.getTranslatedDefaultCaseName(locale));
         return new PetriNetReference(net, locale);
     }
 
+    /** 
+     * Transforms a {@link Transition} into a {@link TransitionReference}.
+     * 
+     * @param net the {@link PetriNet} containing the transition
+     * @param transition the transition to transform
+     * @param locale the locale to use for translations
+     * @return a {@link TransitionReference} for the given transition
+     */
     static TransitionReference transformToReference(PetriNet net, Transition transition, Locale locale) {
         List<com.netgrif.application.engine.workflow.web.responsebodies.DataFieldReference> list = new ArrayList<>();
         transition.getImmediateData().forEach(fieldId -> list.add(new com.netgrif.application.engine.workflow.web.responsebodies.DataFieldReference(net.getDataSet().get(fieldId), locale)));
         return new TransitionReference(transition.getStringId(), transition.getTitle().getTranslation(locale), net.getStringId(), list);
     }
 
+    /** 
+     * Transforms a {@link Field} into a {@link DataFieldReference}.
+     * 
+     * @param net the {@link PetriNet} containing the field
+     * @param transition the {@link Transition} containing the field
+     * @param field the field to transform
+     * @param locale the locale to use for translations
+     * @return a {@link DataFieldReference} for the given field
+     */
     static DataFieldReference transformToReference(PetriNet net, Transition transition, Field field, Locale locale) {
         return new DataFieldReference(field.getStringId(), field.getName().getTranslation(locale), net.getStringId(), transition.getStringId());
     }
 
+    /**
+     * Imports a PetriNet from XML input. 
+     * 
+     * @param xmlFile the input stream of the XML file
+     * @param releaseType the type of release
+     * @param user the user performing the import
+     * @return an {@link ImportPetriNetEventOutcome} representing the result
+     * @throws IOException if an I/O error occurs
+     * @throws MissingPetriNetMetaDataException if metadata is incomplete
+     * @throws MissingIconKeyException if an icon key is missing
+     * @deprecated Use {@link #importPetriNet(InputStream, VersionType, LoggedUser)} instead.
+     */
     @Deprecated
     ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, String releaseType, LoggedUser user) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException;
 
+    /**
+     * Imports a PetriNet from XML input.
+     *
+     * @param xmlFile the input stream of the XML file
+     * @param releaseType the type of release {@link VersionType}
+     * @param user the user performing the import
+     * @return an {@link ImportPetriNetEventOutcome} representing the result
+     * @throws IOException if an I/O error occurs
+     * @throws MissingPetriNetMetaDataException if metadata is incomplete
+     * @throws MissingIconKeyException if an icon key is missing
+     */
     ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser user) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException;
 
+
+    /**
+     * Imports a PetriNet from XML input.
+     *
+     * @param xmlFile the input stream of the XML file
+     * @param releaseType the type of release {@link VersionType}
+     * @param user the user performing the import
+     * @param params additional parameters
+     * @return an {@link ImportPetriNetEventOutcome} representing the result
+     * @throws IOException if an I/O error occurs
+     * @throws MissingPetriNetMetaDataException if metadata is incomplete
+     * @throws MissingIconKeyException if an icon key is missing
+     */
     ImportPetriNetEventOutcome importPetriNet(InputStream xmlFile, VersionType releaseType, LoggedUser user, Map<String, String> params) throws IOException, MissingPetriNetMetaDataException, MissingIconKeyException;
 
+    /**
+     * Saves a PetriNet object.
+     *
+     * @param petriNet the PetriNet to save
+     * @return an {@link Optional} containing the saved PetriNet or empty if unsuccessful
+     */
     Optional<PetriNet> save(PetriNet petriNet);
 
+    /**
+     * Retrieves a {@link PetriNet} by its ID.
+     *
+     * @param id the ID of the PetriNet
+     * @return the corresponding {@link PetriNet}
+     */
     PetriNet getPetriNet(String id);
 
+    /**
+     * Retrieves a {@link PetriNet} by its identifier and version.
+     *
+     * @param identifier the unique identifier of the PetriNet
+     * @param version the version of the PetriNet
+     * @return the {@link PetriNet} matching the provided identifier and version
+     */
     PetriNet getPetriNet(String identifier, Version version);
 
+    /**
+     * Retrieves a paginated list of {@link PetriNet} objects by their identifier.
+     *
+     * @param identifier the unique identifier of the PetriNets
+     * @param pageable the pagination information
+     * @return a paginated list of {@link PetriNet} objects
+     */
     Page<PetriNet> getByIdentifier(String identifier, Pageable pageable);
 
+    /**
+     * Finds all {@link PetriNet} objects by their IDs.
+     *
+     * @param ids a list of PetriNet IDs to retrieve
+     * @return a list of {@link PetriNet} objects matching the provided IDs
+     */
     List<PetriNet> findAllById(List<String> ids);
 
+    /**
+     * Retrieves the newest version of a {@link PetriNet} by its identifier.
+     *
+     * @param identifier the unique identifier of the PetriNet
+     * @return the newest version of the {@link PetriNet} matching the provided identifier
+     */
     PetriNet getNewestVersionByIdentifier(String identifier);
 
+    /**
+     * Retrieves a paginated list of all {@link PetriNet} objects.
+     *
+     * @param pageable the pagination information
+     * @return a paginated list of all {@link PetriNet} objects
+     */
     Page<PetriNet> getAll(Pageable pageable);
 
+    /**
+     * Retrieves a {@link FileSystemResource} representing a file associated with a {@link PetriNet}.
+     *
+     * @param netId the unique ID of the PetriNet
+     * @param title the title of the file to retrieve
+     * @return a {@link FileSystemResource} containing the file
+     */
     FileSystemResource getFile(String netId, String title);
 
+    /**
+     * Retrieves a paginated list of PetriNet references accessible by the given user.
+     *
+     * @param user the logged-in user
+     * @param locale the locale for translations
+     * @param pageable the pagination information
+     * @return a {@link Page} of {@link PetriNetReference} objects
+     */ 
     Page<PetriNetReference> getReferences(LoggedUser user, Locale locale, Pageable pageable);
 
+    /**
+     * Retrieves a paginated list of {@link PetriNetReference} objects by their identifier.
+     *
+     * @param identifier the unique identifier of the PetriNet
+     * @param user the logged-in user making the request
+     * @param locale the locale for translations
+     * @param pageable the pagination information
+     * @return a paginated list of {@link PetriNetReference} objects
+     */
     Page<PetriNetReference> getReferencesByIdentifier(String identifier, LoggedUser user, Locale locale, Pageable pageable);
 
+    /**
+     * Retrieves a paginated list of {@link PetriNetReference} objects by version.
+     *
+     * @param version the {@link Version} of the PetriNet
+     * @param user the logged-in user making the request
+     * @param locale the locale for translations
+     * @param pageable the pagination information
+     * @return a paginated list of {@link PetriNetReference} objects
+     */
     Page<PetriNetReference> getReferencesByVersion(Version version, LoggedUser user, Locale locale, Pageable pageable);
 
+    /**
+     * Retrieves a list of {@link PetriNetReference} objects accessible by the user's process roles.
+     *
+     * @param user the logged-in user making the request
+     * @param locale the locale for translations
+     * @return a list of {@link PetriNetReference} objects accessible by the user
+     */
     List<PetriNetReference> getReferencesByUsersProcessRoles(LoggedUser user, Locale locale);
 
+    /**
+     * Retrieves a single {@link PetriNetReference} by identifier and version.
+     *
+     * @param identifier the unique identifier of the PetriNet
+     * @param version the {@link Version} of the PetriNet
+     * @param user the logged-in user making the request
+     * @param locale the locale for translations
+     * @return the {@link PetriNetReference} object corresponding to the given identifier and version
+     */
     PetriNetReference getReference(String identifier, Version version, LoggedUser user, Locale locale);
 
+    /**
+     * Retrieves a list of {@link TransitionReference} objects for the given PetriNet IDs.
+     *
+     * @param netsIds the list of PetriNet IDs for which to retrieve transitions
+     * @param user the logged-in user making the request
+     * @param locale the locale for translations
+     * @return a list of {@link TransitionReference} objects for the given PetriNet IDs
+     */
     List<TransitionReference> getTransitionReferences(List<String> netsIds, LoggedUser user, Locale locale);
 
+    /**
+     * Retrieves a list of {@link DataFieldReference} objects for the given transition references.
+     *
+     * @param transitions the list of {@link TransitionReference} objects
+     * @param locale the locale for translations
+     * @return a list of {@link DataFieldReference} objects
+     */
     List<DataFieldReference> getDataFieldReferences(List<TransitionReference> transitions, Locale locale);
 
+    /**
+     * Performs a search for {@link PetriNetReference} objects based on the provided criteria.
+     *
+     * @param criteria the {@link PetriNetSearch} criteria to filter results
+     * @param user the logged-in user making the request
+     * @param pageable the pagination information
+     * @param locale the locale for translations
+     * @return a paginated list of {@link PetriNetReference} objects matching the criteria
+     */
     Page<PetriNetReference> search(PetriNetSearch criteria, LoggedUser user, Pageable pageable, Locale locale);
 
+    /**
+     * Finds a {@link PetriNet} by its import ID.
+     *
+     * @param id the import ID of the PetriNet
+     * @return an {@link Optional} containing the {@link PetriNet} if found, otherwise empty
+     */
     Optional<PetriNet> findByImportId(String id);
 
+    /**
+     * Evicts all cached PetriNet data from caches.
+     */
     void evictAllCaches();
 
+    /**
+     * Evicts the cache for the given {@link PetriNet}.
+     *
+     * @param net the {@link PetriNet} to remove from the cache
+     */
     void evictCache(PetriNet net);
 
+    /**
+     * Retrieves a {@link PetriNet} by its MongoDB {@link ObjectId}.
+     *
+     * @param petriNetId the {@link ObjectId} of the PetriNet to retrieve
+     * @return the {@link PetriNet} associated with the given ID
+     */
     PetriNet get(ObjectId petriNetId);
 
+    /**
+     * Retrieves a list of {@link PetriNet} objects by their MongoDB {@link ObjectId}s.
+     *
+     * @param petriNetId a collection of {@link ObjectId}s of the PetriNets to retrieve
+     * @return a list of {@link PetriNet} objects corresponding to the provided IDs
+     */
     List<PetriNet> get(Collection<ObjectId> petriNetId);
 
+    /**
+     * Retrieves a list of {@link PetriNet} objects by their unique string identifiers.
+     *
+     * @param petriNetIds a list of unique identifiers of PetriNets to retrieve
+     * @return a list of {@link PetriNet} objects corresponding to the provided IDs
+     */
     List<PetriNet> get(List<String> petriNetIds);
 
+    /**
+     * Deletes a PetriNet by its ID.
+     *
+     * @param id the ID of the PetriNet to delete
+     * @param loggedUser the user requesting the deletion
+     */
     void deletePetriNet(String id, LoggedUser loggedUser);
 
+    /**
+     * Runs the specified set of actions on a PetriNet.
+     *
+     * @param actions the actions to execute
+     * @param petriNet the PetriNet on which actions are executed
+     */
     void runActions(List<Action> actions, PetriNet petriNet);
 
+    /**
+     * Retrieves a list of existing PetriNet identifiers from the given list of identifiers.
+     * Each provided identifier is validated, and only identifiers corresponding to existing PetriNets are returned.
+     *
+     * @param identifiers the list of PetriNet identifiers to validate
+     * @return a list of validated and existing PetriNet identifiers
+     */
     List<String> getExistingPetriNetIdentifiersFromIdentifiersList(List<String> identifiers);
 
+    /**
+     * Retrieves the reference of a {@link PetriNet} associated with a case ID.
+     *
+     * @param caseId the ID of the workflow case
+     * @return a {@link PetriNetImportReference} linking the PetriNet
+     */
     PetriNetImportReference getNetFromCase(String caseId);
-}
+}
\ No newline at end of file
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java
index 436a376bc4..aca8f7b720 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java
@@ -3,7 +3,6 @@
 import com.netgrif.application.engine.objects.auth.domain.LoggedUser;
 import com.netgrif.application.engine.objects.petrinet.domain.I18nString;
 import com.netgrif.application.engine.objects.petrinet.domain.PetriNet;
-import com.netgrif.application.engine.objects.petrinet.domain.dataset.Field;
 import com.netgrif.application.engine.objects.workflow.domain.Case;
 import com.netgrif.application.engine.objects.workflow.domain.Task;
 import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.caseoutcomes.CreateCaseEventOutcome;
@@ -68,20 +67,13 @@ public interface IWorkflowService {
 
     Map<String, I18nString> listToMap(List<Case> cases);
 
-//    @Deprecated
-//    List<Field> getData(String caseId);
-
     Page<Case> search(Map<String, Object> request, Pageable pageable, LoggedUser user, Locale locale);
 
     long count(Map<String, Object> request, LoggedUser user, Locale locale);
 
-//    List<Case> getCaseFieldChoices(Pageable pageable, String caseId, String fieldId);
-
     boolean removeTasksFromCase(List<Task> tasks, String caseId);
 
     boolean removeTasksFromCase(List<Task> tasks, Case useCase);
 
-    Case decrypt(Case useCase);
-
     Page<Case> search(Predicate predicate, Pageable pageable);
 }
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java
index 25c83ca385..98ea7e6da8 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/WorkflowController.java
@@ -94,7 +94,7 @@ public EntityModel<EventOutcomeWithMessage> createCase(@RequestBody CreateCaseBo
         }
     }
 
-    @Operation(summary = "Get all cases of the system", security = {@SecurityRequirement(name = "BasicAuth")})
+    @Operation(summary = "Get all cases of the system, paginated", security = {@SecurityRequirement(name = "BasicAuth")})
     @GetMapping(value = "/all", produces = MediaTypes.HAL_JSON_VALUE)
     public PagedModel<CaseResource> getAll(Pageable pageable, PagedResourcesAssembler<Case> assembler) {
         Page<Case> cases = workflowService.getAll(pageable);
@@ -105,7 +105,7 @@ public PagedModel<CaseResource> getAll(Pageable pageable, PagedResourcesAssemble
         return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements()));
     }
 
-    @Operation(summary = "Generic case search with QueryDSL predicate", security = {@SecurityRequirement(name = "BasicAuth")})
+    @Operation(summary = "Generic case search with QueryDSL predicate, paginated", security = {@SecurityRequirement(name = "BasicAuth")})
     @PostMapping(value = "/case/search2", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE)
     public PagedModel<CaseResource> search2(@QuerydslPredicate(root = Case.class) Predicate predicate, Pageable pageable, PagedResourcesAssembler<Case> assembler) {
         Page<Case> cases = workflowService.search(predicate, pageable);
@@ -116,7 +116,7 @@ public PagedModel<CaseResource> search2(@QuerydslPredicate(root = Case.class) Pr
         return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements()));
     }
 
-    @Operation(summary = "Generic case search on Elasticsearch database", security = {@SecurityRequirement(name = "BasicAuth")})
+    @Operation(summary = "Generic case search on Elasticsearch database, paginated", security = {@SecurityRequirement(name = "BasicAuth")})
     @PostMapping(value = "/case/search", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE)
     public PagedModel<CaseResource> search(@RequestBody SingleCaseSearchRequestAsList searchBody, @RequestParam(defaultValue = "OR") MergeFilterOperation operation, Pageable pageable, PagedResourcesAssembler<Case> assembler, Authentication auth, Locale locale) {
         LoggedUser user = (LoggedUser) auth.getPrincipal();
@@ -129,7 +129,7 @@ public PagedModel<CaseResource> search(@RequestBody SingleCaseSearchRequestAsLis
         return PagedModel.of(cases.stream().map(CaseResource::new).toList(), new PagedModel.PageMetadata(pageable.getPageSize(), pageable.getPageNumber(), cases.getTotalElements()));
     }
 
-    @Operation(summary = "Generic case search on Mongo database", security = {@SecurityRequirement(name = "BasicAuth")})
+    @Operation(summary = "Generic case search on Mongo database, paginated", security = {@SecurityRequirement(name = "BasicAuth")})
     @PostMapping(value = "/case/search_mongo", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaTypes.HAL_JSON_VALUE)
     public PagedModel<CaseResource> searchMongo(@RequestBody Map<String, Object> searchBody, Pageable pageable, Authentication auth, PagedResourcesAssembler<Case> assembler, Locale locale) {
         Page<Case> cases = workflowService.search(searchBody, pageable, (LoggedUser) auth.getPrincipal(), locale);
@@ -157,7 +157,7 @@ public CaseResource getOne(@PathVariable("id") String caseId) {
         return new CaseResource(aCase);
     }
 
-    @Operation(summary = "Get all cases by user that created them", security = {@SecurityRequirement(name = "BasicAuth")})
+    @Operation(summary = "Get all cases by user that created them, paginated", security = {@SecurityRequirement(name = "BasicAuth")})
     @RequestMapping(value = "/case/author/{id}", method = RequestMethod.POST, consumes = MediaType.TEXT_PLAIN_VALUE, produces = MediaTypes.HAL_JSON_VALUE)
     public PagedModel<CaseResource> findAllByAuthor(@PathVariable("id") String authorId, @RequestBody String petriNet, PagedResourcesAssembler<Case> assembler, Pageable pageable) {
         Page<Case> cases = workflowService.findAllByAuthor(authorId, petriNet, pageable);
diff --git a/application-engine/src/main/resources/petriNets/engine-processes/preference_item.xml b/application-engine/src/main/resources/petriNets/engine-processes/preference_item.xml
index 719c2bbe78..eec1f3aa97 100644
--- a/application-engine/src/main/resources/petriNets/engine-processes/preference_item.xml
+++ b/application-engine/src/main/resources/petriNets/engine-processes/preference_item.xml
@@ -147,9 +147,9 @@
 		<action trigger="get">
 			moveDestUri: f.move_dest_uri;
 
-			String uriNodeId = elasticCaseService.findUriNodeId(useCase)
-			def node = uriService.findById(uriNodeId)
-			updateMultichoiceWithCurrentNode(moveDestUri, node)
+//			String uriNodeId = elasticCaseService.findUriNodeId(useCase)
+//			def node = uriService.findById(uriNodeId)
+//			updateMultichoiceWithCurrentNode(moveDestUri, node)
 		</action>
 		<action trigger="set">
 			prevDestUri: f.move_previous_dest_uri,
diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java
index b39bb79382..d2ccbaf0a6 100644
--- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java
+++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java
@@ -4,12 +4,26 @@
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Configuration;
 
+/**
+ * Configuration properties class for pagination settings in the application.
+ * This class retrieves and holds pagination-related properties defined under the {@code netgrif.pagination} prefix.
+ */
 @Data
 @Configuration
 @ConfigurationProperties(prefix = "netgrif.pagination")
 public class PaginationProperties {
 
+    /**
+     * The size of a single page for backend pagination.
+     * This value determines the number of records or elements that should be fetched per page in backend-related operations.
+     * The default value is {@code 100}.
+     */
     private int backendPageSize = 100;
 
+    /**
+     * The size of a single page for frontend pagination.
+     * This value determines the number of records or elements that should be displayed per page in the application's frontend.
+     * The default value is {@code 20}.
+     */
     private int frontendPageSize = 20;
-}
+}
\ No newline at end of file
diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
index efdba39a8b..49b7c7f1a7 100644
--- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
+++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/GroupServiceImpl.java
@@ -1,5 +1,6 @@
 package com.netgrif.application.engine.auth.service;
 
+import com.netgrif.application.engine.adapter.spring.utils.PaginationProperties;
 import com.netgrif.application.engine.auth.config.GroupConfigurationProperties;
 import com.netgrif.application.engine.auth.repository.GroupRepository;
 import com.netgrif.application.engine.objects.auth.domain.Authority;
@@ -7,11 +8,13 @@
 import com.netgrif.application.engine.objects.auth.domain.IUser;
 import com.netgrif.application.engine.objects.common.ResourceNotFoundException;
 import com.netgrif.application.engine.objects.common.ResourceNotFoundExceptionCode;
+import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole;
 import com.querydsl.core.types.Predicate;
 import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
 import org.springframework.data.domain.Pageable;
 
 import java.util.*;
@@ -30,6 +33,8 @@ public class GroupServiceImpl implements GroupService {
 
     private Group defaultSystemGroup;
 
+    private PaginationProperties paginationProperties;
+
     @Autowired
     public void setGroupRepository(GroupRepository groupRepository) {
         this.groupRepository = groupRepository;
@@ -50,6 +55,11 @@ public void setAuthorityService(AuthorityService authorityService) {
         this.authorityService = authorityService;
     }
 
+    @Autowired
+    public void setPaginationProperties(PaginationProperties paginationProperties) {
+        this.paginationProperties = paginationProperties;
+    }
+
     @Override
     public void delete(Group group) {
         if (!groupRepository.existsById(group.getStringId())) {
@@ -98,7 +108,7 @@ public Group getDefaultSystemGroup() {
     @Override
     public Group create(IUser user) {
         log.info("Creating default group for user: [{}]", user.getStringId());
-        Set<Group> userGroups = groupRepository.findByOwnerId(user.getStringId());
+        Page<Group> userGroups = groupRepository.findByOwnerId(user.getStringId(), Pageable.ofSize(1));
         if (!userGroups.isEmpty() && !Objects.equals(user.getStringId(), userService.getSystem().getStringId())) {
             throw new IllegalArgumentException("Default group for user [%s] already exists.".formatted(user.getUsername()));
         }
@@ -121,12 +131,20 @@ public Group create(String identifier, String title, IUser user) {
 
     @Override
     public Group getDefaultUserGroup(IUser user) {
-        Set<Group> userGroup = groupRepository.findByOwnerId(user.getStringId());
         String errorMessage = "Default user group for user [%s] does not exist.".formatted(user.getUsername());
-        if (userGroup.isEmpty()) {
-            throw new ResourceNotFoundException(ResourceNotFoundExceptionCode.DEFAULT_USER_GROUP_NOT_FOUND, errorMessage);
-        }
-        return userGroup.stream().filter(g -> g.getIdentifier().equals(user.getUsername())).findFirst().orElseThrow(() -> new ResourceNotFoundException(ResourceNotFoundExceptionCode.DEFAULT_USER_GROUP_NOT_FOUND, errorMessage));
+        Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
+        Page<Group> userGroups;
+        do {
+            userGroups = groupRepository.findByOwnerId(user.getStringId(), pageable);
+
+            Optional<Group> group = userGroups.stream().filter(g -> g.getIdentifier().equals(user.getUsername())).findFirst();
+            if (group.isPresent()) {
+                return group.get();
+            }
+
+            pageable = pageable.next();
+        } while (userGroups.hasNext());
+        throw new ResourceNotFoundException(ResourceNotFoundExceptionCode.DEFAULT_USER_GROUP_NOT_FOUND, errorMessage);
     }
 
     @Override
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/AuthorityRepository.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/AuthorityRepository.java
index 79252267f1..faddbf55eb 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/AuthorityRepository.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/AuthorityRepository.java
@@ -6,14 +6,31 @@
 import org.springframework.data.domain.Pageable;
 import org.springframework.data.mongodb.repository.MongoRepository;
 import org.springframework.stereotype.Repository;
+
 import java.util.List;
 import java.util.Optional;
 
+/**
+ * Repository interface for managing {@link Authority} entities in the MongoDB database.
+ * Extends the {@link MongoRepository} interface for basic CRUD operations.
+ */
 @Repository
 public interface AuthorityRepository extends MongoRepository<Authority, String> {
-    Optional<Authority> findByName(String name);
 
-    List<Authority> findAllByNameStartsWith(String prefix);
+    /**
+     * Finds an {@link Authority} entity by its name.
+     *
+     * @param name the name of the authority to search for
+     * @return an {@link Optional} containing the {@link Authority} if found, or empty if not found
+     */
+    Optional<Authority> findByName(String name);
 
+    /**
+     * Retrieves a paginated list of {@link Authority} entities whose IDs match a given list of {@link ObjectId}s.
+     *
+     * @param ids a list of {@link ObjectId}s to search for
+     * @param pageable the pagination information defined by a {@link Pageable} object
+     * @return a {@link Page} containing the matching {@link Authority} entities
+     */
     Page<Authority> findAllBy_idIn(List<ObjectId> ids, Pageable pageable);
-}
+}
\ No newline at end of file
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java
index ad5150fe87..7cfc8a7820 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/GroupRepository.java
@@ -9,17 +9,46 @@
 
 import java.util.Collection;
 import java.util.Optional;
-import java.util.Set;
 
+/**
+ * Repository interface for managing {@link Group} entities in the MongoDB database.
+ * Extends the {@link MongoRepository} interface for basic CRUD operations.
+ */
 @Repository
 public interface GroupRepository extends MongoRepository<Group, String>, QuerydslPredicateExecutor<Group> {
-    Set<Group> findByOwnerId(String id);
 
-    Optional<Group> findByIdentifier(String identifier);
+    /**
+     * Finds paginated list of all {@link Group} entities that have the given owner ID.
+     *
+     * @param id the ID of the owner
+     * @param pageable the pagination information
+     * @return a set of {@link Group} entities associated with the specified owner ID
+     */
+    Page<Group> findByOwnerId(String id, Pageable pageable);
 
-    Page<Group> findAllByMemberIdsContains(String memberId, Pageable pageable);
+    /**
+     * Finds a {@link Group} by its unique identifier.
+     *
+     * @param identifier the unique identifier of the group
+     * @return an {@link Optional} containing the {@link Group} if it exists, otherwise {@code Optional.empty()}
+     */
+    Optional<Group> findByIdentifier(String identifier);
 
+    /**
+     * Finds all {@link Group}s with given IDs in a pageable format.
+     *
+     * @param ids the collection of group IDs to query
+     * @param pageable the pagination information
+     * @return a {@link Page} of {@link Group}s with the specified IDs
+     */
     Page<Group> findAllByIdIn(Collection<String> ids, Pageable pageable);
 
+    /**
+     * Finds all {@link Group}s associated with a particular realm ID in a pageable format.
+     *
+     * @param realmId the ID of the realm
+     * @param pageable the pagination information
+     * @return a {@link Page} of {@link Group}s belonging to the specified realm
+     */
     Page<Group> findAllByRealmId(String realmId, Pageable pageable);
-}
+}
\ No newline at end of file
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java
index 3f557f0240..9f60c1d2a4 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/repository/UserRepository.java
@@ -24,24 +24,61 @@
 import java.util.Set;
 import java.util.stream.Collectors;
 
+/**
+ * Repository interface for managing {@link User} entities in the MongoDB database.
+ * Extends the {@link MongoRepository} interface for basic CRUD operations.
+ */
 @Repository
 public interface UserRepository extends MongoRepository<User, String>, QuerydslPredicateExecutor<User> {
 
+    /**
+     * Throws an exception since this method is not supported.
+     * Use {@link #saveUser(User, MongoTemplate, String)} instead.
+     *
+     * @param entity the entity to save
+     * @param <S>    subtype of {@link User}
+     * @return never returns, always throws exception
+     * @throws UnsupportedOperationException as this method is not supported
+     */
     @Override
     default <S extends User> S save(S entity) {
         throw new UnsupportedOperationException("This method is not supported. Use 'UserRepository.saveUser' instead.'");
     }
 
+    /**
+     * Throws an exception since this method is not supported.
+     * Use {@link #findAllByQuery(Predicate, Pageable, MongoTemplate, String)} instead.
+     *
+     * @param predicate filter condition
+     * @param pageable  pagination details
+     * @return never returns, always throws exception
+     * @throws UnsupportedOperationException as this method is not supported
+     */
     @Override
     default Page<User> findAll(Predicate predicate, Pageable pageable) {
         throw new UnsupportedOperationException("This method is not supported. Use 'UserRepository.findAll(Predicate, Pageable, MongoTemplate, Collection<String>)' instead.'");
     }
 
+    /**
+     * Throws an exception since this method is not supported.
+     * Use {@link #deleteAll(MongoTemplate, Collection)} instead.
+     *
+     * @throws UnsupportedOperationException as this method is not supported
+     */
     @Override
     default void deleteAll() {
         throw new UnsupportedOperationException("This method is not supported. Use 'UserRepository.deleteAll(MongoTemplate, Collection<String>)' instead.'");
     }
 
+    /**
+     * Finds a paginated list of all {@link User} entities matching the given {@link Predicate}.
+     *
+     * @param predicate     the condition to filter users
+     * @param pageable      pagination details
+     * @param mongoTemplate the MongoDB template
+     * @param collection    the target collection
+     * @return paginated result of users matching the condition
+     */
     default Page<User> findAllByQuery(Predicate predicate, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
         Assert.notNull(predicate, "Predicate must not be null");
         Assert.notNull(pageable, "Pageable must not be null");
@@ -50,32 +87,79 @@ default Page<User> findAllByQuery(Predicate predicate, Pageable pageable, MongoT
         return query.fetchPage(pageable).map(User.class::cast);
     }
 
+    /**
+     * Finds a {@link User} by its ObjectId.
+     *
+     * @param objectId       the ID of the user to search
+     * @param mongoTemplate  the MongoDB template
+     * @param collectionName the collection name
+     * @return optional of the user if found, otherwise empty
+     */
     default Optional<User> findById(ObjectId objectId, MongoTemplate mongoTemplate, String collectionName) {
         return Optional.ofNullable(
                 mongoTemplate.findOne(Query.query(Criteria.where("id").is(objectId)), com.netgrif.application.engine.adapter.spring.auth.domain.User.class, collectionName)
         );
     }
 
+    /**
+     * Finds a {@link User} by their username.
+     *
+     * @param username       the username to search for
+     * @param mongoTemplate  the MongoDB template
+     * @param collectionName the collection name
+     * @return optional of the user if found, otherwise empty
+     */
     default Optional<User> findByUsername(String username, MongoTemplate mongoTemplate, String collectionName) {
         return Optional.ofNullable(
                 mongoTemplate.findOne(Query.query(Criteria.where("username").is(username)), com.netgrif.application.engine.adapter.spring.auth.domain.User.class, collectionName)
         );
     }
 
+    /**
+     * Finds a {@link User} by their email address.
+     *
+     * @param email          the email to search for
+     * @param mongoTemplate  the MongoDB template
+     * @param collectionName the collection name
+     * @return optional of the user if found, otherwise empty
+     */
     default Optional<User> findByEmail(String email, MongoTemplate mongoTemplate, String collectionName) {
         return Optional.ofNullable(
                 mongoTemplate.findOne(Query.query(Criteria.where("email").is(email)), com.netgrif.application.engine.adapter.spring.auth.domain.User.class, collectionName)
         );
     }
 
+    /**
+     * Saves a {@link User} into the specified collection.
+     *
+     * @param user           the user to save
+     * @param mongoTemplate  the MongoDB template
+     * @param collectionName the collection to save the user in
+     * @return the saved user
+     */
     default User saveUser(User user, MongoTemplate mongoTemplate, String collectionName) {
         return mongoTemplate.save(user, collectionName);
     }
 
+    /**
+     * Deletes all data from specified collections.
+     *
+     * @param mongoTemplate  the MongoDB template
+     * @param collectionName the names of the collections to clear
+     */
     default void deleteAll(MongoTemplate mongoTemplate, Collection<String> collectionName) {
         collectionName.forEach(collection -> mongoTemplate.remove(new Query(), collection));
     }
 
+    /**
+     * Finds a paginated list of all {@link User} entities using a specified {@link Query}
+     *
+     * @param query         the Mongo query to execute; if null, a new {@link Query} object will be created.
+     * @param pageable      the pagination information to apply to the query results.
+     * @param mongoTemplate the MongoDB template used to perform the query.
+     * @param collection    the name of the collection where the query will be executed.
+     * @return a {@link Page} of {@link User} entities obtained from the query results.
+     */
     default Page<User> findAllByQuery(Query query, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
         if (query == null) {
             query = new Query();
@@ -83,6 +167,16 @@ default Page<User> findAllByQuery(Query query, Pageable pageable, MongoTemplate
         return resolveUserPage(pageable, mongoTemplate, collection, query);
     }
 
+    /**
+     * Finds a paginated list of distinct {@link User}s by their {@link UserState} and process role IDs.
+     *
+     * @param state         the state of the users
+     * @param roleId        the IDs of the process roles
+     * @param pageable      pagination details
+     * @param mongoTemplate the MongoDB template
+     * @param collection    the collection name
+     * @return a paginated list of distinct users with the specified state and process roles
+     */
     default Page<User> findDistinctByStateAndProcessRoles__idIn(UserState state, Collection<ProcessResourceId> roleId, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
         Query query = Query.query(
                 Criteria.where("state").is(state)
@@ -90,18 +184,45 @@ default Page<User> findDistinctByStateAndProcessRoles__idIn(UserState state, Col
         return resolveUserPage(pageable, mongoTemplate, collection, query);
     }
 
+    /**
+     * Finds a paginated list of all {@link User} entities by their associated process role IDs.
+     *
+     * @param rolesId       the collection of {@link ProcessResourceId}s to filter users by.
+     * @param pageable      the pagination details for the query result.
+     * @param mongoTemplate the MongoDB template used to perform the query.
+     * @param collection    the name of the collection where the query will be executed.
+     * @return a {@link Page} containing the {@link User} entities that match the specified process role IDs.
+     */
     default Page<User> findAllByProcessRoles__idIn(Collection<ProcessResourceId> rolesId, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
         Query query = Query.query(
                 Criteria.where("processRoles._id").in(rolesId));
         return resolveUserPage(pageable, mongoTemplate, collection, query);
     }
 
+    /**
+     * Removes all {@link User} entities with the specified state and an expiration date before the given time.
+     *
+     * @param state           the {@link UserState} to filter users.
+     * @param dateTime        the expiration date before which users will be deleted.
+     * @param mongoTemplate   the MongoDB template used to perform the operation.
+     * @param collectionNames the set of collection names in which the deletion will be performed.
+     */
     default void removeAllByStateAndExpirationDateBefore(UserState state, LocalDateTime dateTime, MongoTemplate mongoTemplate, Set<String> collectionNames) {
         collectionNames.forEach(collectionName ->
                 mongoTemplate.remove(Query.query(Criteria.where("state").is(state).and("credentials.token.properties.expirationDate").lt(dateTime)), com.netgrif.application.engine.adapter.spring.auth.domain.User.class, collectionName)
         );
     }
 
+    /**
+     * Finds a paginated list of all {@link User} entities with the specified state and an expiration date before the given time
+     *
+     * @param state         the {@link UserState} to filter the users.
+     * @param dateTime      the expiration date before which users should be filtered.
+     * @param pageable      the pagination details for the query result.
+     * @param mongoTemplate the MongoDB template used to perform the query.
+     * @param collection    the name of the collection where the query will be executed.
+     * @return a {@link Page} containing {@link User} entities matching the specified state and expiration date criteria.
+     */
     default Page<User> findAllByStateAndExpirationDateBefore(UserState state, LocalDateTime dateTime, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
         Query query = Query.query(
                 Criteria.where("state").is(state)
@@ -109,23 +230,43 @@ default Page<User> findAllByStateAndExpirationDateBefore(UserState state, LocalD
         return resolveUserPage(pageable, mongoTemplate, collection, query);
     }
 
+    /**
+     * Finds a paginated list of all {@link User}s by their IDs.
+     *
+     * @param ids           the list of user IDs
+     * @param pageable      pagination details
+     * @param mongoTemplate the MongoDB template
+     * @param collection    the collection name
+     * @return a paginated list of users with the specified IDs
+     */
     default Page<User> findAllByIds(Collection<ObjectId> ids, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
         Query query = Query.query(
                 Criteria.where("id").in(ids));
         return resolveUserPage(pageable, mongoTemplate, collection, query);
     }
 
-    default Page<User> findAllByIdInAndState(Collection<ObjectId> ids, UserState state, Pageable pageable, MongoTemplate mongoTemplate, String collection) {
-        Query query = Query.query(
-                        Criteria.where("id").in(ids)
-                                .and("state").is(state));
-        return resolveUserPage(pageable, mongoTemplate, collection, query);
-    }
-
+    /**
+     * Creates a {@link SpringDataMongodbQuery} for {@link com.netgrif.application.engine.adapter.spring.auth.domain.User}.
+     *
+     * @param predicate      the filter condition to apply to the query.
+     * @param mongoTemplate  the MongoDB template used to execute the query.
+     * @param collectionName the name of the collection to query.
+     * @return a {@link SpringDataMongodbQuery} configured with the given predicate and collection name.
+     */
     private SpringDataMongodbQuery<com.netgrif.application.engine.adapter.spring.auth.domain.User> createQuery(Predicate predicate, MongoTemplate mongoTemplate, String collectionName) {
         return new SpringDataMongodbQuery<>(mongoTemplate, com.netgrif.application.engine.adapter.spring.auth.domain.User.class, collectionName).where(predicate);
     }
 
+    /**
+     * Resolves a {@link PageImpl} of {@link User} entities.
+     * Helper method to execute queries and handle pagination.
+     *
+     * @param pageable      the pagination information
+     * @param mongoTemplate the MongoDB template
+     * @param collection    the collection name
+     * @param query         the query to execute
+     * @return a paginated implementation of users
+     */
     private static PageImpl<User> resolveUserPage(Pageable pageable, MongoTemplate mongoTemplate, String collection, Query query) {
         List<User> resultUserList = mongoTemplate.find(
                         query.with(pageable),
@@ -137,4 +278,4 @@ private static PageImpl<User> resolveUserPage(Pageable pageable, MongoTemplate m
         long total = mongoTemplate.count(query.limit(-1).skip(-1), com.netgrif.application.engine.adapter.spring.auth.domain.User.class);
         return new PageImpl<>(resultUserList, pageable, total);
     }
-}
+}
\ No newline at end of file
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/AuthorityService.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/AuthorityService.java
index 13860f6ca1..59aeb2b9e9 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/AuthorityService.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/service/AuthorityService.java
@@ -6,13 +6,41 @@
 
 import java.util.List;
 
+/**
+ * Service interface for managing {@link Authority} entities.
+ */
 public interface AuthorityService {
 
+    /**
+     * Retrieves all {@link Authority} entities with pagination support.
+     *
+     * @param pageable the {@link Pageable} object containing pagination information.
+     * @return a {@link Page} of {@link Authority} entities.
+     */
     Page<Authority> findAll(Pageable pageable);
 
+    /**
+     * Retrieves an existing {@link Authority} entity by its name or creates a new one if it does not exist.
+     *
+     * @param name the name of the {@link Authority}.
+     * @return the {@link Authority} entity.
+     */
     Authority getOrCreate(String name);
 
+    /**
+     * Retrieves a single {@link Authority} entity by its unique identifier.
+     *
+     * @param id the unique identifier of the {@link Authority}.
+     * @return the {@link Authority} entity.
+     */
     Authority getOne(String id);
 
+    /**
+     * Retrieves all {@link Authority} entities matching the given list of identifiers, with pagination support.
+     *
+     * @param ids      the list of unique identifiers for the {@link Authority} entities.
+     * @param pageable the {@link Pageable} object containing pagination information.
+     * @return a {@link Page} of {@link Authority} entities.
+     */
     Page<Authority> findAllByIds(List<String> ids, Pageable pageable);
-}
+}
\ No newline at end of file
diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/requestbodies/UserSearchRequestBody.java b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/requestbodies/UserSearchRequestBody.java
index f24856fb4e..9c2d38dc1f 100644
--- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/requestbodies/UserSearchRequestBody.java
+++ b/nae-user-common/src/main/java/com/netgrif/application/engine/auth/web/requestbodies/UserSearchRequestBody.java
@@ -4,14 +4,33 @@
 
 import java.util.List;
 
+/**
+ * Represents the request body for searching users with specific criteria.
+ */
 @Data
 public class UserSearchRequestBody {
 
+    /**
+     * The ID of the realm in which to search for users.
+     * This field is used to scope the search to a particular multi-tenancy realm.
+     */
     private String realmId;
 
+    /**
+     * A full-text search string to filter the users.
+     * The search is typically applied to user attributes like name, username, or email.
+     */
     private String fulltext;
 
+    /**
+     * A list of roles the users must have.
+     * The search will include users who possess all of the specified roles.
+     */
     private List<String> roles;
 
+    /**
+     * A list of roles the users must not have.
+     * The search will exclude users who possess any of the specified roles.
+     */
     private List<String> negativeRoles;
 }
\ No newline at end of file

From 28da58362793dc938394eefe59dc931e8953baae Mon Sep 17 00:00:00 2001
From: Jozef Daxner <daxner.dodo@gmail.com>
Date: Fri, 4 Jul 2025 16:05:25 +0200
Subject: [PATCH 7/9] [NAE-2122] Implement Structured and Efficient Pagination
 in gRPC

- Fix tests
---
 .../domain/roles/ProcessRoleRepository.java   | 25 ++++++++-----------
 .../petrinet/service/PetriNetService.java     | 19 +++++++++-----
 .../workflow/service/WorkflowService.java     |  6 -----
 .../engine/action/AssignActionTest.groovy     |  5 ++--
 .../engine/auth/UserServiceTest.groovy        |  4 +--
 .../petrinet/domain/FunctionsTest.groovy      |  5 ++--
 .../petrinet/domain/PetriNetTest.groovy       |  3 ++-
 7 files changed, 33 insertions(+), 34 deletions(-)

diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java
index 78b4835ab5..ed42cbc04e 100755
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleRepository.java
@@ -103,19 +103,15 @@ default Set<ProcessRole> findAllByIdsSet(Collection<String> ids) {
 
         List<ObjectId> forObjectIds = partitionedIds.get(false).stream()
                 .map(ObjectId::new)
-                .collect(Collectors.toList());
+                .toList();
 
-        List<ObjectId> forNetworkObjectIds = new ArrayList<>();
-        List<String> forNetworkNetworkIds = new ArrayList<>();
-        partitionedIds.get(true).forEach(id -> {
-            String[] parts = id.split(ProcessResourceId.ID_SEPARATOR);
-            forNetworkNetworkIds.add(parts[0]);
-            forNetworkObjectIds.add(new ObjectId(parts[1]));
-        });
+        List<ProcessResourceId> processResourceIds = partitionedIds.get(true).stream()
+                .map(ProcessResourceId::new)
+                .toList();
 
         List<ProcessRole> processRoles = new ArrayList<>();
         processRoles.addAll(findByObjectIds(forObjectIds));
-        processRoles.addAll(findByNetworkIdsAndObjectIds(forNetworkNetworkIds, forNetworkObjectIds));
+        processRoles.addAll(findByProcessResourceIds(processResourceIds));
         return new HashSet<>(processRoles);
     }
 
@@ -148,12 +144,11 @@ default Optional<ProcessRole> findByCompositeId(String compositeId) {
     Optional<ProcessRole> findByNetworkIdAndObjectId(String networkId, ObjectId objectId);
 
     /**
-     * Finds all {@link ProcessRole} entities matching the specified network IDs and object IDs.
+     * Finds all {@link ProcessRole} entities by a collection of composite resource IDs.
      *
-     * @param networkIds a collection of short process IDs
-     * @param objectIds  a collection of object IDs
-     * @return a {@link List} of {@link ProcessRole} entities
+     * @param compositeIds a collection of {@link ProcessResourceId} instances representing the composite IDs to filter
+     * @return a {@link List} of {@link ProcessRole} entities matching the provided composite IDs
      */
-    @Query("{ $or: [ { '_id.shortProcessId': ?0, '_id.objectId': ?1 } ] }")
-    List<ProcessRole> findByNetworkIdsAndObjectIds(Collection<String> networkIds, Collection<ObjectId> objectIds);
+    @Query("{ '_id': { $in: ?0 } }")
+    List<ProcessRole> findByProcessResourceIds(Collection<ProcessResourceId> compositeIds);
 }
\ No newline at end of file
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
index 447aa56a86..6cc675bb3f 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
@@ -358,12 +358,19 @@ public Page<PetriNetReference> getReferencesByVersion(Version version, LoggedUse
         Page<PetriNetReference> references;
         if (version == null) {
             GroupOperation groupByIdentifier = Aggregation.group("identifier").max("version").as("version");
-            Aggregation aggregation = Aggregation.newAggregation(
-                    groupByIdentifier,
-                    Aggregation.sort(pageable.getSort()),
-                    Aggregation.skip((long) pageable.getPageNumber() * pageable.getPageSize()),
-                    Aggregation.limit(pageable.getPageSize())
-            );
+            Aggregation aggregation;
+            if (pageable == null || pageable.isUnpaged()) {
+                 aggregation = Aggregation.newAggregation(
+                         groupByIdentifier
+                );
+            } else {
+                aggregation = Aggregation.newAggregation(
+                        groupByIdentifier,
+                        Aggregation.sort(pageable.getSort()),
+                        Aggregation.skip((long) pageable.getPageNumber() * pageable.getPageSize()),
+                        Aggregation.limit(pageable.getPageSize())
+                );
+            }
             List<Document> results = mongoTemplate.aggregate(aggregation, "petriNet", Document.class).getMappedResults();
             List<PetriNetReference> referenceList = results.stream()
                     .map(doc -> {
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
index 84dd4d7a90..e6dca3319d 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
@@ -456,12 +456,6 @@ public boolean removeTasksFromCase(List<Task> tasks, Case useCase) {
         return useCase.removeTasks(tasks);
     }
 
-    @Override
-    public Case decrypt(Case useCase) {
-        decryptDataSet(useCase);
-        return useCase;
-    }
-
     @Override
     public Page<Case> searchAll(Predicate predicate) {
         return search(predicate, new FullPageRequest());
diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignActionTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignActionTest.groovy
index c8138c3274..af79e522a0 100644
--- a/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignActionTest.groovy
+++ b/application-engine/src/test/groovy/com/netgrif/application/engine/action/AssignActionTest.groovy
@@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.extension.ExtendWith
 import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.data.domain.Pageable
 import org.springframework.data.mongodb.core.MongoTemplate
 import org.springframework.hateoas.MediaTypes
 import org.springframework.http.MediaType
@@ -148,8 +149,8 @@ class AssignActionTest {
         IUser updatedUser = userService.findByEmail(USER_EMAIL, null)
         Set<ProcessRole> roles = updatedUser.getProcessRoles()
 
-        String adminMainId = processRoleRepository.findAllByName_DefaultValue("admin_main")?.first()?.stringId
-        String adminSecondaryId = processRoleRepository.findAllByName_DefaultValue("admin_secondary")?.first()?.stringId
+        String adminMainId = processRoleRepository.findAllByName_DefaultValue("admin_main", Pageable.ofSize(1))?.first()?.stringId
+        String adminSecondaryId = processRoleRepository.findAllByName_DefaultValue("admin_secondary", Pageable.ofSize(1))?.first()?.stringId
 
         assert roles.find { it.stringId == adminMainId }
         assert roles.find { it.stringId == adminSecondaryId }
diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/auth/UserServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/auth/UserServiceTest.groovy
index 5b7020b010..1bff0d38ef 100644
--- a/application-engine/src/test/groovy/com/netgrif/application/engine/auth/UserServiceTest.groovy
+++ b/application-engine/src/test/groovy/com/netgrif/application/engine/auth/UserServiceTest.groovy
@@ -103,7 +103,7 @@ class UserServiceTest {
         user = userService.addRole(user, dummyRole.get_id())
         assert user.getProcessRoles().size() == 2 && user.getProcessRoles().stream().anyMatch { it.stringId == dummyRole.stringId }
 
-        List<IUser> userList = userService.findAllByProcessRoles(Set.of(dummyRole.get_id()), null)
+        List<IUser> userList = userService.findAllByProcessRoles(Set.of(dummyRole.get_id()), null, Pageable.unpaged())
         assert userList.size() == 1 && userList.getFirst().stringId == user.stringId
     }
 
@@ -114,7 +114,7 @@ class UserServiceTest {
         ((User) user).setExpirationDate(LocalDateTime.now())
         userService.saveUser(user, null)
 
-        List<IUser> userList = userService.findAllByStateAndExpirationDateBefore(UserState.INACTIVE, LocalDateTime.now(), null)
+        List<IUser> userList = userService.findAllByStateAndExpirationDateBefore(UserState.INACTIVE, LocalDateTime.now(), null, Pageable.unpaged())
         assert userList.size() == 1 && userList.getFirst().stringId == user.stringId
     }
 
diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/FunctionsTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/FunctionsTest.groovy
index 6a33aa08bb..acd971bd0a 100644
--- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/FunctionsTest.groovy
+++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/FunctionsTest.groovy
@@ -19,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.beans.factory.annotation.Value
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.core.io.Resource
+import org.springframework.data.domain.Pageable
 import org.springframework.test.context.ActiveProfiles
 import org.springframework.test.context.junit.jupiter.SpringExtension
 
@@ -123,7 +124,7 @@ class FunctionsTest {
     @Test
     void testNamespaceFunctionException() {
         assertThrows(NullPointerException.class, () -> {
-            def nets = petriNetService.getByIdentifier(FUNCTION_RES_IDENTIFIER)
+            def nets = petriNetService.getByIdentifier(FUNCTION_RES_IDENTIFIER, Pageable.unpaged())
             if (nets) {
                 nets.each {
                     petriNetService.deletePetriNet(it.getStringId(), userService.transformToLoggedUser(userService.getLoggedOrSystem()))
@@ -141,7 +142,7 @@ class FunctionsTest {
     @Test
     void testNewNetVersionMissingMethodException() {
         assertThrows(NullPointerException.class, () -> {
-            def nets = petriNetService.getByIdentifier(FUNCTION_TEST_IDENTIFIER)
+            def nets = petriNetService.getByIdentifier(FUNCTION_TEST_IDENTIFIER, Pageable.unpaged())
             if (nets) {
                 nets.each {
                     petriNetService.deletePetriNet(it.getStringId(), userService.transformToLoggedUser(userService.getLoggedOrSystem()))
diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy
index 72b43e2d37..3df33d1905 100644
--- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy
+++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy
@@ -18,6 +18,7 @@ import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.beans.factory.annotation.Value
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.core.io.Resource
+import org.springframework.data.domain.Pageable
 import org.springframework.test.context.ActiveProfiles
 import org.springframework.test.context.junit.jupiter.SpringExtension
 
@@ -88,7 +89,7 @@ class PetriNetTest {
         def netOptional3 = petriNetService.importPetriNet(netResource2.inputStream, VersionType.MAJOR, superCreator.loggedSuper)
         assert netOptional3.getNet() != null
 
-        def nets = petriNetService.getReferencesByVersion(null, superCreator.loggedSuper, Locale.UK)
+        def nets = petriNetService.getReferencesByVersion(null, superCreator.loggedSuper, Locale.UK, Pageable.unpaged())
         assert nets.findAll { it.identifier in [netOptional.getNet().identifier, netOptional3.getNet().identifier] }.size() == 2
         assert nets.find { it.identifier == "new_model" }.version == "1.0.0"
         assert nets.find { it.identifier == "test" }.version == "2.0.0"

From cb7a59f854feec7b331ba25ad8e39426b7de693d Mon Sep 17 00:00:00 2001
From: Jozef Daxner <daxner.dodo@gmail.com>
Date: Fri, 18 Jul 2025 13:37:06 +0200
Subject: [PATCH 8/9] [NAE-2122] Implement Structured and Efficient Pagination
 in gRPC

- changes/fixes based on PR comments.
---
 .../engine/petrinet/service/PetriNetService.java         | 2 +-
 .../engine/petrinet/service/ProcessRoleService.java      | 2 +-
 .../engine/startup/runner/SuperCreatorRunner.java        | 2 +-
 .../adapter/spring/utils/PaginationProperties.java       | 2 +-
 .../application/engine/auth/service/UserServiceImpl.java | 9 ++-------
 5 files changed, 6 insertions(+), 11 deletions(-)

diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
index 6cc675bb3f..e2ba27cc7e 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
@@ -385,7 +385,7 @@ public Page<PetriNetReference> getReferencesByVersion(Version version, LoggedUse
             );
             AggregationResults<Document> countResults = mongoTemplate.aggregate(
                     countAggregation,
-                    "your_collection_name",
+                    "petriNet",
                     Document.class
             );
             long total = countResults.getUniqueMappedResult() != null
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java
index b43fba519b..bc97690e3d 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java
@@ -235,7 +235,7 @@ public ProcessRole findById(ProcessResourceId processResourceId) {
     @Override
     public List<ProcessRole> saveAll(Iterable<ProcessRole> entities) {
         return StreamSupport.stream(entities.spliterator(), false).map(processRole -> {
-            if (!processRole.isGlobal() || processRoleRepository.findByImportId(processRole.getImportId()) == null) {
+            if (!processRole.isGlobal() || processRoleRepository.findByImportId(processRole.getImportId()).isEmpty()) {
                 return processRoleRepository.save(processRole);
             }
             return null;
diff --git a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java
index ad3ec6d87c..00e3bb9b56 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/startup/runner/SuperCreatorRunner.java
@@ -113,7 +113,7 @@ public void setAllProcessRoles() {
     }
 
     public void setAllAuthorities() {
-        superUser.setAuthorities(Set.copyOf(authorityService.findAll(Pageable.unpaged()).stream().toList()));
+        superUser.setAuthorities(new HashSet<>(authorityService.findAll(Pageable.unpaged()).stream().toList()));
         superUser = userService.saveUser(superUser, null);
     }
 
diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java
index d2ccbaf0a6..5e0cf46de7 100644
--- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java
+++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/PaginationProperties.java
@@ -10,7 +10,7 @@
  */
 @Data
 @Configuration
-@ConfigurationProperties(prefix = "netgrif.pagination")
+@ConfigurationProperties(prefix = "netgrif.engine.pagination")
 public class PaginationProperties {
 
     /**
diff --git a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
index 63dfa7bcb2..029b9dd11e 100644
--- a/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
+++ b/nae-user-ce/src/main/java/com/netgrif/application/engine/auth/service/UserServiceImpl.java
@@ -252,13 +252,8 @@ public void addAllRolesToAdminByUsername(String username) {
         }
         IUser user = userOptional.get();
 
-        Pageable pageable = PageRequest.of(0, paginationProperties.getBackendPageSize());
-        Page<ProcessRole> processRoles;
-        do {
-            processRoles = processRoleService.findAll(pageable);
-            user.getProcessRoles().addAll(processRoles.getContent());
-            pageable = pageable.next();
-        } while (processRoles.hasNext());
+        Page<ProcessRole> processRoles = processRoleService.findAll(Pageable.unpaged());
+        user.getProcessRoles().addAll(processRoles.getContent());
 
         saveUser(user, user.getRealmId());
     }

From c1af45df3e830c9f8148bb26dcd51e42d04572c0 Mon Sep 17 00:00:00 2001
From: Machac <machac@netgrif.com>
Date: Sun, 20 Jul 2025 20:42:17 +0200
Subject: [PATCH 9/9] [NAE-2122] Implement Structured and Efficient Pagination
 in gRPC

- Updated tests in `PetriNetTest.groovy` by introducing the `Page<PetriNetReference>` type for the `nets` variable to ensure type safety and clarity.
- Imported `Page` and `PetriNetReference` and adjusted relevant usages in `PetriNetTest.groovy`.
- Refined total result calculation logic in `PetriNetService` by transitioning the `total` value handling to use a `Number` type, avoiding potential null pointer exceptions.
- Adopted safer numeric type conversions ensuring robust and error-free handling of MongoDB aggregation results.
- Maintained backward compatibility while improving code readability and reliability.
---
 .../engine/petrinet/service/PetriNetService.java         | 9 ++++++---
 .../engine/petrinet/domain/PetriNetTest.groovy           | 4 +++-
 2 files changed, 9 insertions(+), 4 deletions(-)

diff --git a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
index e2ba27cc7e..8ded889885 100644
--- a/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
+++ b/application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
@@ -388,9 +388,12 @@ public Page<PetriNetReference> getReferencesByVersion(Version version, LoggedUse
                     "petriNet",
                     Document.class
             );
-            long total = countResults.getUniqueMappedResult() != null
-                    ? countResults.getUniqueMappedResult().getLong("total")
-                    : 0L;
+
+            Number totalNumber = countResults.getUniqueMappedResult() != null
+                    ? countResults.getUniqueMappedResult().get("total", Number.class)
+                    : 0;
+            long total = totalNumber != null ? totalNumber.longValue() : 0L;
+
             references = new PageImpl<>(referenceList, pageable, total);
         } else {
             references = repository.findAllByVersion(version, pageable).map(net -> transformToReference(net, locale));
diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy
index 3df33d1905..24bc1fa01f 100644
--- a/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy
+++ b/application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy
@@ -10,6 +10,7 @@ import com.netgrif.application.engine.objects.petrinet.domain.arcs.ReadArc
 import com.netgrif.application.engine.objects.petrinet.domain.arcs.ResetArc
 import com.netgrif.application.engine.petrinet.domain.roles.ProcessRoleRepository
 import com.netgrif.application.engine.petrinet.service.interfaces.IPetriNetService
+import com.netgrif.application.engine.petrinet.web.responsebodies.PetriNetReference
 import com.netgrif.application.engine.startup.runner.SuperCreatorRunner
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
@@ -18,6 +19,7 @@ import org.springframework.beans.factory.annotation.Autowired
 import org.springframework.beans.factory.annotation.Value
 import org.springframework.boot.test.context.SpringBootTest
 import org.springframework.core.io.Resource
+import org.springframework.data.domain.Page
 import org.springframework.data.domain.Pageable
 import org.springframework.test.context.ActiveProfiles
 import org.springframework.test.context.junit.jupiter.SpringExtension
@@ -89,7 +91,7 @@ class PetriNetTest {
         def netOptional3 = petriNetService.importPetriNet(netResource2.inputStream, VersionType.MAJOR, superCreator.loggedSuper)
         assert netOptional3.getNet() != null
 
-        def nets = petriNetService.getReferencesByVersion(null, superCreator.loggedSuper, Locale.UK, Pageable.unpaged())
+        Page<PetriNetReference> nets = petriNetService.getReferencesByVersion(null, superCreator.loggedSuper, Locale.UK, Pageable.unpaged())
         assert nets.findAll { it.identifier in [netOptional.getNet().identifier, netOptional3.getNet().identifier] }.size() == 2
         assert nets.find { it.identifier == "new_model" }.version == "1.0.0"
         assert nets.find { it.identifier == "test" }.version == "2.0.0"
