Skip to content

Conversation

@SamuelPalaj
Copy link
Contributor

@SamuelPalaj SamuelPalaj commented Jan 7, 2026

Description

  • added permissions, users and userRefs attributes of case and ElasticCase to copy constructor of ElasticCase
    Fixes NAE-2316

Test Configuration

Name Tested on
OS windows 10
Runtime java 21
Dependency Manager Maven 3.9.9
Framework version Spring Boot 3.4.4
Run parameters
Other configuration

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • My changes have been checked, personally or remotely, with @...
  • I have commented my code, particularly in hard-to-understand areas
  • I have resolved all conflicts with the target branch of the PR
  • I have updated and synced my code with the target branch
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing tests pass locally with my changes:
    • Lint test
    • Unit tests
    • Integration tests
  • I have checked my contribution with code analysis tools:
  • I have made corresponding changes to the documentation:
    • Developer documentation
    • User Guides
    • Migration Guides

Summary by CodeRabbit

  • Bug Fixes

    • Case collections (tasks, roles, users, permissions, tags, datasets, immediate data) are now copied on create/update to prevent shared references, stale state, and to handle null/empty inputs safely.
  • New Features

    • Added copy/clone constructors for many field types to enable safe deep-copying.
    • Immediate data items can be created with explicit id, localized name, and type.

✏️ Tip: You can customize this high-level summary in your review settings.

machacjozef and others added 2 commits December 15, 2025 14:43
…loyment

- added permissions, users and userRefs attributes of case and ElasticCase to copy constructor of ElasticCase
@SamuelPalaj SamuelPalaj self-assigned this Jan 7, 2026
@SamuelPalaj SamuelPalaj added bugfix A change that fixes a bug Extra Small labels Jan 7, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

Walkthrough

ElasticCase now initializes and updates nested collections/maps using null‑safe new instances and deep copies; many elastic domain field classes received copy constructors; ImmediateField gained a constructor accepting explicit stringId, I18nString name, and type.

Changes

Cohort / File(s) Summary
ElasticCase collection & deep-copy changes
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
Constructor and update(ElasticCase) replaced direct assignments with null‑safe new collections/maps (e.g., taskIds, taskMongoIds, tasks, role/user sets, tags), rebuild dataSet element-wise via copy utility, rebuild immediateData wrapping names with I18nString, and added private static Map<String, Map<String, Boolean>> deepCopy(...).
ImmediateField constructor
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ImmediateField.java
Added public ImmediateField(String stringId, I18nString name, String type) and refactored ImmediateField(Field<?>) to delegate to it.
Elastic domain: copy constructors (objects package)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/*
Added copy constructors to many field classes performing null‑safe deep/shallow copies of arrays, lists, and maps and introducing necessary imports. Affected: BooleanField, ButtonField, CaseField, DataField, DateField, FieldWithAllowedNetsField, FileField, FilterField, I18nField, MapField, NumberField, StringCollectionField, TextField, UserField, UserListField.
Spring adapter domain: copy constructors (adapter package)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/*
Added public copy constructors mirroring object-layer additions (delegating to super(field)) for multiple field types. Affected: BooleanField, ButtonField, CaseField, DateField, FileField, FilterField, I18nField, MapField, NumberField, StringCollectionField, TextField, UserField, UserListField.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly identifies the specific bug being fixed (case permissions not propagating in cloud deployment) and maps directly to the main change of propagating permissions/users/userRefs in the ElasticCase copy constructor.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (1)

112-129: Critical: update() method must also refresh permissions, users, and userRefs.

The constructor now initializes permissions, users, and userRefs (lines 105-107), but the update() method does not refresh these maps when synchronizing state. This creates an inconsistency where:

  1. Initial ElasticCase creation copies permissions correctly
  2. Subsequent updates via update() leave permissions stale
  3. Permission changes in the source system won't propagate to Elasticsearch

Since this PR specifically addresses permission propagation issues (NAE-2316), omitting these fields from update() means the bug is only partially fixed.

🔒 Proposed fix to synchronize permission maps in update()
     tags = useCase.getTags();
+    permissions = useCase.getPermissions();
+    users = useCase.getUsers();
+    userRefs = useCase.getUserRefs();

     dataSet = useCase.getDataSet();
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f57b74 and 668fa4d.

📒 Files selected for processing (1)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: Build
  • GitHub Check: task-list-completed
🔇 Additional comments (1)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (1)

105-107: LGTM! Constructor now properly initializes permission maps.

The addition of permissions, users, and userRefs initialization in the constructor correctly addresses the permission propagation issue described in the PR objective. The defensive copy pattern is appropriate and consistent with other map initializations in this constructor.

coderabbitai[bot]
coderabbitai bot previously approved these changes Jan 7, 2026
…loyment

- added permissions, users and userRefs to update method of ElasticCase
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In
@nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:
- Around line 126-128: The fields permissions, users and userRefs in ElasticCase
are not defensively initialized and can be set to null by the update logic;
change their declarations to initialize them to empty HashMap instances (i.e.,
initialize permissions, users and userRefs at declaration to new HashMap<>()) so
they are never null and match the constructor's defensive pattern used
elsewhere.
- Around line 105-107: The constructor in ElasticCase sets permissions, users,
and userRefs without null checks which can cause NPEs (unlike tasks which uses a
null-safe pattern); update the assignments in the ElasticCase constructor to use
null checks and initialize to empty HashMap when
useCase.getPermissions()/getUsers()/getUserRefs() is null (e.g., ternary
checking each getter and creating new HashMap<>(...) when non-null or new
HashMap<>() when null), keeping in mind this remains a shallow copy consistent
with tags handling.
- Around line 105-107: In update(), avoid assigning permissions, users, and
userRefs by reference; mirror the constructor’s defensive-copy behavior by
creating new HashMap instances from useCase.getPermissions(),
useCase.getUsers(), and useCase.getUserRefs() (i.e., set permissions = new
HashMap<>(useCase.getPermissions()), users = new HashMap<>(useCase.getUsers()),
userRefs = new HashMap<>(useCase.getUserRefs())); also add unit tests for
ElasticCase (or relevant behavior) into the application-engine test suite to
verify defensive copying prevents external mutation.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 668fa4d and d408b4d.

📒 Files selected for processing (1)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java:336-343
Timestamp: 2025-12-19T07:44:48.019Z
Learning: In the netgrif/application-engine repository, when a Case is created, the `Case.populateDataSetBehaviorAndComponents()` method automatically copies all transition-level behavior and components to the case-level data fields, keyed by transition ID. This means methods like `isDataFieldEditable` only need to check case-level behavior (dataField.getBehavior().get(transId)), without fallback to transition.getDataSet(), since the behavior has already been copied during case initialization.
📚 Learning: 2025-12-19T07:44:48.019Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java:336-343
Timestamp: 2025-12-19T07:44:48.019Z
Learning: In the netgrif/application-engine repository, when a Case is created, the `Case.populateDataSetBehaviorAndComponents()` method automatically copies all transition-level behavior and components to the case-level data fields, keyed by transition ID. This means methods like `isDataFieldEditable` only need to check case-level behavior (dataField.getBehavior().get(transId)), without fallback to transition.getDataSet(), since the behavior has already been copied during case initialization.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:43.748Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed

@tuplle tuplle changed the base branch from release/7.0.0-rev10 to release/7.0.0-rev9 January 12, 2026 15:00
…loyment

- added defensive copying to update method for better consistency with constructor
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (2)

112-131: update() still risks aliasing (nested maps + elements) and can NPE on version++ / null collections

  • version++ will throw if version is null.
  • new HashSet<>(useCase.getX()) / new HashMap<>(useCase.getX()) will throw if any getter returns null.
  • permissions/users/userRefs are still shallow-copied (inner maps shared).
  • immediateData = new ArrayList<>(...) copies the list, but not necessarily the ImmediateField elements (constructor does element-copy).
Proposed fix (null-safe increment + reuse deep-copy helper + consistent immediateData copy)
@@
     public void update(ElasticCase useCase) {
-        version++;
+        version = (version == null) ? 1L : (version + 1);
         lastModified = useCase.getLastModified();
         title = useCase.getTitle();
-        taskIds = new HashSet<>(useCase.getTaskIds());
+        taskIds = useCase.getTaskIds() == null ? new HashSet<>() : new HashSet<>(useCase.getTaskIds());
@@
-        tags = new HashMap<>(useCase.getTags());
-        permissions = new HashMap<>(useCase.getPermissions());
-        users = new HashMap<>(useCase.getUsers());
-        userRefs = new HashMap<>(useCase.getUserRefs());
+        tags = useCase.getTags() == null ? new HashMap<>() : new HashMap<>(useCase.getTags());
+        permissions = copyNestedBoolMap(useCase.getPermissions());
+        users = copyNestedBoolMap(useCase.getUsers());
+        userRefs = copyNestedBoolMap(useCase.getUserRefs());
         dataSet = new HashMap<>(useCase.getDataSet());
-        immediateData = new ArrayList<>(useCase.getImmediateData());
+        immediateData = useCase.getImmediateData() == null
+                ? new ArrayList<>()
+                : useCase.getImmediateData().stream().map(ImmediateField::new).collect(Collectors.toList());
     }

82-110: Shallow copy of nested maps in constructor + NPE risk from uninitialized version field

Lines 105–107 create shallow copies of the nested permission maps: the outer HashMap is copied, but the inner Map<String, Boolean> instances are shared with the source, allowing mutations to leak across copies.

Additionally, the version field (line 28, declared as Long) is never initialized in the constructor, so calling version++ at line 113 in the update() method will throw a NullPointerException.

Suggested fix for nested map shallow copy
-        permissions = new HashMap<>(useCase.getPermissions());
-        users = new HashMap<>(useCase.getUsers());
-        userRefs = new HashMap<>(useCase.getUserRefs());
+        permissions = copyNestedBoolMap(useCase.getPermissions());
+        users = copyNestedBoolMap(useCase.getUsers());
+        userRefs = copyNestedBoolMap(useCase.getUserRefs());
+
+    private static Map<String, Map<String, Boolean>> copyNestedBoolMap(Map<String, Map<String, Boolean>> source) {
+        if (source == null || source.isEmpty()) {
+            return new HashMap<>();
+        }
+        Map<String, Map<String, Boolean>> copy = new HashMap<>(source.size());
+        source.forEach((k, v) -> copy.put(k, v == null ? null : new HashMap<>(v)));
+        return copy;
+    }

For the version field, initialize it to 0L in the constructor (or use @Data with @Builder to ensure proper initialization).

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d408b4d and 8162c8a.

📒 Files selected for processing (1)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2025-12-19T07:44:48.019Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java:336-343
Timestamp: 2025-12-19T07:44:48.019Z
Learning: In the netgrif/application-engine repository, when a Case is created, the `Case.populateDataSetBehaviorAndComponents()` method automatically copies all transition-level behavior and components to the case-level data fields, keyed by transition ID. This means methods like `isDataFieldEditable` only need to check case-level behavior (dataField.getBehavior().get(transId)), without fallback to transition.getDataSet(), since the behavior has already been copied during case initialization.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:43.748Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java

tuplle
tuplle previously approved these changes Jan 12, 2026
…loyment

- added creation of deep copy to permissions, users and userRefs
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In
@nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:
- Around line 133-138: The deepCopy method can NPE when an entry's inner map
value is null; update deepCopy (the private static Map<String, Map<String,
Boolean>> deepCopy(Map<String, Map<String, Boolean>> map) method) to guard
against null inner maps by replacing null with an empty map when copying (e.g.,
for each Map.Entry use e.getValue() == null ? new HashMap<>() : new
HashMap<>(e.getValue())); keep the existing null/empty check for the outer map
and preserve keys while ensuring no new HashMap is constructed from a null
value.
- Line 118: The current assignment "tasks = new HashSet<>(useCase.getTasks())"
makes only a shallow copy of the Set and can still share mutable ElasticTaskPair
instances; change it to perform a deep copy of the elements (create new
ElasticTaskPair instances from each element of useCase.getTasks()) so
ElasticCase's tasks contains independent copies—use the same cloning/constructor
pattern used earlier (e.g., map each task to new ElasticTaskPair(task) and
collect into a new Set) to keep behavior consistent with the constructor's
deep-copy approach.
- Around line 116-130: The update() method (and the corresponding constructor
that mirrors it) wraps many collection fields like taskIds, taskMongoIds, tasks,
enabledRoles, viewRoles, viewUserRefs, negativeViewRoles, viewUsers,
negativeViewUsers, tags, dataSet, immediateData with new HashSet/HashMap without
null checks, which can NPE after deserialization; change each assignment to
defensively handle null (e.g., if useCase.getX() != null then new
HashSet<>(useCase.getX()) else new HashSet<>() or new HashMap<>()), and mirror
the same null-safe pattern used by deepCopy() for permissions, users, and
userRefs so all collections are consistently non-null after construction/update.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8162c8a and 2ea03c5.

📒 Files selected for processing (1)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2025-12-19T07:44:48.019Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java:336-343
Timestamp: 2025-12-19T07:44:48.019Z
Learning: In the netgrif/application-engine repository, when a Case is created, the `Case.populateDataSetBehaviorAndComponents()` method automatically copies all transition-level behavior and components to the case-level data fields, keyed by transition ID. This means methods like `isDataFieldEditable` only need to check case-level behavior (dataField.getBehavior().get(transId)), without fallback to transition.getDataSet(), since the behavior has already been copied during case initialization.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:43.748Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (2)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (2)

105-107: Core fix looks correct.

Deep copying permissions, users, and userRefs properly isolates the nested map structures, preventing unintended shared state that was causing the permission propagation bug in cloud deployment.


126-128: Consistent application of the fix in update method.

The same deep copy pattern is correctly applied here, ensuring updates also maintain isolated state for permissions.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
@nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:
- Around line 129-130: The copies for dataSet and immediateData can NPE; guard
them like the other collection copies by checking useCase.getDataSet() and
useCase.getImmediateData() and initializing dataSet to an empty HashMap and
immediateData to an empty ArrayList when the getters return null—update the
ElasticCase constructor (or the method copying from UseCase) to use conditional
initialization for the dataSet and immediateData fields.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ea03c5 and 1d6d21b.

📒 Files selected for processing (1)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2025-12-19T07:44:48.019Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java:336-343
Timestamp: 2025-12-19T07:44:48.019Z
Learning: In the netgrif/application-engine repository, when a Case is created, the `Case.populateDataSetBehaviorAndComponents()` method automatically copies all transition-level behavior and components to the case-level data fields, keyed by transition ID. This means methods like `isDataFieldEditable` only need to check case-level behavior (dataField.getBehavior().get(transId)), without fallback to transition.getDataSet(), since the behavior has already been copied during case initialization.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:43.748Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-12-19T07:55:13.427Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java:239-247
Timestamp: 2025-12-19T07:55:13.427Z
Learning: In the netgrif/application-engine project, the `taskService.findOne()` method (from ITaskService) never returns null. When a task is not found, it handles the error through other means (such as throwing an exception) rather than returning null, so null checks after `findOne()` calls are unnecessary.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-20T07:27:02.660Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java:38-45
Timestamp: 2025-08-20T07:27:02.660Z
Learning: When reviewing ElasticTaskQueueManager changes, task.getTask().getId() returns the document identifier while task.getTaskId() returns the business task identifier. The queue operations should use consistent identifiers throughout the lifecycle (scheduling, processing, cleanup).

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
🔇 Additional comments (2)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (2)

105-107: LGTM — Deep copy ensures permission isolation.

Using deepCopy() for the nested permission maps correctly prevents shared mutable state between the source Case and the constructed ElasticCase. This addresses the root cause of NAE-2316.


133-138: LGTM — Clean null-safe deep copy implementation.

The helper correctly handles null/empty outer maps and null inner map values, returning fresh mutable copies at both levels.

tuplle
tuplle previously approved these changes Jan 12, 2026
Retoocs
Retoocs previously approved these changes Jan 12, 2026
…loyment

- deep copying of tasks in update method added
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
@nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:
- Around line 116-117: The update() method assumes source collections are
non-null and can NPE; change each assignment in update() (e.g., taskIds,
taskMongoIds and the other collection fields around lines 121-127 and 131) to
defensively handle nulls by using a null-coalescing pattern: wrap useCase.getX()
with (useCase.getX() == null ? Collections.emptySet() : useCase.getX()) (or
Collections.emptyList() as appropriate) before constructing new
HashSet/ArrayList, so new HashSet<>(useCase.getTaskIds() == null ?
Collections.emptySet() : useCase.getTaskIds()) etc.; apply the same pattern for
all collection copies in update().
- Line 132: The update() method in ElasticCase performs a shallow copy of
immediateData causing shared ImmediateField/I18nString instances; change the
update(ElasticCase) implementation to deep-copy immediateData the same way the
constructor does (i.e., replace new ArrayList<>(useCase.getImmediateData()) with
a stream/map that constructs new ImmediateField(...) for each entry, ensuring
each ImmediateField's I18nString name is copied via its copy constructor or
factory if available) so updated ElasticCase gets independent ImmediateField and
I18nString instances.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d6d21b and 7f21900.

📒 Files selected for processing (1)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 402
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:118-118
Timestamp: 2026-01-13T06:40:13.919Z
Learning: In ElasticCase (nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java), the tasks field containing ElasticTaskPair instances should be deep copied (creating new ElasticTaskPair instances) rather than shallow copied, both in the constructor and the update() method, to prevent sharing mutable references.
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2026-01-13T06:40:13.919Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 402
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:118-118
Timestamp: 2026-01-13T06:40:13.919Z
Learning: In ElasticCase (nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java), the tasks field containing ElasticTaskPair instances should be deep copied (creating new ElasticTaskPair instances) rather than shallow copied, both in the constructor and the update() method, to prevent sharing mutable references.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-12-19T07:44:48.019Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java:336-343
Timestamp: 2025-12-19T07:44:48.019Z
Learning: In the netgrif/application-engine repository, when a Case is created, the `Case.populateDataSetBehaviorAndComponents()` method automatically copies all transition-level behavior and components to the case-level data fields, keyed by transition ID. This means methods like `isDataFieldEditable` only need to check case-level behavior (dataField.getBehavior().get(transId)), without fallback to transition.getDataSet(), since the behavior has already been copied during case initialization.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:43.748Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-20T07:27:02.660Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java:38-45
Timestamp: 2025-08-20T07:27:02.660Z
Learning: When reviewing ElasticTaskQueueManager changes, task.getTask().getId() returns the document identifier while task.getTaskId() returns the business task identifier. The queue operations should use consistent identifiers throughout the lifecycle (scheduling, processing, cleanup).

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-12-19T07:55:13.427Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java:239-247
Timestamp: 2025-12-19T07:55:13.427Z
Learning: In the netgrif/application-engine project, the `taskService.findOne()` method (from ITaskService) never returns null. When a task is not found, it handles the error through other means (such as throwing an exception) rather than returning null, so null checks after `findOne()` calls are unnecessary.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-10-20T11:44:44.907Z
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (4)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (4)

105-107: LGTM! Deep copy of permission maps correctly addresses NAE-2316.

Using deepCopy() for the nested permissions, users, and userRefs maps ensures that both the outer and inner maps are cloned, preventing shared mutable state between the source Case and the constructed ElasticCase.


118-120: LGTM! Tasks are now correctly deep copied.

Creating new ElasticTaskPair instances ensures that mutable task references are not shared between the source and target ElasticCase objects. This aligns with the learning that tasks should be deep copied in both the constructor and update method.


135-140: LGTM! Well-implemented deep copy utility.

The method correctly handles null/empty outer maps and null inner maps, ensuring a fully independent copy of the nested map structure is created.


128-130: Consistent deep copy usage in update method.

Using deepCopy() for permissions, users, and userRefs in the update path ensures the permission propagation fix is complete across all mutation scenarios.

…loyment

- deep copying of ImmediateField in update method added
@coderabbitai coderabbitai bot added the Medium label Jan 13, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
@nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:
- Around line 117-128: The code assigns many collections from useCase directly
which can NPE; make these null-safe like the existing tasks handling by
replacing direct new HashSet<>(useCase.getX()) / new
HashMap<>(useCase.getTags()) with null checks or conditional expressions that
default to empty collections when getters return null (e.g., taskIds =
useCase.getTaskIds() == null ? new HashSet<>() : new
HashSet<>(useCase.getTaskIds()); same for taskMongoIds, enabledRoles, viewRoles,
viewUserRefs, negativeViewRoles, viewUsers, negativeViewUsers, and tags using
new HashMap<>()), and apply the same null-safe pattern used for
immediateData/getDataSet() where relevant.
- Line 132: The shallow copy of dataSet in ElasticCase (dataSet = new
HashMap<>(useCase.getDataSet())) leaves mutable DataField instances shared;
replace this with a deep copy by iterating useCase.getDataSet() and mapping each
key to a cloned DataField (use DataField.deepCopy() or construct new DataField
from the original) so ElasticCase.dataSet contains independent DataField
instances similar to how tasks/permissions/users/userRefs were handled.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f21900 and 325003c.

📒 Files selected for processing (2)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ImmediateField.java
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 402
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:118-118
Timestamp: 2026-01-13T06:40:13.919Z
Learning: In ElasticCase (nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java), the tasks field containing ElasticTaskPair instances should be deep copied (creating new ElasticTaskPair instances) rather than shallow copied, both in the constructor and the update() method, to prevent sharing mutable references.
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.
📚 Learning: 2026-01-13T06:40:13.919Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 402
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:118-118
Timestamp: 2026-01-13T06:40:13.919Z
Learning: In ElasticCase (nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java), the tasks field containing ElasticTaskPair instances should be deep copied (creating new ElasticTaskPair instances) rather than shallow copied, both in the constructor and the update() method, to prevent sharing mutable references.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-12-19T07:44:48.019Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java:336-343
Timestamp: 2025-12-19T07:44:48.019Z
Learning: In the netgrif/application-engine repository, when a Case is created, the `Case.populateDataSetBehaviorAndComponents()` method automatically copies all transition-level behavior and components to the case-level data fields, keyed by transition ID. This means methods like `isDataFieldEditable` only need to check case-level behavior (dataField.getBehavior().get(transId)), without fallback to transition.getDataSet(), since the behavior has already been copied during case initialization.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:43.748Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:13:40.087Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:13:40.087Z
Learning: In CaseField.java, fulltextValue is mapped as a keyword field type in Elasticsearch (for exact matches, filtering, aggregations), while the separate caseValue field serves different Elasticsearch query requirements, allowing the system to support multiple query patterns on the same data through different field mappings.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-20T07:27:02.660Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java:38-45
Timestamp: 2025-08-20T07:27:02.660Z
Learning: When reviewing ElasticTaskQueueManager changes, task.getTask().getId() returns the document identifier while task.getTaskId() returns the business task identifier. The queue operations should use consistent identifiers throughout the lifecycle (scheduling, processing, cleanup).

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-10-20T11:44:44.907Z
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
🔇 Additional comments (4)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ImmediateField.java (1)

29-33: LGTM!

The new constructor provides a clean way to create ImmediateField instances with explicit values, supporting the deep copy requirements in ElasticCase.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (3)

106-108: LGTM!

Deep copying permissions, users, and userRefs prevents shared mutable references between the source Case and the new ElasticCase instance. This correctly addresses the permission propagation issue.


138-143: LGTM!

The deepCopy helper correctly handles null/empty maps and creates new instances for both outer and inner maps, ensuring complete isolation of the nested permission structures.


133-135: No action needed. The I18nString class has a proper copy constructor (lines 40-44) that accepts another I18nString instance and performs a deep copy of all fields including the translations map. The code at lines 133-135 correctly creates a copy of the I18nString object and preserves all localization data.

…loyment

- deep copying of DataSet through reflection in update method of ElasticCase added
- copy constructors added do DataField and its subclasses
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/TextField.java (1)

25-28: Consider defensive copying for consistency.

The existing constructor assigns the array reference directly without cloning, while the new copy constructor defensively copies. If a caller retains and modifies the passed array, textValue would unexpectedly change.

♻️ Suggested defensive copy for consistency
 public TextField(String[] values) {
     super(values);
-    this.textValue = values;
+    this.textValue = values == null ? null : values.clone();
 }
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/UserListField.java (1)

16-30: Remove duplicate field declarations that shadow parent class fields.

UserListField shadows the usernameValue, fullNameValue, userIdValue, and userRealmIdValue fields inherited from UserField. This creates two separate copies of each field in the class hierarchy, leading to data inconsistency. The copy constructor correctly implements the duplicated pattern, but the pattern itself is the problem.

Remove the four field declarations from UserListField (lines 16-22) and rely solely on the inherited fields from UserField. The copy constructor logic will continue to work correctly through the parent class fields.

🤖 Fix all issues with AI agents
In
@nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:
- Around line 110-111: In the ElasticCase constructor, guard against null from
useCase.getImmediateData() like update() does, and ensure ImmediateField is
constructed the same way in update(): when mapping useCase.getImmediateData()
pass field.getType().getName() (string) instead of field.getType() to match the
ImmediateField(copy) constructor that expects a type name; update both the
constructor mapping (use null-check and map to new ImmediateField(...) or use
the copy constructor consistently) and the update() mapping to use
field.getType().getName() for consistency with ImmediateField.
- Around line 133-134: When building dataSet from useCase.getDataSet(), guard
against null entry values to avoid NPE from entry.getValue().getClass(); update
the stream that populates dataSet (the expression assigning dataSet) to filter
out entries with entry.getValue() == null or map them to null copies: e.g. add
.filter(entry -> entry.getValue() != null) before the collect, or in the
value-mapping use a ternary that returns null when entry.getValue() is null and
otherwise calls CopyConstructorUtil.copy(entry.getValue().getClass(),
entry.getValue())); keep the rest of the logic and null fallback for
useCase.getDataSet() unchanged.

In
@nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/MapField.java:
- Around line 22-24: In MapField assignation for keyValueTranslations, remove
the redundant new HashMap<>() wrapper around the stream+Collectors.toMap(...)
result; instead set this.keyValueTranslations to null when
field.keyValueTranslations is null or directly to the Map produced by
field.keyValueTranslations.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
entry -> new I18nString(entry.getValue())))); keep the existing null-check and
I18nString mapping but drop the extra HashMap copy to avoid the unnecessary
intermediate allocation.

In
@nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/UserListField.java:
- Line 6: Remove the unused import of java.util.Arrays from the UserListField
class header; locate the import statement in the UserListField.java file and
delete the line "import java.util.Arrays;" since array copying is handled by the
parent class and no Arrays methods are referenced in UserListField.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 325003c and c83f86f.

📒 Files selected for processing (29)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/BooleanField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ButtonField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/DataField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/DateField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/FieldWithAllowedNetsField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/FileField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/FilterField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/I18nField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/MapField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/NumberField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/TextField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/UserField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/UserListField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/BooleanField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ButtonField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/CaseField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/DateField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/FileField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/FilterField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/I18nField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/MapField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/NumberField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/TextField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/UserField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/UserListField.java
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 402
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:118-118
Timestamp: 2026-01-13T06:40:13.919Z
Learning: In ElasticCase (nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java), the tasks field containing ElasticTaskPair instances should be deep copied (creating new ElasticTaskPair instances) rather than shallow copied, both in the constructor and the update() method, to prevent sharing mutable references.
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:13:40.087Z
Learning: In CaseField.java, fulltextValue is mapped as a keyword field type in Elasticsearch (for exact matches, filtering, aggregations), while the separate caseValue field serves different Elasticsearch query requirements, allowing the system to support multiple query patterns on the same data through different field mappings.
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.
📚 Learning: 2026-01-13T06:40:13.919Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 402
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:118-118
Timestamp: 2026-01-13T06:40:13.919Z
Learning: In ElasticCase (nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java), the tasks field containing ElasticTaskPair instances should be deep copied (creating new ElasticTaskPair instances) rather than shallow copied, both in the constructor and the update() method, to prevent sharing mutable references.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/UserField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/FilterField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/FileField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/I18nField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/DateField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/UserField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/MapField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/UserListField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/CaseField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/MapField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/FilterField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/I18nField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/FieldWithAllowedNetsField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/TextField.java
📚 Learning: 2025-08-19T20:07:43.748Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/CaseField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/DataField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:13:40.087Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:13:40.087Z
Learning: In CaseField.java, fulltextValue is mapped as a keyword field type in Elasticsearch (for exact matches, filtering, aggregations), while the separate caseValue field serves different Elasticsearch query requirements, allowing the system to support multiple query patterns on the same data through different field mappings.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/CaseField.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/DataField.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-12-19T07:44:48.019Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java:336-343
Timestamp: 2025-12-19T07:44:48.019Z
Learning: In the netgrif/application-engine repository, when a Case is created, the `Case.populateDataSetBehaviorAndComponents()` method automatically copies all transition-level behavior and components to the case-level data fields, keyed by transition ID. This means methods like `isDataFieldEditable` only need to check case-level behavior (dataField.getBehavior().get(transId)), without fallback to transition.getDataSet(), since the behavior has already been copied during case initialization.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-20T07:27:02.660Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java:38-45
Timestamp: 2025-08-20T07:27:02.660Z
Learning: When reviewing ElasticTaskQueueManager changes, task.getTask().getId() returns the document identifier while task.getTaskId() returns the business task identifier. The queue operations should use consistent identifiers throughout the lifecycle (scheduling, processing, cleanup).

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-10-20T11:44:44.907Z
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-12-19T07:55:13.427Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java:239-247
Timestamp: 2025-12-19T07:55:13.427Z
Learning: In the netgrif/application-engine project, the `taskService.findOne()` method (from ITaskService) never returns null. When a task is not found, it handles the error through other means (such as throwing an exception) rather than returning null, so null checks after `findOne()` calls are unnecessary.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
🧬 Code graph analysis (1)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java (3)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/DataField.java (1)
  • Data (11-34)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/FieldWithAllowedNetsField.java (1)
  • Data (11-32)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/TextField.java (1)
  • Data (7-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build
🔇 Additional comments (31)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/FieldWithAllowedNetsField.java (1)

18-21: Copy constructor implementation looks correct.

The use of Arrays.copyOf for the String[] is appropriate since String is immutable, ensuring a proper shallow copy of the array is sufficient. The null-safe handling of allowedNets is good.

One minor consideration: the constructor does not guard against field itself being null. If a null argument is passed, both super(field) and field.allowedNets will throw NullPointerException. This aligns with typical copy constructor semantics (caller responsibility), but adding a null check or Objects.requireNonNull(field) would make the failure explicit. Based on learnings, this pattern is consistent with the deep-copy approach used across ElasticCase and related fields.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/NumberField.java (1)

15-18: LGTM!

The copy constructor correctly delegates to the superclass and copies the numberValue field. Since Double is immutable, direct assignment is safe and appropriate for deep-copy semantics.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/NumberField.java (1)

12-14: LGTM!

The copy constructor correctly delegates to the superclass. Since this adapter class only adds Elasticsearch @Field annotations without introducing new fields, no additional copying is needed.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/StringCollectionField.java (1)

17-20: LGTM! Copy constructor correctly implements defensive copying.

The implementation properly calls the parent constructor and creates an independent copy of collectionValue with appropriate null-safety. This follows the established pattern used in DataField and FieldWithAllowedNetsField, ensuring that ElasticCase field data is properly deep-copied to fix the permission propagation issue.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/FileField.java (1)

24-29: LGTM!

The copy constructor correctly deep copies all array fields using Arrays.copyOf() with proper null-safety checks. Since String is immutable, shallow-copying the array elements is sufficient for a true defensive copy. The delegation to super(fileField) ensures parent state is also copied.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/FileField.java (1)

15-17: LGTM!

The copy constructor correctly delegates to the parent class's copy constructor. Since this subclass only adds Elasticsearch annotations to inherited getters and defines no additional fields, delegating to super(field) is sufficient for complete state copying.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/TextField.java (1)

14-17: Copy constructor implementation is correct.

The defensive copy using clone() is appropriate for String[] since strings are immutable. The null-safety check is properly handled.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/UserField.java (1)

11-13: Copy constructor implementation is correct.

The delegation to super(field) is appropriate since this subclass adds no new fields—only Elasticsearch annotation overrides on getters. The parent class (com.netgrif.application.engine.objects.elastic.domain.UserField) properly deep copies all mutable array fields using Arrays.copyOf() for usernameValue, fullNameValue, userIdValue, and userRealmIdValue, preventing shared mutable state.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/TextField.java (1)

15-17: Copy constructor delegation is correct.

The copy constructor properly delegates to the superclass. The parent TextField class defensively copies the String[] field using clone() (line 16 of parent), ensuring array references are not shared between the original and copied instances. Since this subclass has no additional instance fields, delegation to the parent is complete and sufficient.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/FilterField.java (1)

13-15: LGTM!

The copy constructor correctly delegates to the superclass, which is appropriate since this adapter class doesn't introduce additional fields beyond annotations.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/CaseField.java (1)

13-15: LGTM!

The copy constructor correctly delegates to the parent class, which handles copying the caseValue list. Since this adapter class doesn't introduce additional mutable fields, this implementation is sufficient.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/BooleanField.java (1)

15-18: LGTM!

The copy constructor properly delegates to the superclass and copies the booleanValue field. Since Boolean is immutable, direct assignment is appropriate.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/I18nField.java (1)

15-17: LGTM!

The copy constructor correctly delegates to the parent class, which handles deep copying of keyValue and translations maps per the AI summary. Since this adapter class doesn't introduce additional mutable fields, delegation is sufficient.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/DataField.java (1)

22-24: > Likely an incorrect or invalid review comment.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/DateField.java (1)

16-18: LGTM!

The copy constructor correctly delegates to the superclass, which handles copying the dateValue and timestampValue fields. Since this subclass doesn't introduce additional fields and the parent's fields are immutable types (LocalDateTime, Long), the implementation is sound.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ButtonField.java (1)

15-18: LGTM!

The copy constructor properly delegates to the superclass and copies the buttonValue field. Since Integer is immutable, direct assignment is appropriate.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/BooleanField.java (1)

15-18: LGTM!

The copy constructor correctly delegates to the superclass and copies the local booleanValue field. Since Boolean is immutable, direct assignment is safe.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java (1)

18-21: LGTM!

The copy constructor properly handles the mutable caseValue list with null-safety and creates a defensive copy via new ArrayList<>(). Since String elements are immutable, this shallow copy of the list is sufficient to prevent shared mutable state. Based on learnings, the separate caseValue field is intentionally maintained for specific Elasticsearch query requirements.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/DateField.java (1)

21-25: LGTM!

The copy constructor correctly delegates to the superclass and copies both fields. Since LocalDateTime and Long are immutable types, direct assignment is appropriate without needing defensive copies.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/I18nField.java (1)

22-26: LGTM! Copy constructor properly deep-copies mutable state.

The implementation correctly:

  • Delegates to the parent TextField copy constructor
  • Deep copies the keyValue array using Arrays.copyOf
  • Creates an independent copy of translations map (shallow copy is sufficient since String values are immutable)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/MapField.java (1)

16-18: LGTM! Copy constructor correctly delegates to parent.

The implementation properly leverages the parent class's copy constructor, which handles all deep copying of keyValue array and keyValueTranslations map. Since this adapter class extends the domain MapField, polymorphic dispatch ensures correct behavior.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/StringCollectionField.java (1)

16-18: Copy constructor correctly delegates to superclass.

The addition of the copy constructor is consistent with the PR pattern of adding defensive-copy support across field classes. The delegation to super(field) is valid and the parent class hierarchy properly handles deep copying through the chain:

  • DataField deep copies fulltextValue using Arrays.copyOf()
  • TextField deep copies textValue using .clone()
  • StringCollectionField deep copies collectionValue using Arrays.copyOf()

No changes needed.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/UserField.java (1)

27-33: LGTM!

The copy constructor correctly performs null-safe defensive copies of all array fields using Arrays.copyOf. Since String is immutable, shallow array copies are sufficient here.

nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/UserListField.java (1)

13-15: LGTM!

The copy constructor correctly delegates to the parent class which handles the deep copying of all array fields.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (7)

3-4: Imports look correct.

Both imports are used appropriately in the file: I18nString for wrapping field names in ImmediateField construction (line 136), and CopyConstructorUtil for deep copying DataField instances (line 134).


107-109: Correctly deep copies permission maps to fix propagation issue.

Using deepCopy() for permissions, users, and userRefs ensures these nested Map<String, Map<String, Boolean>> structures are fully cloned, preventing shared mutable references between the source Case and this ElasticCase. This directly addresses NAE-2316.


120-122: Tasks are correctly deep copied.

Creating new ElasticTaskPair instances rather than reusing references from the source ensures mutation isolation. This aligns with the requirement to prevent sharing mutable references for the tasks field. Based on learnings, this is the correct approach.


130-132: Permission maps are correctly deep copied in update().

Consistent with the constructor, using deepCopy() for these nested maps ensures the ElasticCase is fully independent of the source's permission structures.


140-145: Well-implemented deep copy utility for nested permission maps.

The method correctly handles:

  • Null outer map → returns empty HashMap
  • Empty outer map → returns empty HashMap (short-circuit)
  • Null inner maps → replaced with empty HashMap
  • Non-null inner maps → copied via new HashMap<>()

This ensures complete isolation from the source maps.


114-138: Good defensive copying pattern in update() method overall.

The refactored update() method consistently creates new collection instances rather than sharing references, which is essential for Elasticsearch document updates. The null-safety guards are thorough for most fields.


100-106: No NPE risk in the constructor—Case guarantees non-null values.

The Case constructor (lines 125-141) initializes enabledRoles, viewRoles, viewUserRefs, negativeViewRoles, viewUsers, negativeViewUsers, and tags to non-null empty collections. Since Case is abstract and all concrete subclasses invoke this initialization, the ElasticCase constructor safely assumes these getters return non-null. The update() method's null checks are defensive because it receives an ElasticCase parameter (which may have null fields), not a Case parameter.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (1)

100-106: Potential NPE: Missing null checks for collection fields.

The constructor lacks null checks for enabledRoles, viewRoles, viewUserRefs, negativeViewRoles, viewUsers, negativeViewUsers, and tags — unlike the update() method (lines 123-129) which handles nulls defensively. If any getter returns null, new HashSet<>(null) or new HashMap<>(null) will throw NullPointerException.

Proposed fix for consistency with update()
-        enabledRoles = new HashSet<>(useCase.getEnabledRoles());
-        viewRoles = new HashSet<>(useCase.getViewRoles());
-        viewUserRefs = new HashSet<>(useCase.getViewUserRefs());
-        negativeViewRoles = new HashSet<>(useCase.getNegativeViewRoles());
-        viewUsers = new HashSet<>(useCase.getViewUsers());
-        negativeViewUsers = new HashSet<>(useCase.getNegativeViewUsers());
-        tags = new HashMap<>(useCase.getTags());
+        enabledRoles = useCase.getEnabledRoles() == null ? new HashSet<>() : new HashSet<>(useCase.getEnabledRoles());
+        viewRoles = useCase.getViewRoles() == null ? new HashSet<>() : new HashSet<>(useCase.getViewRoles());
+        viewUserRefs = useCase.getViewUserRefs() == null ? new HashSet<>() : new HashSet<>(useCase.getViewUserRefs());
+        negativeViewRoles = useCase.getNegativeViewRoles() == null ? new HashSet<>() : new HashSet<>(useCase.getNegativeViewRoles());
+        viewUsers = useCase.getViewUsers() == null ? new HashSet<>() : new HashSet<>(useCase.getViewUsers());
+        negativeViewUsers = useCase.getNegativeViewUsers() == null ? new HashSet<>() : new HashSet<>(useCase.getNegativeViewUsers());
+        tags = useCase.getTags() == null ? new HashMap<>() : new HashMap<>(useCase.getTags());
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c83f86f and f6eb198.

📒 Files selected for processing (3)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/MapField.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/UserListField.java
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 402
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:118-118
Timestamp: 2026-01-13T06:40:13.919Z
Learning: In ElasticCase (nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java), the tasks field containing ElasticTaskPair instances should be deep copied (creating new ElasticTaskPair instances) rather than shallow copied, both in the constructor and the update() method, to prevent sharing mutable references.
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2026-01-13T06:40:13.919Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 402
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:118-118
Timestamp: 2026-01-13T06:40:13.919Z
Learning: In ElasticCase (nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java), the tasks field containing ElasticTaskPair instances should be deep copied (creating new ElasticTaskPair instances) rather than shallow copied, both in the constructor and the update() method, to prevent sharing mutable references.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/MapField.java
📚 Learning: 2025-08-19T20:07:15.621Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-12-19T07:44:48.019Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/service/DataService.java:336-343
Timestamp: 2025-12-19T07:44:48.019Z
Learning: In the netgrif/application-engine repository, when a Case is created, the `Case.populateDataSetBehaviorAndComponents()` method automatically copies all transition-level behavior and components to the case-level data fields, keyed by transition ID. This means methods like `isDataFieldEditable` only need to check case-level behavior (dataField.getBehavior().get(transId)), without fallback to transition.getDataSet(), since the behavior has already been copied during case initialization.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-20T07:27:02.660Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java:38-45
Timestamp: 2025-08-20T07:27:02.660Z
Learning: When reviewing ElasticTaskQueueManager changes, task.getTask().getId() returns the document identifier while task.getTaskId() returns the business task identifier. The queue operations should use consistent identifiers throughout the lifecycle (scheduling, processing, cleanup).

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-10-20T11:44:44.907Z
Learnt from: machacjozef
Repo: netgrif/application-engine PR: 367
File: application-engine/src/main/resources/application.yaml:24-24
Timestamp: 2025-10-20T11:44:44.907Z
Learning: In the netgrif/application-engine project, the correction of the Elasticsearch task index name from "_taks" to "_task" in application.yaml was approved by maintainer machacjozef, indicating that any data migration concerns for this typo fix are handled separately or not applicable to their deployment scenario.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-12-19T07:55:13.427Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java:239-247
Timestamp: 2025-12-19T07:55:13.427Z
Learning: In the netgrif/application-engine project, the `taskService.findOne()` method (from ITaskService) never returns null. When a task is not found, it handles the error through other means (such as throwing an exception) rather than returning null, so null checks after `findOne()` calls are unnecessary.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-08-19T20:07:43.748Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java:16-16
Timestamp: 2025-08-19T20:07:43.748Z
Learning: In CaseField.java, the separate caseValue field (List<String>) is intentionally maintained alongside fulltextValue for specific Elasticsearch query requirements, rather than being derived on-the-fly from fulltextValue.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
📚 Learning: 2025-12-19T09:06:38.316Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 398
File: application-engine/src/main/java/com/netgrif/application/engine/importer/service/Importer.java:57-58
Timestamp: 2025-12-19T09:06:38.316Z
Learning: In the file `application-engine/src/main/java/com/netgrif/application/engine/importer/service/Importer.java`, the `definition` attribute of the `Action` class (from `com.netgrif.application.engine.objects.petrinet.domain.dataset.logic.action.Action`) is never null, so null checks for `action.getDefinition()` are not needed.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
🔇 Additional comments (6)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/UserListField.java (1)

11-13: LGTM!

The copy constructor correctly delegates to the superclass copy constructor, which (per the AI summary) performs deep copying of the array fields using Arrays.copyOf. Since this Spring adapter class doesn't introduce any additional fields beyond the parent, the delegation to super(field) is sufficient for proper cloning.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/MapField.java (1)

19-25: Copy constructor correctly implements deep-copy pattern.

The implementation properly:

  • Delegates to parent TextField copy constructor via super(field), which deep-copies textValue
  • Deep-copies keyValue array using Arrays.copyOf() (safe for immutable String elements)
  • Deep-copies keyValueTranslations by creating new I18nString instances for each map value via stream().collect(Collectors.toMap(...))
  • Includes null checks for both mutable fields

This follows the required pattern of deep-copying mutable references to prevent shared state issues across instances.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (4)

107-111: LGTM! Deep copy for permissions, users, and userRefs.

These changes correctly address NAE-2316 by ensuring nested maps are deep-copied rather than sharing references with the source Case. The immediateData handling with the copy constructor is also properly null-safe.


118-135: Good defensive copying with null-safe handling.

The update() method correctly implements:

  • Null-safe collection initialization with mutable HashSet/HashMap instances
  • Deep copy of tasks via new ElasticTaskPair instances (per established pattern)
  • Deep copy of dataSet values using CopyConstructorUtil.copy for proper polymorphic copying
  • Filtering out null entries in dataSet to prevent downstream issues

141-146: LGTM! Proper nested map deep copy.

The deepCopy helper correctly creates independent copies of nested Map<String, Map<String, Boolean>> structures, handling null outer maps and null inner maps appropriately. This ensures permission maps are fully decoupled from the source object.


136-138: Remove unnecessary I18nString wrapping for ImmediateField.getName().

field.getName() returns I18nString (from Field class), so wrapping it with new I18nString(field.getName()) creates an unnecessary copy. Pass the I18nString directly to the ImmediateField constructor:

new ImmediateField(field.getStringId(), field.getName(), field.getType())
⛔ Skipped due to learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 402
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java:118-118
Timestamp: 2026-01-13T06:40:13.919Z
Learning: In ElasticCase (nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java), the tasks field containing ElasticTaskPair instances should be deep copied (creating new ElasticTaskPair instances) rather than shallow copied, both in the constructor and the update() method, to prevent sharing mutable references.
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java:38-45
Timestamp: 2025-08-20T07:27:02.660Z
Learning: When reviewing ElasticTaskQueueManager changes, task.getTask().getId() returns the document identifier while task.getTaskId() returns the business task identifier. The queue operations should use consistent identifiers throughout the lifecycle (scheduling, processing, cleanup).
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.621Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

…loyment

- constructor call fix in ImmediateField
Retoocs
Retoocs previously approved these changes Jan 13, 2026
@SamuelPalaj SamuelPalaj dismissed Retoocs’s stale review January 13, 2026 15:43

The merge-base changed after approval.

renczesstefan
renczesstefan previously approved these changes Jan 13, 2026
@SamuelPalaj SamuelPalaj dismissed renczesstefan’s stale review January 13, 2026 16:03

The merge-base changed after approval.

tuplle
tuplle previously approved these changes Jan 13, 2026
@SamuelPalaj SamuelPalaj dismissed tuplle’s stale review January 13, 2026 16:09

The merge-base changed after approval.

machacjozef
machacjozef previously approved these changes Jan 13, 2026
@SamuelPalaj SamuelPalaj dismissed machacjozef’s stale review January 13, 2026 16:40

The merge-base changed after approval.

@machacjozef machacjozef merged commit 01e1ee9 into release/7.0.0-rev9 Jan 13, 2026
7 checks passed
@machacjozef machacjozef deleted the NAE-2316 branch January 13, 2026 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bugfix A change that fixes a bug Extra Small Medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants