From e86a370253d81ca161b1dc1401d2bd8fe8de46db Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 27 May 2026 13:54:47 +0200 Subject: [PATCH 1/4] [NAE-2442] Fix dataset update from database and action handling Introduced `updateCaseFromDb` in `IWorkflowService` and its implementation in `WorkflowService` to synchronize case data with the database. Updated `EventService` to invoke this method during action execution. --- .../engine/workflow/service/EventService.java | 6 ++++++ .../engine/workflow/service/WorkflowService.java | 10 ++++++++++ .../workflow/service/interfaces/IWorkflowService.java | 2 ++ 3 files changed, 18 insertions(+) diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/EventService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/EventService.java index e7c8549c214..14eba051496 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/EventService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/EventService.java @@ -51,6 +51,9 @@ public List runActions(List actions, Case useCase, Optiona List functions = useCase == null ? Collections.emptyList() : useCase.getPetriNet().getFunctions(); actions.forEach(action -> { List outcomes = actionsRunner.run(action, useCase, task, params, functions); + if (useCase != null) { + workflowService.updateCaseFromDb(useCase); + } outcomes.stream().filter(SetDataEventOutcome.class::isInstance) .forEach(outcome -> { if (((SetDataEventOutcome) outcome).getChangedFields().isEmpty()) return; @@ -75,6 +78,9 @@ public List runEventActions(Case useCase, Task task, List List functions = useCase == null ? Collections.emptyList() : useCase.getPetriNet().getFunctions(); actions.forEach(action -> { List outcomes = actionsRunner.run(action, useCase, taskOpt, params, functions); + if (useCase != null) { + workflowService.updateCaseFromDb(useCase); + } outcomes.stream().filter(SetDataEventOutcome.class::isInstance) .forEach(outcome -> { if (((SetDataEventOutcome) outcome).getChangedFields().isEmpty()) return; diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java index a16077f5307..a1d6537aec3 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java @@ -252,6 +252,16 @@ public Case resolveActorRef(Case useCase, boolean canSaveUseCase) { return useCase; } + @Override + public void updateCaseFromDb(Case useCase) { + Case actual = findOneNoNet(useCase.getStringId()); + actual.getDataSet().forEach((id, dataField) -> { + if (dataField.isNewerThen(useCase.getDataField(id))) { + useCase.getDataSet().put(id, dataField); + } + }); + } + /** * Resolves actor permissions for the useCase based on the actor list data field. * diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java index 0065281b897..48818587fa9 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IWorkflowService.java @@ -31,6 +31,8 @@ public interface IWorkflowService { Case resolveActorRef(Case useCase, boolean canSaveUseCase); + void updateCaseFromDb(Case useCase); + CreateCaseEventOutcome createCase(CreateCaseParams createCaseParams); Page findAllByAuthor(String authorId, String petriNet, Pageable pageable); From ce8003c66cf3eb770d1becefcc2752c1f120fbfe Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Wed, 27 May 2026 15:28:54 +0200 Subject: [PATCH 2/4] [NAE-2442] Fix dataset update from database and action handling Refactored `setData` method in `ActionApi` to use `Map>` for flexible data handling. Adjusted `WorkflowService` to use the `findOne` method for better database case update synchronization. --- .../com/netgrif/application/engine/actions/ActionApiImpl.java | 2 +- .../application/engine/workflow/service/WorkflowService.java | 2 +- .../application/engine/adapter/spring/actions/ActionApi.java | 2 +- 3 files changed, 3 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 4967e85e3ce..be243a76b8d 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 @@ -111,7 +111,7 @@ public GetDataEventOutcome getData(String taskId, Map params) { } @Override - public SetDataEventOutcome setData(String taskId, Map> dataSet, Map params) throws JsonProcessingException { + public SetDataEventOutcome setData(String taskId, Map> dataSet, Map params) throws JsonProcessingException { log.debug("Setting data for task [{}] with params: [{}]", taskId, params == null ? "null" : params.toString()); ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(dataSet); diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java index a1d6537aec3..1a25eb15fb0 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java @@ -254,7 +254,7 @@ public Case resolveActorRef(Case useCase, boolean canSaveUseCase) { @Override public void updateCaseFromDb(Case useCase) { - Case actual = findOneNoNet(useCase.getStringId()); + Case actual = findOne(useCase.getStringId()); actual.getDataSet().forEach((id, dataField) -> { if (dataField.isNewerThen(useCase.getDataField(id))) { useCase.getDataSet().put(id, dataField); 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 0fcf7a37301..fac2f567093 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 @@ -47,7 +47,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, Map> dataSet, Map params) throws JsonProcessingException; + SetDataEventOutcome setData(String taskId, Map> dataSet, Map params) throws JsonProcessingException; /** * Finds a specific case by its ID. From f385ae1e934680bcaa88762da707d2aeb2869d4c Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 2 Jun 2026 12:01:21 +0200 Subject: [PATCH 3/4] [NAE-2442] Fix dataset update from database and action handling Introduced `type` field and updated constructors to allow runtime type tracking in `Nullable`. Added `getType` method and adjusted factory methods accordingly. --- .../engine/objects/utils/Nullable.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/utils/Nullable.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/utils/Nullable.java index df50295a4ab..f2ede63e924 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/utils/Nullable.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/utils/Nullable.java @@ -25,9 +25,12 @@ public final class Nullable implements Serializable { private static final long serialVersionUID = 8683452581122892189L; private final T value; + + private final Class type; - private Nullable(T value) { + private Nullable(T value, Class type) { this.value = value; + this.type = type; } /** @@ -39,6 +42,15 @@ public T get() { return value; } + /** + * Returns the runtime class type of the value that can be held by this {@code Nullable} instance. + * + * @return the {@code Class} object representing the type {@code T}, or {@code null} if no type was specified + */ + public Class getType() { + return type; + } + /** * Creates a new {@code Nullable} instance containing the given value. * @@ -46,7 +58,19 @@ public T get() { * @return a {@code Nullable} instance wrapping the provided value */ public static Nullable of(T value) { - return new Nullable<>(value); + return new Nullable<>(value, null); + } + + /** + * Creates a new {@code Nullable} instance containing the given value and explicit type information. + * + * @param the type of the value to wrap + * @param value the value to wrap in a {@code Nullable} instance, can be {@code null} + * @param type the {@code Class} object representing the type {@code T}, can be {@code null} + * @return a {@code Nullable} instance wrapping the provided value with type information + */ + public static Nullable of(T value, Class type) { + return new Nullable<>(value, type); } /** @@ -56,7 +80,7 @@ public static Nullable of(T value) { * @return an empty {@code Nullable} instance */ public static Nullable empty() { - return new Nullable<>(null); + return new Nullable<>(null, null); } /** From 0090d657b5487abb2bdcd1ffdcba05982462120a Mon Sep 17 00:00:00 2001 From: renczesstefan Date: Tue, 2 Jun 2026 13:00:52 +0200 Subject: [PATCH 4/4] [NAE-2442] Fix dataset update from database and action handling Added `empty(Class)` factory method for runtime type-safe `Nullable` instances and updated methods to incorporate type handling in equality checks and hash code computation. --- .../engine/objects/utils/Nullable.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/utils/Nullable.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/utils/Nullable.java index f2ede63e924..6326d09ee42 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/utils/Nullable.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/utils/Nullable.java @@ -83,6 +83,17 @@ public static Nullable empty() { return new Nullable<>(null, null); } + /** + * Returns an empty {@code Nullable} instance holding no value but with explicit type information. + * + * @param the type of the value that can be held by this {@code Nullable} instance + * @param type the {@code Class} object representing the type {@code T}, can be {@code null} + * @return an empty {@code Nullable} instance with type information + */ + public static Nullable empty(Class type) { + return new Nullable<>(null, type); + } + /** * Checks if a value is present in this instance. * @@ -149,7 +160,7 @@ public Nullable filter(Predicate predicate) { if (isEmpty()) { return this; } else { - return predicate.test(value) ? this : empty(); + return predicate.test(value) ? this : empty(type); } } @@ -300,7 +311,8 @@ public boolean equals(Object obj) { } return obj instanceof Nullable other - && Objects.equals(value, other.value); + && Objects.equals(value, other.value) + && Objects.equals(type, other.type); } /** @@ -311,7 +323,7 @@ public boolean equals(Object obj) { */ @Override public int hashCode() { - return Objects.hashCode(value); + return Objects.hash(value, type); } /**