Skip to content

[NAE-2266] Introduce active version of process#389

Merged
machacjozef merged 35 commits intorelease/7.0.0-rev9from
NAE-2266
Jan 13, 2026
Merged

[NAE-2266] Introduce active version of process#389
machacjozef merged 35 commits intorelease/7.0.0-rev9from
NAE-2266

Conversation

@Retoocs
Copy link
Copy Markdown
Contributor

@Retoocs Retoocs commented Nov 27, 2025

Description

Introduced new petriNet attribute defaultVersion. The internal implementation no longer finds petri nets by the latest version, but finds the petri net, that is default. Only one version per identifier can be default.

Implements NAE-2266

Dependencies

No new dependencies were introduced

Third party dependencies

No new dependencies were introduced

Blocking Pull requests

There are no dependencies on other PR

How Has Been This Tested?

Manually and by unit tests

PetriNetServiceTest

Test Configuration

Name Tested on
OS Ubuntu 24.04.1 LTS
Runtime Java 21
Dependency Manager Maven 3.6.3
Framework version Spring Boot 3.4.4
Run parameters
Other configuration

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • My changes have been checked, personally or remotely
  • 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

  • Refactor

    • Updated process version selection logic to prioritize default/active versions instead of newest versions throughout the application, improving consistency in process execution.
  • Bug Fixes

    • Enhanced error handling with clearer error messages when required processes cannot be found or are not properly configured.
    • Added validation checks to prevent null reference issues during process retrieval.

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

- add attribute isVersionActive in PetriNet
- introduce PetriNetService.getActiveVersionByIdentifier
- rename PetriNetService.getNewestVersionByIdentifier to PetriNetService.getLatestVersionByIdentifier
- switch usage of getNewestVersionByIdentifier to getActiveVersionByIdentifier
- enhance process import validations
- fix repository query
- fix null pointer exception
- fix unwanted java reference
- implement tests to test the version
- implement API to activate the process
- remove unused PetriNetService.evaluateRules method
- remove the explicit rollback when saving process changes -> resolved in EE as transactions
- remove todos
- implement PetriNetServiceTest.testMakeVersionActive
- split PetriNetService.save into two methods
- rename version active attribute in PetriNet
- remove makeVersionActive API
- update PetriNetService.deletePetriNet to handle versionActive attribute
- implement another test for process deletion
- mark methods import petriNet method transactional
@Retoocs Retoocs self-assigned this Nov 27, 2025
@Retoocs Retoocs 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 labels Nov 27, 2025
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Nov 27, 2025

Walkthrough

This PR refactors PetriNet version management by replacing getNewestVersionByIdentifier() calls with getDefaultVersionByIdentifier() across the codebase. It introduces a new "default" (active) version concept distinct from "latest", updates cache management strategy, adds version comparison utilities, and modifies deletion behavior to activate the latest version when the default is removed.

Changes

Cohort / File(s) Summary
Core Version Service Refactoring
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java, application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
Replaced getNewestVersionByIdentifier() with new getDefaultVersionByIdentifier() and getLatestVersionByIdentifier() methods. Annotated import methods with @Transactional. Enhanced save() to return Optional<PetriNet>. Introduced checkAndHandleProcessVersion() for version validation. Modified deletePetriNet() to activate and persist latest version if default is deleted. Updated logging and indexing behavior.
Cache Configuration
application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java
Replaced petriNetNewest field with petriNetDefault and petriNetLatest. Updated getAllCaches() to use new cache fields.
Repository
application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java
Added new paginated query method findByIdentifierAndDefaultVersion(String identifier, boolean defaultVersion, Pageable pageable).
Workflow & Case Services
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java, application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java, application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java, application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java, application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
Replaced getNewestVersionByIdentifier() with getDefaultVersionByIdentifier(). Added null-checks with ResourceNotFoundException in dashboard/menu services.
Filter & Menu Import/Export
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java, application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java
Updated to use getDefaultVersionByIdentifier() instead of getNewestVersionByIdentifier(). Added null-check with ResourceNotFoundException in FilterImportExportService.
Startup & Import
application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy, application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java, application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java
Replaced getNewestVersionByIdentifier() with getDefaultVersionByIdentifier() for process retrieval during import and initialization.
Action Delegates
application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy, application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy
Updated role collection and assignment to use getDefaultVersionByIdentifier() instead of getNewestVersionByIdentifier(). Added null-checks with IllegalArgumentException.
Domain Model - PetriNet
nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java, nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java
Added defaultVersion boolean field with getters/setters and @Indexed. Added public methods makeDefault() and makeNonDefault(). Updated copy constructor to copy defaultVersion. Added version comparison methods isHigherThan() and isLowerThan().
Domain Model - Elastic & Search
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java, nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNetSearch.java
Added defaultVersion field to ElasticPetriNet with initialization from PetriNet state. Added defaultVersion field to PetriNetSearch.
Exception Codes
nae-object-library/src/main/java/com/netgrif/application/engine/objects/common/ResourceNotFoundExceptionCode.java
Added new enum constant DEFAULT_PROCESS_NOT_FOUND("defaultProcessNotFound").
Tests - Service Tests
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy, application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy, application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy, application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy, application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy
Updated test calls from getNewestVersionByIdentifier() to getDefaultVersionByIdentifier(). Added new test methods for version activation/deactivation. Updated cache assertions to reference Default cache.
Tests - Domain Tests
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy, application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy
Updated retrieval calls to use getDefaultVersionByIdentifier(). Updated exception message assertion for version conflict scenario.
Test Resources
application-engine/src/test/resources/petriNets/process_version_{1,2,3,4,5}_0_0.xml, application-engine/src/test/resources/change_caseref_value_action_test.xml, application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml
Added new versioned PetriNet test resource files (versions 1.0.0 through 5.0.0). Updated test resource XML files to call getDefaultVersionByIdentifier() instead of getNewestVersionByIdentifier().
Spring Adapter
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java
Cosmetic comment update; no functional changes.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~70 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.25% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: introducing an active/default version concept for processes, which aligns with the extensive modifications across the codebase that replace 'newest version' lookups with 'default version' lookups.

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


📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ed4fd57 and 920e636.

📒 Files selected for processing (1)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java
🧬 Code graph analysis (1)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java (2)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java (1)
  • Getter (15-108)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/Case.java (1)
  • Getter (25-328)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (5)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java (5)

79-82: LGTM!

The defaultVersion field is well-defined:

  • Primitive boolean avoids null-handling complexity and defaults to false
  • @Indexed annotation is appropriate for efficient queries filtering by default version
  • Lombok annotations are consistent with other fields in the class

150-175: LGTM!

The explicit this.makeNonDefault() call at the end of the constructor is good for clarity, even though primitive boolean defaults to false. This makes the initialization intent explicit and aligns with the pattern of initializing all fields in the constructor.


453-459: LGTM!

The makeDefault() and makeNonDefault() helper methods provide clear semantic intent, improving readability over direct setDefaultVersion(true/false) calls. The naming convention is consistent with fluent builder patterns.


36-36: Constraint enforcement for defaultVersion is already implemented in the service layer.

The existing TODO about identifier+version uniqueness is a separate concern. The "only one default per identifier" constraint you mentioned is actively enforced: PetriNetService (lines 264–277) automatically makes existing default versions non-default when a new version becomes default, using getDefaultVersionByIdentifier() to find and update the prior default.

Consider whether the TODO should also mention the defaultVersion uniqueness constraint for future maintainers.


177-216: No action needed. The copy constructor is not used when creating new versions of a PetriNet. Version creation is handled explicitly by PetriNetService.checkAndHandleProcessVersion(), which manages the defaultVersion constraint independently. When a new version is imported, the service layer explicitly calls makeNonDefault() on the existing version if needed (line 264-267 of PetriNetService). The copy constructor's behavior of copying defaultVersion is appropriate for its actual use case—duplicating existing PetriNets—and does not conflict with the version constraint enforcement.

Likely an incorrect or invalid review comment.


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

❤️ Share

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

@Retoocs Retoocs marked this pull request as ready for review November 28, 2025 08:46
@coderabbitai coderabbitai Bot added the Large label Nov 28, 2025
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: 9

Caution

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

⚠️ Outside diff range comments (5)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java (1)

63-107: Consider fully removing or restoring the commented clone method.

The extensive commented-out clone() method (lines 64–107) with the TODO comment suggests deferred maintenance. If the method is genuinely not needed, delete it to reduce clutter and maintenance burden; if it may be useful in the future, restore and test it instead of leaving it commented out.

application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java (1)

236-261: Role/net resolution now depends on active versions; consider aligning messaging and null‑safety

  • Using petriNetService.getActiveVersionByIdentifier(netImportId) (Line 240) and getActiveVersionByIdentifier("preference_filter_item") (Line 264) correctly aligns this flow with the new “active version” semantics.
  • The log text on Line 255 still says “latest present version of net”, which is slightly misleading now that you explicitly use the active version. Consider updating it to “active version of net” for clarity.
  • getActiveVersionByIdentifier("preference_filter_item") is dereferenced without a null check. This was structurally identical before with the “newest” call, but with the stricter “active” constraint it might now be easier to end up with no active version configured. If that’s a realistic state, adding an explicit check (and a clear error/log) would make this more robust.

Also applies to: 262-268

application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java (1)

41-54: Config net existence now depends on an active version

Switching to petriNetService.getActiveVersionByIdentifier(netIdentifier) means that if a config net exists only in inactive versions, importProcess will reimport it instead of treating it as already present. For system configuration processes this may be exactly what you want, but it is a behavioral change from the previous “any newest version” check. Please confirm that reimporting when no active version exists is the intended behavior.

application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java (1)

267-285: Potential NPE in full‑text search when no active PetriNet version exists

Here:

petriNets = processes.stream()
        .map(process -> petriNetService.getActiveVersionByIdentifier(process))
        .collect(Collectors.toList());

you later do petriNets.forEach(net -> net.getImmediateFields().forEach(...)) without checking for nulls. If any process has no active version (identifier exists but all versions inactive, or data not yet migrated), getActiveVersionByIdentifier can return null and you’ll get an NPE at runtime.

Consider defensively filtering out nulls and treating “no active nets resolved” as “no full‑text predicates”:

- petriNets = processes.stream()
-         .map(process -> petriNetService.getActiveVersionByIdentifier(process))
-         .collect(Collectors.toList());
+ petriNets = processes.stream()
+         .map(petriNetService::getActiveVersionByIdentifier)
+         .filter(Objects::nonNull)
+         .collect(Collectors.toList());

and keep the existing if (petriNets.isEmpty()) return null; behavior.

This avoids NPEs while still restricting the search to active nets.

application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy (1)

131-140: testVersioningConflicts does not fail if no exception is thrown; message assertion is fragile

Right now, if importPetriNet stops throwing for this scenario, the test will still pass because all assertions are inside the catch block. Also, asserting the full exception message string is brittle to minor wording changes.

Consider tightening the test like this:

-        try {
-            petriNetService.importPetriNet(netResource3.inputStream, VersionType.MAJOR, superCreator.loggedSuper)
-        } catch (Exception e) {
-            assert e.getMessage() == "A process [test] with such version [0.0.1] already exists"
-        }
+        try {
+            petriNetService.importPetriNet(netResource3.inputStream, VersionType.MAJOR, superCreator.loggedSuper)
+            assert false: "Expected importPetriNet to fail for duplicate process version"
+        } catch (Exception e) {
+            assert e.getMessage() == "A process [test] with such version [0.0.1] already exists"
+        }

Optionally, you could relax the message check a bit (e.g. check that it contains the identifier and version, rather than matching the whole string) to reduce brittleness while still verifying the key dynamic parts.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4da5b57 and d457145.

📒 Files selected for processing (34)
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy (1 hunks)
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy (2 hunks)
  • application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy (2 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java (2 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (6 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java (2 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java (3 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy (2 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy (1 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy (2 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy (1 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy (1 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy (5 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy (1 hunks)
  • application-engine/src/test/resources/change_caseref_value_action_test.xml (2 hunks)
  • application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml (1 hunks)
  • application-engine/src/test/resources/petriNets/process_version_1_0_0.xml (1 hunks)
  • application-engine/src/test/resources/petriNets/process_version_2_0_0.xml (1 hunks)
  • application-engine/src/test/resources/petriNets/process_version_3_0_0.xml (1 hunks)
  • application-engine/src/test/resources/petriNets/process_version_4_0_0.xml (1 hunks)
  • application-engine/src/test/resources/petriNets/process_version_5_0_0.xml (1 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java (3 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java (6 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java (2 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java (1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy
  • application-engine/src/test/resources/petriNets/process_version_2_0_0.xml
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java
  • application-engine/src/test/resources/change_caseref_value_action_test.xml
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java
  • application-engine/src/test/resources/petriNets/process_version_3_0_0.xml
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy
  • application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java
  • application-engine/src/test/resources/petriNets/process_version_1_0_0.xml
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
  • application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy
📚 Learning: 2025-09-05T10:21:54.893Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 350
File: application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy:135-135
Timestamp: 2025-09-05T10:21:54.893Z
Learning: In ExportServiceTest.groovy, writing to src/test/resources is intentional to simulate production behavior where the working tree is mutated during file exports. This mirrors how the system works in production.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/PetriNetTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy
  • application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml
📚 Learning: 2025-10-20T11:46:37.958Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 373
File: application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java:105-107
Timestamp: 2025-10-20T11:46:37.958Z
Learning: In ActionApiImpl, the processIdentifier parameter in searchCases() is intentionally unused because it's required by the ActionApi interface for plugin implementations. This implementation returns cases from all processes without filtering by processIdentifier.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java
📚 Learning: 2025-06-23T13:30:13.096Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 318
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java:60-62
Timestamp: 2025-06-23T13:30:13.096Z
Learning: In MenuItemConstants enum in nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java, the field `attributeId` will be renamed to `value` to make it more generic and appropriate for both dataset attribute identifiers and technical constants like PATH_SEPARATOR.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java
📚 Learning: 2025-07-29T17:19:18.300Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 331
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java:45-46
Timestamp: 2025-07-29T17:19:18.300Z
Learning: In ElasticPetriNetService class, petriNetService is properly initialized using Lazy setter injection rather than constructor injection. This pattern with Lazy Autowired setter methods is commonly used in Spring to resolve circular dependencies and is a valid alternative to constructor injection.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java
📚 Learning: 2025-09-29T10:31:57.325Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 362
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java:513-529
Timestamp: 2025-09-29T10:31:57.325Z
Learning: PetriNet.getStringId() returns a simple ObjectId string representation (_id.toString()), not a composite Netgrif ID format, so new ObjectId(petriNetId) works correctly when petriNetId comes from PetriNet.getStringId().

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
  • application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
🧬 Code graph analysis (1)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/ActorTransformer.java (1)
  • ActorTransformer (10-131)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (36)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java (1)

18-62: Verify whether this entity requires active version field mapping.

Based on the PR objective (introducing active version semantics across the codebase), this Spring Data MongoDB entity should be reviewed to ensure it includes necessary field mappings for active version support. The AI summary notes that ElasticPetriNet and Version classes were extended to support versionActive. Please confirm whether this PetriNet class should also have an active version field mapped (e.g., @org.springframework.data.mongodb.core.mapping.Field("versionActive")).

application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml (1)

220-220: Verify that getActiveVersionByIdentifier() contract guarantees non-null and that test setup establishes active versions.

The change aligns well with the PR's semantic shift to active versions. However, the direct chain .stringId assumes non-null return from getActiveVersionByIdentifier().

Per the retrieved learnings, existing nets always have versions assigned; confirm that:

  1. getActiveVersionByIdentifier() has a non-null contract (or clarify fallback behavior if no active version exists)
  2. The test setup for NAE-1290_Export_actions properly marks the PetriNet as "active" before this action executes

If the active version may be absent in edge cases, consider adding a null check or handling the scenario explicitly.

application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy (2)

131-135: Aligning task search with active PetriNet version looks correct

Switching to petriNetService.getActiveVersionByIdentifier("export_test").stringId keeps the test in sync with the new “active version” semantics and remains safe given the DB is truncated and the net is freshly imported in before(), so this call should always resolve to the same net that produced the tasks.


149-156: Consistent use of active version in header test is appropriate

Using getActiveVersionByIdentifier("export_test") here mirrors the previous change and ensures the repository query targets the active net version whose tasks are being exported; this is consistent with the intended behavior and test setup.

application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy (1)

314-314: LGTM! Change aligns with PR objectives.

The replacement of getNewestVersionByIdentifier() with getActiveVersionByIdentifier() is consistent with the PR's goal of introducing semantic distinction between active and latest versions. The test correctly retrieves the active version of the impersonation config Petri net.

Since the entire test class is disabled (line 60), verify that this change works as expected when the impersonation tests are re-enabled and ensure an active version of the config Petri net exists during test execution.

application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy (1)

48-48: Using the active PetriNet version in test setup is consistent with new semantics

Switching from getNewestVersionByIdentifier("new_init_test") to getActiveVersionByIdentifier("new_init_test") matches the new “active vs newest” behavior. Given the DB is truncated in before() and the net is imported right before case creation, resolving the active version here is unambiguous and correctly exercises the active-version path.

Based on learnings, the import flow guarantees nets have a proper version assigned, so relying on an active version here is safe.

application-engine/src/test/resources/petriNets/process_version_1_0_0.xml (1)

1-9: LGTM! Test resource is well-formed.

The XML structure is valid and properly defines a PetriNet process version test resource with appropriate schema references.

application-engine/src/test/resources/petriNets/process_version_2_0_0.xml (1)

1-9: LGTM! Test resource is well-formed.

The XML structure is valid and properly defines version 2.0.0 of the process version test resource.

application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy (2)

131-131: LGTM! Active version retrieval is appropriate here.

The change correctly uses the active version to determine if a PetriNet already exists before creating a new one. The null check on line 132 properly handles the case where no active version exists.


252-252: LGTM! Active version check is correct.

The change appropriately uses the active version to determine if a process has already been imported. The null check on line 253 properly handles cases where no active version exists.

application-engine/src/test/resources/petriNets/process_version_5_0_0.xml (1)

1-9: LGTM! Test resource is well-formed.

The XML structure is valid and properly defines version 5.0.0 of the process version test resource.

application-engine/src/test/resources/petriNets/process_version_3_0_0.xml (1)

1-9: LGTM! Test resource is well-formed.

The XML structure is valid and properly defines version 3.0.0 of the process version test resource.

application-engine/src/test/resources/petriNets/process_version_4_0_0.xml (1)

1-9: LGTM! Test resource is well-formed.

The XML structure is valid and properly defines version 4.0.0 of the process version test resource.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java (1)

55-87: LGTM! Helpful convenience methods added.

The new isHigherThan and isLowerThan methods provide clear, readable alternatives to direct compareTo calls. The implementation correctly delegates to the existing compareTo method, and the Javadoc is well-written.

application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (1)

72-75: LGTM! Null handling is present downstream.

The change to use getActiveVersionByIdentifier is correct. The cachePetriNetFunctions method (line 55) already has a null check, so the flow is safe even if the active version is not found.

application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java (1)

427-430: LGTM! Proper null handling in place.

The change to getActiveVersionByIdentifier includes an appropriate null check with a graceful failure path (returning Optional.empty()). This ensures the filter creation logic is resilient to missing or inactive process definitions.

application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java (1)

287-293: LGTM! Null handling is consistent and appropriate.

All three createCaseByIdentifier methods properly check for null after calling getActiveVersionByIdentifier and throw descriptive IllegalArgumentExceptions. This ensures clear error messages when attempting to create cases from inactive or non-existent process definitions.

application-engine/src/test/resources/change_caseref_value_action_test.xml (1)

32-37: Switch to active PetriNet versions in test actions is consistent

Using petriNetService.getActiveVersionByIdentifier(...) here aligns the test with the new active-version semantics and keeps behavior equivalent under the assumption that the referenced nets are active. No additional changes needed.

Also applies to: 46-52

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

3-3: versionActive flag and initialization look consistent with active‑version model

  • Adding versionActive with an index and accessor methods, plus makeActive()/makeInactive(), cleanly supports the new active‑version concept.
  • The no‑arg constructor now fully initializes collections and calls makeInactive(), which is a safe default for newly constructed nets.
  • The copy constructor copies versionActive from the source net. That’s reasonable for cloning semantics, but any code that uses this copy constructor to create a new version should explicitly reset the flag (e.g., via makeInactive()) before persisting, if a cloned net must start inactive.

Overall the changes are internally consistent; just keep the copy‑semantics in mind at call sites where a “new version” is derived from an existing one.

Also applies to: 72-80, 143-167, 169-204, 441-447

application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy (1)

132-170: Test now correctly validates active vs historical versions

Switching to getActiveVersionByIdentifier("new_model") for both the first and second import assertions matches the new semantics: the active net should move from v1 to v2, while getPetriNet(net.stringId) later still validates that v1 remains intact as a historical version. The control flow and assertions remain coherent.

Also applies to: 206-232

application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java (1)

36-44: Repository method for active/inactive selection is fine; relies on uniqueness invariant

findByIdentifierAndVersionActive(String identifier, boolean versionActive) is consistent with your existing findByIdentifierAndVersion method and matches the new field on PetriNet. It does rely on there being at most one net per (identifier, versionActive) combination; if multiple active versions could ever exist for a given identifier, callers would get an arbitrary one. As long as the service layer enforces “exactly one active version per identifier”, this is fine.

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

27-28: Elastic projection correctly mirrors versionActive state

Introducing versionActive on ElasticPetriNet and populating it both in the constructor (from PetriNet) and in update() ensures the search index reflects the active/inactive status of each net version. The change is straightforward and consistent with the domain model.

Also applies to: 39-44, 50-53

application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy (1)

70-77: Test correctly validates active version cache behavior.

The test properly verifies the cache lifecycle:

  1. Cache is empty before retrieval (line 70, 75)
  2. After calling getActiveVersionByIdentifier, the cache is populated (line 77)

Minor observation: Line 70 checks for "processDeleteTest" as a hardcoded string, while line 75 uses testNet.getIdentifier(). This is intentional as the first check occurs before import, but ensure the identifier in the XML (process_delete_test.xml) matches "processDeleteTest".

application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy (3)

46-46: New constant for version test files.

The format string pattern is clear and follows the existing naming convention.


214-241: Test validates active version reassignment on deletion.

This test correctly verifies that:

  • Deleting an inactive version doesn't change the active version
  • Deleting the active version promotes the latest remaining version to active
  • All versions can be deleted sequentially

The test complements testVersionsOnImport by covering the deletion path of the version lifecycle.


303-305: Good extraction of import helper method.

The helper method reduces duplication across tests and centralizes the import call pattern.

application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java (2)

30-37: Clear semantic separation of cache identifiers.

The distinction between petriNetActive (the designated active version) and petriNetLatest (the highest version number) is well-defined. The documentation comments accurately describe each cache's purpose.


61-66: Both new caches properly included in the aggregate set.

The getAllCaches() method correctly includes both petriNetActive and petriNetLatest in the returned set, ensuring they are managed alongside other caches.

application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (8)

140-146: Cache eviction updated for new cache structure.

Both petriNetActive and petriNetLatest caches are properly cleared, ensuring cache consistency when evicting all caches.


181-184: Transactional annotation added to import methods.

Adding @Transactional ensures atomicity of the import operation. If an exception occurs during import, all database changes (including the deactivation of previous versions) will be rolled back.


243-251: Correct ordering: save inactivated process before new process.

Saving the inactivated process first (line 244) before the new process (line 246) ensures that if the new process save fails, the transaction rollback will restore the previous active state. This is the correct order of operations within a transaction.


254-271: Refactored save with internal delegation.

The split into public save() and protected final doSaveInternal() provides a clear extension point while ensuring consistent persistence behavior. The final modifier on doSaveInternal prevents subclasses from bypassing the core save logic.

Minor: Line 267 log message uses placeholder correctly. Good error handling that logs but doesn't fail the save operation due to indexing issues.


287-296: Null guard prevents cache key issues.

The null check on identifier and version (lines 287-289) prevents NullPointerException in the cache key computation (#identifier+#version.toString() in the @Cacheable annotation). Returning null for invalid inputs is appropriate since the cache is configured with unless = "#result == null".


310-331: New version retrieval methods with proper caching.

Both methods correctly:

  • Include null guards to prevent NPE and cache key issues
  • Use appropriate cache names (petriNetActive, petriNetLatest)
  • Configure unless = "#result == null" to avoid caching null values

getLatestVersionByIdentifier (lines 321-331) uses descending sort on version components to find the highest version, which is correct.


594-602: Active version reassignment on deletion.

When deleting the currently active version, the service correctly promotes the latest remaining version to active. This ensures there's always an active version (if any versions remain) after deletion.

The self.getLatestVersionByIdentifier() call (line 595) uses the proxy to ensure cache semantics are respected, though note that evictCache(petriNet) was called just before (line 592), which evicts the latest cache entry. This should still work correctly as the repository query will be executed.


461-464: getReference defaults to active version when version is null.

This is the correct semantic change - when no specific version is requested, the active version (not just the newest) should be returned. This aligns with the PR's objective of distinguishing between "active" and "latest" versions.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jan 7, 2026
- rename keyword active to default
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: 4

Caution

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

⚠️ Outside diff range comments (1)
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java (1)

263-268: Add null check before dereferencing PetriNet.

Line 264 retrieves the default version of "preference_filter_item" and immediately calls .getStringId() without null validation. This could cause a NullPointerException if the default version doesn't exist.

🛡️ Proposed fix
         //Creating new Case of preference_filter_item net and setting its data...
+        PetriNet preferenceItemNet = petriNetService.getDefaultVersionByIdentifier("preference_filter_item");
+        if (preferenceItemNet == null) {
+            resultMessage.append("- Preference filter item process not found or not active\n");
+            netCheck.set(false);
+            return "";
+        }
         Case menuItemCase = workflowService.createCase(
-                petriNetService.getDefaultVersionByIdentifier("preference_filter_item").getStringId(),
+                preferenceItemNet.getStringId(),
                 item.getEntryName() + "_" + menuIdentifier,
                 "",
                 ActorTransformer.toLoggedUser(userService.getSystem())
         ).getCase();
🤖 Fix all issues with AI agents
In
@application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java:
- Around line 67-69: Replace the IllegalStateException thrown when petriNet is
null in DashboardItemServiceImpl (the check using the petriNet variable) with
the project-wide ResourceNotFoundException: import
com.netgrif.application.engine.objects.common.ResourceNotFoundException and
com.netgrif.application.engine.objects.common.ResourceNotFoundExceptionCode,
then throw new
ResourceNotFoundException(ResourceNotFoundExceptionCode.DEFAULT_PROCESS_NOT_FOUND)
at the same spot to match FilterImportExportService behavior and codebase
conventions.

In
@application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy:
- Line 131: The test calls
petriNetService.getDefaultVersionByIdentifier("export_test").stringId without
checking for null; add an assertion or explicit null check on the result of
getDefaultVersionByIdentifier before accessing .stringId so failures report a
clear message—e.g., assert that the returned object from
petriNetService.getDefaultVersionByIdentifier("export_test") is not null (with a
descriptive message) and only then assign processId from .stringId.
- Line 149: The test currently calls
petriNetService.getDefaultVersionByIdentifier("export_test").stringId without
checking for null; update ExportServiceTest to first assert that
petriNetService.getDefaultVersionByIdentifier("export_test") is not null (or add
a null-check) before accessing .stringId so failures report a clear assertion
error instead of a NullPointerException; reference the call to
petriNetService.getDefaultVersionByIdentifier and the variable processId when
adding the assertion.

In
@application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy:
- Line 314: Add a defensive null check for the PetriNet returned by
petriNetService.getDefaultVersionByIdentifier(ImpersonationRunner.IMPERSONATION_CONFIG_PETRI_NET_IDENTIFIER)
before calling helper.createCase; retrieve the result into a local variable,
assert it is not null (or throw an exception) and then pass that variable to
helper.createCase("config", ...), so the test mirrors production patterns (see
getDefaultVersionByIdentifier,
ImpersonationRunner.IMPERSONATION_CONFIG_PETRI_NET_IDENTIFIER, and
helper.createCase).
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b92ba07 and eb377cc.

📒 Files selected for processing (25)
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy
  • application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
  • application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java
  • application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/common/ResourceNotFoundExceptionCode.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java
🧰 Additional context used
🧠 Learnings (16)
📓 Common learnings
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java
  • application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy
  • application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java
📚 Learning: 2025-12-19T09:06:38.316Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 398
File: application-engine/src/main/java/com/netgrif/application/engine/importer/service/Importer.java:57-58
Timestamp: 2025-12-19T09:06:38.316Z
Learning: In the file `application-engine/src/main/java/com/netgrif/application/engine/importer/service/Importer.java`, the `definition` attribute of the `Action` class (from `com.netgrif.application.engine.objects.petrinet.domain.dataset.logic.action.Action`) is never null, so null checks for `action.getDefinition()` are not needed.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java
  • application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
📚 Learning: 2025-12-12T12:37:58.852Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/test/groovy/com/netgrif/application/engine/auth/WorkflowAuthorizationServiceTest.groovy:4-4
Timestamp: 2025-12-12T12:37:58.852Z
Learning: In tests under application-engine/src/test, GroupService.getDefaultSystemGroup() lazily creates the default system group if it doesn't exist. Tests can rely on this behavior and omit explicit existence/setup checks for the default system group.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-12-12T12:40:25.863Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy:18-20
Timestamp: 2025-12-12T12:40:25.863Z
Learning: In Groovy/Java, if a parameter or collection expects ActorFieldValue, you can pass UserFieldValue (a subclass) due to polymorphism. In tests, verify that ActorListFieldValue accepts ActorFieldValue instances and that passing UserFieldValue instances is valid without casts. This pattern applies to similar Groovy files across the repository.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy
  • application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-12-19T07:55:13.427Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java:239-247
Timestamp: 2025-12-19T07:55:13.427Z
Learning: In the netgrif/application-engine project, the `taskService.findOne()` method (from ITaskService) never returns null. When a task is not found, it handles the error through other means (such as throwing an exception) rather than returning null, so null checks after `findOne()` calls are unnecessary.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
📚 Learning: 2025-11-14T10:22:01.634Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 383
File: application-engine/src/main/java/com/netgrif/application/engine/startup/ApplicationRunnerOrderResolver.java:43-43
Timestamp: 2025-11-14T10:22:01.634Z
Learning: For the netgrif/application-engine repository, avoid flagging trivial or nitpick-level issues such as redundant null checks, minor code style improvements, or obvious simplifications that don't affect functionality or introduce bugs. Focus review comments on substantive issues like logic errors, security concerns, performance problems, or breaking changes.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
  • application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-09-29T10:31:31.469Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 362
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/ProcessRoleController.java:43-45
Timestamp: 2025-09-29T10:31:31.469Z
Learning: In the Netgrif application engine, when Authentication objects reach controller endpoints, auth.getPrincipal() always returns a LoggedUser instance and is never null, so defensive casting checks are not necessary in controller methods.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
📚 Learning: 2025-12-12T12:36:06.651Z
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".

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
📚 Learning: 2025-12-12T13:40:00.786Z
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.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-09-29T10:31:57.325Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 362
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java:513-529
Timestamp: 2025-09-29T10:31:57.325Z
Learning: PetriNet.getStringId() returns a simple ObjectId string representation (_id.toString()), not a composite Netgrif ID format, so new ObjectId(petriNetId) works correctly when petriNetId comes from PetriNet.getStringId().

Applied to files:

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

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
📚 Learning: 2025-06-23T13:30:13.096Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 318
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java:60-62
Timestamp: 2025-06-23T13:30:13.096Z
Learning: In MenuItemConstants enum in nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java, the field `attributeId` will be renamed to `value` to make it more generic and appropriate for both dataset attribute identifiers and technical constants like PATH_SEPARATOR.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java
📚 Learning: 2025-09-05T10:21:54.893Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 350
File: application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy:135-135
Timestamp: 2025-09-05T10:21:54.893Z
Learning: In ExportServiceTest.groovy, writing to src/test/resources is intentional to simulate production behavior where the working tree is mutated during file exports. This mirrors how the system works in production.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-10-20T11:46:37.958Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 373
File: application-engine/src/main/java/com/netgrif/application/engine/actions/ActionApiImpl.java:105-107
Timestamp: 2025-10-20T11:46:37.958Z
Learning: In ActionApiImpl, the processIdentifier parameter in searchCases() is intentionally unused because it's required by the ActionApi interface for plugin implementations. This implementation returns cases from all processes without filtering by processIdentifier.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java
📚 Learning: 2025-07-29T17:19:18.300Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 331
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java:45-46
Timestamp: 2025-07-29T17:19:18.300Z
Learning: In ElasticPetriNetService class, petriNetService is properly initialized using Lazy setter injection rather than constructor injection. This pattern with Lazy Autowired setter methods is commonly used in Spring to resolve circular dependencies and is a valid alternative to constructor injection.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
🧬 Code graph analysis (1)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java (1)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/petrinet/domain/PetriNet.java (1)
  • Getter (15-108)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (39)
application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/ActionDelegate.groovy (1)

2447-2457: LGTM! Null check properly implemented.

The null check after retrieving the default version by identifier correctly handles the case where no default version exists, throwing a descriptive IllegalArgumentException. This addresses the previous review concern and aligns with the PR's shift to default version semantics.

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

76-79: LGTM! Default version field properly implemented.

The defaultVersion field is correctly:

  • Annotated with @Indexed for efficient queries
  • Initialized to false in the constructor (explicit opt-in required)
  • Copied in the copy constructor
  • Managed via clean helper methods makeDefault() and makeNonDefault()

This implementation properly supports the PR's default version resolution semantics.

Also applies to: 166-167, 177-177, 441-447

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

27-28: LGTM! ElasticPetriNet correctly mirrors PetriNet changes.

The defaultVersion field is properly:

  • Added as a field
  • Initialized from the source PetriNet in the constructor
  • Updated in the update() method

This ensures consistency between MongoDB and Elasticsearch representations of PetriNet default version semantics.

Also applies to: 43-43, 52-52

application-engine/src/test/groovy/com/netgrif/application/engine/workflow/NewInitTest.groovy (1)

48-48: LGTM! Test updated for default version API.

The test correctly uses getDefaultVersionByIdentifier instead of getNewestVersionByIdentifier, aligning with the PR's shift to default version semantics for case creation.

application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (1)

74-74: LGTM! Cache reload updated for default version API.

The cache reload correctly uses getDefaultVersionByIdentifier to ensure cached functions are reloaded from the default version of the PetriNet, aligning with the PR's version resolution semantics.

application-engine/src/main/java/com/netgrif/application/engine/workflow/service/MenuImportExportService.java (1)

240-243: LGTM - Proper null handling for PetriNet lookup.

The code correctly retrieves the default version and validates it exists before using it. The error message is logged appropriately when the net is not found.

application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java (1)

68-72: LGTM - Proper null handling and error messaging.

The code correctly retrieves the default version, validates it exists, and throws a clear exception when not found. This follows the pattern established across the codebase for default version lookups.

application-engine/src/main/java/com/netgrif/application/engine/startup/runner/DefaultFiltersRunner.java (1)

425-428: LGTM - Proper null handling with Optional pattern.

The code correctly retrieves the default version and returns Optional.empty() when the filter process doesn't exist, which is the appropriate error handling for this context.

application-engine/src/main/groovy/com/netgrif/application/engine/petrinet/domain/dataset/logic/action/delegate/RoleActionDelegate.groovy (2)

53-59: LGTM - Proper null handling with clear error message.

The code correctly retrieves the default version and validates it exists before use. The error message clearly indicates which process identifier was not found.


80-86: LGTM - Consistent null handling pattern.

The implementation mirrors the assignRole method with proper null validation and clear error messaging, ensuring consistent behavior across role operations.

application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/ImporterTest.groovy (1)

133-133: LGTM! Test assertions correctly updated to use default version retrieval.

The test logic aligns with the PR's objective to switch from newest-version to default-version semantics. After importing each version, the test correctly verifies that getDefaultVersionByIdentifier returns the expected active version.

Also applies to: 207-207

application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ImpersonationRunner.java (1)

42-46: LGTM! Proper null handling for default version retrieval.

The code correctly checks if the default version exists before proceeding to import, with appropriate null handling at line 43.

application-engine/src/main/groovy/com/netgrif/application/engine/startup/ImportHelper.groovy (2)

131-135: LGTM! Proper null handling in upsertNet.

The method correctly uses getDefaultVersionByIdentifier and handles the null case (when no default version exists) with Groovy's truthy check at line 132.


252-256: LGTM! Proper null handling in importProcessOnce.

The method correctly uses getDefaultVersionByIdentifier and handles the null case with an explicit null check at line 253.

application-engine/src/main/java/com/netgrif/application/engine/workflow/service/CaseSearchService.java (1)

283-283: Filter out null results to prevent potential NullPointerException.

If getDefaultVersionByIdentifier(process) returns null for any process identifier (e.g., when no default version exists), the null will be collected into the petriNets list. The isEmpty() check at line 285 won't catch null elements, and the forEach loop at line 302 will throw a NullPointerException when attempting to access methods on a null net.

🛡️ Proposed fix to filter nulls
-            petriNets = processes.stream().map(process -> petriNetService.getDefaultVersionByIdentifier(process)).collect(Collectors.toList());
+            petriNets = processes.stream()
+                    .map(process -> petriNetService.getDefaultVersionByIdentifier(process))
+                    .filter(Objects::nonNull)
+                    .collect(Collectors.toList());

You'll also need to add this import at the top of the file:

import java.util.Objects;
⛔ Skipped due to learnings
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 400
File: application-engine/src/main/java/com/netgrif/application/engine/workflow/web/AbstractTaskController.java:239-247
Timestamp: 2025-12-19T07:55:13.427Z
Learning: In the netgrif/application-engine project, the `taskService.findOne()` method (from ITaskService) never returns null. When a task is not found, it handles the error through other means (such as throwing an exception) rather than returning null, so null checks after `findOne()` calls are unnecessary.
application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java (1)

36-43: Method only supports defaultVersion=true in current codebase; document or restrict accordingly.

The method is only called with defaultVersion=true (in PetriNetService.getDefaultVersionByIdentifier), which is correct since a single active version per identifier is guaranteed. However, the method signature allows defaultVersion=false, which would cause IncorrectResultSizeDataAccessException if called when multiple inactive versions exist for the same identifier, since the return type expects a single entity.

Either document in the Javadoc that this method should only be called with defaultVersion=true, or remove the defaultVersion parameter and create a separate method for the inactive version case if future support is needed.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/common/ResourceNotFoundExceptionCode.java (1)

9-10: LGTM!

The new enum constant follows the existing pattern and provides a clear, specific error code for missing default processes.

application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java (1)

156-169: LGTM!

The method rename from getNewestVersionByIdentifier to getDefaultVersionByIdentifier and the addition of getLatestVersionByIdentifier align well with the PR objectives. The Javadoc clearly documents that both methods can return null, which helps callers handle missing processes appropriately.

application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FilterImportExportService.java (1)

271-274: LGTM!

The change to getDefaultVersionByIdentifier with proper null checking and ResourceNotFoundException follows the correct pattern for handling missing resources. The error message is clear and informative.

application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java (3)

287-293: LGTM! Correct transition to default version retrieval.

The change from getNewestVersionByIdentifier to getDefaultVersionByIdentifier aligns with the PR's introduction of the default version concept. The null-check and exception handling remain appropriate.


301-307: LGTM! Consistent with other overloads.

This overload correctly follows the same pattern as the other createCaseByIdentifier methods, using the default version for case creation.


310-316: LGTM! Third overload correctly updated.

All three createCaseByIdentifier overloads now consistently use the default version, maintaining proper error handling throughout.

application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/CachePetriNetServiceTest.groovy (1)

70-77: LGTM! Test correctly validates default cache behavior.

The test has been properly updated to validate the new default cache semantics:

  • References petriNetDefault cache instead of the removed petriNetNewest
  • Uses getDefaultVersionByIdentifier for retrieval
  • Validates caching behavior for the default version
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy (5)

45-45: Good addition of version test file format constant.

The VERSION_PROCESS_FILE_FORMAT constant provides a clear template for version test files, improving test maintainability.


309-311: LGTM! Helper method reduces test duplication.

The importProcess helper centralizes import invocation with consistent parameters (stream and VersionType.MAJOR), improving test code maintainability.


151-217: Comprehensive test validates version activation semantics.

This test thoroughly covers the version activation logic:

  • ✅ Importing a new version marks it as default
  • ✅ Importing a higher version deactivates the previous default
  • ✅ Importing a lower version doesn't affect default status
  • ✅ Duplicate version imports are rejected
  • ✅ Manual default manipulation is respected
  • ✅ Elasticsearch synchronization is validated

The test validates both in-memory PetriNet state and Elasticsearch indexed state, ensuring consistency across storage layers.


219-246: Excellent test coverage for default version on deletion.

This test validates the critical behavior when deleting PetriNet versions:

  • ✅ Deleting a non-default version preserves the current default
  • ✅ Deleting the default version activates the latest remaining version
  • ✅ Deleting the last version removes it without errors

This aligns with the implementation in PetriNetService.deletePetriNet (lines 624-632).


107-107: Consistent refactoring to use helper method.

The existing processImportAndDelete test has been refactored to use the new importProcess helper, improving consistency across the test suite.

application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java (2)

29-37: LGTM! Cache configuration correctly updated for new semantics.

The replacement of petriNetNewest with petriNetDefault and petriNetLatest provides clearer separation of concerns:

  • petriNetDefault caches the active/default version
  • petriNetLatest caches the highest version number

This distinction supports the new versioning semantics introduced in the PR.


61-66: LGTM! Cache set construction updated correctly.

The getAllCaches() method correctly includes both new cache names (petriNetDefault and petriNetLatest) in place of the removed petriNetNewest.

application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (9)

140-154: LGTM! Cache management correctly updated for new cache structure.

Both evictAllCaches and evictCache have been properly updated:

  • Clear/evict from petriNetDefault and petriNetLatest caches
  • Removed references to the old petriNetNewest
  • Maintained null-safety with requireNonNull

181-194: LGTM! @transactional annotations ensure import atomicity.

Adding @Transactional to all importPetriNet overloads ensures that version checking, default version updates, and persistence operations are atomic. This reduces (though doesn't fully eliminate without database constraints) race condition risks during concurrent imports.


194-224: LGTM! Import flow refactored for clarity.

The import flow has been improved:

  • Version handling logic extracted to checkAndHandleProcessVersion
  • Clear separation between validation, preparation, and persistence phases
  • Properly saves both the new process and any inactivated previous default
  • Uses new doSaveInternal for consistent persistence/indexing

226-280: Comprehensive version handling logic correctly implemented.

The checkAndHandleProcessVersion method implements the version activation requirements:

Duplicate detection (lines 249-252): Throws exception if version already exists
Version initialization (lines 255-263):

  • New identifier → initialize to 1.0.0
  • Existing identifier with no version → increment from latest
  • Explicit version → use as-is and determine if highest

Default version management (lines 264-273):

  • If new version is highest, deactivate current default (either latest or explicitly default)
  • If new version is lower, don't change existing default

Activation (lines 275-277): Only highest version becomes default

The logic correctly handles the requirement stated in past review: "uploading also lower versions must be allowed (patching older version); uploaded process becomes active only if the version is highest."


282-299: LGTM! Centralized persistence logic with improved return type.

The refactoring introduces:

  • doSaveInternal: Protected final method that centralizes persistence and indexing logic
  • Updated save signature: Now returns Optional<PetriNet> (breaking change, but appropriate given potential failures)
  • Consistent usage: Both public save and internal import flow use doSaveInternal

The final modifier on doSaveInternal ensures consistent persistence behavior across the service.


314-324: Good defensive null guard added.

Lines 315-317 add a null check that prevents NullPointerException if either identifier or version is null. This is consistent with the method's contract of returning null when the PetriNet cannot be found.


339-359: LGTM! New version retrieval methods correctly implemented.

Both methods provide clear semantics:

getDefaultVersionByIdentifier (lines 339-345):

  • Retrieves the process marked as default (via defaultVersion flag)
  • Cached in petriNetDefault
  • Requires new repository method findByIdentifierAndDefaultVersion

getLatestVersionByIdentifier (lines 347-359):

  • Retrieves the highest version by sorting (DESC on major, minor, patch)
  • Cached in petriNetLatest
  • Efficiently uses PageRequest.of(0, 1, ...) to fetch only the top result

Both include null guards and appropriate caching strategies.


607-634: Excellent default version management on deletion.

Lines 624-632 implement critical logic:

  • When the default version is deleted, the system automatically promotes the latest remaining version to default
  • Prevents "orphaned" processes where no default exists despite having versions
  • Proper logging helps with debugging and auditing

This behavior is validated by the testVersionActiveOnDelete test and ensures consistent system state.


489-492: LGTM! Reference retrieval correctly uses default version.

Line 490 ensures that when no specific version is requested (version == null), the method returns a reference to the default version rather than an arbitrary version. This aligns with user expectations.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jan 8, 2026
- use ResourceNotFoundException
- add paged result in PetriNetRepository.findByIdentifierAndDefaultVersion
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: 3

🤖 Fix all issues with AI agents
In
@application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:
- Around line 339-365: getDefaultVersionByIdentifier silently returns the first
match if multiple entities have defaultVersion=true; update the method (around
repository.findByIdentifierAndDefaultVersion and the getFirst() return path) to
check if the returned List has size > 1 and log a warning (e.g., logger.warn)
including the identifier and the number of defaults found to surface
data-integrity issues; ensure the logging call uses the class logger (LOGGER or
existing logger) and keep the original return behaviour (still return
result.getFirst()).
- Around line 198-224: The call to doSaveInternal(newProcess) currently discards
the returned persisted instance, then outcome.setNet(...) uses the pre-save
importedProcess; instead capture the return Optional from doSaveInternal (e.g.,
Optional<PetriNet> savedProcess = doSaveInternal(newProcess)) and then set
outcome.setNet(savedProcess.orElseThrow()) so the outcome contains the
DB-persisted PetriNet with assigned IDs/timestamps; keep running post-upload
actions as-is but replace the direct use of importedProcess/newProcess for
outcome.setNet with the savedProcess result.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb377cc and 8028b98.

📒 Files selected for processing (6)
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
  • application-engine/src/test/resources/change_caseref_value_action_test.xml
  • application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml
🧰 Additional context used
🧠 Learnings (12)
📓 Common learnings
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.
📚 Learning: 2025-12-12T12:36:06.651Z
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".

Applied to files:

  • application-engine/src/test/resources/change_caseref_value_action_test.xml
  • application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
📚 Learning: 2025-12-19T09:06:38.316Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 398
File: application-engine/src/main/java/com/netgrif/application/engine/importer/service/Importer.java:57-58
Timestamp: 2025-12-19T09:06:38.316Z
Learning: In the file `application-engine/src/main/java/com/netgrif/application/engine/importer/service/Importer.java`, the `definition` attribute of the `Action` class (from `com.netgrif.application.engine.objects.petrinet.domain.dataset.logic.action.Action`) is never null, so null checks for `action.getDefinition()` are not needed.

Applied to files:

  • application-engine/src/test/resources/change_caseref_value_action_test.xml
  • application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.

Applied to files:

  • application-engine/src/test/resources/change_caseref_value_action_test.xml
  • application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
📚 Learning: 2025-09-05T10:21:54.893Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 350
File: application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy:135-135
Timestamp: 2025-09-05T10:21:54.893Z
Learning: In ExportServiceTest.groovy, writing to src/test/resources is intentional to simulate production behavior where the working tree is mutated during file exports. This mirrors how the system works in production.

Applied to files:

  • application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml
📚 Learning: 2025-07-29T17:19:18.300Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 331
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java:45-46
Timestamp: 2025-07-29T17:19:18.300Z
Learning: In ElasticPetriNetService class, petriNetService is properly initialized using Lazy setter injection rather than constructor injection. This pattern with Lazy Autowired setter methods is commonly used in Spring to resolve circular dependencies and is a valid alternative to constructor injection.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
📚 Learning: 2025-09-29T10:31:57.325Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 362
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java:513-529
Timestamp: 2025-09-29T10:31:57.325Z
Learning: PetriNet.getStringId() returns a simple ObjectId string representation (_id.toString()), not a composite Netgrif ID format, so new ObjectId(petriNetId) works correctly when petriNetId comes from PetriNet.getStringId().

Applied to files:

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

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
📚 Learning: 2025-09-29T10:31:31.469Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 362
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/web/ProcessRoleController.java:43-45
Timestamp: 2025-09-29T10:31:31.469Z
Learning: In the Netgrif application engine, when Authentication objects reach controller endpoints, auth.getPrincipal() always returns a LoggedUser instance and is never null, so defensive casting checks are not necessary in controller methods.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
📚 Learning: 2025-12-12T13:40:00.786Z
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.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java
  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
📚 Learning: 2025-06-23T13:30:13.096Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 318
File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java:60-62
Timestamp: 2025-06-23T13:30:13.096Z
Learning: In MenuItemConstants enum in nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/menu/MenuItemConstants.java, the field `attributeId` will be renamed to `value` to make it more generic and appropriate for both dataset attribute identifiers and technical constants like PATH_SEPARATOR.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
📚 Learning: 2025-11-14T10:22:01.634Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 383
File: application-engine/src/main/java/com/netgrif/application/engine/startup/ApplicationRunnerOrderResolver.java:43-43
Timestamp: 2025-11-14T10:22:01.634Z
Learning: For the netgrif/application-engine repository, avoid flagging trivial or nitpick-level issues such as redundant null checks, minor code style improvements, or obvious simplifications that don't affect functionality or introduce bugs. Focus review comments on substantive issues like logic errors, security concerns, performance problems, or breaking changes.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java
🧬 Code graph analysis (1)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/domain/ActorTransformer.java (1)
  • ActorTransformer (10-131)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: Build
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (11)
application-engine/src/test/resources/change_caseref_value_action_test.xml (1)

32-32: LGTM! API migration aligns with PR objectives.

The replacement of getNewestVersionByIdentifier with getDefaultVersionByIdentifier is consistent with this PR's introduction of the active/default version concept. Both changes correctly update the test resource to use the new API while preserving the test's intent.

Also applies to: 46-46

application-engine/src/test/resources/petriNets/NAE-1290_Export_actions.xml (1)

219-224: Change correctly implements active-version semantics and is safe.

The update from getNewestVersionByIdentifier to getDefaultVersionByIdentifier properly aligns with the PR's shift to default-version support. The test setup in ExportServiceTest.groovy (@beforeeach, line 69) successfully imports and establishes "export_test" as the default version before action_2 executes, so the direct call to .stringId is safe.

application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardManagementServiceImpl.java (1)

70-74: LGTM! Consistent migration to default version semantics.

The change from getNewestVersionByIdentifier to getDefaultVersionByIdentifier with explicit null-checking and ResourceNotFoundException aligns with the PR's objective to use an active/default version instead of newest. The error message is clear and the exception code is appropriate.

application-engine/src/main/java/com/netgrif/application/engine/menu/services/DashboardItemServiceImpl.java (1)

68-72: LGTM! Consistent migration to default version semantics.

The change from getNewestVersionByIdentifier to getDefaultVersionByIdentifier with explicit null-checking and ResourceNotFoundException aligns with the PR's objective. The pattern is consistent with other services in this PR.

application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java (1)

36-44: LGTM! Well-documented repository method.

The new findByIdentifierAndDefaultVersion method follows Spring Data naming conventions and includes comprehensive Javadoc. The paginated return type is appropriate for potentially large result sets.

application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (6)

142-143: LGTM! Cache eviction updated consistently.

The cache eviction logic has been correctly updated to use petriNetDefault and petriNetLatest instead of the deprecated petriNetNewest. Both evictAllCaches and evictCache methods are updated consistently.

Also applies to: 150-151


181-181: LGTM! Critical transactional boundary.

Adding @Transactional to all importPetriNet overloads is essential for maintaining data consistency. The import flow now involves updating the previous default version (making it non-default) and saving the new default version, which must happen atomically to prevent race conditions where multiple processes could be marked as default.

Also applies to: 187-187, 193-193


315-317: LGTM! Prevents cache key generation errors.

Adding null checks for identifier and version is essential because the method is annotated with @Cacheable using a key expression #identifier+#version.toString(). Without these checks, null inputs would cause NPE during cache key generation.


496-496: LGTM! Consistent with default version semantics.

The change from getNewestVersionByIdentifier to getDefaultVersionByIdentifier when version is null aligns with the PR's objective to use explicit default versions instead of newest.


283-299: No breaking change detected.

The save() method already returns Optional<PetriNet> as defined in the IPetriNetService interface; this is not a change from void. The refactoring simply delegates the public save() method to a protected final doSaveInternal() method, separating the public interface from the internal implementation—a reasonable design pattern.

The protected final modifier on doSaveInternal() appropriately prevents subclasses from overriding critical persistence logic.

Likely an incorrect or invalid review comment.


226-280: The version management logic in checkAndHandleProcessVersion is correctly implemented. The caching and transaction isolation are handled appropriately:

  • The @Cacheable methods (getLatestVersionByIdentifier and getDefaultVersionByIdentifier) are called within the @Transactional boundary and provide the initial state
  • In-memory modifications are made to objects (via makeDefault()/makeNonDefault())
  • Cache eviction occurs in doSaveInternal before repository.save(), which is the correct pattern
  • The @Transactional decorator ensures database-level isolation for the entire import operation
  • Subsequent cache accesses will fetch fresh data after persistence

No transaction isolation or cache consistency issues are present. The implementation follows standard Spring caching best practices.

Comment on lines +339 to 365
@Cacheable(value = "petriNetDefault", unless = "#result == null")
public PetriNet getDefaultVersionByIdentifier(String identifier) {
if (identifier == null) {
return null;
}
return nets.getFirst();

List<PetriNet> result = repository.findByIdentifierAndDefaultVersion(identifier, true, PageRequest.of(0, 1)).getContent();
if (!result.isEmpty()) {
return result.getFirst();
}

return null;
}

@Override
@Cacheable(value = "petriNetLatest", unless = "#result == null")
public PetriNet getLatestVersionByIdentifier(String identifier) {
if (identifier == null) {
return null;
}
List<PetriNet> processes = repository.findByIdentifier(identifier, PageRequest.of(0, 1,
Sort.Direction.DESC, "version.major", "version.minor", "version.patch")).getContent();
if (processes.isEmpty()) {
return null;
}
return processes.getFirst();
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider logging warning if multiple default versions found.

Both methods implement correct version lookup logic:

  • getDefaultVersionByIdentifier: Queries by defaultVersion=true flag
  • getLatestVersionByIdentifier: Sorts by version numbers descending

However, getDefaultVersionByIdentifier uses PageRequest.of(0, 1) and getFirst() (lines 345-347). If a data integrity issue causes multiple processes with the same identifier to have defaultVersion=true, this silently returns one of them. Consider logging a warning if the query returns more than one result to detect such integrity violations.

🔍 Proposed logging enhancement
-        List<PetriNet> result = repository.findByIdentifierAndDefaultVersion(identifier, true, PageRequest.of(0, 1)).getContent();
+        Page<PetriNet> page = repository.findByIdentifierAndDefaultVersion(identifier, true, PageRequest.of(0, 2));
+        List<PetriNet> result = page.getContent();
+        if (result.size() > 1) {
+            log.warn("Data integrity issue: Multiple default versions found for identifier [{}]", identifier);
+        }
         if (!result.isEmpty()) {
             return result.getFirst();
         }
🤖 Prompt for AI Agents
In
@application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
around lines 339 - 365, getDefaultVersionByIdentifier silently returns the first
match if multiple entities have defaultVersion=true; update the method (around
repository.findByIdentifierAndDefaultVersion and the getFirst() return path) to
check if the returned List has size > 1 and log a warning (e.g., logger.warn)
including the identifier and the number of defaults found to surface
data-integrity issues; ensure the logging call uses the class logger (LOGGER or
existing logger) and keep the original return behaviour (still return
result.getFirst()).

Comment on lines +630 to +638
if (petriNet.isDefaultVersion()) {
PetriNet processToMakeDefault = self.getLatestVersionByIdentifier(petriNet.getIdentifier());
if (processToMakeDefault != null) {
log.debug("The default version was removed. Making the latest version of the process [{}] with id [{}] as default...",
processToMakeDefault.getIdentifier(), processToMakeDefault.getStringId());
processToMakeDefault.makeDefault();
save(processToMakeDefault);
}
}
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 | 🟡 Minor

Correct default version succession logic; minor race condition.

The logic correctly ensures that when the default version is deleted, the latest remaining version becomes the new default. The cache eviction on line 628 happens before the lookup on line 631, which is the correct order.

However, there's a minor race condition: between the cache eviction (line 628) and the lookup (line 631), another thread could populate the cache with the just-deleted version. While unlikely and low-impact (would self-correct on next cache eviction), consider using a more aggressive eviction strategy for this critical path.

🔒 Optional: Reduce race condition window
         repository.deleteBy_id(petriNet.getObjectId());
         evictCache(petriNet);
+        // Aggressively clear all version caches for this identifier to minimize stale data
+        evictAllCaches();
         functionCacheService.reloadCachedFunctions(petriNet);

Note: This trades performance for correctness. The race condition is minor, so this may not be necessary.

Committable suggestion skipped: line range outside the PR's diff.

- fix outcome consistency in PetriNetService
renczesstefan
renczesstefan previously approved these changes Jan 8, 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: 1

🤖 Fix all issues with AI agents
In
@application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:
- Around line 226-280: The method checkAndHandleProcessVersion does an
unnecessary fetch of existingActiveProcess via getDefaultVersionByIdentifier
when existingLatestProcess is already the default; avoid the redundant DB call
by reusing existingLatestProcess: when makeNonDefaultCurrentVersion is true and
existingLatestProcess != null, check existingLatestProcess.isDefaultVersion()
first and handle makeNonDefault on it and set processToMakeNonDefault to
existingLatestProcess, otherwise only then call getDefaultVersionByIdentifier to
fetch and make that one non-default; update control flow around
existingLatestProcess and existingActiveProcess to prevent the extra fetch.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8028b98 and 59299c5.

📒 Files selected for processing (2)
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/domain/repositories/PetriNetRepository.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
📚 Learning: 2025-07-29T17:19:18.300Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 331
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java:45-46
Timestamp: 2025-07-29T17:19:18.300Z
Learning: In ElasticPetriNetService class, petriNetService is properly initialized using Lazy setter injection rather than constructor injection. This pattern with Lazy Autowired setter methods is commonly used in Spring to resolve circular dependencies and is a valid alternative to constructor injection.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
📚 Learning: 2025-09-29T10:31:57.325Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 362
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/ProcessRoleService.java:513-529
Timestamp: 2025-09-29T10:31:57.325Z
Learning: PetriNet.getStringId() returns a simple ObjectId string representation (_id.toString()), not a composite Netgrif ID format, so new ObjectId(petriNetId) works correctly when petriNetId comes from PetriNet.getStringId().

Applied to files:

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

36-44: LGTM! Well-designed repository method.

The new findByIdentifierAndDefaultVersion method follows Spring Data naming conventions correctly and provides the necessary pagination support for retrieving processes by their default version status. The Javadoc is comprehensive and clearly explains the method's purpose.

application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (7)

142-143: Cache management correctly updated for default/latest semantics.

The cache eviction logic properly reflects the shift from "newest" to separate "default" and "latest" caches, ensuring cache consistency across the new version resolution strategy.

Also applies to: 150-151


181-181: Appropriate use of @transactional for data consistency.

Adding @Transactional to all import overloads ensures atomicity when updating multiple PetriNet entities (e.g., making one non-default while activating another), which is critical for maintaining default version integrity.

Also applies to: 187-187, 193-193


315-317: Good defensive programming with null guards.

Adding null checks for both identifier and version parameters prevents potential NullPointerExceptions and provides clear, predictable behavior when invalid arguments are passed.


339-351: Well-implemented default version retrieval with proper caching.

The method correctly uses the new repository method with efficient pagination (fetching only 1 result) and proper null handling. The cache key strategy (identifier only) is appropriate for default version lookups.


630-638: Critical logic correctly maintains default version invariant.

The deletion logic properly handles the case where the default version is deleted by promoting the latest remaining version to default. This ensures the system invariant (exactly one default version per identifier) is maintained. The null check safely handles the case where no other versions exist.


353-365: The sorting by "version.major", "version.minor", "version.patch" in descending order works correctly. These fields are declared as long primitive types in the Version class, so MongoDB stores them as numeric types and sorts them numerically, not lexicographically. The concern about string sorting issues does not apply here.

Likely an incorrect or invalid review comment.


283-299: The refactoring is safe. There are no subclasses of PetriNetService in the codebase, and no other implementations of IPetriNetService. The save() method itself is not final and can still be overridden if needed. The protected final modifier on doSaveInternal is appropriate—it's an internal method designed to prevent external override while maintaining encapsulation.

Likely an incorrect or invalid review comment.

Comment on lines +226 to 280
/**
* Validates the version of an importing process. This method can update 'newProces': initialize version,
* initialize default version attribute. This method can also update the current default version of the process if needed.
*
* <h4>Version initialization logic</h4>
* <ul>
* <li>Uploaded process becomes default only if its version is the highest</li>
* <li>Only one process can be default. If the importing process is about to get default, the currently default
* process becomes non-default</li>
* <li>If no version is provided, it's initialized to 1.0.0 or by the existing highest version incremented
* by 'releaseType' input parameter</li>
* </ul>
*
* @param newProcess A process to be checked and updated
* @param releaseType requested release type level. It's used for version initialization
*
* @return The process, which has been made non-default or null if no process updated
*
* @throws IllegalArgumentException if the version already exists
* */
private PetriNet checkAndHandleProcessVersion(PetriNet newProcess, VersionType releaseType) {
PetriNet processToMakeNonDefault = null;

if (newProcess.getVersion() != null && self.getPetriNet(newProcess.getIdentifier(), newProcess.getVersion()) != null) {
throw new IllegalArgumentException("A process [%s] with such version [%s] already exists"
.formatted(newProcess.getIdentifier(), newProcess.getVersion()));
}
PetriNet existingLatestProcess = self.getLatestVersionByIdentifier(newProcess.getIdentifier());
boolean makeNonDefaultCurrentVersion = true;
if (existingLatestProcess == null && newProcess.getVersion() == null) {
newProcess.setVersion(new Version());
} else {
if (newProcess.getVersion() == null) {
newProcess.setVersion(existingLatestProcess.getVersion().clone());
newProcess.incrementVersion(releaseType);
} else if (existingLatestProcess != null && newProcess.getVersion().isLowerThan(existingLatestProcess.getVersion())) {
makeNonDefaultCurrentVersion = false;
}
if (makeNonDefaultCurrentVersion && existingLatestProcess != null && existingLatestProcess.isDefaultVersion()) {
existingLatestProcess.makeNonDefault();
processToMakeNonDefault = existingLatestProcess;
} else if (makeNonDefaultCurrentVersion) {
PetriNet existingActiveProcess = self.getDefaultVersionByIdentifier(newProcess.getIdentifier());
if (existingActiveProcess != null) {
existingActiveProcess.makeNonDefault();
processToMakeNonDefault = existingActiveProcess;
}
}
}
if (makeNonDefaultCurrentVersion) {
newProcess.makeDefault();
}

return processToMakeNonDefault;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider optimizing the default version lookup to avoid redundant fetches.

The logic correctly ensures only one default version exists, but there's a potential inefficiency: when existingLatestProcess is also the default version (lines 264-266), the code later fetches existingActiveProcess (line 268) which would be the same entity already in memory.

♻️ Optimization to avoid redundant database fetch
-        if (makeNonDefaultCurrentVersion && existingLatestProcess != null && existingLatestProcess.isDefaultVersion()) {
-            existingLatestProcess.makeNonDefault();
-            processToMakeNonDefault = existingLatestProcess;
-        } else if (makeNonDefaultCurrentVersion) {
-            PetriNet existingActiveProcess = self.getDefaultVersionByIdentifier(newProcess.getIdentifier());
-            if (existingActiveProcess != null) {
-                existingActiveProcess.makeNonDefault();
-                processToMakeNonDefault = existingActiveProcess;
-            }
-        }
+        if (makeNonDefaultCurrentVersion) {
+            if (existingLatestProcess != null && existingLatestProcess.isDefaultVersion()) {
+                existingLatestProcess.makeNonDefault();
+                processToMakeNonDefault = existingLatestProcess;
+            } else {
+                PetriNet existingActiveProcess = self.getDefaultVersionByIdentifier(newProcess.getIdentifier());
+                if (existingActiveProcess != null) {
+                    existingActiveProcess.makeNonDefault();
+                    processToMakeNonDefault = existingActiveProcess;
+                }
+            }
+        }

This avoids fetching existingActiveProcess when existingLatestProcess is already the default version.

🤖 Prompt for AI Agents
In
@application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
around lines 226 - 280, The method checkAndHandleProcessVersion does an
unnecessary fetch of existingActiveProcess via getDefaultVersionByIdentifier
when existingLatestProcess is already the default; avoid the redundant DB call
by reusing existingLatestProcess: when makeNonDefaultCurrentVersion is true and
existingLatestProcess != null, check existingLatestProcess.isDefaultVersion()
first and handle makeNonDefault on it and set processToMakeNonDefault to
existingLatestProcess, otherwise only then call getDefaultVersionByIdentifier to
fetch and make that one non-default; update control flow around
existingLatestProcess and existingActiveProcess to prevent the extra fetch.

tuplle
tuplle previously approved these changes Jan 8, 2026
void testVersionsOnImport() {
ImportPetriNetEventOutcome outcome = importProcess(VERSION_PROCESS_FILE_FORMAT.formatted("2"), superCreator.loggedSuper)
PetriNet petriNetV2 = outcome.getNet()
assert petriNetV2 != null
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For better readability of assertion error and test readability, it is better to use JUnit assertions, like assertNotNull, assertTrue, etc. (https://docs.junit.org/5.14.2/writing-tests/assertions.html)
It is also explained in this article: https://learn-it-university.com/the-ultimate-guide-to-choosing-between-assert-and-junit-assertions/

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.

thanks, I updated the tests

- update test to use assert methods
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: 4

🤖 Fix all issues with AI agents
In
@application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy:
- Line 161: Replace identity comparisons using '==' between Version instances
with value equality checks: change assertions like
assertTrue(petriNetV2.getVersion() == version) to use assertEquals(version,
petriNetV2.getVersion()) or assertTrue(version.equals(petriNetV2.getVersion())).
Apply the same change for the other assertions that compare Version instances
(the ones using petriNetV2.getVersion() and other Version vars) so all version
comparisons use .equals() or assertEquals rather than '=='.
- Line 184: Replace the inconsistent assertion assertTrue(petriNetV1 != null)
with assertNotNull(petriNetV1) to match the other checks (lines using
assertNotNull) and improve readability; update the test that references
petriNetV1 by changing the assertion method from assertTrue to assertNotNull.
- Around line 162-163: Replace the brittle Thread.sleep(5000) calls with a
polling helper that repeatedly calls
elasticPetriNetRepository.findById(petriNetV2.stringId) until present or a
timeout elapses; implement a private waitForElasticSync(String id, long
timeoutMs) that polls (e.g. 100ms intervals) and throws an AssertionError if
timeout is reached, then replace the sleep usages (including the other
occurrences around lines 175-176 and 212-213) with calls to
waitForElasticSync(petriNetV2.stringId).
- Line 103: The tests processImportAndDelete and processSearch are annotated
@Disabled without explanation; either update the test implementations in
PetriNetServiceTest to align with the new default-version semantics and remove
@Disabled from the methods processImportAndDelete and processSearch so they run,
or keep them disabled but add an inline comment and a TODO referencing a
tracking issue/PR that explains why they are incompatible with the new behavior
and what must change to re-enable them; ensure you reference the exact test
method names (processImportAndDelete, processSearch) when adding the comment or
issue link so future reviewers can find and restore them.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59299c5 and f5ca987.

📒 Files selected for processing (1)
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-09-05T10:21:54.893Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 350
File: application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy:135-135
Timestamp: 2025-09-05T10:21:54.893Z
Learning: In ExportServiceTest.groovy, writing to src/test/resources is intentional to simulate production behavior where the working tree is mutated during file exports. This mirrors how the system works in production.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-11-14T10:22:01.634Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 383
File: application-engine/src/main/java/com/netgrif/application/engine/startup/ApplicationRunnerOrderResolver.java:43-43
Timestamp: 2025-11-14T10:22:01.634Z
Learning: For the netgrif/application-engine repository, avoid flagging trivial or nitpick-level issues such as redundant null checks, minor code style improvements, or obvious simplifications that don't affect functionality or introduce bugs. Focus review comments on substantive issues like logic errors, security concerns, performance problems, or breaking changes.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-12-12T13:40:00.786Z
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.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-07-29T17:19:18.300Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 331
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java:45-46
Timestamp: 2025-07-29T17:19:18.300Z
Learning: In ElasticPetriNetService class, petriNetService is properly initialized using Lazy setter injection rather than constructor injection. This pattern with Lazy Autowired setter methods is commonly used in Spring to resolve circular dependencies and is a valid alternative to constructor injection.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-12-12T12:37:58.852Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/test/groovy/com/netgrif/application/engine/auth/WorkflowAuthorizationServiceTest.groovy:4-4
Timestamp: 2025-12-12T12:37:58.852Z
Learning: In tests under application-engine/src/test, GroupService.getDefaultSystemGroup() lazily creates the default system group if it doesn't exist. Tests can rely on this behavior and omit explicit existence/setup checks for the default system group.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-12-12T12:40:25.863Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy:18-20
Timestamp: 2025-12-12T12:40:25.863Z
Learning: In Groovy/Java, if a parameter or collection expects ActorFieldValue, you can pass UserFieldValue (a subclass) due to polymorphism. In tests, verify that ActorListFieldValue accepts ActorFieldValue instances and that passing UserFieldValue instances is valid without casts. This pattern applies to similar Groovy files across the repository.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (5)
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy (5)

4-4: LGTM: Import reorganization supports new versioning tests.

The added imports (wildcard auth.domain, ElasticPetriNet, repositories, and static assertions) are appropriate for the new version management test cases.

Also applies to: 8-10, 16-18, 37-40


48-48: LGTM: Well-named constant for versioned test files.

The format string clearly indicates its purpose and is used consistently in the new version tests.


223-249: LGTM: Solid test coverage for default version cascade on deletion.

The test correctly validates that when the default version is deleted, the system automatically promotes another version to default. The logic flow (delete non-default, delete default, verify cascade) is well-structured.


258-259: LGTM: Consistent use of importProcess helper.

Refactoring to use the new importProcess helper improves test maintainability.


312-314: LGTM: Helper method reduces duplication.

The importProcess helper centralizes the import logic with VersionType.MAJOR, making tests more maintainable and consistent.

assertTrue(petriNetV2.defaultVersion)
Version version = new Version()
version.setMajor(2)
assertTrue(petriNetV2.getVersion() == version)
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 | 🟠 Major

Fix Version equality comparisons.

Using == to compare Version objects checks reference equality, not value equality. In Java/Groovy, this may not work as intended if the objects are different instances with the same version numbers.

🐛 Use assertEquals or .equals() for value comparison

Replace:

-assertTrue(petriNetV2.getVersion() == version)
+assertEquals(version, petriNetV2.getVersion())

Apply similar changes at lines 174, 189, and 211.

Also applies to: 174-174, 189-189, 211-211

🤖 Prompt for AI Agents
In
@application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
at line 161, Replace identity comparisons using '==' between Version instances
with value equality checks: change assertions like
assertTrue(petriNetV2.getVersion() == version) to use assertEquals(version,
petriNetV2.getVersion()) or assertTrue(version.equals(petriNetV2.getVersion())).
Apply the same change for the other assertions that compare Version instances
(the ones using petriNetV2.getVersion() and other Version vars) so all version
comparisons use .equals() or assertEquals rather than '=='.

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: 3

🤖 Fix all issues with AI agents
In
@application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy:
- Line 252: The test method processSearch in PetriNetServiceTest is annotated
with @Disabled; either remove the @Disabled annotation to enable the test now
(so its 9 search scenarios run) or add a clear comment/Javadoc on the test
method explaining the specific blocker (e.g., awaiting NAE-2266 "Introduce
active version of process") and reference the issue id, and if temporarily
disabled wrap it with a conditional/assumption that documents the expected
state; update the test method name processSearch and its annotation accordingly
so the intent is explicit.
- Line 103: The disabled test processImportAndDelete is no longer aligned with
the new version-focused tests; either remove the test entirely or update it to
reflect version semantics: if keeping it, rewrite processImportAndDelete to
import a process using the current version-aware importer (use the same import
helper used by testVersionsOnImport), then assert expected behavior under
versioning (verify created process version, createCase against the imported
process, verify process role creation and user role assignment using the
versioned process identifier, and assert delete behavior using the version-aware
delete flow similar to testVersionActiveOnDelete); if removing, delete the
processImportAndDelete test method and its @Disabled annotation and adjust any
shared fixtures referenced only by that test.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f5ca987 and 0743259.

📒 Files selected for processing (1)
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
🧰 Additional context used
🧠 Learnings (9)
📓 Common learnings
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 334
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java:204-214
Timestamp: 2025-07-31T23:40:46.499Z
Learning: In the PetriNetService.importPetriNet method, existingNet.getVersion() cannot be null because all existing nets in the system were deployed through processes that ensure every net always has a version assigned.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-09-05T10:21:54.893Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 350
File: application-engine/src/test/groovy/com/netgrif/application/engine/export/service/ExportServiceTest.groovy:135-135
Timestamp: 2025-09-05T10:21:54.893Z
Learning: In ExportServiceTest.groovy, writing to src/test/resources is intentional to simulate production behavior where the working tree is mutated during file exports. This mirrors how the system works in production.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-12-12T12:36:06.651Z
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".

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-11-14T10:22:01.634Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 383
File: application-engine/src/main/java/com/netgrif/application/engine/startup/ApplicationRunnerOrderResolver.java:43-43
Timestamp: 2025-11-14T10:22:01.634Z
Learning: For the netgrif/application-engine repository, avoid flagging trivial or nitpick-level issues such as redundant null checks, minor code style improvements, or obvious simplifications that don't affect functionality or introduce bugs. Focus review comments on substantive issues like logic errors, security concerns, performance problems, or breaking changes.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-12-12T13:40:00.786Z
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.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-07-29T17:19:18.300Z
Learnt from: tuplle
Repo: netgrif/application-engine PR: 331
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java:45-46
Timestamp: 2025-07-29T17:19:18.300Z
Learning: In ElasticPetriNetService class, petriNetService is properly initialized using Lazy setter injection rather than constructor injection. This pattern with Lazy Autowired setter methods is commonly used in Spring to resolve circular dependencies and is a valid alternative to constructor injection.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-12-12T12:37:58.852Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/test/groovy/com/netgrif/application/engine/auth/WorkflowAuthorizationServiceTest.groovy:4-4
Timestamp: 2025-12-12T12:37:58.852Z
Learning: In tests under application-engine/src/test, GroupService.getDefaultSystemGroup() lazily creates the default system group if it doesn't exist. Tests can rely on this behavior and omit explicit existence/setup checks for the default system group.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-12-12T12:40:25.863Z
Learnt from: Retoocs
Repo: netgrif/application-engine PR: 397
File: application-engine/src/test/groovy/com/netgrif/application/engine/impersonation/ImpersonationServiceTest.groovy:18-20
Timestamp: 2025-12-12T12:40:25.863Z
Learning: In Groovy/Java, if a parameter or collection expects ActorFieldValue, you can pass UserFieldValue (a subclass) due to polymorphism. In tests, verify that ActorListFieldValue accepts ActorFieldValue instances and that passing UserFieldValue instances is valid without casts. This pattern applies to similar Groovy files across the repository.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (2)
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy (2)

312-314: LGTM! Good refactoring to reduce duplication.

The helper method simplifies test code and provides a consistent way to import processes with sensible defaults.


223-249: Verify auto-promotion logic when default version is deleted.

The test assumes that when the default version (v3) is deleted, the system automatically promotes the oldest remaining version (v1) to be the new default (line 244). This critical business logic must be explicitly verified in PetriNetService.deletePetriNet:

  1. Does the deletion logic include code to promote another version to default when the current default is deleted?
  2. What criteria determine which version becomes the new default (oldest remaining, newest remaining, creation order)?
  3. Is this auto-promotion behavior intentional and properly implemented, or does it rely on implicit ordering side effects?

Without verification of the implementation, the test's expectations cannot be confirmed.

machacjozef and others added 2 commits January 12, 2026 18:50
- Introduced a new `defaultVersion` Boolean property to the `PetriNetSearch` class to enhance extensibility and enable version-specific searches.
# Conflicts:
#	nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java
@machacjozef machacjozef requested review from renczesstefan and tuplle and removed request for tuplle January 13, 2026 13:43
@machacjozef machacjozef merged commit 12d77e1 into release/7.0.0-rev9 Jan 13, 2026
6 of 7 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jan 13, 2026
18 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Apr 23, 2026
18 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking change Fix or feature that would cause existing functionality doesn't work as expected bugfix A change that fixes a bug improvement A change that improves on an existing feature Large new feature A change that introduces new functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants