Skip to content

[ETASK-23] Dynamic view configuration#447

Merged
mazarijuraj merged 51 commits into
release/6.5.0from
ETASK-23
Jun 16, 2026
Merged

[ETASK-23] Dynamic view configuration#447
mazarijuraj merged 51 commits into
release/6.5.0from
ETASK-23

Conversation

@Retoocs

@Retoocs Retoocs commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Description

Fixed:

  • switched menu item search from Elasticsearch to MongoDB. Confusing search results no longer occur
  • fixed behavior of data refs in menu item forms
  • fixed initialization issues when creating menu item by MenuItemService
  • fixed sanitization issues in MenuItemService (when creating folder items)
  • improved input validation when using MenuItemService
  • fixed initialization of field parentIds when using MenuItemService.moveItem

New features / Improvements:

  • improved UX when editing menu items
  • introduced menu item MongoDB indexes to improve search
  • introduced menu item templates to make creation more easy
  • improved menu item workflow for manual creation in the app
  • introduced new view configuration options (simple-case-view, single-task-view, ...)
  • filter is now directly inside case_view_configuration instance and editable
  • allowed nets for the view is separated from the filter configuration
  • improved configuration options for the single_task_view_configuration
  • optimized tests with MongoDB indexes
  • introduced new configuration property for taskRef's component task-list: headers

Other changes:

  • Removed filter process with all the associated code
  • changed the idea behind the MenuItemService.update method. Now the update is alias for: 1. remove, 2 create
  • once the menu item structure is constructed, it can be no longer changed (f.e. change case-view to task-view)

Implements ETASK-23

Dependencies

No new dependencies were introduced

Third party dependencies

No new dependencies were introduced

Blocking Pull requests

Depends on #446

How Has Been This Tested?

Manually and by unit tests

  • MenuItemServiceTest

Test Configuration

Name Tested on
OS Ubuntu 24.04.1 LTS
Runtime Java 11
Dependency Manager Maven 3.6.3
Framework version Spring Boot 2.7.8
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

Release Notes

  • Bug Fixes

    • Improved menu item creation and management with template-based approach and enhanced validation.
  • Removed Features

    • Removed filter import/export functionality from the application.
    • Removed filter discovery and management capabilities.
  • Improvements

    • Restructured view configuration options with new template support.
    • Enhanced menu item service with database indexing for better performance.

Retoocs added 30 commits May 4, 2026 08:08
- update text translations
- fix null node
- add todo
- update petri net of menu_item.xml
- fix handling of uri
- update role permission in menu_item.xml
- refactor menu item view forms
- implement MenuController.getMenuItemData
- implement MenuItemServiceTest.getMenuItemDataTest
- fix resolving menu item in the menu
- add todos
- fix changing menu item by ActionDelegate
- fix menu_item.manageBehaviorOfTabFields
- fix menu_item move_add_node set action
- improve menu_item initialization when creating by forms
- fix next view selection in ticket view
- make single task view as a primary view
- update init values in menu_item.xml
- update tabbed_single_task_view_configuration configurations
- make single task view as untabbed too
- make task view as untabbed too
- make case view as untabbed too
- introduce empty content configuration
- fix null node
- fix test
- update doc
- begin updating filter
- introduce menu item configuration templates
- finish implementation of template configurations
- resolve todos
- fix behavior in menu_item.xml
- add menu item body validation
- add tags in menu_item.xml transitions for better filtering
- change default headers field type to string collection
- introduce CustomViewTemplate
- update default filter bodies to exclude allowed nets
- update CustomViewTemplate
- introduce allAllowedNets metadata for filter
- fix behavior in menu_item.xml
- update templates configuration
- fix custom view creation via template
- rework settings parts UX
- change menu item process identifier constant
- add duplicity check on create item transition
- implement menu item data index creation and usage
- menu item identifier and uri fixes
- refactor MenuItemService.updateMenuItem
- fix MenuItemService.findMenuItem
- improve identifier behavior on creation task
- remove filter case
- remove deprecated methods in ActionDelegate
- update menu configurations for direct use of filter field
- force menu item database indexes
- rename MenuItemView to MenuItemViewType
- fix menu name null node
- update MenuItemServiceTest
- optimize tests by ensuring mongo db indexes
- finish implementation of create menu item test
- introduce temporary method MenuItemService.collectRoles
- implement more tests in MenuItemServiceTest
- remove unnecessary attributes in case_view_configuration.xml
Retoocs added 4 commits June 2, 2026 10:12
- set up headers for task lists
- fix uri change of menu item on creation
- add translation
- update doc
@Retoocs Retoocs self-assigned this Jun 2, 2026
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0fc4b3f6-f29e-4288-9722-22e3742cfeac

📥 Commits

Reviewing files that changed from the base of the PR and between 0b8c36b and a8fdf52.

📒 Files selected for processing (10)
  • src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
  • src/main/java/com/netgrif/application/engine/workflow/domain/Case.java
  • src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
  • src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/menu_item.xml
  • src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml
  • src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy
  • src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java
💤 Files with no reviewable changes (1)
  • src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java

Walkthrough

The PR replaces legacy filter-driven menu wiring with menu view templates and configuration forms, adds Mongo-backed menu item lookups and index creation, rewrites menu Petri nets, removes filter and import/export runtime paths, and updates startup, tests, and docs to the new menu flow.

Changes

Menu and legacy filter refactor

Layer / File(s) Summary
Menu contracts, view bodies, and templates
src/main/java/com/netgrif/application/engine/menu/domain/*, src/main/java/com/netgrif/application/engine/menu/domain/configurations/*, src/main/java/com/netgrif/application/engine/menu/domain/templates/*, src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java
Replaces legacy menu view typing with MenuItemViewType, simplifies FilterBody, adds case/task/single-task view bodies and constants, and registers reusable menu item templates plus configuration-template mapping.
Menu service, startup, and integration coverage
src/main/java/com/netgrif/application/engine/menu/service/*, src/main/java/com/netgrif/application/engine/menu/service/interfaces/*, src/main/groovy/com/netgrif/application/engine/startup/*, src/test/groovy/com/netgrif/application/engine/TestHelper.groovy, src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java
MenuItemService moves to Mongo-backed indexed lookups, creates views from templates, and handles configuration templates; startup imports switch to MenuRunner, Mongo startup ensures menu indexes, and new integration tests cover menu item lifecycle behavior.
Menu configuration Petri nets
src/main/resources/petriNets/engine-processes/menu/menu_item.xml, src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml, src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml, src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml, src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml, src/test/resources/dashboard_management_test.xml
Menu and view configuration nets gain template selection, advanced content task lists, associated-view forms, new task and single-task configuration processes, and updated initialization and visibility routing.
Runtime and ActionDelegate cleanup
src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy, src/main/java/com/netgrif/application/engine/auth/service/UserService.java, src/main/java/com/netgrif/application/engine/workflow/service/DataService.java, src/main/java/com/netgrif/application/engine/workflow/domain/Case.java, src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
Removes legacy filter search/import/export helpers and metadata handling, switches menu item updates to sync transitions, drops user filter import/export creation, and updates immediate-data typing to Field<?>.
Legacy filter and import-export removal
docs/search/*, docs/_sidebar.md, src/main/resources/petriNets/engine-processes/org_group.xml, src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy, src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy, src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IMenuImportExportService.java
Removes filter and filter import/export documentation links, drops org-group menu export entry refresh helpers, removes ActionDelegate filter import/export test coverage, and trims remaining menu import/export test wiring.

Sequence Diagram(s)

sequenceDiagram
  participant Startup as MenuRunner
  participant Importer as ImportHelper
  participant Net as menu_item.xml
  participant Service as MenuItemService
  participant Mongo as MongoTemplate

  Startup->>Importer: importProcess(view_configuration.xml)
  Startup->>Importer: importProcess(menu_item.xml)
  Net->>Service: handleConfigurationTemplate(menuItemCase)
  Service->>Mongo: ensureIndex(...)
  Service->>Mongo: findOne by processIdentifier and dataSet value
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

@Retoocs
Retoocs marked this pull request as ready for review June 2, 2026 14:31
@coderabbitai coderabbitai Bot added bugfix A change that fixes a bug improvement A change that improves on an existing feature new feature A change that introduces new functionality Large Medium labels Jun 2, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 25

Caution

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

⚠️ Outside diff range comments (5)
src/main/resources/petriNets/engine-processes/menu/menu_item.xml (1)

203-216: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject URI separators in move_dest_uri_new_node.

This value is appended verbatim, so entering foo/bar or /foo creates multiple path segments even though the UI asks for a single node. That can silently reshape the URI tree and move the item under an unintended branch.

Proposed fix
         <action trigger="set">
             newNodeName: f.move_dest_uri_new_node,
             selectedUri: f.move_dest_uri;
 
             if (newNodeName.value == null || newNodeName.value == "") {
                 return
             }
+
+            if (newNodeName.value.contains(uriService.getUriSeparator())) {
+                throw new IllegalArgumentException("New node name must not contain URI separators")
+            }
 
             String prefixUri = selectedUri.value.join("/")
             prefixUri = prefixUri.replace("//","/")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml` around
lines 203 - 216, Reject values for move_dest_uri_new_node that contain URI
separator characters instead of appending them verbatim: in the action that
reads newNodeName (symbol newNodeName / move_dest_uri_new_node) validate
newNodeName.value for occurrences of "/" and uriService.getUriSeparator() and if
found either return early (like existing empty check) or surface a validation
error; alternatively trim any leading/trailing separators before building
prefixUri so the code that constructs newUri (uses selectedUri,
uriService.getUriSeparator(), and uriService.getOrCreate) cannot create extra
path segments. Ensure the check/sanitization occurs before concatenating
prefixUri and calling uriService.getOrCreate.
src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy (1)

183-184: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fix missing menuImportExportService collaborator in disabled menu import/export test (lines 183-184)

MenuImportExportTest still calls menuImportExportService.invokeMethod(...) at lines 183–184, but the test class has no @Autowired/field/property named menuImportExportService (only this usage). With the test enabled, it will fail at runtime (missing property/bean). The MenuAndFilters type/import is still present, so that part is not an issue.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy`
around lines 183 - 184, The test calls
menuImportExportService.invokeMethod("loadFromXML", ...) but the test class
lacks a menuImportExportService collaborator; add a collaborator field or bean
for MenuImportExportService (e.g., declare and annotate a
MenuImportExportService menuImportExportService with `@Autowired` or initialize a
mock and inject it) so the invokeMethod calls on menuImportExportService (used
to loadFromXML) resolve at runtime; ensure the field name matches
menuImportExportService and that the service provides the loadFromXML behavior
expected by the test.
src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FieldType.groovy (1)

24-37: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Match serialized field names in fromString().

Line 37 breaks the new filter types: "caseFilter", "taskFilter", and "processFilter" are converted to CASEFILTER/TASKFILTER/PROCESSFILTER, which do not match the new enum constants with underscores. Any path that round-trips getName() back through fromString() will now fail for these fields.

💡 Proposed fix
+import java.util.Arrays
+
 enum FieldType {
@@
     static FieldType fromString(String name) {
-        return valueOf(name.toUpperCase())
+        return Arrays.stream(values())
+                .filter(type -> type.name.equals(name) || type.name().equalsIgnoreCase(name) || type.name().equalsIgnoreCase(name))
+                .findFirst()
+                .orElseThrow(() -> new IllegalArgumentException("Unknown field type: " + name))
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FieldType.groovy`
around lines 24 - 37, fromString(String name) currently calls
valueOf(name.toUpperCase()) which converts serialized names like
"caseFilter"/"taskFilter"/"processFilter" into
CASEFILTER/TASKFILTER/PROCESSFILTER and fails to match the enum constants;
update FieldType.fromString to lookup by the enum instance's serialized name
property instead (e.g., iterate FieldType.values() and return the entry whose
'name' field equals the input, with a case-insensitive comparison or sensible
fallback), and keep a fallback to valueOf(name.toUpperCase()) only if necessary
to preserve backwards compatibility.
src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewType.java (1)

74-94: 🧹 Nitpick | 🔵 Trivial | ⚖️ Poor tradeoff

Simplify the tabbed/untabbed filter predicate; current form has a latent correctness gap.

The predicate (view.isTabbed == isTabbed || view.isUntabbed != isTabbed) is hard to reason about and does not express the apparent intent ("when querying tabbed, match tabbed-capable views; when querying non-tabbed, match untabbed-capable views"). It is masked today only because every enum value has isTabbed == true, but it is fragile: a future view with isTabbed=false, isUntabbed=false would be incorrectly returned when isTabbed=false is requested (since isUntabbed != falsefalse != false is false... conversely view.isUntabbed != isTabbed evaluates to false, but view.isTabbed == isTabbedfalse == false is true, so it is wrongly included). The same predicate is duplicated in findAllByIsTabbedAndParentIdentifier.

Consider replacing both occurrences with an explicit selector.

♻️ Proposed clarification
-                .filter(view -> (view.isTabbed == isTabbed || view.isUntabbed != isTabbed) && view.isPrimary == isPrimary)
+                .filter(view -> (isTabbed ? view.isTabbed : view.isUntabbed) && view.isPrimary == isPrimary)
-                .filter(view -> (view.isTabbed == isTabbed || view.isUntabbed != isTabbed)
+                .filter(view -> (isTabbed ? view.isTabbed : view.isUntabbed)
                         && parentView.getAllowedAssociatedViews().contains(view.identifier))

Please confirm the intended semantics: should a view be selectable in non-tabbed mode strictly when isUntabbed == true?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewType.java`
around lines 74 - 94, The tabbed/untabbed predicate is unclear and brittle;
update both findAllByIsTabbedAndIsPrimary and
findAllByIsTabbedAndParentIdentifier to explicitly match the intended semantics:
when isTabbed==true filter by view.isTabbed==true, otherwise filter by
view.isUntabbed==true; keep the other condition in each method (isPrimary match
or parentView.getAllowedAssociatedViews().contains(view.identifier)) unchanged
so only the tabbed check is simplified and made explicit using the view.isTabbed
and view.isUntabbed fields.
src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java (1)

70-78: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Downgrade NPE concern in ViewBody.toDataSet(Case) (safe in current call path, optional robustness guard)

  • ViewBody.toDataSet(Case associatedViewCase) can NPE when associatedViewCase != null but this.getAssociatedViewBody() is null (e.g., TaskViewBody), but current caller path in MenuItemService.createView(...) only creates/passes associatedViewCase when body.hasAssociatedView() is true, which implies body.getAssociatedViewBody() != null.
  • Optional hardening: make the guard consistent with the dereference.
🛡️ Suggested guard
-        if (associatedViewCase != null) {
+        if (associatedViewCase != null && hasAssociatedView()) {
             outcome.putDataSetEntry(ViewConstants.FIELD_CONFIGURATION_TYPE, FieldType.ENUMERATION_MAP,
                     this.getAssociatedViewBody().getViewType().getIdentifier());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java`
around lines 70 - 78, In ViewBody.toDataSet(Case) the code dereferences
this.getAssociatedViewBody() while only checking associatedViewCase != null;
change the guard to ensure the associated view body is non-null as well (e.g.,
if (associatedViewCase != null && this.getAssociatedViewBody() != null) or use
this.hasAssociatedView()) so you don't risk NPE when getAssociatedViewBody() can
be null (references: ViewBody.toDataSet(Case), getAssociatedViewBody(),
hasAssociatedView(), MenuItemService.createView(...)).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/groovy/com/netgrif/application/engine/startup/MenuRunner.groovy`:
- Around line 32-38: In createConfigurationNets() the code uses
MenuItemViewType.values().each { ... }.collect() which returns the original
enums instead of the imported PetriNet objects; change the iteration to use
collect with a closure that returns the result of helper.importProcess so the
method actually returns List<PetriNet> (e.g. MenuItemViewType.values().collect {
view -> helper.importProcess(...) }), or alternatively if the returned list is
unused by run(), change createConfigurationNets() signature to void and keep the
current each block that only calls helper.importProcess for side effects.

In `@src/main/groovy/com/netgrif/application/engine/startup/MongoDbRunner.groovy`:
- Around line 27-28: MongoDbRunner currently runs at startup and can trigger a
MongoDB drop under test configs; modify MongoDbRunner to avoid dropping
non-isolated DBs by adding a guard: detect active profiles or the property
spring.profiles.active and skip the drop when the "test" profile is active (or
alternatively annotate the class with `@Profile`("!test")), or read
spring.data.mongodb.drop and the resolved database name from Environment and
only perform the drop if drop==true AND the database name clearly indicates an
isolated test DB (e.g., endsWith("_test") or matches a CI-only pattern);
implement the check in MongoDbRunner's startup method (the run/init method in
class MongoDbRunner) so the drop is skipped unless the safe conditions are met.

In
`@src/main/java/com/netgrif/application/engine/importer/service/FieldFactory.java`:
- Around line 796-802: resolveAttributeValues currently only sets allowedNets
when field.getType().equals(FieldType.CASE_REF), which means immediate FILTER
fields (or any FieldWithAllowedNets used as immediate data fields) won't get
allowedNets populated; change resolveAttributeValues in FieldFactory so that it
assigns allowedNets to any field implementing FieldWithAllowedNets (use
instanceof FieldWithAllowedNets) rather than only for CASE_REF, or alternatively
call resolveAllowedNets(...) from buildImmediateField to ensure consistency with
buildField; update references to DataField.getAllowedNets() and
((FieldWithAllowedNets) field).setAllowedNets(...) accordingly.

In `@src/main/java/com/netgrif/application/engine/importer/service/Importer.java`:
- Line 243: The method signature change to use CaseActorRef/ActorRef breaks
compilation because those types are not present in
com.netgrif.application.engine.importer.model; restore the original generated
model types (the previous importer model classes used by resolveUserRef and any
other affected methods such as the one at line ~732) or add the corresponding
model/schema classes to com.netgrif.application.engine.importer.model in this PR
so callers and method signatures remain consistent; update the signatures of
resolveUserRef and any other methods that reference CaseActorRef/ActorRef back
to the original model types (or ensure the newly added classes match the
expected names and packages) and re-run compilation to confirm the pipeline
passes.

In
`@src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java`:
- Line 29: Rename the boolean field isHeaderModeChangeable in class CaseViewBody
to headerModeChangeable to make Lombok generate predictable accessors (getter
isHeaderModeChangeable() and setter setHeaderModeChangeable(...) currently
confuse callers/serializers); update all usages of isHeaderModeChangeable, any
direct field references, and any JSON bindings or tests to use
headerModeChangeable so accessor names and serialized property match the other
boolean fields (e.g., follow the pattern used for other booleans in
CaseViewBody).
- Around line 69-98: The code unconditionally writes nullable Text fields
bannedNetsInCreation and emptyContentIcon into the dataset via
ToDataSetOutcome.putDataSetEntry (in CaseViewBody), causing explicit null
entries; instead guard those entries like other optional fields and only call
putDataSetEntry for CaseViewConstants.FIELD_BANNED_NETS_IN_CREATION and
CaseViewConstants.FIELD_EMPTY_CONTENT_ICON when bannedNetsInCreation and
emptyContentIcon are non-null (respectively), so the dataset omits missing
values rather than persisting value: null.

In
`@src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java`:
- Around line 76-81: TaskViewBody.toDataSetInternal always emits
TaskViewConstants.FIELD_EMPTY_CONTENT_ICON even when emptyContentIcon is null;
add the same null guard used for emptyContentText so the icon entry is only put
when non-null. Modify the code path in TaskViewBody.toDataSetInternal to check
this.emptyContentIcon != null before calling
outcome.putDataSetEntry(TaskViewConstants.FIELD_EMPTY_CONTENT_ICON,
FieldType.TEXT, this.emptyContentIcon), and apply the identical guard to
CaseViewBody where the same asymmetry exists.

In
`@src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java`:
- Around line 16-20: The constructor
ConfigurationTemplateOutcome(ToDataSetOutcome toDataSetOutcome) should
defensively validate inputs: first check toDataSetOutcome and
toDataSetOutcome.getDataSet() for null before iterating; inside the forEach
ensure each fieldMap is non-null and contains the "value" key (and that
fieldMap.get("value") is non-null if you don't want nulls stored) before calling
this.mapping.put(fieldId, ...). Update the constructor to skip or handle entries
that fail those checks so no NullPointerException is thrown when
ToDataSetOutcome, the dataset, a fieldMap, or the "value" entry is missing.
- Line 10: The public mutable Map field mapping on ConfigurationTemplateOutcome
breaks encapsulation; make the field private (private final Map<String,Object>
mapping) and add a public accessor method (e.g., getMapping()) that returns
either an unmodifiable view (Collections.unmodifiableMap(mapping)) or a
defensive copy to prevent external mutation; update any direct accesses to the
mapping field to use getMapping() so callers cannot mutate the internal state
directly.

In `@src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java`:
- Around line 22-29: In FilterBody.toDataSet(), guard against a null type to
avoid NPE: check if this.type is null before calling this.type.getName() and
handle it defensively (e.g., compute a safe typeName = (this.type != null ?
this.type.getName() : null) or throw a clear IllegalStateException); then put
that safe typeName into the dataSetValues map and continue to populate value and
insert into viewDataSetOutcome.getDataSet(). Also consider adding a unit test
for FilterBody.toDataSet() when type is null; reference: FilterBody.toDataSet(),
the field type, and the flow where ViewBody.toDataSet() calls
filterBody.setType(getFilterType()).

In `@src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java`:
- Around line 112-119: The conditional in MenuItemBody that sets useTabbedView
only when viewType.isTabbed() != viewType.isUntabbed() is subtle; update the
code comments (or add a small helper like MenuItemViewType.isDeterminate()) to
explicitly state that isTabbed==isUntabbed indicates an indeterminate/neutral
view type and therefore useTabbedView must remain unchanged, and add Javadoc to
MenuItemViewType explaining why both flags might be equal (e.g., legacy/neutral
types or mutually inclusive flags) so future readers understand the intended
behavior; reference the viewBody assignment, MenuItemViewType,
viewType.isTabbed(), viewType.isUntabbed(), and the useTabbedView field when
adding the explanation.
- Line 189: The call
outcome.putDataSetEntry(MenuItemConstants.FIELD_CONFIGURATION_TEMPLATES,
FieldType.ENUMERATION_MAP, this.configurationTemplateIdentifier) in MenuItemBody
may store a null; add a null-safety guard so you only call putDataSetEntry when
configurationTemplateIdentifier is non-null (or pass a safe default/empty map
instead). Locate the occurrence in the MenuItemBody class and update the logic
around outcome.putDataSetEntry to check this.configurationTemplateIdentifier (or
replace it with a non-null fallback) before invoking the method.

In
`@src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java`:
- Around line 140-149: The update flow in updateMenuItem currently deletes the
existing case via workflowService.deleteCase(itemCase) before creating the
replacement, making the operation non-atomic; change the flow to first validate
and create the replacement using createMenuItem(body), verify the new case and
its view/folder references were created successfully, then remove the old case
and update parent/view references (or swap identifiers) so the original is only
deleted after the new one is confirmed; ensure logging (log.debug) reflects
creation success prior to calling workflowService.deleteCase and handle failures
by rolling back or leaving the original intact.
- Around line 72-83: The current indexes created in ensureDatabaseIndexes
(CUSTOM_MENU_ITEM_INDEXES via mongoTemplate.indexOps(Case.class).ensureIndex)
are non-unique, so concurrent createMenuItem() calls can still insert
duplicates; update ensureDatabaseIndexes to create unique compound indexes for
(processIdentifier + identifier) and (processIdentifier + nodePath) by setting
the index definitions to unique (use the unique option on the
CompoundIndexDefinition/IndexDefinition used), and keep the existing index names
from CUSTOM_MENU_ITEM_INDEXES; additionally harden createMenuItem() by catching
the Mongo duplicate-key exception (e.g., DuplicateKeyException) around the
insert/persist and translate it into the same error behavior as existsMenuItem()
would indicate (reject/return conflict), ensuring
findMenuItem()/findFolderCase() cannot observe duplicates.
- Around line 96-103: Normalize the menu identifier once at the start of each
entry point: call the existing sanitize(...) helper (or add one if missing) and
use the resulting normalizedId for all lookups, uniqueness checks
(existsMenuItem), and persistence instead of raw body.getIdentifier(); for
example in createMenuItem, createOrUpdateMenuItem, and createOrIgnoreMenuItem
obtain String normalizedId = sanitize(body.getIdentifier()), replace uses of
body.getIdentifier() with normalizedId for existsMenuItem calls and when
creating/updating the Case, and optionally set body.setIdentifier(normalizedId)
after validation so all downstream code sees the normalized value; apply the
same change to the other affected blocks referenced (around the other line
ranges) to ensure consistent identifier handling.
- Around line 458-480: handleConfigurationTemplate currently calls createView
which immediately creates/persists initialized view cases (via
MenuItemBody.hasView -> createView) causing orphaned persisted cases if the
template selection is later cancelled or changed; change the flow so template
previewing does not persist: update createView to support a non-persisting mode
(e.g., an additional parameter or a new method createTransientView) that builds
and initializes the Case in-memory without saving, use that non-persisting
variant from handleConfigurationTemplate before calling
MenuItemBody.toDataSetByConfigTemplate(viewCase), and if any callers need
persisted behavior keep the original createView signature or add a separate
persistedCreateView while updating usages accordingly; ensure any code paths
that do persist now are deliberate and add cleanup logic for any edge-case
persistence if rollback is required.

In `@src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java`:
- Around line 48-57: The code treats the URI separator as a regex: change
uri.split(uriService.getUriSeparator()) to use a literal split (e.g.
Pattern.quote(uriService.getUriSeparator())) so the separator is quoted, and
replace replaceAll("//", uriService.getUriSeparator()) with a literal
replacement (e.g. String.replace("//", uriService.getUriSeparator()) or use
Matcher.quoteReplacement(...) if keeping replaceAll) to avoid regex/replacement
metacharacter issues; update references in MenuItemUtils where uri.split(...)
and the final replaceAll(...) are used and add necessary imports
(Pattern/Matcher) if required.

In `@src/main/java/com/netgrif/application/engine/menu/web/MenuController.java`:
- Around line 33-34: The endpoint in MenuController annotated with
Operation("Get relevant data for the menu item") is missing authorization; add
an explicit access check before returning menu data by verifying the current
principal can access the target case id (or related resource) — either annotate
the controller method with a suitable Spring Security annotation (e.g.,
`@PreAuthorize`) AND/OR call an authorization helper (e.g.,
authorizationService.authorizeCaseAccess(caseId) or
searchAuthorizationService.authorizeSearch(user, criteria)) at the start of the
handler; return 403 when unauthorized and ensure the check uses the decoded case
id the method currently accepts to prevent exposing menu data without proper
permissions.
- Around line 37-44: The code in MenuController currently decodes encodedCaseId
with Base64.getDecoder().decode(...) and catches all Exceptions, causing client
errors to return 500; change the decoding to use new
String(Base64.getDecoder().decode(encodedCaseId), StandardCharsets.UTF_8) to fix
charset dependence, then add a specific catch for IllegalArgumentException
(thrown by Base64 decoding) that throws new
ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid encodedCaseId", e) and
keep the existing generic catch (Exception e) to rethrow 500 for true server
errors; reference Base64.getDecoder().decode, encodedCaseId, new String(...,
StandardCharsets.UTF_8), and the existing catch block in MenuController to
locate where to apply the changes.

In
`@src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java`:
- Around line 12-16: The Javadoc for the MenuItemDataResponse.data field
incorrectly describes a Map; update the comment to describe the actual type: a
List<DataGroup>. Edit the Javadoc above the private final List<DataGroup> data
field in class MenuItemDataResponse to state that it is a list of DataGroup
objects representing immediate fields (or groups of fields) for the menu item
(e.g., one DataGroup per view or section), ensuring the wording matches the
List<DataGroup> type and references DataGroup semantics.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml`:
- Around line 928-954: After applying the configuration template in the finish
event (where menuItemService.handleConfigurationTemplate(useCase) sets
outcome.mapping and values on useCase fields), re-run the tab-field visibility
logic so changes to use_tabbed_view/use_tab_icon take effect; specifically,
after the loop that applies outcome.mapping call the same initializer or
function used elsewhere (e.g., manageBehaviorOfTabFields() or the view_settings
initialization routine) to recompute visibility for view_settings, tabIcon,
tabName, etc., so templates that disable tabs actually hide tab-related fields;
apply the same fix to the other finish block at the 1425-1449 region.
- Around line 847-867: Compute the sanitized identifier up front using
com.netgrif.application.engine.menu.utils.MenuItemUtils.sanitize(menu_item_identifier.value)
and use that sanitizedIdentifier when calling
menuItemService.existsMenuItem(...) and when setting validations; if the
sanitizedIdentifier differs from the raw value, still update the field value via
the existing change menu_item_identifier value block (so the stored value is
normalized) but perform the uniqueness check against sanitizedIdentifier (and
update the validation logic around menu_item_identifier accordingly).

In
`@src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy`:
- Around line 72-73: The field in MenuImportExportTest is misleadingly named
filterRunner while its type is MenuRunner and it's unused; either rename the
field to menuRunner to match the type (change the declaration from filterRunner
to menuRunner) and update any references in the test class to use menuRunner, or
remove the field entirely if there are no usages; ensure the `@Autowired`
annotation remains on the renamed field if kept.

In `@src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java`:
- Around line 724-733: Replace the fixed Thread.sleep(2000) with a polling wait
(e.g. Awaitility) that repeatedly reloads netgrifFolder via
workflowService.findOne(netgrifFolderId) and checks
MenuItemUtils.getCaseIdsFromCaseRef(netgrifFolder,
MenuItemConstants.FIELD_CHILD_ITEM_IDS) no longer contains
testFolder.getStringId(), and that workflowService.findOne(...) throws
IllegalArgumentException for testFolder.getStringId(),
leafItemCase.getStringId(), tabbedCaseViewId and tabbedTaskViewId; use
await().atMost(timeout).untilAsserted(() -> { assertNotNull(updatedChildIds);
assertFalse(updatedChildIds.contains(...));
assertThrows(IllegalArgumentException.class, () ->
workflowService.findOne(...)); ... }) so the test polls until the cascade
deletion completes instead of sleeping.
- Around line 560-566: The assertion in MenuItemServiceTest incorrectly compares
the settings form variable duplicatedFormValue to originAllFormValue; change the
assertion to compare duplicatedAllFormValue.get(0) to originAllFormValue.get(0)
so the duplicated all-data-form is validated, i.e., replace the use of
duplicatedFormValue with duplicatedAllFormValue in the failing assertNotEquals
that references duplicatedFormValue and originAllFormValue (the test variables
involved: duplicatedAllFormValue, duplicatedFormValue, originAllFormValue and
the constant MenuItemConstants.FIELD_VIEW_CONFIGURATION_ALL_DATA_FORM).

---

Outside diff comments:
In
`@src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FieldType.groovy`:
- Around line 24-37: fromString(String name) currently calls
valueOf(name.toUpperCase()) which converts serialized names like
"caseFilter"/"taskFilter"/"processFilter" into
CASEFILTER/TASKFILTER/PROCESSFILTER and fails to match the enum constants;
update FieldType.fromString to lookup by the enum instance's serialized name
property instead (e.g., iterate FieldType.values() and return the entry whose
'name' field equals the input, with a case-insensitive comparison or sensible
fallback), and keep a fallback to valueOf(name.toUpperCase()) only if necessary
to preserve backwards compatibility.

In
`@src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java`:
- Around line 70-78: In ViewBody.toDataSet(Case) the code dereferences
this.getAssociatedViewBody() while only checking associatedViewCase != null;
change the guard to ensure the associated view body is non-null as well (e.g.,
if (associatedViewCase != null && this.getAssociatedViewBody() != null) or use
this.hasAssociatedView()) so you don't risk NPE when getAssociatedViewBody() can
be null (references: ViewBody.toDataSet(Case), getAssociatedViewBody(),
hasAssociatedView(), MenuItemService.createView(...)).

In
`@src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewType.java`:
- Around line 74-94: The tabbed/untabbed predicate is unclear and brittle;
update both findAllByIsTabbedAndIsPrimary and
findAllByIsTabbedAndParentIdentifier to explicitly match the intended semantics:
when isTabbed==true filter by view.isTabbed==true, otherwise filter by
view.isUntabbed==true; keep the other condition in each method (isPrimary match
or parentView.getAllowedAssociatedViews().contains(view.identifier)) unchanged
so only the tabbed check is simplified and made explicit using the view.isTabbed
and view.isUntabbed fields.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml`:
- Around line 203-216: Reject values for move_dest_uri_new_node that contain URI
separator characters instead of appending them verbatim: in the action that
reads newNodeName (symbol newNodeName / move_dest_uri_new_node) validate
newNodeName.value for occurrences of "/" and uriService.getUriSeparator() and if
found either return early (like existing empty check) or surface a validation
error; alternatively trim any leading/trailing separators before building
prefixUri so the code that constructs newUri (uses selectedUri,
uriService.getUriSeparator(), and uriService.getOrCreate) cannot create extra
path segments. Ensure the check/sanitization occurs before concatenating
prefixUri and calling uriService.getOrCreate.

In
`@src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy`:
- Around line 183-184: The test calls
menuImportExportService.invokeMethod("loadFromXML", ...) but the test class
lacks a menuImportExportService collaborator; add a collaborator field or bean
for MenuImportExportService (e.g., declare and annotate a
MenuImportExportService menuImportExportService with `@Autowired` or initialize a
mock and inject it) so the invokeMethod calls on menuImportExportService (used
to loadFromXML) resolve at runtime; ensure the field name matches
menuImportExportService and that the service provides the loadFromXML behavior
expected by the test.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 42646096-e6e5-4bbb-936d-5e1f70198cd5

📥 Commits

Reviewing files that changed from the base of the PR and between 7dabfe9 and 97e9c12.

📒 Files selected for processing (96)
  • docs/_sidebar.md
  • docs/search/filter.md
  • docs/search/filter_import_export.md
  • src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/CaseFilterField.groovy
  • src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FieldType.groovy
  • src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FilterField.groovy
  • src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/ProcessFilterField.groovy
  • src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/TaskFilterField.groovy
  • src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
  • src/main/groovy/com/netgrif/application/engine/startup/DefaultDashboardRunner.groovy
  • src/main/groovy/com/netgrif/application/engine/startup/DefaultFiltersRunner.groovy
  • src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy
  • src/main/groovy/com/netgrif/application/engine/startup/MenuRunner.groovy
  • src/main/groovy/com/netgrif/application/engine/startup/MongoDbRunner.groovy
  • src/main/groovy/com/netgrif/application/engine/startup/RunnerController.groovy
  • src/main/java/com/netgrif/application/engine/auth/service/UserService.java
  • src/main/java/com/netgrif/application/engine/importer/service/FieldFactory.java
  • src/main/java/com/netgrif/application/engine/importer/service/Importer.java
  • src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java
  • src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java
  • src/main/java/com/netgrif/application/engine/menu/domain/MenuItemBody.java
  • src/main/java/com/netgrif/application/engine/menu/domain/MenuItemConstants.java
  • src/main/java/com/netgrif/application/engine/menu/domain/MenuItemViewType.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewConstants.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewBody.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/SingleTaskViewConstants.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTicketViewBody.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewBody.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TaskViewConstants.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewBody.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/ViewConstants.java
  • src/main/java/com/netgrif/application/engine/menu/domain/templates/CustomViewTemplate.java
  • src/main/java/com/netgrif/application/engine/menu/domain/templates/README.md
  • src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleCaseViewTemplate.java
  • src/main/java/com/netgrif/application/engine/menu/domain/templates/SimpleTaskViewTemplate.java
  • src/main/java/com/netgrif/application/engine/menu/domain/templates/SingleTaskViewTemplate.java
  • src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedCaseViewTemplate.java
  • src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTaskViewTemplate.java
  • src/main/java/com/netgrif/application/engine/menu/domain/templates/TabbedTicketViewTemplate.java
  • src/main/java/com/netgrif/application/engine/menu/domain/templates/Template.java
  • src/main/java/com/netgrif/application/engine/menu/service/DashboardItemServiceImpl.java
  • src/main/java/com/netgrif/application/engine/menu/service/DashboardManagementServiceImpl.java
  • src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java
  • src/main/java/com/netgrif/application/engine/menu/service/MenuItemTemplateHolder.java
  • src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardItemService.java
  • src/main/java/com/netgrif/application/engine/menu/service/interfaces/DashboardManagementService.java
  • src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java
  • src/main/java/com/netgrif/application/engine/menu/utils/MenuItemUtils.java
  • src/main/java/com/netgrif/application/engine/menu/web/MenuController.java
  • src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java
  • src/main/java/com/netgrif/application/engine/workflow/domain/Case.java
  • src/main/java/com/netgrif/application/engine/workflow/domain/DataField.java
  • src/main/java/com/netgrif/application/engine/workflow/service/DataService.java
  • src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java
  • src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java
  • src/main/java/com/netgrif/application/engine/workflow/service/UserFilterSearchService.java
  • src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
  • src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFilterImportExportService.java
  • src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IMenuImportExportService.java
  • src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IUserFilterSearchService.java
  • src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/LocalisedCaseFilterField.java
  • src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/LocalisedFieldFactory.java
  • src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/LocalisedFilterField.java
  • src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/LocalisedProcessFilterField.java
  • src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/LocalisedTaskFilterField.java
  • src/main/resources/petriNets/engine-processes/export_filters.xml
  • src/main/resources/petriNets/engine-processes/filter.xml
  • src/main/resources/petriNets/engine-processes/import_filters.xml
  • src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/menu_item.xml
  • src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/org_group.xml
  • src/main/resources/petriNets/engine-processes/preference_filter_item.xml
  • src/test/groovy/com/netgrif/application/engine/TestHelper.groovy
  • src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy
  • src/test/groovy/com/netgrif/application/engine/action/FilterApiTest.groovy
  • src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy
  • src/test/groovy/com/netgrif/application/engine/export/service/XlsExportServiceTest.java
  • src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy
  • src/test/groovy/com/netgrif/application/engine/filters/FilterTest.groovy
  • src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy
  • src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java
  • src/test/resources/dashboard_management_test.xml
  • src/test/resources/petriNets/filter_api_test.xml
  • src/test/resources/petriNets/filter_test.xml
💤 Files with no reviewable changes (32)
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewConstants.java
  • src/main/resources/petriNets/engine-processes/menu/tabbed_task_view_configuration.xml
  • src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFilterImportExportService.java
  • src/test/resources/petriNets/filter_api_test.xml
  • src/main/resources/petriNets/engine-processes/menu/tabbed_single_task_view_configuration.xml
  • src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IMenuImportExportService.java
  • src/main/resources/petriNets/engine-processes/import_filters.xml
  • src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IUserFilterSearchService.java
  • src/test/groovy/com/netgrif/application/engine/action/FilterApiTest.groovy
  • docs/search/filter_import_export.md
  • src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/LocalisedFilterField.java
  • src/main/java/com/netgrif/application/engine/workflow/service/UserFilterSearchService.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewConstants.java
  • docs/search/filter.md
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewConstants.java
  • src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/FilterField.groovy
  • src/main/resources/petriNets/engine-processes/filter.xml
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedSingleTaskViewBody.java
  • src/main/resources/petriNets/engine-processes/preference_filter_item.xml
  • src/main/groovy/com/netgrif/application/engine/startup/DefaultFiltersRunner.groovy
  • src/main/java/com/netgrif/application/engine/auth/service/UserService.java
  • src/main/resources/petriNets/engine-processes/export_filters.xml
  • src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedTaskViewBody.java
  • src/main/groovy/com/netgrif/application/engine/startup/FilterRunner.groovy
  • src/main/java/com/netgrif/application/engine/workflow/domain/DataField.java
  • src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy
  • docs/_sidebar.md
  • src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java
  • src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/TabbedCaseViewBody.java
  • src/main/resources/petriNets/engine-processes/org_group.xml

Comment thread src/main/groovy/com/netgrif/application/engine/startup/MenuRunner.groovy Outdated

@Transactional
protected void resolveUserRef(CaseUserRef userRef) {
protected void resolveUserRef(CaseActorRef userRef) {

@coderabbitai coderabbitai Bot Jun 2, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Restore the importer model types here; the file no longer compiles.

The new CaseActorRef/ActorRef signatures do not exist in com.netgrif.application.engine.importer.model, and the pipeline is already failing on these two lines. Revert to the existing generated model types or add the corresponding model/schema classes in the same PR so the callers and method signatures stay consistent.

Also applies to: 732-732

🧰 Tools
🪛 GitHub Actions: Pull Request / 1_Build.txt

[error] 243-243: Maven compilation failure (maven-compiler-plugin:3.10.1:compile). cannot find symbol: class CaseActorRef (Importer.java:[243,35]).

🪛 GitHub Actions: Pull Request / Build

[error] 243-243: Compilation error: cannot find symbol 'CaseActorRef' (referenced in class com.netgrif.application.engine.importer.service.Importer) during 'maven-compiler-plugin:3.10.1:compile (default-compile)'

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/netgrif/application/engine/importer/service/Importer.java`
at line 243, The method signature change to use CaseActorRef/ActorRef breaks
compilation because those types are not present in
com.netgrif.application.engine.importer.model; restore the original generated
model types (the previous importer model classes used by resolveUserRef and any
other affected methods such as the one at line ~732) or add the corresponding
model/schema classes to com.netgrif.application.engine.importer.model in this PR
so callers and method signatures remain consistent; update the signatures of
resolveUserRef and any other methods that reference CaseActorRef/ActorRef back
to the original model types (or ensure the newly added classes match the
expected names and packages) and re-run compilation to confirm the pipeline
passes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

New schema must be first published. It is waiting on PR petriflow/petriflow#30. Then this is going to be ok

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@Retoocs, understood — this is the same intentional breakage tracked from PR #446. I already have a note in my long-term memory that CaseActorRef/ActorRef are not yet available in com.netgrif.application.engine.importer.model and the build will remain broken until petriflow/petriflow#30 is merged and the importer model is regenerated.

Would you like me to resolve this review comment since the failure is expected and will be addressed by the upstream schema release?

(=🐇=)


🧠 Learnings used
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 446
File: src/main/java/com/netgrif/application/engine/importer/service/Importer.java:243-243
Timestamp: 2026-06-01T07:23:09.747Z
Learning: In `src/main/java/com/netgrif/application/engine/importer/service/Importer.java`, the PR NAE-2439 (PR `#446`) changes the `resolveUserRef` method signature from `CaseUserRef` to `CaseActorRef` and `addUserLogic` from `UserRef` to `ActorRef`. These types (`CaseActorRef`, `ActorRef`) are not yet available in `com.netgrif.application.engine.importer.model` — they will be generated once the upstream petriflow schema change (https://github.com/petriflow/petriflow/pull/30) is released. The build intentionally breaks until that schema lands and the importer model is regenerated.

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.

Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java:98-104
Timestamp: 2025-12-12T13:40:00.786Z
Learning: In application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java, the permission constraint logic in buildQuery() intentionally applies negative actor constraints globally: ((roleConstraint AND NOT negRoleConstraint) OR actorConstraint) AND NOT negActorConstraint. This means users in negativeViewActors are excluded even if they have positive role permissions, which is the intended authorization behavior.

Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/main/resources/petriNets/engine-processes/org_group.xml:36-39
Timestamp: 2025-12-12T12:36:06.651Z
Learning: In the org_group.xml file (application-engine/src/main/resources/petriNets/engine-processes/org_group.xml), variable names in action blocks like userField and user_selection should be kept as-is even when the field type changes from "user" to "actor".

Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 348
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java:4-4
Timestamp: 2026-01-13T16:33:27.510Z
Learning: In the netgrif/application-engine repository, wildcard imports are preferred for the com.netgrif.application.engine.objects.petrinet.domain package. Do not suggest replacing wildcard imports with explicit imports for this package.

Learnt from: Smotanka
Repo: netgrif/application-engine PR: 401
File: src/main/java/com/netgrif/application/engine/elastic/service/ElasticsearchQuerySanitizer.java:57-66
Timestamp: 2025-12-18T19:20:32.658Z
Learning: When performing multiple string replacements in Java files (e.g., sanitization/escaping logic like ElasticsearchQuerySanitizer), prefer using StringUtils.replaceEach() over chaining multiple StringUtils.replace() calls. This avoids cascading replacements where the result of one replacement triggers another replacement and potentially corrupts the final output. Apply this pattern to other similar sanitization code across the codebase as appropriate.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

no

Comment thread src/main/resources/petriNets/engine-processes/menu/menu_item.xml
Comment thread src/main/resources/petriNets/engine-processes/menu/menu_item.xml
Comment thread src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy Outdated
Comment thread src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy (1)

119-187: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

This disabled test still calls a removed collaborator.

Lines 183-184 dereference menuImportExportService, but that field is no longer declared in this class. The test is disabled today, yet the first re-enable will fail immediately instead of validating import/export behavior.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy`
around lines 119 - 187, The test testMenuImportExport dereferences
menuImportExportService.loadFromXML but the menuImportExportService collaborator
was removed; restore a reference to the proper service (declare a field named
menuImportExportService of the correct type used elsewhere in tests, e.g.,
MenuImportExportService or the class that exposes loadFromXML) and inject it
(e.g., with `@Autowired` or the existing test setup method), or update the test to
call the current service that provides loadFromXML/export functionality; ensure
the symbol menuImportExportService and the method loadFromXML remain available
to the test so the final assertions comparing MenuAndFilters work.
src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java (1)

437-440: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Authorize getMenuItemData() before returning menu configuration data.

MenuController’s new /api/menu/{encodedCaseId} endpoint calls this method directly, and this implementation returns the all_menu_data task payload for any decoded case id without checking whether the current user may view that menu item/task. That makes the new endpoint an IDOR-style read path for menu configuration data.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java`
around lines 437 - 440, The method getMenuItemData currently returns the
all_menu_data payload for any decoded case id without permission checks; before
calling dataService.getDataGroups you must authorize that the current principal
may view the menu item/task. Locate getMenuItemData and, after obtaining
menuItemCase and taskId via workflowService.findOne and
MenuItemUtils.findTaskIdInCase (TRANS_ALL_MENU_DATA), invoke the existing
authorization mechanism (e.g., authorizationService/permissionService or
workflowService authorization helper) to verify read/view rights on the task or
case and throw an access denied exception if unauthorized; only then call
dataService.getDataGroups(taskId, locale). Ensure the check uses the same
authority semantics as other endpoints (e.g., MenuController) so behavior is
consistent.
♻️ Duplicate comments (3)
src/main/resources/petriNets/engine-processes/menu/menu_item.xml (1)

928-954: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Recompute tab-field behavior after applying template/system initialization.

These finish hooks update use_tabbed_view/use_tab_icon, but they never re-run manageBehaviorOfTabFields(...). With the current defaults in this file (use_tabbed_view starts as true, and the tab refs in view_settings are visible/editable by default), a non-tabbed template still leaves tab_name, use_tab_icon, tab_icon, and tab_icon_preview exposed until the user toggles the checkbox manually.

💡 Suggested fix
                 <action>
                     trans: t.view_settings,
+                    useTabbedView: f.use_tabbed_view,
                     useTabIcon: f.use_tab_icon,
                     view_configuration_form: f.view_configuration_form,
                     view_configuration_type: f.view_configuration_type,
@@
                     use_custom_view: f.use_custom_view,
                     selector: f.custom_view_selector;
 
+                    manageBehaviorOfTabFields(useTabIcon.value, useTabbedView.value)
+
                     if (use_custom_view.value) {
                         make selector, editable on trans when { true }
                         make [useTabIcon, tabIconPreview, tabName, tabIcon, view_configuration_form,
                                 view_configuration_type], hidden on trans when { true }
                     }
@@
                 <action>
                     trans: t.view_settings,
+                    useTabbedView: f.use_tabbed_view,
                     useTabIcon: f.use_tab_icon,
                     view_configuration_form: f.view_configuration_form,
                     view_configuration_type: f.view_configuration_type,
@@
                     use_custom_view: f.use_custom_view,
                     selector: f.custom_view_selector;
 
+                    manageBehaviorOfTabFields(useTabIcon.value, useTabbedView.value)
+
                     if (use_custom_view.value) {
                         make selector, editable on trans when { true }
                         make [useTabIcon, tabIconPreview, tabName, tabIcon, view_configuration_form,
                                 view_configuration_type], hidden on trans when { true }
                     }

Also applies to: 1425-1444

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml` around
lines 928 - 954, After applying the configuration template in the finish event
(where menuItemService.handleConfigurationTemplate(useCase) maps values into
fields like use_tabbed_view/use_tab_icon/tab_name/tab_icon/tab_icon_preview and
view_settings), explicitly invoke the function that enforces tab-field
visibility/behavior (manageBehaviorOfTabFields or equivalent) so the
visibility/editability of tab-related fields (use_tab_icon, tab_icon_preview,
tab_name, tab_icon, view_configuration_form, view_configuration_type,
custom_view_selector) is recomputed; do this immediately after the
template-mapping action in the finish hook(s) (both at the shown block and the
similar block around lines 1425-1444) so non-tabbed templates do not leave tab
fields exposed.
src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java (1)

162-170: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize and validate the identifier once before lookup and persistence.

createOrUpdateMenuItem() and createOrIgnoreMenuItem() search with a sanitized identifier, but validateMenuItemBody() still accepts blank identifiers and createMenuItem() persists the raw value. That leaves the service inconsistent again: logically equivalent inputs can bypass each other’s uniqueness checks, and the stored identifier/node path can differ from the value used for lookup.

💡 Suggested fix
 public Case createOrUpdateMenuItem(MenuItemBody body) throws TransitionNotExecutableException {
-    if (body == null) {
-        throw new IllegalArgumentException("Menu item body cannot be null");
-    }
-    Case itemCase = findMenuItem(MenuItemUtils.sanitize(body.getIdentifier()));
+    validateMenuItemBody(body);
+    Case itemCase = findMenuItem(body.getIdentifier());
     if (itemCase != null) {
         return updateMenuItem(itemCase, body);
     } else {
         return createMenuItem(body);
     }
 }
 
 public Case createOrIgnoreMenuItem(MenuItemBody body) throws TransitionNotExecutableException {
-    if (body == null) {
-        throw new IllegalArgumentException("Menu item body cannot be null");
-    }
-    String sanitizedIdentifier = MenuItemUtils.sanitize(body.getIdentifier());
-    Case itemCase = findMenuItem(sanitizedIdentifier);
+    validateMenuItemBody(body);
+    Case itemCase = findMenuItem(body.getIdentifier());
     if (itemCase != null) {
         log.debug("Ignored creation or update of menu item case [{}] with identifier [{}].", itemCase.getStringId(),
-                sanitizedIdentifier);
+                body.getIdentifier());
         return itemCase;
     } else {
         return createMenuItem(body);
     }
 }
 
 protected void validateMenuItemBody(MenuItemBody body) {
     if (body == null) {
         throw new IllegalArgumentException("Input data cannot be null");
     }
-    if (body.getIdentifier() == null) {
+    if (body.getIdentifier() == null || body.getIdentifier().isBlank()) {
         throw new IllegalArgumentException("Identifier cannot be null");
     }
+    body.setIdentifier(MenuItemUtils.sanitize(body.getIdentifier()));
     if (body.getUri() == null || body.getUri().isBlank()) {
         throw new IllegalArgumentException("Uri cannot be null");
     } else {
         body.setUri(MenuItemUtils.sanitizeUriSegments(body.getUri(), uriService));
         List<String> uriSegments = List.of(body.getUri().split(uriService.getUriSeparator()));

Also applies to: 183-195, 542-558

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java`
around lines 162 - 170, Normalize and validate the identifier once up front so
lookup, validation and persistence use the same value: in createOrUpdateMenuItem
and createOrIgnoreMenuItem call MenuItemUtils.sanitize(body.getIdentifier())
into a local variable, replace body.getIdentifier() with that sanitized value
before calling findMenuItem/update/create, and pass the sanitized identifier
into validateMenuItemBody; update validateMenuItemBody to reject blank/empty
identifiers after sanitization; and ensure createMenuItem persists the sanitized
identifier (i.e., set the sanitized id on the MenuItemBody or Case being saved)
so stored node paths match the lookup key.
src/main/java/com/netgrif/application/engine/menu/web/MenuController.java (1)

41-47: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Stack trace not preserved in ResponseStatusException.

Lines 43 and 46 throw ResponseStatusException without passing the caught exception as the cause parameter. While the exceptions are logged, Spring's error handling won't include the stack trace in monitoring/error responses, complicating debugging.

🔧 Recommended fix to preserve stack traces
         } catch (IllegalArgumentException e) {
             log.warn("Requested with invalid input", e);
-            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Requested with invalid input");
+            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Requested with invalid input", e);
         } catch (Exception e) {
             log.error("Getting menu item data failed", e);
-            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Getting menu item data failed");
+            throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Getting menu item data failed", e);
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/java/com/netgrif/application/engine/menu/web/MenuController.java`
around lines 41 - 47, The catch blocks in MenuController catching
IllegalArgumentException and Exception currently throw new
ResponseStatusException without preserving the original cause; update both catch
blocks (the ones handling IllegalArgumentException and the generic Exception) to
pass the caught exception as the cause to the ResponseStatusException
constructor (e.g., new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Requested with invalid input", e)) so the original stack traces are preserved
for downstream error handling and monitoring.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java`:
- Around line 437-440: The method getMenuItemData currently returns the
all_menu_data payload for any decoded case id without permission checks; before
calling dataService.getDataGroups you must authorize that the current principal
may view the menu item/task. Locate getMenuItemData and, after obtaining
menuItemCase and taskId via workflowService.findOne and
MenuItemUtils.findTaskIdInCase (TRANS_ALL_MENU_DATA), invoke the existing
authorization mechanism (e.g., authorizationService/permissionService or
workflowService authorization helper) to verify read/view rights on the task or
case and throw an access denied exception if unauthorized; only then call
dataService.getDataGroups(taskId, locale). Ensure the check uses the same
authority semantics as other endpoints (e.g., MenuController) so behavior is
consistent.

In
`@src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy`:
- Around line 119-187: The test testMenuImportExport dereferences
menuImportExportService.loadFromXML but the menuImportExportService collaborator
was removed; restore a reference to the proper service (declare a field named
menuImportExportService of the correct type used elsewhere in tests, e.g.,
MenuImportExportService or the class that exposes loadFromXML) and inject it
(e.g., with `@Autowired` or the existing test setup method), or update the test to
call the current service that provides loadFromXML/export functionality; ensure
the symbol menuImportExportService and the method loadFromXML remain available
to the test so the final assertions comparing MenuAndFilters work.

---

Duplicate comments:
In
`@src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java`:
- Around line 162-170: Normalize and validate the identifier once up front so
lookup, validation and persistence use the same value: in createOrUpdateMenuItem
and createOrIgnoreMenuItem call MenuItemUtils.sanitize(body.getIdentifier())
into a local variable, replace body.getIdentifier() with that sanitized value
before calling findMenuItem/update/create, and pass the sanitized identifier
into validateMenuItemBody; update validateMenuItemBody to reject blank/empty
identifiers after sanitization; and ensure createMenuItem persists the sanitized
identifier (i.e., set the sanitized id on the MenuItemBody or Case being saved)
so stored node paths match the lookup key.

In `@src/main/java/com/netgrif/application/engine/menu/web/MenuController.java`:
- Around line 41-47: The catch blocks in MenuController catching
IllegalArgumentException and Exception currently throw new
ResponseStatusException without preserving the original cause; update both catch
blocks (the ones handling IllegalArgumentException and the generic Exception) to
pass the caught exception as the cause to the ResponseStatusException
constructor (e.g., new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Requested with invalid input", e)) so the original stack traces are preserved
for downstream error handling and monitoring.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml`:
- Around line 928-954: After applying the configuration template in the finish
event (where menuItemService.handleConfigurationTemplate(useCase) maps values
into fields like use_tabbed_view/use_tab_icon/tab_name/tab_icon/tab_icon_preview
and view_settings), explicitly invoke the function that enforces tab-field
visibility/behavior (manageBehaviorOfTabFields or equivalent) so the
visibility/editability of tab-related fields (use_tab_icon, tab_icon_preview,
tab_name, tab_icon, view_configuration_form, view_configuration_type,
custom_view_selector) is recomputed; do this immediately after the
template-mapping action in the finish hook(s) (both at the shown block and the
similar block around lines 1425-1444) so non-tabbed templates do not leave tab
fields exposed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5bfb56b8-995b-4274-ae00-85ab82b3a012

📥 Commits

Reviewing files that changed from the base of the PR and between 97e9c12 and 956e72d.

📒 Files selected for processing (10)
  • src/main/groovy/com/netgrif/application/engine/startup/MenuRunner.groovy
  • src/main/java/com/netgrif/application/engine/menu/domain/ConfigurationTemplateOutcome.java
  • src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java
  • src/main/java/com/netgrif/application/engine/menu/domain/configurations/CaseViewBody.java
  • src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java
  • src/main/java/com/netgrif/application/engine/menu/web/MenuController.java
  • src/main/java/com/netgrif/application/engine/menu/web/responsebodies/MenuItemDataResponse.java
  • src/main/resources/petriNets/engine-processes/menu/menu_item.xml
  • src/test/groovy/com/netgrif/application/engine/menu/MenuImportExportTest.groovy
  • src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java

@dpulls

dpulls Bot commented Jun 5, 2026

Copy link
Copy Markdown

🎉 All dependencies have been resolved !

Retoocs added 2 commits June 12, 2026 10:33
- remove MenuController
- switch system role to default role on all data transition
- fix updating URI of menu item

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (3)
src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml (1)

962-969: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

The custom-default-headers toggle uses the visibility condition in the wrong direction in both view configs. In both files, the dependent headers field is explicitly made visible when the toggle is false, so the form still exposes a field that the toggle just disabled.

  • src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml#L962-L969: make case_default_headers visible only when use_case_default_headers is true, and hide/optional it when false.
  • src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml#L600-L607: make task_default_headers visible only when use_task_default_headers is true, and hide/optional it when false.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml`
around lines 962 - 969, The visibility conditions for the dependent headers
fields are inverted in both configuration files. In
src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml
at lines 962-969, the case_default_headers field is currently made visible when
use_case_default_headers is false (the !use.value condition), but it should be
visible only when use_case_default_headers is true. Swap the visibility
conditions so that the make headers,visible statement uses { use.value } instead
of { !use.value }, and the make headers,editable statement uses { !use.value }
instead of { use.value }. Apply the same fix in
src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml
at lines 600-607, swapping the visibility conditions for task_default_headers
based on use_task_default_headers, so the field is only visible when the toggle
is enabled.
src/main/resources/petriNets/engine-processes/menu/menu_item.xml (2)

51-68: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Refresh advanced_content_form after changing the linked view.

initializeAdvancedForm() derives the task-list from view_configuration_id, but it only runs during initialize / system_initialize. view_configuration_form stays editable in view_settings, so after switching the associated view, item_settings.advanced_content_form still points at the old task set.

Proposed fix
 		<event type="finish">
 			<id>finish_0</id>
+			<actions phase="post">
+				<action>
+					initializeAdvancedForm()
+				</action>
+			</actions>
 			<title/>
 		</event>

Also applies to: 1760-1785

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml` around
lines 51 - 68, The initializeAdvancedForm function only executes during
initialization phases, so when view_configuration_id is changed via
view_settings, the advanced_content_form field is not refreshed with the updated
task list from the newly selected view. Add a trigger to call
initializeAdvancedForm whenever view_configuration_id changes in the
view_settings context to ensure advanced_content_form stays synchronized with
the current linked view configuration.

1431-1468: ⚠️ Potential issue | 🔴 Critical

system_initialize transition does not process configuration templates, leaving template-derived view configurations unapplied.

When MenuItemService.createMenuItem() is called with a MenuItemBody containing configurationTemplateIdentifier, the template ID is stored in the case but the system_initialize transition executes without calling handleConfigurationTemplate(). This means the template-derived view configurations and field mappings are never materialized. The initialize transition includes this call (line 937 in menu_item.xml), but system_initialize (lines 1431–1468) skips it entirely.

If menu items are created programmatically with templates populated, those templates will be inert.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml` around
lines 1431 - 1468, The system_initialize transition is missing a call to
handleConfigurationTemplate() in its post-phase actions within the finish event,
which means configuration templates are never processed when menu items are
created programmatically. Add a call to handleConfigurationTemplate() in the
post-phase actions section of the system_initialize transition's finish event
(similar to how the initialize transition handles it at line 937), ensuring that
template-derived view configurations and field mappings are properly
materialized when a MenuItemBody with configurationTemplateIdentifier is
provided.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml`:
- Around line 962-969: The visibility conditions for the dependent headers
fields are inverted in both configuration files. In
src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml
at lines 962-969, the case_default_headers field is currently made visible when
use_case_default_headers is false (the !use.value condition), but it should be
visible only when use_case_default_headers is true. Swap the visibility
conditions so that the make headers,visible statement uses { use.value } instead
of { !use.value }, and the make headers,editable statement uses { !use.value }
instead of { use.value }. Apply the same fix in
src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml
at lines 600-607, swapping the visibility conditions for task_default_headers
based on use_task_default_headers, so the field is only visible when the toggle
is enabled.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml`:
- Around line 51-68: The initializeAdvancedForm function only executes during
initialization phases, so when view_configuration_id is changed via
view_settings, the advanced_content_form field is not refreshed with the updated
task list from the newly selected view. Add a trigger to call
initializeAdvancedForm whenever view_configuration_id changes in the
view_settings context to ensure advanced_content_form stays synchronized with
the current linked view configuration.
- Around line 1431-1468: The system_initialize transition is missing a call to
handleConfigurationTemplate() in its post-phase actions within the finish event,
which means configuration templates are never processed when menu items are
created programmatically. Add a call to handleConfigurationTemplate() in the
post-phase actions section of the system_initialize transition's finish event
(similar to how the initialize transition handles it at line 937), ensuring that
template-derived view configurations and field mappings are properly
materialized when a MenuItemBody with configurationTemplateIdentifier is
provided.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 70214ad4-a64a-4aa4-bde5-dd59b4617ecb

📥 Commits

Reviewing files that changed from the base of the PR and between 956e72d and 0b8c36b.

📒 Files selected for processing (7)
  • src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java
  • src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java
  • src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/menu_item.xml
  • src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml
💤 Files with no reviewable changes (2)
  • src/main/java/com/netgrif/application/engine/menu/service/interfaces/IMenuItemService.java
  • src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java

Retoocs added 3 commits June 14, 2026 12:57
# Conflicts:
#	src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy
- fix tests after merge
- update data group for all data transition

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (4)
src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml (1)

40-46: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Same NPE risk pattern as other view configurations.

The find() calls can return null, leading to NPE when accessing .task.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml`
around lines 40 - 46, The initializeAdvancedForm function contains two find()
calls that search for task pairs by transition name ("filter_settings" and
"header_settings"), but these calls lack null safety checks before accessing the
.task property. Add null safety checks to both find() operations using the safe
navigation operator or explicit null checks to prevent NullPointerException when
a matching task pair is not found. Ensure the value assignment to
advancedContentFormField only includes non-null task objects, filtering out any
null results from the find() operations.
src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml (1)

40-45: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Add null-safety for task lookup.

The find() call can return null if no task matches the transition, which would cause an NPE when accessing .task. While filter_settings is defined in this net, defensive coding would be prudent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml`
around lines 40 - 45, The initializeAdvancedForm function uses a find() call on
useCase.tasks to locate a task with transition == "filter_settings" and
immediately accesses the .task property on the result. If no matching task
exists, find() returns null and accessing .task will cause a
NullPointerException. Add null-safety handling around the find() call using an
elvis operator or explicit null check to safely handle cases where the expected
task transition is not found, ensuring the advanced content form field is set to
an appropriate default value when the lookup fails.
src/main/resources/petriNets/engine-processes/menu/menu_item.xml (1)

51-69: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Incorrect task extraction in make statement.

At line 63, the expression viewCase.tasks.findAll { it.transition == "settings" }?.task is problematic:

  1. findAll returns a list of matching task pairs, not a single element
  2. Accessing .task on a list doesn't make sense and will fail or produce unexpected results
  3. The safe navigation ?.task won't help since findAll always returns a list (possibly empty), never null

The intent appears to be hiding the "settings" task form in the referenced view case. This should use collect to extract task IDs:

Proposed fix
-				make ["advanced_content_form"], hidden on (viewCase.tasks.findAll { it.transition == "settings" }?.task as List) when { true }
+				def settingsTaskIds = viewCase.tasks.findAll { it.transition == "settings" }.collect { it.task }
+				if (!settingsTaskIds.isEmpty()) {
+					make ["advanced_content_form"], hidden on settingsTaskIds when { true }
+				}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml` around
lines 51 - 69, The make statement in the initializeAdvancedForm function
incorrectly accesses the task property on the list returned by findAll. The
expression viewCase.tasks.findAll { it.transition == "settings" }?.task attempts
to get .task from a list, which doesn't work as intended. Replace .task with
.collect { it.task } to properly extract the task ID from each element in the
findAll result, ensuring the hidden condition receives a correctly formatted
list of task IDs for the settings transition.
src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml (1)

59-71: ⚠️ Potential issue | 🟡 Minor

Apply safe navigation operator to all find() calls on lines 62-64 for consistency and robustness.

In initializeAdvancedForm(), lines 62-64 use unsafe .task access on find() results without null-checking. Line 65 correctly uses the safe navigation operator ?.task for the optional open_case_settings transition. For consistency and defensive programming, apply the same safe pattern to all three required transitions: header_settings, create_case_btn_settings, and filter_settings. While these transitions are currently defined in the petrinet, the unsafe pattern leaves the code vulnerable to NPE if the petrinet structure changes or this code is reused elsewhere.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml`
around lines 59 - 71, In the initializeAdvancedForm function, the three find()
calls for header_settings, create_case_btn_settings, and filter_settings
transitions (lines 62-64) use unsafe direct `.task` access without
null-checking, while the open_case_settings transition on line 65 correctly uses
the safe navigation operator `?.task`. Apply the safe navigation operator
`?.task` to all three of the earlier find() calls to maintain consistency and
prevent potential null pointer exceptions if the petrinet structure changes.
This aligns all four transition lookups with the same defensive programming
pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy`:
- Around line 1551-1554: The getTaskIds method lacks a null/empty guard for the
transitionIds parameter, causing a NullPointerException when called with null
values. Add a guard clause at the start of the getTaskIds method that checks if
transitionIds is null or empty, and returns an empty list in those cases before
proceeding with the findAll and collect operations on the transitionIds list.
- Around line 1539-1542: The getTaskId method calls .find() on the refs list and
immediately dereferences the result with .stringId without checking if a
matching task reference exists, which will throw a NullPointerException if no
task is found for the given transitionId. Add a null guard after the .find()
call to check if a matching task reference was found before accessing its
stringId property. If no task is found, return a controlled value (such as null)
or throw a meaningful exception instead of allowing the NullPointerException to
occur.

In
`@src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy`:
- Around line 257-259: The test code directly indexes the result of
petriNetService.getByIdentifier("menu_item") with [0] without verifying the list
is non-empty, which can result in an IndexOutOfBoundsException with poor
diagnostics. Add an assertion or verification before accessing the list element
to ensure the getByIdentifier call returns a non-empty list, making test
failures more diagnosable when the menu_item net cannot be found.
- Around line 453-455: The importTestPetriNet() helper method opens a
FileInputStream that is never closed, causing a resource leak. Refactor this
method to properly close the stream after the petriNetService.importPetriNet()
call completes by wrapping the stream in a try-with-resources statement or using
a try-finally block with an explicit close() call to ensure the stream is
released even if an exception occurs.

---

Outside diff comments:
In
`@src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml`:
- Around line 59-71: In the initializeAdvancedForm function, the three find()
calls for header_settings, create_case_btn_settings, and filter_settings
transitions (lines 62-64) use unsafe direct `.task` access without
null-checking, while the open_case_settings transition on line 65 correctly uses
the safe navigation operator `?.task`. Apply the safe navigation operator
`?.task` to all three of the earlier find() calls to maintain consistency and
prevent potential null pointer exceptions if the petrinet structure changes.
This aligns all four transition lookups with the same defensive programming
pattern.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml`:
- Around line 51-69: The make statement in the initializeAdvancedForm function
incorrectly accesses the task property on the list returned by findAll. The
expression viewCase.tasks.findAll { it.transition == "settings" }?.task attempts
to get .task from a list, which doesn't work as intended. Replace .task with
.collect { it.task } to properly extract the task ID from each element in the
findAll result, ensuring the hidden condition receives a correctly formatted
list of task IDs for the settings transition.

In
`@src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml`:
- Around line 40-45: The initializeAdvancedForm function uses a find() call on
useCase.tasks to locate a task with transition == "filter_settings" and
immediately accesses the .task property on the result. If no matching task
exists, find() returns null and accessing .task will cause a
NullPointerException. Add null-safety handling around the find() call using an
elvis operator or explicit null check to safely handle cases where the expected
task transition is not found, ensuring the advanced content form field is set to
an appropriate default value when the lookup fails.

In
`@src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml`:
- Around line 40-46: The initializeAdvancedForm function contains two find()
calls that search for task pairs by transition name ("filter_settings" and
"header_settings"), but these calls lack null safety checks before accessing the
.task property. Add null safety checks to both find() operations using the safe
navigation operator or explicit null checks to prevent NullPointerException when
a matching task pair is not found. Ensure the value assignment to
advancedContentFormField only includes non-null task objects, filtering out any
null results from the find() operations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0fc4b3f6-f29e-4288-9722-22e3742cfeac

📥 Commits

Reviewing files that changed from the base of the PR and between 0b8c36b and a8fdf52.

📒 Files selected for processing (10)
  • src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
  • src/main/java/com/netgrif/application/engine/workflow/domain/Case.java
  • src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
  • src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/menu_item.xml
  • src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml
  • src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy
  • src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java
💤 Files with no reviewable changes (1)
  • src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

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 (4)
src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml (1)

40-46: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Same NPE risk pattern as other view configurations.

The find() calls can return null, leading to NPE when accessing .task.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml`
around lines 40 - 46, The initializeAdvancedForm function contains two find()
calls that search for task pairs by transition name ("filter_settings" and
"header_settings"), but these calls lack null safety checks before accessing the
.task property. Add null safety checks to both find() operations using the safe
navigation operator or explicit null checks to prevent NullPointerException when
a matching task pair is not found. Ensure the value assignment to
advancedContentFormField only includes non-null task objects, filtering out any
null results from the find() operations.
src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml (1)

40-45: 🧹 Nitpick | 🔵 Trivial | 💤 Low value

Add null-safety for task lookup.

The find() call can return null if no task matches the transition, which would cause an NPE when accessing .task. While filter_settings is defined in this net, defensive coding would be prudent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml`
around lines 40 - 45, The initializeAdvancedForm function uses a find() call on
useCase.tasks to locate a task with transition == "filter_settings" and
immediately accesses the .task property on the result. If no matching task
exists, find() returns null and accessing .task will cause a
NullPointerException. Add null-safety handling around the find() call using an
elvis operator or explicit null check to safely handle cases where the expected
task transition is not found, ensuring the advanced content form field is set to
an appropriate default value when the lookup fails.
src/main/resources/petriNets/engine-processes/menu/menu_item.xml (1)

51-69: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Incorrect task extraction in make statement.

At line 63, the expression viewCase.tasks.findAll { it.transition == "settings" }?.task is problematic:

  1. findAll returns a list of matching task pairs, not a single element
  2. Accessing .task on a list doesn't make sense and will fail or produce unexpected results
  3. The safe navigation ?.task won't help since findAll always returns a list (possibly empty), never null

The intent appears to be hiding the "settings" task form in the referenced view case. This should use collect to extract task IDs:

Proposed fix
-				make ["advanced_content_form"], hidden on (viewCase.tasks.findAll { it.transition == "settings" }?.task as List) when { true }
+				def settingsTaskIds = viewCase.tasks.findAll { it.transition == "settings" }.collect { it.task }
+				if (!settingsTaskIds.isEmpty()) {
+					make ["advanced_content_form"], hidden on settingsTaskIds when { true }
+				}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml` around
lines 51 - 69, The make statement in the initializeAdvancedForm function
incorrectly accesses the task property on the list returned by findAll. The
expression viewCase.tasks.findAll { it.transition == "settings" }?.task attempts
to get .task from a list, which doesn't work as intended. Replace .task with
.collect { it.task } to properly extract the task ID from each element in the
findAll result, ensuring the hidden condition receives a correctly formatted
list of task IDs for the settings transition.
src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml (1)

59-71: ⚠️ Potential issue | 🟡 Minor

Apply safe navigation operator to all find() calls on lines 62-64 for consistency and robustness.

In initializeAdvancedForm(), lines 62-64 use unsafe .task access on find() results without null-checking. Line 65 correctly uses the safe navigation operator ?.task for the optional open_case_settings transition. For consistency and defensive programming, apply the same safe pattern to all three required transitions: header_settings, create_case_btn_settings, and filter_settings. While these transitions are currently defined in the petrinet, the unsafe pattern leaves the code vulnerable to NPE if the petrinet structure changes or this code is reused elsewhere.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml`
around lines 59 - 71, In the initializeAdvancedForm function, the three find()
calls for header_settings, create_case_btn_settings, and filter_settings
transitions (lines 62-64) use unsafe direct `.task` access without
null-checking, while the open_case_settings transition on line 65 correctly uses
the safe navigation operator `?.task`. Apply the safe navigation operator
`?.task` to all three of the earlier find() calls to maintain consistency and
prevent potential null pointer exceptions if the petrinet structure changes.
This aligns all four transition lookups with the same defensive programming
pattern.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy`:
- Around line 1551-1554: The getTaskIds method lacks a null/empty guard for the
transitionIds parameter, causing a NullPointerException when called with null
values. Add a guard clause at the start of the getTaskIds method that checks if
transitionIds is null or empty, and returns an empty list in those cases before
proceeding with the findAll and collect operations on the transitionIds list.
- Around line 1539-1542: The getTaskId method calls .find() on the refs list and
immediately dereferences the result with .stringId without checking if a
matching task reference exists, which will throw a NullPointerException if no
task is found for the given transitionId. Add a null guard after the .find()
call to check if a matching task reference was found before accessing its
stringId property. If no task is found, return a controlled value (such as null)
or throw a meaningful exception instead of allowing the NullPointerException to
occur.

In
`@src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy`:
- Around line 257-259: The test code directly indexes the result of
petriNetService.getByIdentifier("menu_item") with [0] without verifying the list
is non-empty, which can result in an IndexOutOfBoundsException with poor
diagnostics. Add an assertion or verification before accessing the list element
to ensure the getByIdentifier call returns a non-empty list, making test
failures more diagnosable when the menu_item net cannot be found.
- Around line 453-455: The importTestPetriNet() helper method opens a
FileInputStream that is never closed, causing a resource leak. Refactor this
method to properly close the stream after the petriNetService.importPetriNet()
call completes by wrapping the stream in a try-with-resources statement or using
a try-finally block with an explicit close() call to ensure the stream is
released even if an exception occurs.

---

Outside diff comments:
In
`@src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml`:
- Around line 59-71: In the initializeAdvancedForm function, the three find()
calls for header_settings, create_case_btn_settings, and filter_settings
transitions (lines 62-64) use unsafe direct `.task` access without
null-checking, while the open_case_settings transition on line 65 correctly uses
the safe navigation operator `?.task`. Apply the safe navigation operator
`?.task` to all three of the earlier find() calls to maintain consistency and
prevent potential null pointer exceptions if the petrinet structure changes.
This aligns all four transition lookups with the same defensive programming
pattern.

In `@src/main/resources/petriNets/engine-processes/menu/menu_item.xml`:
- Around line 51-69: The make statement in the initializeAdvancedForm function
incorrectly accesses the task property on the list returned by findAll. The
expression viewCase.tasks.findAll { it.transition == "settings" }?.task attempts
to get .task from a list, which doesn't work as intended. Replace .task with
.collect { it.task } to properly extract the task ID from each element in the
findAll result, ensuring the hidden condition receives a correctly formatted
list of task IDs for the settings transition.

In
`@src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml`:
- Around line 40-45: The initializeAdvancedForm function uses a find() call on
useCase.tasks to locate a task with transition == "filter_settings" and
immediately accesses the .task property on the result. If no matching task
exists, find() returns null and accessing .task will cause a
NullPointerException. Add null-safety handling around the find() call using an
elvis operator or explicit null check to safely handle cases where the expected
task transition is not found, ensuring the advanced content form field is set to
an appropriate default value when the lookup fails.

In
`@src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml`:
- Around line 40-46: The initializeAdvancedForm function contains two find()
calls that search for task pairs by transition name ("filter_settings" and
"header_settings"), but these calls lack null safety checks before accessing the
.task property. Add null safety checks to both find() operations using the safe
navigation operator or explicit null checks to prevent NullPointerException when
a matching task pair is not found. Ensure the value assignment to
advancedContentFormField only includes non-null task objects, filtering out any
null results from the find() operations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0fc4b3f6-f29e-4288-9722-22e3742cfeac

📥 Commits

Reviewing files that changed from the base of the PR and between 0b8c36b and a8fdf52.

📒 Files selected for processing (10)
  • src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
  • src/main/java/com/netgrif/application/engine/workflow/domain/Case.java
  • src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
  • src/main/resources/petriNets/engine-processes/menu/case_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/menu_item.xml
  • src/main/resources/petriNets/engine-processes/menu/single_task_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/tabbed_ticket_view_configuration.xml
  • src/main/resources/petriNets/engine-processes/menu/task_view_configuration.xml
  • src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy
  • src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java
💤 Files with no reviewable changes (1)
  • src/test/java/com/netgrif/application/engine/menu/MenuItemServiceTest.java
🛑 Comments failed to post (4)
src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy (2)

1539-1542: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard missing task reference resolution in getTaskId.

If no task exists for the requested transition in the given case, this dereferences null and throws an NPE. Return a controlled error instead.

💡 Proposed fix
 String getTaskId(String transitionId, Case aCase = useCase) {
     List<TaskReference> refs = taskService.findAllByCase(aCase.stringId, null)
-    return refs.find { it.transitionId == transitionId }.stringId
+    TaskReference ref = refs.find { it.transitionId == transitionId }
+    if (ref == null) {
+        throw new IllegalArgumentException("Could not find task for transition [${transitionId}] in case [${aCase.stringId}]")
+    }
+    return ref.stringId
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    String getTaskId(String transitionId, Case aCase = useCase) {
        List<TaskReference> refs = taskService.findAllByCase(aCase.stringId, null)
        TaskReference ref = refs.find { it.transitionId == transitionId }
        if (ref == null) {
            throw new IllegalArgumentException("Could not find task for transition [${transitionId}] in case [${aCase.stringId}]")
        }
        return ref.stringId
    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy`
around lines 1539 - 1542, The getTaskId method calls .find() on the refs list
and immediately dereferences the result with .stringId without checking if a
matching task reference exists, which will throw a NullPointerException if no
task is found for the given transitionId. Add a null guard after the .find()
call to check if a matching task reference was found before accessing its
stringId property. If no task is found, return a controlled value (such as null)
or throw a meaningful exception instead of allowing the NullPointerException to
occur.

1551-1554: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add null/empty guard for transitionIds in bulk task lookup.

getTaskIds currently throws when transitionIds is null, and this propagates into all new bulk task event methods.

💡 Proposed fix
 List<String> getTaskIds(List<String> transitionIds, Case aCase = useCase) {
+    if (transitionIds == null || transitionIds.empty) {
+        return []
+    }
     List<TaskReference> refs = taskService.findAllByCase(aCase.stringId, null)
     return refs.findAll { transitionIds.contains(it.transitionId) }.collect { it.stringId}
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    List<String> getTaskIds(List<String> transitionIds, Case aCase = useCase) {
        if (transitionIds == null || transitionIds.empty) {
            return []
        }
        List<TaskReference> refs = taskService.findAllByCase(aCase.stringId, null)
        return refs.findAll { transitionIds.contains(it.transitionId) }.collect { it.stringId}
    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy`
around lines 1551 - 1554, The getTaskIds method lacks a null/empty guard for the
transitionIds parameter, causing a NullPointerException when called with null
values. Add a guard clause at the start of the getTaskIds method that checks if
transitionIds is null or empty, and returns an empty list in those cases before
proceeding with the findAll and collect operations on the transitionIds list.
src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy (2)

257-259: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Avoid unchecked list indexing when resolving menu_item net in test.

Indexing [0] without asserting presence can throw IndexOutOfBoundsException and make failures less diagnosable.

💡 Proposed fix
-        PetriNet menuItemNet = petriNetService.getByIdentifier("menu_item")[0]
+        List<PetriNet> menuItemNets = petriNetService.getByIdentifier("menu_item")
+        assertNotNull(menuItemNets)
+        assertFalse(menuItemNets.isEmpty())
+        PetriNet menuItemNet = menuItemNets[0]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        List<PetriNet> menuItemNets = petriNetService.getByIdentifier("menu_item")
        assertNotNull(menuItemNets)
        assertFalse(menuItemNets.isEmpty())
        PetriNet menuItemNet = menuItemNets[0]
        String mongoId2 = menuItemNet.stringId
        ObjectId objectId3 = menuItemNet.objectId
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy`
around lines 257 - 259, The test code directly indexes the result of
petriNetService.getByIdentifier("menu_item") with [0] without verifying the list
is non-empty, which can result in an IndexOutOfBoundsException with poor
diagnostics. Add an assertion or verification before accessing the list element
to ensure the getByIdentifier call returns a non-empty list, making test
failures more diagnosable when the menu_item net cannot be found.

453-455: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Close the test Petri net input stream.

The helper opens a FileInputStream without closing it.

💡 Proposed fix
 private PetriNet importTestPetriNet() {
-    return petriNetService.importPetriNet(new FileInputStream("src/test/resources/petriNets/NAE-2390_action_api_improvements.xml"), VersionType.MAJOR, userService.getLoggedOrSystem().transformToLoggedUser()).getNet()
+    return new File("src/test/resources/petriNets/NAE-2390_action_api_improvements.xml").withInputStream { input ->
+        petriNetService.importPetriNet(input, VersionType.MAJOR, userService.getLoggedOrSystem().transformToLoggedUser()).getNet()
+    }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

    private PetriNet importTestPetriNet() {
        return new File("src/test/resources/petriNets/NAE-2390_action_api_improvements.xml").withInputStream { input ->
            petriNetService.importPetriNet(input, VersionType.MAJOR, userService.getLoggedOrSystem().transformToLoggedUser()).getNet()
        }
    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/groovy/com/netgrif/application/engine/action/ActionDelegateTest.groovy`
around lines 453 - 455, The importTestPetriNet() helper method opens a
FileInputStream that is never closed, causing a resource leak. Refactor this
method to properly close the stream after the petriNetService.importPetriNet()
call completes by wrapping the stream in a try-with-resources statement or using
a try-finally block with an explicit close() call to ensure the stream is
released even if an exception occurs.

@mazarijuraj
mazarijuraj merged commit 3bd3b78 into release/6.5.0 Jun 16, 2026
5 of 7 checks passed
@mazarijuraj
mazarijuraj deleted the ETASK-23 branch June 16, 2026 04:48
@coderabbitai coderabbitai Bot mentioned this pull request Jul 6, 2026
17 tasks
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 improvement A change that improves on an existing feature Large Medium new feature A change that introduces new functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants