Skip to content

[ETASK-23] Dynamic view configuration#447

Open
Retoocs wants to merge 51 commits into
release/6.5.0from
ETASK-23
Open

[ETASK-23] Dynamic view configuration#447
Retoocs wants to merge 51 commits into
release/6.5.0from
ETASK-23

Conversation

@Retoocs
Copy link
Copy Markdown
Contributor

@Retoocs Retoocs commented Jun 2, 2026

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

  • New Features

    • Specialized filter types for case/task/process filtering
    • Menu item templates and templates registry for quick menu creation
    • New menu data endpoint to fetch rendered menu data
  • Documentation

    • Removed legacy filter and filter import/export docs
  • Refactor

    • Reworked menu/configuration and view models for richer view settings
    • Simplified filter handling and removed legacy import/export flows

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 15 commits May 27, 2026 10:16
- fix allowExport after merge
- introduce new filter fields
- remove old filter field
- remove transactional in Importer
- handle deprecated filter data type when importing process
- remove use of deleted filter attributes
- force component use for dedicated filter fields
- fix cloning filter fields
- close input stream
- disable MenuItemApiTest.groovy
# Conflicts:
#	src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
#	src/main/java/com/netgrif/application/engine/menu/domain/FilterBody.java
#	src/main/java/com/netgrif/application/engine/menu/service/MenuItemService.java
#	src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java
#	src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFilterImportExportService.java
#	src/main/resources/petriNets/engine-processes/import_filters.xml
#	src/test/groovy/com/netgrif/application/engine/action/MenuItemApiTest.groovy
#	src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy
- add todos
- resolve todos
- introduce allowed net fields for view configurations
- update FilterBody attributes
- improve initial values for filter fields
- remove unused field from task_view_configuration
- make editable boolean field in task_view_configuration
- fix MenuItemService.moveItem case override
- enhance MenuItemTemplateHolder methods
- 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
Copy link
Copy Markdown

coderabbitai Bot commented Jun 2, 2026

Review Change Stack

Walkthrough

Replaces legacy filter model with Case/Task/Process filter fields, refactors menu domain/types/templates and MenuItemService to Mongo with indexes, updates Petri net menu/view configs and startup runners, removes filter import/export/search services, adds MenuController and new menu APIs, and updates tests and docs.

Changes

Core menu and filter refactor

Layer / File(s) Summary
Comprehensive refactor checkpoint
src/main/java/...menu/..., src/main/groovy/.../startup/*, src/main/.../workflow/*, src/main/resources/petriNets/..., src/test/**/*, docs/_sidebar.md
Adds CaseFilterField/TaskFilterField/ProcessFilterField and updates FieldType/FieldFactory; removes FilterField/filterMetadata handling across Case/DataField/DataService/Importer; introduces MenuItemViewType, view bodies/constants, FilterBody changes, templates, MenuItemService Mongo refactor and new APIs, MenuController endpoint, MenuRunner startup, widespread Petri net/menu XML rewrites, removal of legacy filter import/export/search services, and corresponding test/resource/doc adjustments.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(66, 135, 245, 0.5)
  participant Client
  participant MenuController
  end
  rect rgba(40, 167, 69, 0.5)
  participant IMenuItemService
  participant MongoTemplate
  participant WorkflowService
  end
  Client->>MenuController: GET /api/menu/{encodedCaseId}
  MenuController->>IMenuItemService: getMenuItemData(caseId, locale)
  IMenuItemService->>MongoTemplate: query menu case by indexed fields
  IMenuItemService->>WorkflowService: resolve task/data groups
  IMenuItemService-->>MenuController: List<DataGroup>
  MenuController-->>Client: HAL JSON (EntityModel)
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
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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) {
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot Jun 2, 2026

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
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

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
Copy link
Copy Markdown

dpulls Bot commented Jun 5, 2026

🎉 All dependencies have been resolved !

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.

1 participant