From 35b953e5b1e192fb66ce7611642de2ada843a2f4 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 7 Oct 2025 15:00:40 +0200 Subject: [PATCH 01/20] - simplified AbstractActionAPI --- .../spring/actions/AbstractActionAPI.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/AbstractActionAPI.java diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/AbstractActionAPI.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/AbstractActionAPI.java new file mode 100644 index 0000000000..e8632f2bdb --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/AbstractActionAPI.java @@ -0,0 +1,47 @@ +package com.netgrif.application.engine.adapter.spring.actions; + +import com.netgrif.application.engine.objects.petrinet.domain.Transition; +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.dataoutcomes.SetDataEventOutcome; +import com.netgrif.application.engine.objects.workflow.domain.filter.Predicate; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public abstract class AbstractActionAPI { + + public abstract void make(List> field, String behavior, Transition on); + + public abstract void change(Field field, Case targetCase, Task optionalTargetTask, String attribute, Object newValue); + + public abstract Page findCases(Predicate predicate, Pageable pageable); + + public abstract Page findCases(List elasticStringQueries, String loggedUserId, Pageable pageable, Locale locale, boolean isIntersection); + + public abstract Case createCase(String identifier, String title, String color, String userId, Locale locale, Map params); + + public abstract Page findTasks(Predicate predicate, Pageable pageable); + + public abstract Page findTasks(List elasticStringQueries, String loggedUserId, Pageable pageable, Locale locale, boolean isIntersection); + + public abstract Task assignTask(String taskId, String userId, Map params); + + public abstract Task cancelTask(String taskId, String userId, Map params); + + public abstract Task finishTask(String taskId, String finishedById, Map params); + + public abstract void assignRole(String roleId, String userId, Map params); + + public abstract void removeRole(String roleId, String userId, Map params); + + public abstract SetDataEventOutcome setData(String taskId, Map> dataSet, Map params); + + public abstract Map> getData(String taskId, Map params); + + +} From c50012bd17e4b3f52f227a010a699922e41b3f51 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Thu, 9 Oct 2025 13:43:43 +0200 Subject: [PATCH 02/20] Add Action API implementation and related configurations Introduced `ActionApi` interface and its implementation `ActionApiImpl` to standardize and handle various workflow operations like task assignment, case creation, and data --- .../actions/ActionApiConfiguration.java | 16 ++ .../engine/actions/ActionApiImpl.java | 152 ++++++++++++++++++ .../ElasticTaskSearchRequest.java | 2 + .../objects/auth/dto/AuthPrincipalDto.java | 17 ++ .../spring/actions/AbstractActionAPI.java | 47 ------ .../adapter/spring/actions/ActionApi.java | 46 ++++++ .../spring/actions/ActionApiMethods.java | 34 ++++ 7 files changed, 267 insertions(+), 47 deletions(-) create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiConfiguration.java create mode 100644 application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java create mode 100644 nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java delete mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/AbstractActionAPI.java create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApi.java create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java diff --git a/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiConfiguration.java new file mode 100644 index 0000000000..4ea84c3976 --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiConfiguration.java @@ -0,0 +1,16 @@ +package com.netgrif.application.engine.actions; + +import com.netgrif.application.engine.adapter.spring.actions.ActionApi; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ActionApiConfiguration { + + @Bean + @ConditionalOnMissingBean + public ActionApi actionApi() { + return new ActionApiImpl(); + } +} 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 new file mode 100644 index 0000000000..b8486507cd --- /dev/null +++ b/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java @@ -0,0 +1,152 @@ +package com.netgrif.application.engine.actions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.netgrif.application.engine.adapter.spring.actions.ActionApi; +import com.netgrif.application.engine.auth.service.UserService; +import com.netgrif.application.engine.elastic.service.interfaces.IElasticCaseService; +import com.netgrif.application.engine.elastic.service.interfaces.IElasticTaskService; +import com.netgrif.application.engine.elastic.web.requestbodies.CaseSearchRequest; +import com.netgrif.application.engine.elastic.web.requestbodies.ElasticTaskSearchRequest; +import com.netgrif.application.engine.objects.auth.domain.AbstractUser; +import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; +import com.netgrif.application.engine.objects.auth.domain.LoggedUser; +import com.netgrif.application.engine.objects.petrinet.domain.throwable.TransitionNotExecutableException; +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; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.caseoutcomes.DeleteCaseEventOutcome; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.dataoutcomes.GetDataEventOutcome; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.dataoutcomes.SetDataEventOutcome; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.taskoutcomes.AssignTaskEventOutcome; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.taskoutcomes.CancelTaskEventOutcome; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.taskoutcomes.FinishTaskEventOutcome; +import com.netgrif.application.engine.workflow.service.interfaces.IDataService; +import com.netgrif.application.engine.workflow.service.interfaces.ITaskService; +import com.netgrif.application.engine.workflow.service.interfaces.IWorkflowService; +import com.querydsl.core.types.Predicate; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.List; +import java.util.Locale; +import java.util.Map; + +@Slf4j +public class ActionApiImpl implements ActionApi { + + private UserService userService; + + private IDataService dataService; + + private ITaskService taskService; + + private IWorkflowService workflowService; + + private IElasticCaseService elasticCaseService; + + private IElasticTaskService elasticTaskService; + + public void setDataService(@Autowired IDataService dataService) { + this.dataService = dataService; + } + + public void setTaskService(@Autowired ITaskService taskService) { + this.taskService = taskService; + } + + public void setWorkflowService(@Autowired IWorkflowService workflowService) { + this.workflowService = workflowService; + } + + public void setElasticCaseService(@Autowired IElasticCaseService elasticCaseService) { + this.elasticCaseService = elasticCaseService; + } + + public void setUserService(@Autowired UserService userService) { + this.userService = userService; + } + + @Override + public GetDataEventOutcome getData(String taskId, Map params) { + return dataService.getData(taskId, params); + } + + @Override + public SetDataEventOutcome setData(String taskId, Map> dataSet, Map params) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + String json = mapper.writeValueAsString(dataSet); + ObjectNode values = (ObjectNode) mapper.readTree(json); + return dataService.setData(taskId, values, params); + } + + @Override + public Page searchCases(Predicate predicate, Pageable pageable) { + return workflowService.search(predicate, pageable); + } + + @Override + public Page searchCases(List elasticStringQueries, Pageable pageable, Boolean isIntersection) { + List caseSearchRequests = elasticStringQueries.stream().map(query -> CaseSearchRequest.builder().query(query).build()).toList(); + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); + Locale locale = LocaleContextHolder.getLocale(); + return elasticCaseService.search(caseSearchRequests, loggedUser, pageable, locale, isIntersection); + } + + @Override + public CreateCaseEventOutcome createCase(String netId, String title, String color, Map params) { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); + Locale locale = LocaleContextHolder.getLocale(); + return workflowService.createCase(netId, title, color, loggedUser, locale, params); + } + + @Override + public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, Map params) { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); + Locale locale = LocaleContextHolder.getLocale(); + return workflowService.createCaseByIdentifier(identifier, title, color, loggedUser, locale, params); + } + + @Override + public DeleteCaseEventOutcome deleteCase(String caseId, Map params) { + return workflowService.deleteCase(caseId, params); + } + + @Override + public Page searchTasks(Predicate predicate, Pageable pageable) { + return taskService.search(predicate, pageable); + } + + @Override + public Page searchTasks(List elasticStringQueries, Pageable pageable, Boolean isIntersection) { + List taskSearchRequests = elasticStringQueries.stream().map(query -> ElasticTaskSearchRequest.builder().query(query).build()).toList(); + LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); + Locale locale = LocaleContextHolder.getLocale(); + return elasticTaskService.search(taskSearchRequests, loggedUser, pageable, locale, isIntersection); + } + + @Override + public AssignTaskEventOutcome assignTask(String taskId, String userId, String realmId, Map params) throws TransitionNotExecutableException { + Task task = taskService.findOne(taskId); + AbstractUser user = userService.findById(userId, realmId); + return taskService.assignTask(task, user, params); + } + + @Override + public CancelTaskEventOutcome cancelTask(String taskId, String userId, String realmId, Map params) { + Task task = taskService.findOne(taskId); + AbstractUser user = userService.findById(userId, realmId); + return taskService.cancelTask(task, user, params); + } + + @Override + public FinishTaskEventOutcome finishTask(String taskId, String userId, String realmId, Map params) throws TransitionNotExecutableException { + Task task = taskService.findOne(taskId); + AbstractUser user = userService.findById(userId, realmId); + return taskService.finishTask(task, user, params); + } +} diff --git a/application-engine/src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/ElasticTaskSearchRequest.java b/application-engine/src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/ElasticTaskSearchRequest.java index a01e639ae7..f30bd91155 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/ElasticTaskSearchRequest.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/ElasticTaskSearchRequest.java @@ -4,12 +4,14 @@ import com.netgrif.application.engine.workflow.web.requestbodies.taskSearch.PetriNet; import com.netgrif.application.engine.workflow.web.requestbodies.taskSearch.TaskSearchCaseRequest; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.NoArgsConstructor; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +@Builder @NoArgsConstructor @AllArgsConstructor public class ElasticTaskSearchRequest extends TaskSearchRequest { 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 new file mode 100644 index 0000000000..bf3d90f923 --- /dev/null +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java @@ -0,0 +1,17 @@ +package com.netgrif.application.engine.objects.auth.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +@Data +public class AuthPrincipalDto implements Serializable { + + @Serial + private static final long serialVersionUID = 6725518942728316525L; + + private String username; + private String realmId; + private String sessionId; +} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/AbstractActionAPI.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/AbstractActionAPI.java deleted file mode 100644 index e8632f2bdb..0000000000 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/AbstractActionAPI.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.netgrif.application.engine.adapter.spring.actions; - -import com.netgrif.application.engine.objects.petrinet.domain.Transition; -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.dataoutcomes.SetDataEventOutcome; -import com.netgrif.application.engine.objects.workflow.domain.filter.Predicate; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.Pageable; - -import java.util.List; -import java.util.Locale; -import java.util.Map; - -public abstract class AbstractActionAPI { - - public abstract void make(List> field, String behavior, Transition on); - - public abstract void change(Field field, Case targetCase, Task optionalTargetTask, String attribute, Object newValue); - - public abstract Page findCases(Predicate predicate, Pageable pageable); - - public abstract Page findCases(List elasticStringQueries, String loggedUserId, Pageable pageable, Locale locale, boolean isIntersection); - - public abstract Case createCase(String identifier, String title, String color, String userId, Locale locale, Map params); - - public abstract Page findTasks(Predicate predicate, Pageable pageable); - - public abstract Page findTasks(List elasticStringQueries, String loggedUserId, Pageable pageable, Locale locale, boolean isIntersection); - - public abstract Task assignTask(String taskId, String userId, Map params); - - public abstract Task cancelTask(String taskId, String userId, Map params); - - public abstract Task finishTask(String taskId, String finishedById, Map params); - - public abstract void assignRole(String roleId, String userId, Map params); - - public abstract void removeRole(String roleId, String userId, Map params); - - public abstract SetDataEventOutcome setData(String taskId, Map> dataSet, Map params); - - public abstract Map> getData(String taskId, Map 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 new file mode 100644 index 0000000000..1ae8b867ed --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApi.java @@ -0,0 +1,46 @@ +package com.netgrif.application.engine.adapter.spring.actions; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.netgrif.application.engine.objects.petrinet.domain.throwable.TransitionNotExecutableException; +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; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.caseoutcomes.DeleteCaseEventOutcome; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.dataoutcomes.GetDataEventOutcome; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.dataoutcomes.SetDataEventOutcome; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.taskoutcomes.AssignTaskEventOutcome; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.taskoutcomes.CancelTaskEventOutcome; +import com.netgrif.application.engine.objects.workflow.domain.eventoutcomes.taskoutcomes.FinishTaskEventOutcome; +import com.querydsl.core.types.Predicate; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; + +import java.util.List; +import java.util.Map; + +public interface ActionApi { + + GetDataEventOutcome getData(String taskId, Map params); + + SetDataEventOutcome setData(String taskId, Map> dataSet, Map params) throws JsonProcessingException; + + Page searchCases(Predicate predicate, Pageable pageable); + + Page searchCases(List elasticStringQueries, Pageable pageable, Boolean isIntersection); + + CreateCaseEventOutcome createCase(String netId, String title, String color, Map params); + + CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, Map params); + + DeleteCaseEventOutcome deleteCase(String caseId, Map params); + + Page searchTasks(Predicate predicate, Pageable pageable); + + Page searchTasks(List elasticStringQueries, Pageable pageable, Boolean isIntersection); + + AssignTaskEventOutcome assignTask(String taskId, String userId, String realmId, Map params) throws TransitionNotExecutableException; + + CancelTaskEventOutcome cancelTask(String taskId, String userId, String realmId, Map params); + + FinishTaskEventOutcome finishTask(String taskId, String userId, String realmId, Map params) throws TransitionNotExecutableException; +} diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java new file mode 100644 index 0000000000..2ad39d2f34 --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java @@ -0,0 +1,34 @@ +package com.netgrif.application.engine.adapter.spring.actions; + +import java.util.Arrays; + +public enum ActionApiMethods { + + GET_DATA("getData"), + SET_DATA("setData"), + SEARCH_CASES("searchCases"), + CREATE_CASE("createCase"), + CREATE_CASE_BY_IDENTIFIER("createCaseByIdentifier"), + DELETE_CASE("deleteCase"), + SEARCH_TASKS("searchTasks"), + ASSIGN_TASK("assignTask"), + CANCEL_TASK("cancelTask"), + FINISH_TASK("finishTask"),; + + private String methodName; + + ActionApiMethods(String methodName) { + this.methodName = methodName; + } + + public String getMethodName() { + return methodName; + } + + public static ActionApiMethods fromMethodName(String methodName) { + return Arrays.stream(ActionApiMethods.values()) + .filter(type -> type.methodName.equals(methodName)) + .findFirst() + .orElse(null); + } +} From 78de22f36066778be498b9ff5d5a2156b5689f35 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 15 Oct 2025 13:35:24 +0200 Subject: [PATCH 03/20] Refactor ActionApi methods to enhance type safety and clarity Updated `Map` to `HashMap` for parameters to ensure consistent type usage. Added `processIdentifier` parameter to `searchCases` and `searchTasks` methods. Replaced `userId` with `username` in task-related methods to better align with modern practices. Removed unused `createCase` method to eliminate redundancy. --- .../engine/actions/ActionApiImpl.java | 53 +++++++++---------- .../adapter/spring/actions/ActionApi.java | 21 ++++---- .../spring/actions/ActionApiMethods.java | 1 - 3 files changed, 36 insertions(+), 39 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 b8486507cd..dc7019d0e0 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 @@ -32,9 +32,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import java.util.List; -import java.util.Locale; -import java.util.Map; +import java.util.*; @Slf4j public class ActionApiImpl implements ActionApi { @@ -51,33 +49,38 @@ public class ActionApiImpl implements ActionApi { private IElasticTaskService elasticTaskService; - public void setDataService(@Autowired IDataService dataService) { + @Autowired + public void setDataService(IDataService dataService) { this.dataService = dataService; } - public void setTaskService(@Autowired ITaskService taskService) { + @Autowired + public void setTaskService(ITaskService taskService) { this.taskService = taskService; } - public void setWorkflowService(@Autowired IWorkflowService workflowService) { + @Autowired + public void setWorkflowService(IWorkflowService workflowService) { this.workflowService = workflowService; } - public void setElasticCaseService(@Autowired IElasticCaseService elasticCaseService) { + @Autowired + public void setElasticCaseService(IElasticCaseService elasticCaseService) { this.elasticCaseService = elasticCaseService; } - public void setUserService(@Autowired UserService userService) { + @Autowired + public void setUserService(UserService userService) { this.userService = userService; } @Override - public GetDataEventOutcome getData(String taskId, Map params) { + public GetDataEventOutcome getData(String taskId, HashMap params) { return dataService.getData(taskId, params); } @Override - public SetDataEventOutcome setData(String taskId, Map> dataSet, Map params) throws JsonProcessingException { + public SetDataEventOutcome setData(String taskId, HashMap> dataSet, HashMap params) throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(dataSet); ObjectNode values = (ObjectNode) mapper.readTree(json); @@ -85,7 +88,7 @@ public SetDataEventOutcome setData(String taskId, Map searchCases(Predicate predicate, Pageable pageable) { + public Page searchCases(String processIdentifier, Predicate predicate, Pageable pageable) { return workflowService.search(predicate, pageable); } @@ -98,26 +101,19 @@ public Page searchCases(List elasticStringQueries, Pageable pageab } @Override - public CreateCaseEventOutcome createCase(String netId, String title, String color, Map params) { - LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); - Locale locale = LocaleContextHolder.getLocale(); - return workflowService.createCase(netId, title, color, loggedUser, locale, params); - } - - @Override - public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, Map params) { + public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, HashMap params) { LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); Locale locale = LocaleContextHolder.getLocale(); return workflowService.createCaseByIdentifier(identifier, title, color, loggedUser, locale, params); } @Override - public DeleteCaseEventOutcome deleteCase(String caseId, Map params) { + public DeleteCaseEventOutcome deleteCase(String caseId, HashMap params) { return workflowService.deleteCase(caseId, params); } @Override - public Page searchTasks(Predicate predicate, Pageable pageable) { + public Page searchTasks(String processIdentifier, Predicate predicate, Pageable pageable) { return taskService.search(predicate, pageable); } @@ -130,23 +126,26 @@ public Page searchTasks(List elasticStringQueries, Pageable pageab } @Override - public AssignTaskEventOutcome assignTask(String taskId, String userId, String realmId, Map params) throws TransitionNotExecutableException { + public AssignTaskEventOutcome assignTask(String taskId, String username, String realmId, HashMap params) throws TransitionNotExecutableException { Task task = taskService.findOne(taskId); - AbstractUser user = userService.findById(userId, realmId); + Optional userOptional = userService.findUserByUsername(username, realmId); + AbstractUser user = userOptional.orElseThrow(() -> new IllegalArgumentException("User with username [%s] and realm ID [%s] not found".formatted(username, realmId))); return taskService.assignTask(task, user, params); } @Override - public CancelTaskEventOutcome cancelTask(String taskId, String userId, String realmId, Map params) { + public CancelTaskEventOutcome cancelTask(String taskId, String username, String realmId, HashMap params) { Task task = taskService.findOne(taskId); - AbstractUser user = userService.findById(userId, realmId); + Optional userOptional = userService.findUserByUsername(username, realmId); + AbstractUser user = userOptional.orElseThrow(() -> new IllegalArgumentException("User with username [%s] and realm ID [%s] not found".formatted(username, realmId))); return taskService.cancelTask(task, user, params); } @Override - public FinishTaskEventOutcome finishTask(String taskId, String userId, String realmId, Map params) throws TransitionNotExecutableException { + public FinishTaskEventOutcome finishTask(String taskId, String username, String realmId, HashMap params) throws TransitionNotExecutableException { Task task = taskService.findOne(taskId); - AbstractUser user = userService.findById(userId, realmId); + Optional userOptional = userService.findUserByUsername(username, realmId); + AbstractUser user = userOptional.orElseThrow(() -> new IllegalArgumentException("User with username [%s] and realm ID [%s] not found".formatted(username, realmId))); return taskService.finishTask(task, user, 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 1ae8b867ed..cfd1131efd 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 @@ -15,32 +15,31 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import java.util.HashMap; import java.util.List; import java.util.Map; public interface ActionApi { - GetDataEventOutcome getData(String taskId, Map params); + GetDataEventOutcome getData(String taskId, HashMap params); - SetDataEventOutcome setData(String taskId, Map> dataSet, Map params) throws JsonProcessingException; + SetDataEventOutcome setData(String taskId, HashMap> dataSet, HashMap params) throws JsonProcessingException; - Page searchCases(Predicate predicate, Pageable pageable); + Page searchCases(String processIdentifier, Predicate predicate, Pageable pageable); Page searchCases(List elasticStringQueries, Pageable pageable, Boolean isIntersection); - CreateCaseEventOutcome createCase(String netId, String title, String color, Map params); + CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, HashMap params); - CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, Map params); + DeleteCaseEventOutcome deleteCase(String caseId, HashMap params); - DeleteCaseEventOutcome deleteCase(String caseId, Map params); - - Page searchTasks(Predicate predicate, Pageable pageable); + Page searchTasks(String processIdentifier, Predicate predicate, Pageable pageable); Page searchTasks(List elasticStringQueries, Pageable pageable, Boolean isIntersection); - AssignTaskEventOutcome assignTask(String taskId, String userId, String realmId, Map params) throws TransitionNotExecutableException; + AssignTaskEventOutcome assignTask(String taskId, String username, String realmId, HashMap params) throws TransitionNotExecutableException; - CancelTaskEventOutcome cancelTask(String taskId, String userId, String realmId, Map params); + CancelTaskEventOutcome cancelTask(String taskId, String username, String realmId, HashMap params); - FinishTaskEventOutcome finishTask(String taskId, String userId, String realmId, Map params) throws TransitionNotExecutableException; + FinishTaskEventOutcome finishTask(String taskId, String username, String realmId, HashMap params) throws TransitionNotExecutableException; } diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java index 2ad39d2f34..874fd64031 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java @@ -7,7 +7,6 @@ public enum ActionApiMethods { GET_DATA("getData"), SET_DATA("setData"), SEARCH_CASES("searchCases"), - CREATE_CASE("createCase"), CREATE_CASE_BY_IDENTIFIER("createCaseByIdentifier"), DELETE_CASE("deleteCase"), SEARCH_TASKS("searchTasks"), From 8cb4abc5537967b0b341ee715218b4af586bbb30 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 15 Oct 2025 13:45:58 +0200 Subject: [PATCH 04/20] Refactor Action API to use AuthPrincipalDto for user context Replaced username and realmId parameters with AuthPrincipalDto to standardize user context handling across Action API methods. Added a helper method to resolve AbstractUser from AuthPrincipalDto, ensuring consistent and concise user lookup logic. This change improves maintainability and aligns with updated authentication patterns. --- .../engine/actions/ActionApiImpl.java | 33 ++++++++++--------- .../adapter/spring/actions/ActionApi.java | 14 ++++---- 2 files changed, 25 insertions(+), 22 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 dc7019d0e0..5e89f98c3d 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 @@ -12,6 +12,7 @@ import com.netgrif.application.engine.objects.auth.domain.AbstractUser; import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; +import com.netgrif.application.engine.objects.auth.dto.AuthPrincipalDto; import com.netgrif.application.engine.objects.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.objects.workflow.domain.Case; import com.netgrif.application.engine.objects.workflow.domain.Task; @@ -93,16 +94,16 @@ public Page searchCases(String processIdentifier, Predicate predicate, Pag } @Override - public Page searchCases(List elasticStringQueries, Pageable pageable, Boolean isIntersection) { + public Page searchCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection) { List caseSearchRequests = elasticStringQueries.stream().map(query -> CaseSearchRequest.builder().query(query).build()).toList(); - LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); + LoggedUser loggedUser = ActorTransformer.toLoggedUser(resolveAbstractUser(authPrincipalDto)); Locale locale = LocaleContextHolder.getLocale(); return elasticCaseService.search(caseSearchRequests, loggedUser, pageable, locale, isIntersection); } @Override - public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, HashMap params) { - LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); + public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, AuthPrincipalDto authPrincipalDto, HashMap params) { + LoggedUser loggedUser = ActorTransformer.toLoggedUser(resolveAbstractUser(authPrincipalDto)); Locale locale = LocaleContextHolder.getLocale(); return workflowService.createCaseByIdentifier(identifier, title, color, loggedUser, locale, params); } @@ -118,34 +119,36 @@ public Page searchTasks(String processIdentifier, Predicate predicate, Pag } @Override - public Page searchTasks(List elasticStringQueries, Pageable pageable, Boolean isIntersection) { + public Page searchTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection) { List taskSearchRequests = elasticStringQueries.stream().map(query -> ElasticTaskSearchRequest.builder().query(query).build()).toList(); - LoggedUser loggedUser = ActorTransformer.toLoggedUser(userService.getLoggedOrSystem()); + LoggedUser loggedUser = ActorTransformer.toLoggedUser(resolveAbstractUser(authPrincipalDto)); Locale locale = LocaleContextHolder.getLocale(); return elasticTaskService.search(taskSearchRequests, loggedUser, pageable, locale, isIntersection); } @Override - public AssignTaskEventOutcome assignTask(String taskId, String username, String realmId, HashMap params) throws TransitionNotExecutableException { + public AssignTaskEventOutcome assignTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) throws TransitionNotExecutableException { Task task = taskService.findOne(taskId); - Optional userOptional = userService.findUserByUsername(username, realmId); - AbstractUser user = userOptional.orElseThrow(() -> new IllegalArgumentException("User with username [%s] and realm ID [%s] not found".formatted(username, realmId))); + AbstractUser user = resolveAbstractUser(authPrincipalDto); return taskService.assignTask(task, user, params); } @Override - public CancelTaskEventOutcome cancelTask(String taskId, String username, String realmId, HashMap params) { + public CancelTaskEventOutcome cancelTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) { Task task = taskService.findOne(taskId); - Optional userOptional = userService.findUserByUsername(username, realmId); - AbstractUser user = userOptional.orElseThrow(() -> new IllegalArgumentException("User with username [%s] and realm ID [%s] not found".formatted(username, realmId))); + AbstractUser user = resolveAbstractUser(authPrincipalDto); return taskService.cancelTask(task, user, params); } @Override - public FinishTaskEventOutcome finishTask(String taskId, String username, String realmId, HashMap params) throws TransitionNotExecutableException { + public FinishTaskEventOutcome finishTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) throws TransitionNotExecutableException { Task task = taskService.findOne(taskId); - Optional userOptional = userService.findUserByUsername(username, realmId); - AbstractUser user = userOptional.orElseThrow(() -> new IllegalArgumentException("User with username [%s] and realm ID [%s] not found".formatted(username, realmId))); + AbstractUser user = resolveAbstractUser(authPrincipalDto); return taskService.finishTask(task, user, params); } + + private AbstractUser resolveAbstractUser(AuthPrincipalDto authPrincipalDto) { + 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()))); + } } 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 cfd1131efd..af491663fd 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 @@ -1,6 +1,7 @@ package com.netgrif.application.engine.adapter.spring.actions; import com.fasterxml.jackson.core.JsonProcessingException; +import com.netgrif.application.engine.objects.auth.dto.AuthPrincipalDto; import com.netgrif.application.engine.objects.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.objects.workflow.domain.Case; import com.netgrif.application.engine.objects.workflow.domain.Task; @@ -17,7 +18,6 @@ import java.util.HashMap; import java.util.List; -import java.util.Map; public interface ActionApi { @@ -27,19 +27,19 @@ public interface ActionApi { Page searchCases(String processIdentifier, Predicate predicate, Pageable pageable); - Page searchCases(List elasticStringQueries, Pageable pageable, Boolean isIntersection); + Page searchCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection); - CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, HashMap params); + CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, AuthPrincipalDto authPrincipalDto, HashMap params); DeleteCaseEventOutcome deleteCase(String caseId, HashMap params); Page searchTasks(String processIdentifier, Predicate predicate, Pageable pageable); - Page searchTasks(List elasticStringQueries, Pageable pageable, Boolean isIntersection); + Page searchTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection); - AssignTaskEventOutcome assignTask(String taskId, String username, String realmId, HashMap params) throws TransitionNotExecutableException; + AssignTaskEventOutcome assignTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) throws TransitionNotExecutableException; - CancelTaskEventOutcome cancelTask(String taskId, String username, String realmId, HashMap params); + CancelTaskEventOutcome cancelTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params); - FinishTaskEventOutcome finishTask(String taskId, String username, String realmId, HashMap params) throws TransitionNotExecutableException; + FinishTaskEventOutcome finishTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) throws TransitionNotExecutableException; } From 111827a09c55dbf2ef1dada8fb258e7d36ed6689 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Thu, 16 Oct 2025 13:18:00 +0200 Subject: [PATCH 05/20] Add searchUsers, findCase, and findTask methods to Action API Introduced new methods in ActionApi and implemented them in ActionApiImpl, enabling user, case, and task operations. Updated related services and repositories to support user search by realm with pagination and predicate filtering. --- .../engine/actions/ActionApiImpl.java | 16 +++ .../petrinet/service/ProcessRoleService.java | 6 +- .../adapter/spring/actions/ActionApi.java | 125 ++++++++++++++++++ .../spring/actions/ActionApiMethods.java | 5 +- .../engine/auth/service/UserServiceImpl.java | 16 +++ .../engine/auth/service/UserService.java | 11 ++ 6 files changed, 174 insertions(+), 5 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 5e89f98c3d..a3f58fef21 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 @@ -12,6 +12,7 @@ import com.netgrif.application.engine.objects.auth.domain.AbstractUser; import com.netgrif.application.engine.objects.auth.domain.ActorTransformer; import com.netgrif.application.engine.objects.auth.domain.LoggedUser; +import com.netgrif.application.engine.objects.auth.domain.User; import com.netgrif.application.engine.objects.auth.dto.AuthPrincipalDto; import com.netgrif.application.engine.objects.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.objects.workflow.domain.Case; @@ -147,6 +148,21 @@ public FinishTaskEventOutcome finishTask(String taskId, AuthPrincipalDto authPri return taskService.finishTask(task, user, params); } + @Override + public Case findCase(String caseId) { + return workflowService.findOne(caseId); + } + + @Override + public Task findTask(String taskId) { + return taskService.findOne(taskId); + } + + @Override + public Page searchUsers(Predicate predicate, Pageable pageable, String realmId) { + return userService.search(predicate, pageable, realmId); + } + private AbstractUser resolveAbstractUser(AuthPrincipalDto authPrincipalDto) { 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()))); 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 1c94340d3a..1da1afa0b0 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 @@ -15,8 +15,6 @@ import com.netgrif.application.engine.objects.importer.model.EventPhaseType; import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.dataset.logic.action.Action; -import com.netgrif.application.engine.objects.workflow.domain.Case; -import com.netgrif.application.engine.objects.workflow.domain.QCase; import com.netgrif.application.engine.petrinet.domain.dataset.logic.action.context.RoleContext; import com.netgrif.application.engine.petrinet.domain.dataset.logic.action.runner.RoleActionsRunner; import com.netgrif.application.engine.objects.petrinet.domain.events.Event; @@ -102,8 +100,8 @@ public Optional get(ProcessResourceId processResourceId) { } @Override - public void delete(String s) { - Optional processRole = processRoleRepository.findByCompositeId(s); + public void delete(String roleId) { + Optional processRole = processRoleRepository.findByCompositeId(roleId); processRole.ifPresent(processRoleRepository::delete); } 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 af491663fd..9de149ced2 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 @@ -1,6 +1,7 @@ package com.netgrif.application.engine.adapter.spring.actions; import com.fasterxml.jackson.core.JsonProcessingException; +import com.netgrif.application.engine.objects.auth.domain.User; import com.netgrif.application.engine.objects.auth.dto.AuthPrincipalDto; import com.netgrif.application.engine.objects.petrinet.domain.throwable.TransitionNotExecutableException; import com.netgrif.application.engine.objects.workflow.domain.Case; @@ -19,27 +20,151 @@ import java.util.HashMap; import java.util.List; + +/** + * ActionApi provides methods for performing operations and actions on workflow-related entities such as cases, + * tasks, and users. It facilitates data retrieval, task management, and realm-specific searches within the system. + */ public interface ActionApi { + /** + * Retrieves data associated with a specific task. + * + * @param taskId the ID of the task for which data is to be retrieved + * @param params additional parameters for data retrieval + * @return the outcome of the data retrieval operation + */ GetDataEventOutcome getData(String taskId, HashMap params); + /** + * Sets or updates data for a specific task. + * + * @param taskId the ID of the task to use to update data - task does not have to contain the data that is updated + * @param dataSet the data to be set, organized as a map + * @param params additional parameters for the operation + * @return the outcome of the set data operation + * @throws JsonProcessingException if there is an error processing JSON data + */ SetDataEventOutcome setData(String taskId, HashMap> dataSet, HashMap params) throws JsonProcessingException; + /** + * Finds a specific case by its ID. + * + * @param caseId the ID of the case to find + * @return the found case + */ + Case findCase(String caseId); + + /** + * Searches for cases matching the given process identifier and predicate. + * + * @param processIdentifier the identifier of the process + * @param predicate the criteria for filtering cases + * @param pageable the pagination information + * @return a page of cases matching the criteria + */ Page searchCases(String processIdentifier, Predicate predicate, Pageable pageable); + /** + * 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 + * @return a page of cases matching the criteria + */ Page searchCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection); + /** + * Creates a new case identified by the given process identifier. + * + * @param identifier the process identifier for creating the case + * @param title the title of the new case + * @param color the color associated with the case + * @param authPrincipalDto the authorization principal for creating the case + * @param params additional parameters for the operation + * @return the outcome of the case creation operation + */ CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, AuthPrincipalDto authPrincipalDto, HashMap params); + /** + * Deletes a specific case by its ID. + * + * @param caseId the ID of the case to delete + * @param params additional parameters for the operation + * @return the outcome of the delete operation + */ DeleteCaseEventOutcome deleteCase(String caseId, HashMap params); + /** + * Finds a specific task by its ID. + * + * @param taskId the ID of the task to find + * @return the found task + */ + Task findTask(String taskId); + + /** + * Searches for tasks matching the given process identifier and predicate. + * + * @param processIdentifier the identifier of the process + * @param predicate the criteria for filtering tasks + * @param pageable the pagination information + * @return a page of tasks matching the criteria + */ Page searchTasks(String processIdentifier, Predicate predicate, Pageable pageable); + /** + * 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 + * @return a page of tasks matching the criteria + */ Page searchTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection); + /** + * Assigns a specific task to a user. + * + * @param taskId the ID of the task to assign + * @param authPrincipalDto the authorization principal used for the operation + * @param params additional parameters for the operation + * @return the outcome of the task assignment operation + * @throws TransitionNotExecutableException if the task's transition cannot be executed + */ AssignTaskEventOutcome assignTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) throws TransitionNotExecutableException; + /** + * Cancels a specific task. + * + * @param taskId the ID of the task to cancel + * @param authPrincipalDto the authorization principal used for the operation + * @param params additional parameters for the operation + * @return the outcome of the task cancellation operation + */ CancelTaskEventOutcome cancelTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params); + /** + * Marks a specific task as finished. + * + * @param taskId the ID of the task to finish + * @param authPrincipalDto the authorization principal used for the operation + * @param params additional parameters for the operation + * @return the outcome of the task completion operation + * @throws TransitionNotExecutableException if the task's transition cannot be executed + */ FinishTaskEventOutcome finishTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) throws TransitionNotExecutableException; + + /** + * Searches for users in a specific realm based on a predicate. + * + * @param predicate the criteria for filtering users + * @param pageable the pagination information + * @param realmId the ID of the realm to search + * @return a page of users matching the criteria + */ + Page searchUsers(Predicate predicate, Pageable pageable, String realmId); } diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java index 874fd64031..10e41d0464 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java @@ -6,13 +6,16 @@ public enum ActionApiMethods { GET_DATA("getData"), SET_DATA("setData"), + FIND_CASE("findCase"), SEARCH_CASES("searchCases"), CREATE_CASE_BY_IDENTIFIER("createCaseByIdentifier"), DELETE_CASE("deleteCase"), + FIND_TASK("findTask"), SEARCH_TASKS("searchTasks"), ASSIGN_TASK("assignTask"), CANCEL_TASK("cancelTask"), - FINISH_TASK("finishTask"),; + FINISH_TASK("finishTask"), + SEARCH_USER("searchUsers"); private String methodName; 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 9820e6296b..217c9e8c33 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 @@ -12,6 +12,7 @@ import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole; import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId; +import com.querydsl.core.types.Predicate; import com.querydsl.core.types.dsl.BooleanExpression; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -588,6 +589,21 @@ public void updateAdminWithRoles(Collection roles) { log.debug("Admin [{}] now has [{}] process roles", admin.getUsername(), admin.getProcessRoles().size()); } + + /** + * Searches for users in the specified realm based on the provided predicate and pagination parameters. + * + * @param predicate the query conditions to filter users + * @param pageable the pagination parameters for the search results + * @param realmId the name of the realm, used to determine which collection to query + * @return a paginated list of users matching the predicate within the specified realm + */ + @Override + public Page search(Predicate predicate, Pageable pageable, String realmId) { + String collectionName = collectionNameProvider.getCollectionNameForRealm(realmId); + return userRepository.findAllByQuery(predicate, pageable, mongoTemplate, collectionName); + } + protected User initializeNewUser(String username, String email, String firstName, String lastName, String password, String realmId) { log.trace("Initializing new user [{}] in realm [{}]", username, realmId); User user = new User(); 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 63e0111c82..9490c21308 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 @@ -5,6 +5,7 @@ import com.netgrif.application.engine.objects.petrinet.domain.PetriNet; import com.netgrif.application.engine.objects.petrinet.domain.roles.ProcessRole; import com.netgrif.application.engine.objects.workflow.domain.ProcessResourceId; +import com.querydsl.core.types.Predicate; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.mongodb.core.query.Query; @@ -424,4 +425,14 @@ Page searchAllCoMembers(String query, Collection search(Predicate predicate, Pageable pageable, String realmId); } From 582f418bcf6be9c44f0a2861d95273a796a5c5b8 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 17 Oct 2025 09:06:06 +0200 Subject: [PATCH 06/20] Refactor Querydsl dependencies and package structure. Moved Querydsl dependencies and plugin configuration from `nae-user-common` to `nae-spring-core-adapter`. Updated package structure to align with the new module structure, improving separation of concerns and maintainability. --- nae-spring-core-adapter/pom.xml | 34 ++++++++++++++++++ .../engine/adapter/spring}/package-info.java | 2 +- nae-user-common/pom.xml | 35 ------------------- 3 files changed, 35 insertions(+), 36 deletions(-) rename {nae-user-common/src/main/java/com/netgrif/application/engine/auth => nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring}/package-info.java (94%) diff --git a/nae-spring-core-adapter/pom.xml b/nae-spring-core-adapter/pom.xml index baa4218272..7a8bff888f 100644 --- a/nae-spring-core-adapter/pom.xml +++ b/nae-spring-core-adapter/pom.xml @@ -107,6 +107,16 @@ org.apache.commons commons-lang3 + + com.querydsl + querydsl-core + ${querydsl.version} + + + com.querydsl + querydsl-apt + ${querydsl.version} + @@ -131,6 +141,30 @@ + + com.mysema.maven + apt-maven-plugin + 1.1.3 + + + + process + + + target/generated-sources/java + + + com.querydsl.apt.QuerydslAnnotationProcessor + + + + groovy.lang.MetaClass + true + + + + + diff --git a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/package-info.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/package-info.java similarity index 94% rename from nae-user-common/src/main/java/com/netgrif/application/engine/auth/package-info.java rename to nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/package-info.java index eb189e2a25..742273d634 100644 --- a/nae-user-common/src/main/java/com/netgrif/application/engine/auth/package-info.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/package-info.java @@ -10,7 +10,7 @@ Group.class, User.class }) -package com.netgrif.application.engine.auth; +package com.netgrif.application.engine.adapter.spring; import com.netgrif.application.engine.adapter.spring.petrinet.domain.PetriNet; import com.netgrif.application.engine.adapter.spring.petrinet.domain.roles.ProcessRole; diff --git a/nae-user-common/pom.xml b/nae-user-common/pom.xml index c4e756a301..6c38baf881 100644 --- a/nae-user-common/pom.xml +++ b/nae-user-common/pom.xml @@ -117,17 +117,6 @@ ${mapstruct.version} provided - - - com.querydsl - querydsl-core - ${querydsl.version} - - - com.querydsl - querydsl-apt - ${querydsl.version} - com.querydsl querydsl-mongodb @@ -164,30 +153,6 @@ - - com.mysema.maven - apt-maven-plugin - 1.1.3 - - - - process - - - target/generated-sources/java - - - com.querydsl.apt.QuerydslAnnotationProcessor - - - - groovy.lang.MetaClass - true - - - - - From 95e084d285ca52180f1ad3114d256883e7bc0ef5 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Fri, 17 Oct 2025 10:46:52 +0200 Subject: [PATCH 07/20] Refactor to use Map instead of HashMap and add method resolution logic Replaced HashMap with Map in ActionApi and ActionApiImpl for flexibility and cleaner API design. Introduced method resolution with superclass parameter types in NaeReflectionUtils, along with custom exception handling for ambiguous method calls. --- .../engine/actions/ActionApiImpl.java | 16 ++-- .../adapter/spring/actions/ActionApi.java | 17 ++-- .../spring/utils/NaeReflectionUtils.java | 79 +++++++++++++++++++ .../AmbiguousMethodCallException.java | 12 +++ 4 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.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 a3f58fef21..e9ff90dad1 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 @@ -77,13 +77,13 @@ public void setUserService(UserService userService) { } @Override - public GetDataEventOutcome getData(String taskId, HashMap params) { + public GetDataEventOutcome getData(String taskId, Map params) { return dataService.getData(taskId, params); } @Override - public SetDataEventOutcome setData(String taskId, HashMap> dataSet, HashMap params) throws JsonProcessingException { - ObjectMapper mapper = new ObjectMapper(); + public SetDataEventOutcome setData(String taskId, Map> dataSet, Map params) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(dataSet); ObjectNode values = (ObjectNode) mapper.readTree(json); return dataService.setData(taskId, values, params); @@ -103,14 +103,14 @@ public Page searchCases(List elasticStringQueries, AuthPrincipalDt } @Override - public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, AuthPrincipalDto authPrincipalDto, HashMap params) { + public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, AuthPrincipalDto authPrincipalDto, Map params) { LoggedUser loggedUser = ActorTransformer.toLoggedUser(resolveAbstractUser(authPrincipalDto)); Locale locale = LocaleContextHolder.getLocale(); return workflowService.createCaseByIdentifier(identifier, title, color, loggedUser, locale, params); } @Override - public DeleteCaseEventOutcome deleteCase(String caseId, HashMap params) { + public DeleteCaseEventOutcome deleteCase(String caseId, Map params) { return workflowService.deleteCase(caseId, params); } @@ -128,21 +128,21 @@ public Page searchTasks(List elasticStringQueries, AuthPrincipalDt } @Override - public AssignTaskEventOutcome assignTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) throws TransitionNotExecutableException { + public AssignTaskEventOutcome assignTask(String taskId, AuthPrincipalDto authPrincipalDto, Map params) throws TransitionNotExecutableException { Task task = taskService.findOne(taskId); AbstractUser user = resolveAbstractUser(authPrincipalDto); return taskService.assignTask(task, user, params); } @Override - public CancelTaskEventOutcome cancelTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) { + public CancelTaskEventOutcome cancelTask(String taskId, AuthPrincipalDto authPrincipalDto, Map params) { Task task = taskService.findOne(taskId); AbstractUser user = resolveAbstractUser(authPrincipalDto); return taskService.cancelTask(task, user, params); } @Override - public FinishTaskEventOutcome finishTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) throws TransitionNotExecutableException { + public FinishTaskEventOutcome finishTask(String taskId, AuthPrincipalDto authPrincipalDto, Map params) throws TransitionNotExecutableException { Task task = taskService.findOne(taskId); AbstractUser user = resolveAbstractUser(authPrincipalDto); return taskService.finishTask(task, user, 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 9de149ced2..73740c91af 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 @@ -17,8 +17,9 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import java.util.HashMap; +import java.util.Map; import java.util.List; +import java.util.Map; /** @@ -34,7 +35,7 @@ public interface ActionApi { * @param params additional parameters for data retrieval * @return the outcome of the data retrieval operation */ - GetDataEventOutcome getData(String taskId, HashMap params); + GetDataEventOutcome getData(String taskId, Map params); /** * Sets or updates data for a specific task. @@ -45,7 +46,7 @@ public interface ActionApi { * @return the outcome of the set data operation * @throws JsonProcessingException if there is an error processing JSON data */ - SetDataEventOutcome setData(String taskId, HashMap> dataSet, HashMap params) throws JsonProcessingException; + SetDataEventOutcome setData(String taskId, Map> dataSet, Map params) throws JsonProcessingException; /** * Finds a specific case by its ID. @@ -86,7 +87,7 @@ public interface ActionApi { * @param params additional parameters for the operation * @return the outcome of the case creation operation */ - CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, AuthPrincipalDto authPrincipalDto, HashMap params); + CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, AuthPrincipalDto authPrincipalDto, Map params); /** * Deletes a specific case by its ID. @@ -95,7 +96,7 @@ public interface ActionApi { * @param params additional parameters for the operation * @return the outcome of the delete operation */ - DeleteCaseEventOutcome deleteCase(String caseId, HashMap params); + DeleteCaseEventOutcome deleteCase(String caseId, Map params); /** * Finds a specific task by its ID. @@ -135,7 +136,7 @@ public interface ActionApi { * @return the outcome of the task assignment operation * @throws TransitionNotExecutableException if the task's transition cannot be executed */ - AssignTaskEventOutcome assignTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) throws TransitionNotExecutableException; + AssignTaskEventOutcome assignTask(String taskId, AuthPrincipalDto authPrincipalDto, Map params) throws TransitionNotExecutableException; /** * Cancels a specific task. @@ -145,7 +146,7 @@ public interface ActionApi { * @param params additional parameters for the operation * @return the outcome of the task cancellation operation */ - CancelTaskEventOutcome cancelTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params); + CancelTaskEventOutcome cancelTask(String taskId, AuthPrincipalDto authPrincipalDto, Map params); /** * Marks a specific task as finished. @@ -156,7 +157,7 @@ public interface ActionApi { * @return the outcome of the task completion operation * @throws TransitionNotExecutableException if the task's transition cannot be executed */ - FinishTaskEventOutcome finishTask(String taskId, AuthPrincipalDto authPrincipalDto, HashMap params) throws TransitionNotExecutableException; + FinishTaskEventOutcome finishTask(String taskId, AuthPrincipalDto authPrincipalDto, Map params) throws TransitionNotExecutableException; /** * Searches for users in a specific realm based on a predicate. diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java index dea75bc483..92b6642aa4 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java @@ -1,7 +1,10 @@ package com.netgrif.application.engine.adapter.spring.utils; +import com.netgrif.application.engine.adapter.spring.utils.exceptions.AmbiguousMethodCallException; import org.springframework.aop.framework.AopProxyUtils; +import java.lang.reflect.Method; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -54,4 +57,80 @@ public static int indexOfClass(List list, Class clazz) { } + /** + * Finds and returns the {@code Method} object for the specified method name and parameter types on the given bean. + * This method first attempts to locate the method on the target class of the bean. + * If not found, it tries to locate a method using superclasses of the parameter types. + * + * @param bean the bean instance on which the method is to be located + * @param methodToExecute the name of the method to be invoked + * @param requestParamTypes the types of the parameters expected by the method + * @return the {@code Method} object corresponding to the specified name and parameters + * @throws NoSuchMethodException if the method cannot be found + * @throws AmbiguousMethodCallException if multiple methods match the specified criteria, resulting in ambiguity + */ + public static Method findMethod(Object bean, String methodToExecute, Class[] requestParamTypes) + throws NoSuchMethodException, AmbiguousMethodCallException { + try { + return NaeReflectionUtils.resolveClass(bean).getMethod(methodToExecute, requestParamTypes); + } catch (NoSuchMethodException e) { + return findMethodWithSuperClassParams(bean, methodToExecute, requestParamTypes, e); + } + } + + /** + * Attempts to find a method on the target class of the given bean using superclass parameter types when an exact match is not found. + * If a method with the specified name is located, and the parameters can be assigned from the provided parameter types, + * the method is returned. If multiple methods match the criteria, an {@code AmbiguousMethodCallException} is thrown. + * + * @param bean the bean instance on which the method is to be located + * @param methodToExecute the name of the method to be invoked + * @param requestParamTypes the types of the parameters expected by the method + * @param caughtException the exception caught from a previous attempt to find the method (used for re-throw if no method is found) + * @return the {@code Method} object that matches the specified criteria + * @throws NoSuchMethodException if no suitable method is found + * @throws AmbiguousMethodCallException if multiple methods match the specified name and parameters, causing ambiguity + */ + private static Method findMethodWithSuperClassParams(Object bean, String methodToExecute, Class[] requestParamTypes, + NoSuchMethodException caughtException) + throws NoSuchMethodException, AmbiguousMethodCallException { + Class cls = NaeReflectionUtils.resolveClass(bean); + Method[] methods = cls.getMethods(); + Method methodToInvoke = null; + outerLoop: + for (Method method : methods) { + if (!methodToExecute.equals(method.getName())) { + continue; + } + + Class[] paramTypes = method.getParameterTypes(); + int requestParamsLen = requestParamTypes.length; + int paramsLen = paramTypes.length; + + if (requestParamsLen == 0 && paramsLen == 0) { + methodToInvoke = method; + break; + } else if (paramsLen == 0 || paramsLen != requestParamsLen) { + continue; + } + + for (int i = 0; i < requestParamTypes.length; ++i) { + if (!paramTypes[i].isAssignableFrom(requestParamTypes[i])) { + continue outerLoop; + } + } + + if (methodToInvoke != null) { + throw new AmbiguousMethodCallException(String.format("Method %s is ambiguous for the param types %s", + methodToExecute, Arrays.toString(requestParamTypes))); + } + methodToInvoke = method; + } + + if (methodToInvoke == null) { + throw caughtException; + } else { + return methodToInvoke; + } + } } diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java new file mode 100644 index 0000000000..27e5ddc61b --- /dev/null +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java @@ -0,0 +1,12 @@ +package com.netgrif.application.engine.adapter.spring.utils.exceptions; + +public class AmbiguousMethodCallException extends RuntimeException { + + public AmbiguousMethodCallException(String message) { + super(message); + } + + public AmbiguousMethodCallException(String message, Throwable throwable) { + super(message, throwable); + } +} From cf80649baa924d26bf770087ffb539790174ab45 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 20 Oct 2025 13:32:47 +0200 Subject: [PATCH 08/20] Refactor ActionApiImpl and enhance exception class. Refactored `ActionApiImpl` to include `ObjectMapper` and handle `isIntersection` using a safer boolean check. Updated `AmbiguousMethodCallException` to implement `Serializable` and added `serialVersionUID` for better compatibility. These changes improve code clarity, safety, and functionality. --- .../engine/actions/ActionApiImpl.java | 21 +++++++++++++++++-- .../AmbiguousMethodCallException.java | 8 ++++++- 2 files changed, 26 insertions(+), 3 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 e9ff90dad1..53ff7d0686 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 @@ -51,6 +51,8 @@ public class ActionApiImpl implements ActionApi { private IElasticTaskService elasticTaskService; + private ObjectMapper objectMapper; + @Autowired public void setDataService(IDataService dataService) { this.dataService = dataService; @@ -76,6 +78,16 @@ public void setUserService(UserService userService) { this.userService = userService; } + @Autowired + public void setElasticTaskService(IElasticTaskService elasticTaskService) { + this.elasticTaskService = elasticTaskService; + } + + @Autowired + public void setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + @Override public GetDataEventOutcome getData(String taskId, Map params) { return dataService.getData(taskId, params); @@ -96,10 +108,11 @@ public Page searchCases(String processIdentifier, Predicate predicate, Pag @Override public Page searchCases(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean isIntersection) { + boolean intersect = Boolean.TRUE.equals(isIntersection); List caseSearchRequests = elasticStringQueries.stream().map(query -> CaseSearchRequest.builder().query(query).build()).toList(); LoggedUser loggedUser = ActorTransformer.toLoggedUser(resolveAbstractUser(authPrincipalDto)); Locale locale = LocaleContextHolder.getLocale(); - return elasticCaseService.search(caseSearchRequests, loggedUser, pageable, locale, isIntersection); + return elasticCaseService.search(caseSearchRequests, loggedUser, pageable, locale, intersect); } @Override @@ -121,10 +134,11 @@ public Page searchTasks(String processIdentifier, Predicate predicate, Pag @Override public Page searchTasks(List elasticStringQueries, AuthPrincipalDto authPrincipalDto, Pageable pageable, Boolean 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(); - return elasticTaskService.search(taskSearchRequests, loggedUser, pageable, locale, isIntersection); + return elasticTaskService.search(taskSearchRequests, loggedUser, pageable, locale, intersect); } @Override @@ -164,6 +178,9 @@ public Page searchUsers(Predicate predicate, Pageable pageable, String rea } 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()))); } diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java index 27e5ddc61b..594b133b47 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java @@ -1,6 +1,12 @@ package com.netgrif.application.engine.adapter.spring.utils.exceptions; -public class AmbiguousMethodCallException extends RuntimeException { +import java.io.Serial; +import java.io.Serializable; + +public final class AmbiguousMethodCallException extends RuntimeException implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; public AmbiguousMethodCallException(String message) { super(message); From 42ec6b42cbff6a5bbfe9d68c2be10b763c557335 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 20 Oct 2025 13:33:22 +0200 Subject: [PATCH 09/20] Refine ConditionalOnMissingBean annotation for ActionApi Specify ActionApi class in the ConditionalOnMissingBean annotation to ensure proper bean initialization. This prevents unintended behavior when similar beans are present in the context. --- .../application/engine/actions/ActionApiConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiConfiguration.java b/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiConfiguration.java index 4ea84c3976..eed32fcfd8 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiConfiguration.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiConfiguration.java @@ -9,7 +9,7 @@ public class ActionApiConfiguration { @Bean - @ConditionalOnMissingBean + @ConditionalOnMissingBean(ActionApi.class) public ActionApi actionApi() { return new ActionApiImpl(); } From a1f164dbf39c0b04a8ee3324b7d172bff232a9c4 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 20 Oct 2025 13:38:35 +0200 Subject: [PATCH 10/20] Add annotations to exclude sessionId from toString and equals The @ToString.Exclude and @EqualsAndHashCode.Exclude annotations were added to the sessionId field in AuthPrincipalDto. This ensures that the sessionId is not included in toString or equals/hashCode operations, improving security and avoiding unintended leaks of sensitive data. --- .../engine/objects/auth/dto/AuthPrincipalDto.java | 5 +++++ 1 file changed, 5 insertions(+) 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 bf3d90f923..f5225f1991 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 @@ -1,6 +1,8 @@ package com.netgrif.application.engine.objects.auth.dto; import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; import java.io.Serial; import java.io.Serializable; @@ -13,5 +15,8 @@ public class AuthPrincipalDto implements Serializable { private String username; private String realmId; + + @ToString.Exclude + @EqualsAndHashCode.Exclude private String sessionId; } From 9eacdd08f226646e3b17acdde93bb101822440a2 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 20 Oct 2025 13:43:27 +0200 Subject: [PATCH 11/20] Fix typo in enum constant name from SEARCH_USER to SEARCH_USERS Corrected the enum constant to accurately reflect the plural form, ensuring consistency with the corresponding method name. This improves code clarity and prevents potential mismatches or confusion. --- .../engine/adapter/spring/actions/ActionApiMethods.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java index 10e41d0464..c3d387940e 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java @@ -15,7 +15,7 @@ public enum ActionApiMethods { ASSIGN_TASK("assignTask"), CANCEL_TASK("cancelTask"), FINISH_TASK("finishTask"), - SEARCH_USER("searchUsers"); + SEARCH_USERS("searchUsers"); private String methodName; From 6642234883b503214f856dd1275de279761c2457 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 20 Oct 2025 13:45:02 +0200 Subject: [PATCH 12/20] - removed duplicate dependency --- nae-spring-core-adapter/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/nae-spring-core-adapter/pom.xml b/nae-spring-core-adapter/pom.xml index 7a8bff888f..e3edd06578 100644 --- a/nae-spring-core-adapter/pom.xml +++ b/nae-spring-core-adapter/pom.xml @@ -98,11 +98,6 @@ jackson-module-jsonSchema ${jackson.version} - - com.querydsl - querydsl-core - ${querydsl.version} - org.apache.commons commons-lang3 From 9e726d1390dda16c486228fd3c5a1293ff912a3f Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 20 Oct 2025 13:48:42 +0200 Subject: [PATCH 13/20] - removed unused import --- .../application/engine/adapter/spring/actions/ActionApi.java | 1 - 1 file changed, 1 deletion(-) 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 73740c91af..7f4bf4b669 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 @@ -19,7 +19,6 @@ import java.util.Map; import java.util.List; -import java.util.Map; /** From 905a142b9139307ecc1e6cf2e62c3cc08d66aaf2 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 20 Oct 2025 13:59:15 +0200 Subject: [PATCH 14/20] Remove redundant Serializable implementation in exception class The `Serializable` interface was unnecessary in `AmbiguousMethodCallException` since `RuntimeException` already implements it. This change simplifies the class and avoids redundant code. --- .../spring/utils/exceptions/AmbiguousMethodCallException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java index 594b133b47..c2287972ca 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java @@ -3,7 +3,7 @@ import java.io.Serial; import java.io.Serializable; -public final class AmbiguousMethodCallException extends RuntimeException implements Serializable { +public final class AmbiguousMethodCallException extends RuntimeException { @Serial private static final long serialVersionUID = 1L; From c497abaad1733d6006c44790753901406d13c676 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 20 Oct 2025 14:00:16 +0200 Subject: [PATCH 15/20] Remove redundant Serializable implementation in exception class The `Serializable` interface was unnecessary in `AmbiguousMethodCallException` since `RuntimeException` already implements it. This change simplifies the class and avoids redundant code. --- .../spring/utils/exceptions/AmbiguousMethodCallException.java | 1 - 1 file changed, 1 deletion(-) diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java index c2287972ca..e872386604 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java @@ -1,7 +1,6 @@ package com.netgrif.application.engine.adapter.spring.utils.exceptions; import java.io.Serial; -import java.io.Serializable; public final class AmbiguousMethodCallException extends RuntimeException { From f5df0dc8d05bd66929dcff40f2efe1873ab2360e Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Mon, 20 Oct 2025 14:11:41 +0200 Subject: [PATCH 16/20] Enhance method handling and enforce non-null constraints Added `@NonNull` annotations to critical fields in `AuthPrincipalDto` to ensure required values are always provided. Refined method retrieval in `NaeReflectionUtils` to filter only bridge and synthetic methods, improving specificity. Expanded `AmbiguousMethodCallException` with detailed Javadoc for better clarity and usage documentation. --- .../engine/objects/auth/dto/AuthPrincipalDto.java | 4 ++++ .../adapter/spring/utils/NaeReflectionUtils.java | 2 +- .../exceptions/AmbiguousMethodCallException.java | 15 +++++++++++++++ 3 files changed, 20 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 f5225f1991..c5cf003963 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 @@ -2,6 +2,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NonNull; import lombok.ToString; import java.io.Serial; @@ -13,7 +14,10 @@ public class AuthPrincipalDto implements Serializable { @Serial private static final long serialVersionUID = 6725518942728316525L; + @NonNull private String username; + + @NonNull private String realmId; @ToString.Exclude diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java index 92b6642aa4..77866fb39f 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java @@ -95,7 +95,7 @@ private static Method findMethodWithSuperClassParams(Object bean, String methodT NoSuchMethodException caughtException) throws NoSuchMethodException, AmbiguousMethodCallException { Class cls = NaeReflectionUtils.resolveClass(bean); - Method[] methods = cls.getMethods(); + Method[] methods = Arrays.stream(cls.getMethods()).filter(m -> m.isBridge() || m.isSynthetic()).toArray(Method[]::new); Method methodToInvoke = null; outerLoop: for (Method method : methods) { diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java index e872386604..32ffaf8363 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/exceptions/AmbiguousMethodCallException.java @@ -2,15 +2,30 @@ import java.io.Serial; +/** + * Exception thrown when a method call cannot be resolved unambiguously + * due to multiple possible matches. + */ public final class AmbiguousMethodCallException extends RuntimeException { @Serial private static final long serialVersionUID = 1L; + /** + * Constructs a new exception with the specified detail message. + * + * @param message a description of the ambiguity that caused the exception + */ public AmbiguousMethodCallException(String message) { super(message); } + /** + * Constructs a new exception with the specified detail message and cause. + * + * @param message a description of the ambiguity that caused the exception + * @param throwable the cause of the exception + */ public AmbiguousMethodCallException(String message, Throwable throwable) { super(message, throwable); } From e1dad6cd2e4924d90789eb1e565fbb53c3a9989b Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Thu, 6 Nov 2025 11:13:08 +0100 Subject: [PATCH 17/20] Remove @NonNull annotations from AuthPrincipalDto fields The @NonNull annotations were removed from the `username` and `realmId` fields in `AuthPrincipalDto` to relax validation constraints. This allows greater flexibility in handling null values for these fields where necessary. --- .../application/engine/objects/auth/dto/AuthPrincipalDto.java | 2 -- 1 file changed, 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 c5cf003963..50d6ead9fb 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 @@ -14,10 +14,8 @@ public class AuthPrincipalDto implements Serializable { @Serial private static final long serialVersionUID = 6725518942728316525L; - @NonNull private String username; - @NonNull private String realmId; @ToString.Exclude From 50a4fb99f31f65b4f3131dfde8d3a0d453702636 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 2 Dec 2025 15:34:37 +0100 Subject: [PATCH 18/20] Improve null handling, performance, and serialization Added `@JsonIgnore` to prevent `sessionId` serialization. Enhanced null-checking and parameter handling in `NaeReflectionUtils`. Optimized `ActionApiMethods` lookup with a prebuilt map for better performance. --- .../engine/objects/auth/dto/AuthPrincipalDto.java | 3 ++- .../engine/adapter/spring/actions/ActionApi.java | 2 +- .../adapter/spring/actions/ActionApiMethods.java | 10 ++++++---- .../adapter/spring/utils/NaeReflectionUtils.java | 9 +++++++-- 4 files changed, 16 insertions(+), 8 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 50d6ead9fb..3333122aae 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 @@ -1,8 +1,8 @@ package com.netgrif.application.engine.objects.auth.dto; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.NonNull; import lombok.ToString; import java.io.Serial; @@ -20,5 +20,6 @@ public class AuthPrincipalDto implements Serializable { @ToString.Exclude @EqualsAndHashCode.Exclude + @JsonIgnore private String sessionId; } 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 7f4bf4b669..497a920487 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 @@ -51,7 +51,7 @@ public interface ActionApi { * Finds a specific case by its ID. * * @param caseId the ID of the case to find - * @return the found case + * @return the found case, otherwise returns null */ Case findCase(String caseId); diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java index c3d387940e..ed4a19d075 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/actions/ActionApiMethods.java @@ -1,6 +1,8 @@ package com.netgrif.application.engine.adapter.spring.actions; import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; public enum ActionApiMethods { @@ -19,6 +21,8 @@ public enum ActionApiMethods { private String methodName; + private static final Map METHODS_BY_NAME_MAP = Arrays.stream(values()).collect(Collectors.toUnmodifiableMap(ActionApiMethods::getMethodName, e -> e)); + ActionApiMethods(String methodName) { this.methodName = methodName; } @@ -27,10 +31,8 @@ public String getMethodName() { return methodName; } + public static ActionApiMethods fromMethodName(String methodName) { - return Arrays.stream(ActionApiMethods.values()) - .filter(type -> type.methodName.equals(methodName)) - .findFirst() - .orElse(null); + return METHODS_BY_NAME_MAP.get(methodName); } } diff --git a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java index 77866fb39f..eadac8c7e9 100644 --- a/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java +++ b/nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/utils/NaeReflectionUtils.java @@ -71,8 +71,11 @@ public static int indexOfClass(List list, Class clazz) { */ public static Method findMethod(Object bean, String methodToExecute, Class[] requestParamTypes) throws NoSuchMethodException, AmbiguousMethodCallException { + Objects.requireNonNull(bean, "bean must not be null"); + Objects.requireNonNull(methodToExecute, "methodToExecute must not be null"); + final Class[] paramTypes = (requestParamTypes == null) ? new Class[0] : requestParamTypes; try { - return NaeReflectionUtils.resolveClass(bean).getMethod(methodToExecute, requestParamTypes); + return NaeReflectionUtils.resolveClass(bean).getMethod(methodToExecute, paramTypes); } catch (NoSuchMethodException e) { return findMethodWithSuperClassParams(bean, methodToExecute, requestParamTypes, e); } @@ -95,7 +98,9 @@ private static Method findMethodWithSuperClassParams(Object bean, String methodT NoSuchMethodException caughtException) throws NoSuchMethodException, AmbiguousMethodCallException { Class cls = NaeReflectionUtils.resolveClass(bean); - Method[] methods = Arrays.stream(cls.getMethods()).filter(m -> m.isBridge() || m.isSynthetic()).toArray(Method[]::new); + Method[] methods = Arrays.stream(cls.getMethods()) + .filter(m -> !m.isBridge() && !m.isSynthetic()) + .toArray(Method[]::new); Method methodToInvoke = null; outerLoop: for (Method method : methods) { From b33499ea7ca7599f7f7ec5d133b7f1b979379fa4 Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 3 Dec 2025 13:21:08 +0100 Subject: [PATCH 19/20] [NAE-2292] Make force delete of petriNets non-transactional - updated docs according to PR --- .../engine/adapter/spring/actions/ActionApi.java | 7 +++++-- 1 file changed, 5 insertions(+), 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 497a920487..b0ea0fdd2c 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 @@ -56,9 +56,12 @@ public interface ActionApi { Case findCase(String caseId); /** - * Searches for cases matching the given process identifier and predicate. + * Searches for cases matching the given predicate. * - * @param processIdentifier the identifier of the process + * @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 * @return a page of cases matching the criteria From 8d2aa1324041d7566222eceb20ed4b7f9de5789d Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 3 Dec 2025 13:32:32 +0100 Subject: [PATCH 20/20] - updated javadocs --- .../application/engine/adapter/spring/actions/ActionApi.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 b0ea0fdd2c..dc9f5dd5a4 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 @@ -51,7 +51,8 @@ public interface ActionApi { * Finds a specific case by its ID. * * @param caseId the ID of the case to find - * @return the found case, otherwise returns null + * @return the found case + * @throws IllegalArgumentException if not found */ Case findCase(String caseId); @@ -105,6 +106,7 @@ public interface ActionApi { * * @param taskId the ID of the task to find * @return the found task + * @throws IllegalArgumentException if not found */ Task findTask(String taskId);