From d47b5e0d47154d51a2772d26fbcc513ad6a5caa6 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Thu, 14 May 2026 17:38:20 +0200 Subject: [PATCH 1/7] Add support for additional parameters in case and task APIs Introduced methods to handle additional parameters across case and task search and count operations. Created a constants class for default process parameter names and added overloads with parameter maps to improve flexibility. --- .../engine/actions/ActionApiImpl.java | 42 ++++++++++- .../adapter/spring/actions/ActionApi.java | 75 +++++++++++++++++++ .../actions/ActionApiDefaultParamNames.java | 8 ++ 3 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiDefaultParamNames.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java b/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java index e53d436208..fb6c51ee4c 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java @@ -108,12 +108,27 @@ public SetDataEventOutcome setData(String taskId, Map searchCases(String processIdentifier, Predicate predicate, Pageable pageable) { + return searchCases(processIdentifier, predicate, pageable, new HashMap<>()); + } + + @Override + public Page searchCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection) { + return searchCases(elasticStringQueries, authPrincipalDto, pageable, isIntersection, new HashMap<>()); + } + + @Override + public Long countCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Boolean isIntersection) { + return countCases(elasticStringQueries, authPrincipalDto, isIntersection, new HashMap<>()); + } + + @Override + public Page searchCases(String processIdentifier, Predicate predicate, Pageable pageable, Map params) { log.debug("Searching cases for process identifier [{}] with predicate [{}], pageable [{}]", processIdentifier, predicate, pageable); return workflowService.search(predicate, pageable); } @Override - public Page searchCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection) { + public Page searchCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection, Map params) { log.debug("Searching cases for elastic queries [{}] with auth principal [{}], pageable [{}], intersect [{}]", elasticStringQueries, authPrincipalDto, pageable, isIntersection); boolean intersect = Boolean.TRUE.equals(isIntersection); List caseSearchRequests = elasticStringQueries.stream().map(query -> CaseSearchRequest.builder().query(query).build()).toList(); @@ -124,7 +139,7 @@ public Page searchCases(List elasticStringQueries, AuthPrincipalDt } @Override - public Long countCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Boolean isIntersection) { + public Long countCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Boolean isIntersection, Map params) { log.debug("Counting cases for elastic queries [{}] with auth principal [{}], intersect [{}]", elasticStringQueries, authPrincipalDto, isIntersection); boolean intersect = Boolean.TRUE.equals(isIntersection); List caseSearchRequests = elasticStringQueries.stream().map(query -> CaseSearchRequest.builder().query(query).build()).toList(); @@ -159,12 +174,22 @@ public DeleteCaseEventOutcome deleteCase(String caseId, Map para @Override public Page searchTasks(String processIdentifier, Predicate predicate, Pageable pageable) { + return searchTasks(processIdentifier, predicate, pageable, new HashMap<>()); + } + + @Override + public Page searchTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection) { + return searchTasks(elasticStringQueries, authPrincipalDto, pageable, isIntersection, new HashMap<>()); + } + + @Override + public Page searchTasks(String processIdentifier, Predicate predicate, Pageable pageable, Map params) { log.debug("Searching tasks for process identifier [{}] with predicate [{}], pageable [{}]", processIdentifier, predicate, pageable); return taskService.search(predicate, pageable); } @Override - public Page searchTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection) { + public Page searchTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection, Map params) { log.debug("Searching tasks for elastic queries [{}] with auth principal [{}], pageable [{}], intersect [{}]", elasticStringQueries, authPrincipalDto, pageable, isIntersection); boolean intersect = Boolean.TRUE.equals(isIntersection); List taskSearchRequests = elasticStringQueries.stream().map(query -> ElasticTaskSearchRequest.builder().query(query).build()).toList(); @@ -174,6 +199,17 @@ public Page searchTasks(List elasticStringQueries, AuthPrincipalDt return elasticTaskService.search(taskSearchRequests, loggedUser, pageable, locale, intersect); } + @Override + public Long countTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Boolean isIntersection, Map params) { + log.debug("Counting tasks for elastic queries [{}] with auth principal [{}], intersect [{}]", elasticStringQueries, authPrincipalDto, isIntersection); + boolean intersect = Boolean.TRUE.equals(isIntersection); + List taskSearchRequests = elasticStringQueries.stream().map(query -> ElasticTaskSearchRequest.builder().query(query).build()).toList(); + LoggedUser loggedUser = ActorTransformer.toLoggedUser(resolveAbstractUser(authPrincipalDto)); + Locale locale = LocaleContextHolder.getLocale(); + log.trace("Counting tasks for elastic queries [{}] with auth principal [{}], intersect [{}], locale [{}]", elasticStringQueries, authPrincipalDto, isIntersection, locale); + return elasticTaskService.count(taskSearchRequests, loggedUser, locale, intersect); + } + @Override public AssignTaskEventOutcome assignTask(String taskId, AuthPrincipalDto authPrincipalDto, Map params) throws TransitionNotExecutableException { log.debug("Assigning task [{}] with auth principal [{}] and params [{}]", taskId, authPrincipalDto, params); diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApi.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApi.java index 09db189894..e3f2b0376c 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApi.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApi.java @@ -94,6 +94,44 @@ public interface ActionApi { */ Long countCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Boolean isIntersection); + /** + * Searches for cases matching the given predicate. + * + * @param processIdentifier reserved for interface compatibility; this implementation + * does not filter by process identifier and returns cases + * from all processes. Use the predicate parameter to filter + * by specific process(es) if needed. + * @param predicate the criteria for filtering cases + * @param pageable the pagination information + * @param params additional parameters for the operation + * @return a page of cases matching the criteria + */ + Page searchCases(String processIdentifier, Predicate predicate, Pageable pageable, Map params); + + /** + * Searches for cases using elastic search queries. + * + * @param elasticStringQueries a list of queries for filtering cases + * @param authPrincipalDto the authorization principal used for the search + * @param pageable the pagination information + * @param isIntersection true to intersect results of all queries; false for union + * @param params additional parameters for the operation + * @return a page of cases matching the criteria + */ + Page searchCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection, Map params); + + + /** + * Counts the number of cases that match the provided elastic search queries. + * + * @param elasticStringQueries a list of elastic search queries used for filtering cases + * @param authPrincipalDto the authorization principal for the operation + * @param isIntersection true to intersect the results of all queries; false for union + * @param params additional parameters for the operation + * @return the total number of cases matching the criteria + */ + Long countCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Boolean isIntersection, Map params); + /** * Creates a new case identified by the given process identifier. * @@ -148,6 +186,43 @@ public interface ActionApi { */ Page searchTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection); + /** + * Searches for tasks matching the given predicate. + * + * @param processIdentifier reserved for interface compatibility; this implementation + * does not filter by process identifier and returns tasks + * from all processes. Use the predicate parameter to filter + * by specific process(es) if needed. + * @param predicate the criteria for filtering tasks + * @param pageable the pagination information + * @param params additional params for the operation + * @return a page of tasks matching the criteria + */ + Page searchTasks(String processIdentifier, Predicate predicate, Pageable pageable, Map params); + + /** + * Searches for tasks using elastic search queries. + * + * @param elasticStringQueries a list of queries for filtering tasks + * @param authPrincipalDto the authorization principal used for the search + * @param pageable the pagination information + * @param isIntersection true to intersect results of all queries; false for union + * @param params additional params for the operation + * @return a page of tasks matching the criteria + */ + Page searchTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection, Map params); + + /** + * Counts tasks using elastic search queries. + * + * @param elasticStringQueries a list of queries for filtering tasks + * @param authPrincipalDto the authorization principal used for the search + * @param isIntersection true to intersect results of all queries; false for union + * @param params additional params for the operation + * @return a page of tasks matching the criteria + */ + Long countTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Boolean isIntersection, Map params); + /** * Assigns a specific task to a user. * diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiDefaultParamNames.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiDefaultParamNames.java new file mode 100644 index 0000000000..fe7358e7bd --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiDefaultParamNames.java @@ -0,0 +1,8 @@ +package com.netgrif.application.engine.adapter.spring.actions; + +public final class ActionApiDefaultParamNames { + public static final String PROCESS_IDENTIFIER = "processIdentifier"; + public static final String PROCESS_ID = "processId"; + public static final String PROCESS_VERSION = "processVersion"; + public static final String PROCESS_RESOURCE_ID = "processResourceId"; +} From 31fc2f30385ba382214a42085849829d5f1814c5 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 15 May 2026 13:22:47 +0200 Subject: [PATCH 2/7] [NAE-2423] Extend Action API with wrapper support Replaced the `AuthPrincipalDto` class with a Java record for improved immutability and concise representation. Adjusted related code to align with the record's accessors and updated the system username constant in `UserConstants` for consistency. --- .../engine/actions/ActionApiImpl.java | 4 ++-- .../objects/auth/constants/UserConstants.java | 1 + .../objects/auth/dto/AuthPrincipalDto.java | 18 ++++++------------ .../engine/auth/service/UserServiceImpl.java | 2 +- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java b/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java index fb6c51ee4c..cf4d8c5503 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java @@ -329,7 +329,7 @@ private AbstractUser resolveAbstractUser(AuthPrincipalDto authPrincipalDto) { if (authPrincipalDto == null) { throw new IllegalArgumentException("AuthPrincipalDto cannot be null."); } - Optional userOptional = userService.findUserByUsername(authPrincipalDto.getUsername(), authPrincipalDto.getRealmId()); - return userOptional.orElseThrow(() -> new IllegalArgumentException("User with username [%s] and realm ID [%s] not found".formatted(authPrincipalDto.getUsername(), authPrincipalDto.getRealmId()))); + Optional userOptional = userService.findUserByUsername(authPrincipalDto.username(), authPrincipalDto.realmId()); + return userOptional.orElseThrow(() -> new IllegalArgumentException("User with username [%s] and realm ID [%s] not found".formatted(authPrincipalDto.username(), authPrincipalDto.realmId()))); } } diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/constants/UserConstants.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/constants/UserConstants.java index 75c7769fde..584dc83bfb 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/constants/UserConstants.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/constants/UserConstants.java @@ -1,6 +1,7 @@ package com.netgrif.application.engine.objects.auth.constants; public final class UserConstants { + public static final String SYSTEM_USER_USERNAME = "system"; public static final String SYSTEM_USER_EMAIL = "engine@netgrif.com"; public static final String SYSTEM_USER_NAME = "application"; public static final String SYSTEM_USER_SURNAME = "engine"; diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java index 3333122aae..750665ac24 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java @@ -8,18 +8,12 @@ import java.io.Serial; import java.io.Serializable; -@Data -public class AuthPrincipalDto implements Serializable { - +public record AuthPrincipalDto(String username, + String realmId, + @ToString.Exclude + @EqualsAndHashCode.Exclude + @JsonIgnore + String sessionId) implements Serializable { @Serial private static final long serialVersionUID = 6725518942728316525L; - - private String username; - - private String realmId; - - @ToString.Exclude - @EqualsAndHashCode.Exclude - @JsonIgnore - private String sessionId; } 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 7a35d67771..c5e7e2c1fd 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 @@ -573,7 +573,7 @@ public AbstractUser createSystemUser() { User system = (User) findByEmail(UserConstants.SYSTEM_USER_EMAIL, null); if (system == null) { system = new User(); - system.setUsername(UserConstants.SYSTEM_USER_EMAIL); + system.setUsername(UserConstants.SYSTEM_USER_USERNAME); system.setEmail(UserConstants.SYSTEM_USER_EMAIL); system.setPassword("n/a"); system.setFirstName(UserConstants.SYSTEM_USER_NAME); From 76c13672b4f329a7c16a545427d4719645f96223 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 18 May 2026 08:16:54 +0200 Subject: [PATCH 3/7] Update countTasks method return type and JavaDoc Modified the countTasks method to return the number of tasks instead of a page of tasks. Also updated the corresponding JavaDoc to reflect the change accurately. Removed an unused import to improve code cleanliness. --- .../application/engine/adapter/spring/actions/ActionApi.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApi.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApi.java index e3f2b0376c..cd4084fb16 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApi.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApi.java @@ -18,7 +18,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import java.io.FileNotFoundException; import java.io.IOException; import java.util.Map; import java.util.List; @@ -219,7 +218,7 @@ public interface ActionApi { * @param authPrincipalDto the authorization principal used for the search * @param isIntersection true to intersect results of all queries; false for union * @param params additional params for the operation - * @return a page of tasks matching the criteria + * @return number of tasks matching the query */ Long countTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Boolean isIntersection, Map params); From eeb7ba6d4f0b5083cb41e4613ae5779741d365c8 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 18 May 2026 15:14:00 +0200 Subject: [PATCH 4/7] Refactor tests to replace email usage with username constants Updated test cases to consistently use `username` instead of `email` for identifying users. This ensures better alignment with domain constants and improved code maintainability. Changes include replacing hardcoded email references with `UserConstants.SYSTEM_USER_USERNAME` or user `username` fields where applicable. --- .../application/engine/elastic/DataSearchRequestTest.groovy | 4 ++-- .../application/engine/event/GroovyShellFactoryTest.groovy | 2 +- .../engine/orgstructure/groups/GroupServiceTest.groovy | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/elastic/DataSearchRequestTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/elastic/DataSearchRequestTest.groovy index 9fcd5a7353..2c6fa62c6d 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/elastic/DataSearchRequestTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/elastic/DataSearchRequestTest.groovy @@ -196,8 +196,8 @@ class DataSearchRequestTest { new AbstractMap.SimpleEntry("fileList.fileNameValue.keyword" as String, "multifile2" as String), new AbstractMap.SimpleEntry("fileList.fileExtensionValue.keyword" as String, "txt" as String), new AbstractMap.SimpleEntry("fileList.fileExtensionValue.keyword" as String, "pdf" as String), - new AbstractMap.SimpleEntry("userList" as String, "${testUser1.name} ${testUser1.email}" as String), - new AbstractMap.SimpleEntry("userList" as String, "${testUser2.name} ${testUser2.email}" as String), + new AbstractMap.SimpleEntry("userList" as String, "${testUser1.name} ${testUser1.username}" as String), + new AbstractMap.SimpleEntry("userList" as String, "${testUser2.name} ${testUser2.username}" as String), new AbstractMap.SimpleEntry("userList" as String, "${groupService.getDefaultSystemGroup().getFullName()}" as String), new AbstractMap.SimpleEntry("userList.usernameValue.keyword" as String, "${testUser1.username}" as String), new AbstractMap.SimpleEntry("userList.usernameValue.keyword" as String, "${testUser2.username}" as String), diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/event/GroovyShellFactoryTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/event/GroovyShellFactoryTest.groovy index 97216ff8bf..09d6ddd72d 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/event/GroovyShellFactoryTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/event/GroovyShellFactoryTest.groovy @@ -72,7 +72,7 @@ class GroovyShellFactoryTest { void roleActionsTest() { roleService.metaClass.groovyShellTestMethod = { String string, I18nString i18nString -> println("groovyShellTestMethod") } - def user = userService.findUserByUsername(userService.getSystem().getEmail(), null) + def user = userService.findUserByUsername(userService.getSystem().getUsername(), null) def processRoleCount = user.get().processRoles.size() def roles = roleService.findAllByNetStringId(net.getStringId()) assert roles.size() == 1 diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/orgstructure/groups/GroupServiceTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/orgstructure/groups/GroupServiceTest.groovy index e577779fae..8701f7d64e 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/orgstructure/groups/GroupServiceTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/orgstructure/groups/GroupServiceTest.groovy @@ -3,6 +3,7 @@ package com.netgrif.application.engine.orgstructure.groups import com.netgrif.application.engine.TestHelper import com.netgrif.application.engine.auth.service.GroupService import com.netgrif.application.engine.auth.service.UserService +import com.netgrif.application.engine.objects.auth.constants.UserConstants import com.netgrif.application.engine.objects.auth.domain.Authority import com.netgrif.application.engine.objects.auth.domain.Group import com.netgrif.application.engine.objects.auth.domain.QGroup @@ -88,7 +89,7 @@ class GroupServiceTest { QGroup qGroup = new QGroup("group") Group group = groupService.findByPredicate(qGroup.identifier.eq("CUSTOM_GROUP_1"), new FullPageRequest()).getContent().get(0) groupService.addUser(userService.findUserByUsername(CUSTOMER_USER_MAIL, null).get(), group) - groupService.addUser(userService.findUserByUsername("engine@netgrif.com", null).get(), group) + groupService.addUser(userService.findUserByUsername(UserConstants.SYSTEM_USER_USERNAME, null).get(), group) return group } From 3bcc865a492ff96b4dc2a4869028e1c41ef069a5 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 18 May 2026 15:25:42 +0200 Subject: [PATCH 5/7] Refactor AuthPrincipalDto to include custom toString, equals, and hashCode Enhanced the AuthPrincipalDto class by overriding toString, equals, and hashCode methods for better readability and consistency in object comparisons and hashing. This change improves debugging and ensures hash-based collections work as expected. The sessionId remains excluded from these methods to preserve confidentiality. --- .../objects/auth/dto/AuthPrincipalDto.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java index 750665ac24..1d8e99119e 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java @@ -10,10 +10,29 @@ public record AuthPrincipalDto(String username, String realmId, - @ToString.Exclude - @EqualsAndHashCode.Exclude @JsonIgnore String sessionId) implements Serializable { @Serial private static final long serialVersionUID = 6725518942728316525L; + + @Override + public String toString() { + return "AuthPrincipalDto{" + + "username='" + username + '\'' + + ", realmId='" + realmId + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + AuthPrincipalDto that = (AuthPrincipalDto) o; + return username.equals(that.username) && realmId.equals(that.realmId); + } + + @Override + public int hashCode() { + return java.util.Objects.hash(username, realmId); + } } From 1a9c1a577f5d873549ca901b0d488a88ddecf43d Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 18 May 2026 16:36:35 +0200 Subject: [PATCH 6/7] Fix username lookup in GroovyShellFactoryTest Updated the test to retrieve the system user by username instead of email. This aligns with the correct method usage and improves test accuracy. --- .../application/engine/event/GroovyShellFactoryTest.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-engine/src/test/groovy/com/netgrif/application/engine/event/GroovyShellFactoryTest.groovy b/application-engine/src/test/groovy/com/netgrif/application/engine/event/GroovyShellFactoryTest.groovy index 09d6ddd72d..5011988f2e 100644 --- a/application-engine/src/test/groovy/com/netgrif/application/engine/event/GroovyShellFactoryTest.groovy +++ b/application-engine/src/test/groovy/com/netgrif/application/engine/event/GroovyShellFactoryTest.groovy @@ -81,7 +81,7 @@ class GroovyShellFactoryTest { new HashSet(roles.collect { it._id } + user.get().processRoles.collect { it._id }), new LoggedUserImpl(new ObjectId(), null, "a", "a", "", "b", "test@mail.com", "", null, null, null, null) ) - user = userService.findUserByUsername(userService.getSystem().getEmail(), null) + user = userService.findUserByUsername(userService.getSystem().getUsername(), null) assert user.get().processRoles.size() == processRoleCount + 1 } From bf35c9e28bf88623628663aaaa320bbf054af58a Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 18 May 2026 16:43:28 +0200 Subject: [PATCH 7/7] Ensure null checks in AuthPrincipalDto equals method Added validation to prevent null pointer exceptions by checking if `username` or `realmId` is null before proceeding with equality checks. This change improves robustness and avoids potential runtime errors. --- .../application/engine/objects/auth/dto/AuthPrincipalDto.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java index 1d8e99119e..2632ce05b8 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java @@ -7,6 +7,7 @@ import java.io.Serial; import java.io.Serializable; +import java.util.Objects; public record AuthPrincipalDto(String username, String realmId, @@ -28,7 +29,7 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AuthPrincipalDto that = (AuthPrincipalDto) o; - return username.equals(that.username) && realmId.equals(that.realmId); + return Objects.equals(username, that.username) && Objects.equals(realmId, that.realmId); } @Override