Skip to content

Conversation

@dominikvozr
Copy link

@dominikvozr dominikvozr commented Sep 2, 2025

Description

This PR introduces a GenericMapCache implementation of Spring’s Cache interface, backed by in-memory Maps. The solution replaces ad-hoc local maps with proper cache wrappers that integrate with Spring Boot Actuator’s /caches endpoints.

This makes all cache operations consistent and manageable via Actuator.

Implements NAE-2182

Blocking PRs

This PR depends on NAE-2212/PR #363

How Has Been This Tested?

The functionality was tested using Postman to verify cache operations through the Actuator endpoints.

Name Tested on
OS Windows 11
Runtime Java 21
Dependency Manager Apache Maven 3.9.9
Framework version Spring Boot 3.2.5, NAE 7.0.0-rev7
Run parameters mvn clean install
Other configuration

Checklist:

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

Summary by CodeRabbit

  • New Features

    • Dedicated, size‑configurable caches for actions, functions and global functions with on‑demand and batch population
    • New overloads to reload global functions by ID or by object; added retrieval/export of global functions cache
  • Bug Fixes

    • Safer, thread‑safe cache behavior with improved null handling and lazy loading
    • Automatic promotion of latest PetriNet version when default is deleted
    • Clearer error message when default PetriNet version is missing
  • Chores

    • Centralized cache configuration and naming; removed legacy runner cache settings
    • Expanded management endpoint exposure
  • Documentation

    • Minor docs whitespace cleanup

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

@coderabbitai
Copy link

coderabbitai bot commented Sep 2, 2025

Warning

Rate limit exceeded

@renczesstefan has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 17 minutes and 53 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 07aa67f and 64e0c8b.

📒 Files selected for processing (1)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java

Walkthrough

Reworks caching infrastructure: adds typed GenericMapCache and CacheMapKeys; CacheConfiguration now implements CachingConfigurer and registers configured caches (including ACTIONS, FUNCTIONS, GLOBAL_FUNCTIONS) into a SimpleCacheManager; FieldActionsCacheService now uses CacheManager-backed caches and adds global-function reload/precache APIs; cache sizing properties added; PetriNet deletion promotes latest version when removing the default.

Changes

Cohort / File(s) Summary
Cache configuration & primitives
application-engine/src/main/java/.../CacheConfiguration.java, application-engine/src/main/java/.../CacheMapKeys.java, application-engine/src/main/java/.../GenericMapCache.java
CacheConfiguration now implements CachingConfigurer with Lombok constructor, builds a SimpleCacheManager registering configured caches plus typed GenericMapCache entries for ACTIONS, FUNCTIONS, GLOBAL_FUNCTIONS. Adds CacheMapKeys constants and a new generic GenericMapCache<V,E> implementation with type enforcement and lazy loading.
Field actions cache service & interface
application-engine/src/main/java/.../FieldActionsCacheService.java, application-engine/src/main/java/.../interfaces/IFieldActionsCacheService.java
Replaces in-class maps with Spring CacheManager caches; constructor now takes CacheConfigurationProperties and CacheManager; adds `reloadCachedGlobalFunctions(String
Cache properties & runner config
application-engine/src/main/java/.../properties/CacheConfigurationProperties.java, application-engine/src/main/java/.../properties/RunnerConfigurationProperties.java
Adds validated integer properties: functionCachingPageSize, actionCacheSize, functionsCacheSize, globalFunctionsCacheSize; removes nested FieldRunnerProperties and its cache fields from RunnerConfigurationProperties. getAllCaches() updated to reflect changes.
PetriNet service & imports
application-engine/src/main/java/.../PetriNetService.java, application-engine/src/main/java/.../interfaces/IPetriNetService.java
deletePetriNet now calls reloadCachedGlobalFunctions(...) and, when deleting the default version, promotes the latest version to default (if present). Import cleanup/wildcarding adjusted.
Configuration & minor behavior text
application-engine/src/main/resources/application.yaml, application-engine/src/main/java/.../WorkflowService.java
Actuator exposure expanded (management.endpoints.web.exposure.include: "*") and a create-case error message adjusted to indicate missing default Petri net version.
Docs/formatting & small Javadoc
nae-object-library/src/main/java/.../Version.java, docs/process/functions.md
Minor Javadoc spacing and trailing-space cleanup.

Sequence Diagram(s)

sequenceDiagram
  actor Startup
  participant CacheConfiguration
  participant SimpleCacheManager
  participant FieldActionsCacheService
  participant PetriNetService

  Startup->>CacheConfiguration: initialize cacheManager bean
  CacheConfiguration->>SimpleCacheManager: register configured caches + add ACTIONS/FUNCTIONS/GLOBAL_FUNCTIONS (GenericMapCache)
  Startup->>FieldActionsCacheService: cacheAllPetriNetFunctions()
  FieldActionsCacheService->>PetriNetService: fetch paged PetriNets
  PetriNetService-->>FieldActionsCacheService: return PetriNet pages
  loop per PetriNet
    FieldActionsCacheService->>SimpleCacheManager: getCache("globalFunctions")
    SimpleCacheManager-->>FieldActionsCacheService: Cache instance
    FieldActionsCacheService->>FieldActionsCacheService: compute CachedFunction/Closure
    FieldActionsCacheService->>SimpleCacheManager: put(petriNetId, CachedFunction)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Title check ⚠️ Warning The title refers to a real aspect of the change (cache clearing), but the main point of the PR is introducing GenericMapCache and Spring Cache integration, not specifically clearing actions cache. Update the title to reflect the primary change, such as 'Introduce GenericMapCache for Spring Cache integration' or 'Migrate field action caches to Spring Cache interface'.
Docstring Coverage ⚠️ Warning Docstring coverage is 28.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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


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

❤️ Share

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (6)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)

3-7: Make utility holder non-instantiable and final.

Prevents accidental instantiation/extension and clarifies intent.

-public class CacheMapKeys {
+public final class CacheMapKeys {
     public static final String ACTIONS = "actionsCache";
     public static final String FUNCTIONS = "functionsCache";
     public static final String NAMESPACE_FUNCTIONS = "namespaceFunctionsCache";
+
+    private CacheMapKeys() {
+        throw new AssertionError("No instances");
+    }
 }
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)

22-24: Leaking the mutable Map via getNativeCache couples callers to internals.

FieldActionsCacheService manipulating the map directly bypasses cache invariants/locking.

Prefer cache APIs (get/put/evict/clear). If direct access is required, document it and ensure all such usage is confined and synchronized.

application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)

43-65: Confirm thread-safety of MaxSizeHashMap or wrap it.

If MaxSizeHashMap is not concurrent, concurrent access through the cache will race.

Options:

  • Wrap: () -> java.util.Collections.synchronizedMap(new MaxSizeHashMap<>(...))
  • Or rely on the synchronized GenericMapCache changes above.
  • Add a brief Javadoc on MaxSizeHashMap usage here clarifying concurrency expectations.
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (3)

142-143: Avoid raw cast in getNamespaceFunctionCache; ensure type-safety and null-safety.

Keeps method contract explicit and prevents CCE if the impl changes.

-        return new HashMap<>((Map) cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).getNativeCache());
+        Map<String, List<CachedFunction>> nativeMap =
+                (Map<String, List<CachedFunction>>) getRequiredCache(CacheMapKeys.NAMESPACE_FUNCTIONS).getNativeCache();
+        return new HashMap<>(nativeMap);

147-148: Clear methods: use guarded cache retrieval.

-        cacheManager.getCache(CacheMapKeys.ACTIONS).clear();
+        getRequiredCache(CacheMapKeys.ACTIONS).clear();
@@
-        cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).clear();
+        getRequiredCache(CacheMapKeys.NAMESPACE_FUNCTIONS).clear();
@@
-        cacheManager.getCache(CacheMapKeys.FUNCTIONS).clear();
+        getRequiredCache(CacheMapKeys.FUNCTIONS).clear();

Also applies to: 151-153, 156-158


74-84: Keep this service cache-implementation-agnostic; avoid getNativeCache() and instanceof Map.

Relying on the native Map couples you to GenericMapCache and its thread-safety characteristics. Using Cache APIs (get(key, Callable), evict, clear) preserves portability and simplifies reasoning.

Also applies to: 89-99, 142-143

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between b8abb83 and e917f76.

📒 Files selected for processing (5)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (4 hunks)
  • application-engine/src/main/resources/application.yaml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
  • CacheMapKeys (3-7)
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (2)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
  • CacheMapKeys (3-7)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
  • Slf4j (8-78)
⏰ 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). (9)
  • 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: Build
  • GitHub Check: task-list-completed
🔇 Additional comments (3)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (2)

67-70: CacheManager wiring LGTM.

Unified SimpleCacheManager with default and specialized caches is clean and extensible.


37-41: Exclude specialized cache names from default caches. Filter out CacheMapKeys.ACTIONS, CacheMapKeys.FUNCTIONS and CacheMapKeys.NAMESPACE_FUNCTIONS from the properties.getAllCaches() stream before mapping to ConcurrentMapCache to avoid registering those caches twice.

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

28-37: Verify FieldActionsCacheService bean registration
Confirm that FieldActionsCacheService is annotated (e.g. @service or @component) or declared via a @bean method so Spring will pick up the new constructor signature, and search your configuration for any @bean methods or manual instantiations of FieldActionsCacheService using the old two-arg constructor.

@coderabbitai coderabbitai bot added improvement A change that improves on an existing feature Large labels Sep 3, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 19

♻️ Duplicate comments (5)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)

15-15: Make map-backed cache thread-safe; compute-and-store must be atomic.

Backed by a plain Map with unsynchronized mutations; races with put/evict/clear and get(loader). Clear also reassigns the map mid-flight. Synchronize mutations and use double-checked locking in get(loader).

 public abstract class GenericMapCache<V> implements Cache {
@@
-    protected volatile Map<String, V> map;
+    protected volatile Map<String, V> map;
+    private final Object monitor = new Object();
@@
-    @Override
-    public <T> T get(Object key, java.util.concurrent.Callable<T> loader) {
+    @Override
+    public <T> T get(Object key, java.util.concurrent.Callable<T> loader) {
         String stringKey = String.valueOf(key);
-        Object present = map.get(stringKey);
+        Object present = map.get(stringKey);
 
         if (present != null) {
             return (T) present;
         }
 
-        try {
-            T computed = loader.call();
-            if (computed != null) {
-                map.put(stringKey, safeCast(computed));
-            }
-            return computed;
-        } catch (Exception ex) {
-            throw new org.springframework.cache.Cache.ValueRetrievalException(stringKey, loader, ex);
-        }
+        synchronized (monitor) {
+            present = map.get(stringKey);
+            if (present != null) {
+                return (T) present;
+            }
+            try {
+                T computed = loader.call();
+                if (computed != null) {
+                    map.put(stringKey, safeCast(computed));
+                }
+                return computed;
+            } catch (Exception ex) {
+                throw new org.springframework.cache.Cache.ValueRetrievalException(stringKey, loader, ex);
+            }
+        }
     }
@@
-    public void put(Object key, Object value) {
-        map.put(String.valueOf(key), safeCast(value));
+    public void put(Object key, Object value) {
+        synchronized (monitor) {
+            map.put(String.valueOf(key), safeCast(value));
+        }
     }
@@
-    public void evict(Object key) {
-        map.remove(String.valueOf(key));
+    public void evict(Object key) {
+        synchronized (monitor) {
+            map.remove(String.valueOf(key));
+        }
     }
@@
-    public void clear() {
-        this.map = mapFactory.get();
+    public void clear() {
+        synchronized (monitor) {
+            this.map = mapFactory.get();
+        }
         log.info("{} cache cleared", this.getName());
     }

Also applies to: 33-51, 53-67

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

55-62: Fail fast on missing cache; avoid NPE.

-        Cache namespaceFunctionsCache = cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS);
+        Cache namespaceFunctionsCache = getRequiredCache(CacheMapKeys.NAMESPACE_FUNCTIONS);
@@
-            namespaceFunctionsCache.evictIfPresent(petriNet.getIdentifier());
+            namespaceFunctionsCache.evictIfPresent(petriNet.getIdentifier());

Support code (add once in this class):

private Cache getRequiredCache(String name) {
    Cache cache = cacheManager.getCache(name);
    if (cache == null) throw new IllegalStateException("Cache '" + name + "' not configured");
    return cache;
}

65-69: Guard cache retrieval and reload atomically.

-    public void reloadCachedFunctions(String petriNetId) {
-        cacheManager.getCache(CacheMapKeys.NAMESPACE_FUNCTIONS).evictIfPresent(petriNetId);
-        cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNetId));
-    }
+    public void reloadCachedFunctions(String petriNetId) {
+        getRequiredCache(CacheMapKeys.NAMESPACE_FUNCTIONS).evictIfPresent(petriNetId);
+        cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNetId));
+    }

79-89: Use Cache APIs; avoid peeking into native Map and eliminate TOCTOU.

-        Cache actionsCache = cacheManager.getCache(CacheMapKeys.ACTIONS);
-        Object nativeActionsCache = actionsCache.getNativeCache();
-
-        if (nativeActionsCache instanceof Map<?, ?> map) {
-            if (shouldRewriteCachedActions || !map.containsKey(stringId) ) {
-                Closure code = (Closure) shell.evaluate("{-> " + action.getDefinition() + "}");
-                actionsCache.put(stringId, code);
-            }
-        }
-        return (Closure) actionsCache.get(stringId).get();
+        Cache actionsCache = getRequiredCache(CacheMapKeys.ACTIONS);
+        if (shouldRewriteCachedActions) {
+            actionsCache.evict(stringId);
+        }
+        return actionsCache.get(stringId,
+                () -> (Closure) shell.evaluate("{-> " + action.getDefinition() + "}"));

93-105: Compute functions atomically and avoid native cache access.

-        Cache functionsCache = cacheManager.getCache(CacheMapKeys.FUNCTIONS);
-        Object nativeFunctionsCache = functionsCache.getNativeCache();
-
-        if (nativeFunctionsCache instanceof Map<?, ?> map) {
-            functions.forEach(function -> {
-                if (!map.containsKey(function.getStringId())) {
-                    functionsCache.put(function.getStringId(), CachedFunction.build(shell, function));
-                }
-                cachedFunctions.add((CachedFunction) functionsCache.get(function.getStringId()).get());
-            });
-        }
+        Cache functionsCache = getRequiredCache(CacheMapKeys.FUNCTIONS);
+        for (Function function : functions) {
+            CachedFunction cf = functionsCache.get(function.getStringId(),
+                    () -> CachedFunction.build(shell, function));
+            cachedFunctions.add(cf);
+        }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e917f76 and bd719b5.

📒 Files selected for processing (9)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (3 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java (2 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (4 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
PR: netgrif/application-engine#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
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
🧬 Code graph analysis (3)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (2)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
  • Slf4j (10-80)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
  • Slf4j (66-581)
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (4)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
  • CacheMapKeys (3-7)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)
  • Slf4j (10-41)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
  • Slf4j (10-80)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
  • Slf4j (66-581)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)
  • Slf4j (10-41)
⏰ 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 (6)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java (1)

4-4: No action on wildcard import.

Acceptable here unless the project enforces “no wildcard imports”.

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

8-8: No action on wildcard import.

Safe refactor.


558-569: Add index on functions._id in petriNet collection
The query in PetriNetService.findByFunctionId filters on functions._id; without an index this will trigger a full collection scan. Verify (via @indexed on Function.objectId, a database migration, or manual index setup) that an index exists for functions._id.

application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (1)

15-17: Ignore namespace vs PetriNet ID key concern

All cache operations on NAMESPACE_FUNCTIONS use petriNet.getIdentifier() as the key (see FieldActionsCacheService.java:55 and 67), so the cache is intentionally keyed by PetriNet ID. No changes required.

Likely an incorrect or invalid review comment.

application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)

13-13: Confirm reload key semantics

reloadCachedFunctions(stringKey) presumes stringKey is the Petri net identifier the service expects. Please confirm the key format aligns with IFieldActionsCacheService’s contract; otherwise, misses will never populate.

I can generate a short repo scan to locate all reloadCachedFunctions(...) overloads and usages to confirm the expected key shape. Do you want that?

application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)

46-50: Filtering specialized cache names is unnecessary CacheConfigurationProperties.getAllCaches() returns only the default caches (petriNetById, petriNetByIdentifier, petriNetNewest, petriNetCache, loadedModules) and any user-added names—it never includes CacheMapKeys.ACTIONS, CacheMapKeys.FUNCTIONS, or CacheMapKeys.NAMESPACE_FUNCTIONS, so no duplicate registrations can occur by default.

Likely an incorrect or invalid review comment.

@Retoocs
Copy link
Contributor

Retoocs commented Sep 3, 2025

There are test errors

@dominikvozr dominikvozr marked this pull request as draft September 4, 2025 10:45
@Retoocs Retoocs self-requested a review September 4, 2025 10:48
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 10

♻️ Duplicate comments (9)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (2)

21-32: Honor Cache.get contract; avoid NPE; remove unnecessary PetriNet fetch; handle contention.

  • Return null on miss, not ValueWrapper(null).
  • Use reload-by-id API; fetching PetriNet is unnecessary.
  • Prevent thundering herd by delegating to GenericMapCache.get(key, loader).

Apply:

     @Override
-    public ValueWrapper get(Object key) {
-        String stringKey = String.valueOf(key);
-
-        Object valueObject = map().get(stringKey);
-        if (valueObject != null) {
-            return new SimpleValueWrapper(List.copyOf((List<CachedFunction>) valueObject));
-        }
-        PetriNet petriNet = petriNetService.getPetriNet(stringKey);
-        fieldActionsCacheService.cachePetriNetFunctions(petriNet);
-        return new SimpleValueWrapper(List.copyOf((List<CachedFunction>) map().get(stringKey)));
-    }
+    public ValueWrapper get(Object key) {
+        if (key == null) throw new IllegalArgumentException("Cache key must not be null");
+        final String stringKey = key.toString();
+        List<CachedFunction> value = this.get(stringKey, () -> {
+            fieldActionsCacheService.reloadCachedFunctions(stringKey);
+            List<CachedFunction> v = (List<CachedFunction>) map().get(stringKey);
+            return v == null ? null : List.copyOf(v);
+        });
+        return value == null ? null : new SimpleValueWrapper(value);
+    }

34-46: Override typed get; mirror null-miss semantics; avoid PetriNet fetch.

Also add key validation and contention-safe loader.

Apply:

-    public <T> T get(Object key, Class<T> type) {
-        String stringKey = String.valueOf(key);
-        Object valueObject = map().get(stringKey);
-
-        if (valueObject != null) {
-            return type.cast(List.copyOf((List<CachedFunction>) valueObject));
-        }
-
-        PetriNet petriNet = petriNetService.getPetriNet(stringKey);
-        fieldActionsCacheService.cachePetriNetFunctions(petriNet);
-        return type.cast(List.copyOf((List<CachedFunction>) map().get(stringKey)));
-
-    }
+    @Override
+    public <T> T get(Object key, Class<T> type) {
+        if (key == null) throw new IllegalArgumentException("Cache key must not be null");
+        final String stringKey = key.toString();
+        List<CachedFunction> value = this.get(stringKey, () -> {
+            fieldActionsCacheService.reloadCachedFunctions(stringKey);
+            List<CachedFunction> v = (List<CachedFunction>) map().get(stringKey);
+            return v == null ? null : List.copyOf(v);
+        });
+        return value == null ? null : type.cast(value);
+    }
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (3)

15-22: Parameterize the cache; align with stored type; add @OverRide imports not needed.

Removes raw type usage and keeps type-safety.

Apply:

-@Slf4j
-public class FunctionsMapCache extends GenericMapCache {
+@Slf4j
+public class FunctionsMapCache extends GenericMapCache<CachedFunction> {
@@
-    public FunctionsMapCache(String name, java.util.function.Supplier<Map<String, CachedFunction>> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService, IGroovyShellFactory shellFactory) {
-        super(name, CachedFunction.class, mapFactory, fieldActionsCacheService, petriNetService);
+    public FunctionsMapCache(String name,
+                             java.util.function.Supplier<Map<String, CachedFunction>> mapFactory,
+                             IFieldActionsCacheService fieldActionsCacheService,
+                             IPetriNetService petriNetService,
+                             IGroovyShellFactory shellFactory) {
+        super(name, CachedFunction.class, mapFactory, fieldActionsCacheService, petriNetService);
         this.shell = shellFactory.getGroovyShell();
     }

25-40: Fix miss semantics; avoid double build; use super.put; validate key.

  • Return null when absent.
  • Build once on miss; reuse built instance for storage and response (or consistently return a copy).

Apply:

     @Override
     public synchronized ValueWrapper get(Object key) {
-        String stringKey = String.valueOf(key);
+        if (key == null) throw new IllegalArgumentException("Cache key must not be null");
+        String stringKey = key.toString();
 
         Object valueObject = map().get(stringKey);
         if (valueObject != null) {
-            return new SimpleValueWrapper(CachedFunction.copyOf(shell, (CachedFunction) valueObject));
+            return new SimpleValueWrapper(CachedFunction.copyOf(shell, (CachedFunction) valueObject));
         }
 
         Function function = petriNetService.findByFunctionId(stringKey);
         if (function != null) {
-            map().put(stringKey, CachedFunction.build(shell, function));
-            return new SimpleValueWrapper(CachedFunction.build(shell, function));
+            CachedFunction built = CachedFunction.build(shell, function);
+            super.put(stringKey, built);
+            return new SimpleValueWrapper(CachedFunction.copyOf(shell, built));
         }
 
-        return new SimpleValueWrapper(null);
+        return null;
     }

42-57: Override typed get; unify semantics with untyped; return null on miss.

Also avoid double build and write via super.put.

Apply:

-    public synchronized  <T> T get(Object key, Class<T> type) {
-        String stringKey = String.valueOf(key);
+    @Override
+    public synchronized <T> T get(Object key, Class<T> type) {
+        if (key == null) throw new IllegalArgumentException("Cache key must not be null");
+        String stringKey = key.toString();
         Object valueObject = map().get(stringKey);
 
         if (valueObject != null) {
-            return type.cast(valueObject);
+            return type.cast(CachedFunction.copyOf(shell, (CachedFunction) valueObject));
         }
 
         Function function = petriNetService.findByFunctionId(stringKey);
         if (function != null) {
-            map().put(stringKey, CachedFunction.build(shell, function));
-            return type.cast(CachedFunction.build(shell, function));
+            CachedFunction built = CachedFunction.build(shell, function);
+            super.put(stringKey, built);
+            return type.cast(CachedFunction.copyOf(shell, built));
         }
 
-        return type.cast(null);
+        return null;
     }
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (3)

12-12: Parameterize the cache with Closure to eliminate raw types

Extend GenericMapCache<Closure> for compile-time safety.

- public class ActionsMapCache extends GenericMapCache {
+ public class ActionsMapCache extends GenericMapCache<Closure> {

22-28: Honor Cache contract: return null on miss, don’t wrap null

Wrapping null in SimpleValueWrapper violates Spring’s Cache contract.

         Object valueObject = map().get(stringKey);
         if (valueObject != null) {
             return new SimpleValueWrapper(valueObject);
         }
         fieldActionsCacheService.reloadCachedFunctions(stringKey);
-        return new SimpleValueWrapper(map().get(stringKey));
+        Object refreshed = map().get(stringKey);
+        return refreshed != null ? new SimpleValueWrapper(refreshed) : null;

30-41: Add @OverRide and mirror null semantics for typed get

Also avoid type.cast(null) and return null directly.

-    public synchronized <T> T get(Object key, Class<T> type) {
+    @Override
+    public synchronized <T> T get(Object key, Class<T> type) {
         String stringKey = String.valueOf(key);
         Object valueObject = map().get(stringKey);
 
         if (valueObject != null) {
             return type.cast(valueObject);
         }
 
         fieldActionsCacheService.reloadCachedFunctions(stringKey);
-        return type.cast(map().get(stringKey));
+        Object refreshed = map().get(stringKey);
+        return refreshed != null ? type.cast(refreshed) : null;
 
     }

Run to locate similar violations across caches:

#!/bin/bash
rg -nP --type=java 'SimpleValueWrapper\\(null\\)|return\\s+type\\.cast\\(null\\)'
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)

17-18: Tighten generics: use Class and make it final

Reduces unchecked casts and encodes the value type at the compiler level.

-    protected Class<?> valueType;
+    protected final Class<V> valueType;
@@
-    public GenericMapCache(String name, Class<?> valueType, java.util.function.Supplier<Map<String, V>> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) {
+    public GenericMapCache(String name, Class<V> valueType, java.util.function.Supplier<Map<String, V>> mapFactory, IFieldActionsCacheService fieldActionsCacheService, IPetriNetService petriNetService) {

Also applies to: 26-26

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bd719b5 and e976081.

📒 Files selected for processing (6)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/domain/CachedFunction.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (4)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)
  • Slf4j (11-42)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (1)
  • Slf4j (14-53)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
  • Slf4j (14-104)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
  • Slf4j (66-581)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (4)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)
  • Slf4j (11-42)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (1)
  • Slf4j (14-58)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (1)
  • Slf4j (14-53)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
  • Slf4j (66-581)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
  • CacheMapKeys (3-7)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (4)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (1)
  • Slf4j (14-58)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (1)
  • Slf4j (14-53)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
  • Slf4j (14-104)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
  • Slf4j (66-581)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsNamespaceMapCache.java (4)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ActionsMapCache.java (1)
  • Slf4j (11-42)
application-engine/src/main/java/com/netgrif/application/engine/configuration/FunctionsMapCache.java (1)
  • Slf4j (14-58)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
  • Slf4j (14-104)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
  • Slf4j (66-581)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)

53-82: Good: bounded maps wrapped with synchronization

Wrapping MaxSizeHashMap with Collections.synchronizedMap addresses immediate thread-safety concerns for shared caches.

@dominikvozr dominikvozr self-assigned this Sep 9, 2025
…namespaceFunctionsCache from local Map to Redis
@dominikvozr dominikvozr marked this pull request as ready for review September 16, 2025 09:02
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (8)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)

59-61: Fix compile error: qualify ValueRetrievalException

It’s an inner type of Cache.

-        } catch (Exception ex) {
-            throw new ValueRetrievalException(stringKey, loader, ex);
+        } catch (Exception ex) {
+            throw new Cache.ValueRetrievalException(stringKey, loader, ex);
         }
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (6)

58-65: Fail fast on missing namespace functions cache

Use a required-cache helper to avoid NPEs and produce clear errors.

-        Cache namespaceFunctionsCache = cacheManager.getCache(properties.getNamespaceFunctions());
+        Cache namespaceFunctionsCache = getRequiredCache(properties.getNamespaceFunctions());

149-151: Return a typed copy of the namespace cache; avoid raw casts

Safer and future‑proof.

-        return new HashMap<>((Map) cacheManager.getCache(properties.getNamespaceFunctions()).getNativeCache());
+        @SuppressWarnings("unchecked")
+        Map<String, List<CachedFunction>> nativeMap =
+                (Map<String, List<CachedFunction>>) getRequiredCache(properties.getNamespaceFunctions()).getNativeCache();
+        return new HashMap<>(nativeMap);

154-166: Clear caches via required-cache helper

Prevents NPEs if a cache is missing.

-        cacheManager.getCache(CacheMapKeys.ACTIONS).clear();
+        getRequiredCache(CacheMapKeys.ACTIONS).clear();
@@
-        cacheManager.getCache(properties.getNamespaceFunctions()).clear();
+        getRequiredCache(properties.getNamespaceFunctions()).clear();
@@
-        cacheManager.getCache(CacheMapKeys.FUNCTIONS).clear();
+        getRequiredCache(CacheMapKeys.FUNCTIONS).clear();

69-72: Guard reload path with required-cache helper

Prevents NPE when cache is misconfigured.

-        cacheManager.getCache(properties.getNamespaceFunctions()).evictIfPresent(petriNetId);
+        getRequiredCache(properties.getNamespaceFunctions()).evictIfPresent(petriNetId);
         cachePetriNetFunctions(petriNetService.getNewestVersionByIdentifier(petriNetId));

80-92: Use atomic Cache#get(key, loader) and evict on rewrite; stop peeking into native map

This fixes potential NPE and races, and honors rewrite semantics.

-        Cache actionsCache = cacheManager.getCache(CacheMapKeys.ACTIONS);
-        Object nativeActionsCache = actionsCache.getNativeCache();
-
-        if (nativeActionsCache instanceof Map<?, ?> map) {
-            if (shouldRewriteCachedActions || !map.containsKey(stringId) ) {
-                Closure code = (Closure) shell.evaluate("{-> " + action.getDefinition() + "}");
-                actionsCache.put(stringId, code);
-            }
-        }
-        return (Closure) actionsCache.get(stringId).get();
+        Cache actionsCache = getRequiredCache(CacheMapKeys.ACTIONS);
+        if (shouldRewriteCachedActions) {
+            actionsCache.evict(stringId);
+        }
+        return actionsCache.get(stringId, () -> (Closure) shell.evaluate("{-> " + action.getDefinition() + "}"));

95-107: Compute functions via Cache API, no native map casting

Cleaner, thread-safe, and backend-agnostic.

-        Cache functionsCache = cacheManager.getCache(CacheMapKeys.FUNCTIONS);
-        Object nativeFunctionsCache = functionsCache.getNativeCache();
-
-        if (nativeFunctionsCache instanceof Map<?, ?> map) {
-            functions.forEach(function -> {
-                if (!map.containsKey(function.getStringId())) {
-                    functionsCache.put(function.getStringId(), CachedFunction.build(shell, function));
-                }
-                cachedFunctions.add((CachedFunction) functionsCache.get(function.getStringId()).get());
-            });
-        }
+        Cache functionsCache = getRequiredCache(CacheMapKeys.FUNCTIONS);
+        for (Function function : functions) {
+            CachedFunction cf = functionsCache.get(function.getStringId(),
+                    () -> CachedFunction.build(shell, function));
+            cachedFunctions.add(cf);
+        }
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)

38-42: Avoid duplicate registrations for specialized caches

Filter out ACTIONS and FUNCTIONS from the generic cache list before adding specialized implementations.

-        Set<String> cacheNames = properties.getAllCaches();
-        List<Cache> caches = cacheNames.stream()
+        Set<String> cacheNames = new LinkedHashSet<>(properties.getAllCaches());
+        cacheNames.removeAll(Set.of(CacheMapKeys.ACTIONS, CacheMapKeys.FUNCTIONS));
+        List<Cache> caches = cacheNames.stream()
                 .map(ConcurrentMapCache::new)
                 .collect(Collectors.toCollection(ArrayList::new));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e976081 and 8323adb.

📒 Files selected for processing (5)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1 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/workflow/service/FieldActionsCacheService.java (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
  • CacheMapKeys (3-6)
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (3)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
  • CacheMapKeys (3-6)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
  • Slf4j (12-122)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
  • Slf4j (66-581)
⏰ 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: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java (1)

44-48: LGTM on adding namespaceFunctions property and wiring it into getAllCaches()

Also applies to: 62-63

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8323adb and f9796d6.

📒 Files selected for processing (4)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (2 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
PR: netgrif/application-engine#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/interfaces/IPetriNetService.java
  • 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). (5)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: Build
🔇 Additional comments (4)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (2)

567-567: No functional change here.

Whitespace-only change; nothing to address.


8-8: Cache names match — no action required.
@Cacheable values in PetriNetService ("petriNetCache" — line 158; "petriNetById" — 250; "petriNetByIdentifier" — 261; "petriNetNewest" — 284) exactly match defaults in CacheConfigurationProperties (lines 22, 27, 32, 37).

application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)

3-7: LGTM — constants holder is correctly non-instantiable.
Matches prior guidance: final class + private ctor; fields are public static final. No issues.

application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)

33-39: No action required — factories already return synchronized maps. CacheConfiguration creates GenericMapCache instances with Suppliers that wrap MaxSizeHashMap via Collections.synchronizedMap (application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java:45–58), so the current unsynchronized reads are safe.

@renczesstefan renczesstefan changed the base branch from release/7.0.0-rev7 to release/7.0.0-rev8 September 23, 2025 07:31
Copy link
Member

@renczesstefan renczesstefan left a comment

Choose a reason for hiding this comment

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

Please review coderabbitAI comments.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9796d6 and d2227d0.

📒 Files selected for processing (4)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (2 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-31T23:40:46.499Z
Learnt from: tuplle
PR: netgrif/application-engine#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
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
🧬 Code graph analysis (2)
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (3)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
  • CacheMapKeys (3-7)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)
  • Slf4j (12-110)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)
  • Slf4j (68-570)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
  • CacheMapKeys (3-7)
⏰ 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). (10)
  • 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
  • GitHub Check: Build
  • GitHub Check: task-list-completed
🔇 Additional comments (16)
application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java (1)

8-11: Explicit imports look good—thanks for aligning with the project style.

The explicit imports remove the wildcard and keep the dependency surface clear.

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

58-65: LGTM: fail‑fast cache retrieval and eviction on empty set

Using getRequiredCache() avoids NPEs; evicting when empty keeps the cache tidy.


155-166: LGTM: clearing caches via required cache helper

Safe and consistent with the new cache infra.


168-174: LGTM: centralized required-cache helper

Good error message and avoids scattered null checks.


150-151: Return a typed copy of namespace function cache

Remove raw cast; return a typed map copy for safety.

-        return new HashMap<>((Map) getRequiredCache(properties.getNamespaceFunctions()).getNativeCache());
+        @SuppressWarnings("unchecked")
+        Map<String, List<CachedFunction>> nativeMap =
+                (Map<String, List<CachedFunction>>) getRequiredCache(properties.getNamespaceFunctions()).getNativeCache();
+        return new HashMap<>(nativeMap);

95-108: Major: Cache logic bypassed when native isn’t Map; also potential NPE via ValueWrapper.get()

Avoid peeking into native map and rely on Cache#get(key, loader) to compute‑if‑absent.

-        Cache functionsCache = getRequiredCache(CacheMapKeys.FUNCTIONS);
-        Object nativeFunctionsCache = functionsCache.getNativeCache();
-
-        if (nativeFunctionsCache instanceof Map<?, ?> map) {
-            functions.forEach(function -> {
-                if (!map.containsKey(function.getStringId())) {
-                    functionsCache.put(function.getStringId(), CachedFunction.build(shell, function));
-                }
-                cachedFunctions.add((CachedFunction) functionsCache.get(function.getStringId()).get());
-            });
-        }
+        Cache functionsCache = getRequiredCache(CacheMapKeys.FUNCTIONS);
+        for (Function function : functions) {
+            CachedFunction cf = functionsCache.get(function.getStringId(),
+                    () -> CachedFunction.build(shell, function));
+            cachedFunctions.add(cf);
+        }

82-91: Critical: NPE risk and brittle native-cache introspection; use Cache#get(key, loader)

If the native cache isn’t a Map (e.g., Caffeine), the compute path is skipped and actionsCache.get(...).get() can NPE. Use compute‑if‑absent via Cache API and evict on rewrite.

-        Cache actionsCache = getRequiredCache(CacheMapKeys.ACTIONS);
-        Object nativeActionsCache = actionsCache.getNativeCache();
-
-        if (nativeActionsCache instanceof Map<?, ?> map) {
-            if (shouldRewriteCachedActions || !map.containsKey(stringId) ) {
-                Closure code = (Closure) shell.evaluate("{-> " + action.getDefinition() + "}");
-                actionsCache.put(stringId, code);
-            }
-        }
-        return (Closure) actionsCache.get(stringId).get();
+        Cache actionsCache = getRequiredCache(CacheMapKeys.ACTIONS);
+        if (shouldRewriteCachedActions) {
+            actionsCache.evict(stringId);
+        }
+        return actionsCache.get(stringId, () -> (Closure) shell.evaluate("{-> " + action.getDefinition() + "}"));
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (4)

41-48: LGTM: bounded ACTIONS cache with thread‑safe backing

Synchronized MaxSizeHashMap + GenericMapCache is acceptable for now.


50-57: Confirm FUNCTIONS value type matches usage

Storing CachedFunction aligns with FieldActionsCacheService.getCachedFunctions().


59-62: LGTM: cache manager assembly

SimpleCacheManager wiring is correct; Actuator should pick these up.


35-39: Filter out ACTIONS and FUNCTIONS from generic caches
Exclude the reserved CACHE_MAP keys before creating the generic ConcurrentMapCache instances:

- Set<String> cacheNames = properties.getAllCaches();
+ Set<String> cacheNames = new LinkedHashSet<>(properties.getAllCaches());
+ cacheNames.removeAll(Set.of(CacheMapKeys.ACTIONS, CacheMapKeys.FUNCTIONS));
  List<Cache> caches = cacheNames.stream()
          .map(ConcurrentMapCache::new)
          .collect(Collectors.toCollection(ArrayList::new));
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (5)

32-63: LGTM: per‑key locking for atomic compute

Correct double‑checked compute with per‑key locks and proper exception wrapping.


98-109: LGTM: runtime type safety via valueType

safeCast guards value type mismatches appropriately.


30-31: Expose true native cache to align with Spring semantics

Returning Map.copyOf breaks native identity and snapshots values; callers expect the live backing store. Return the backing map.

-    @Override public Object getNativeCache() { return Map.copyOf(map()); }
+    @Override public Object getNativeCache() { return map(); }

72-87: Reduce contention: remove method‑level synchronized on simple ops

Backed by a thread‑safe map plus per‑key locks for loader; method‑level synchronized on get(type), put, evict is unnecessary and harms throughput.

-    public synchronized <T> T get(Object key, Class<T> type) {
+    public <T> T get(Object key, Class<T> type) {
         String stringKey = String.valueOf(key);
         Object valueObject = map().get(stringKey);
         return valueObject != null ? type.cast(valueObject) : null;
     }
@@
-    public synchronized void put(Object key, Object value) {
+    public void put(Object key, Object value) {
         map().put(String.valueOf(key), safeCast(value));
     }
@@
-    public synchronized void evict(Object key) {
+    public void evict(Object key) {
         map().remove(String.valueOf(key));
     }

89-92: Preserve native identity on clear()

Reassigning the map breaks existing native references; clear in place.

-    public synchronized void clear() {
-        this.atomicMapRef.set(mapFactory.get());
-    }
+    public synchronized void clear() {
+        map().clear();
+        log.debug("{} cache cleared", name);
+    }

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In
@application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java:
- Around line 29-48: The get method in GenericMapCache uses map.computeIfAbsent
on a Collections.synchronizedMap which is not atomic and can cause the loader to
run multiple times; fix by synchronizing around the compound operation: in
GenericMapCache.get(Object key, Callable<T> loader) acquire a lock on the
underlying map (synchronized(map) { ... }) and perform the compute-if-absent
logic (or check-then-put) inside that synchronized block so the loader.call() is
only invoked once per key; alternatively, replace the synchronizedMap with a
thread-safe ConcurrentHashMap-backed implementation and use its computeIfAbsent
to get atomic semantics—update references to map, computeIfAbsent, and the get
method accordingly.
- Line 27: The getNativeCache implementation uses Map.copyOf(map), which throws
NPE if the stored map contains null values; change
GenericMapCache.getNativeCache to create a new HashMap copy via the HashMap(Map)
constructor instead, and perform the copy while synchronizing on the underlying
map (synchronize on the map field) to avoid concurrent-modification during
iteration; return the new copy (or an unmodifiable view of that new HashMap if
immutability is desired) so null values are preserved and thread-safety is
maintained.

In
@application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java:
- Around line 71-78: In reloadCachedGlobalFunctions, ensure stale cache entries
are evicted even when
petriNetService.getDefaultVersionByIdentifier(processIdentifier) returns null:
call
getRequiredCache(CacheMapKeys.GLOBAL_FUNCTIONS).evictIfPresent(processIdentifier)
before returning (and only call cachePetriNetFunctions(petriNet) when petriNet
!= null); alternatively, if retaining cache for missing nets is intentional, add
a clear comment in reloadCachedGlobalFunctions explaining that design decision.
- Around line 99-116: Replace the manual cache-aside logic in getCachedFunctions
with Cache.get(key, Callable) on the functionsCache (obtained via
getRequiredCache(CacheMapKeys.FUNCTIONS)) so that for each Function you call
functionsCache.get(function.getStringId(), () -> CachedFunction.build(shell,
function)); this removes the explicit ValueWrapper null check and put, still
uses CachedFunction.build(...) as the loader, and collects the returned
CachedFunction instances into the cachedFunctions list.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 03cb8ea and 173592d.

📒 Files selected for processing (2)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 363
File: application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java:66-69
Timestamp: 2026-01-08T08:37:58.317Z
Learning: In the CacheConfigurationProperties class, the cache property for namespace/global functions is named `globalFunctions`, not `namespaceFunctions`. This property was renamed as part of the refactoring from namespace-scoped to global-scoped function caching.
📚 Learning: 2026-01-08T08:37:58.317Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 363
File: application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java:66-69
Timestamp: 2026-01-08T08:37:58.317Z
Learning: In the CacheConfigurationProperties class, the cache property for namespace/global functions is named `globalFunctions`, not `namespaceFunctions`. This property was renamed as part of the refactoring from namespace-scoped to global-scoped function caching.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.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/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.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/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.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/FieldActionsCacheService.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/workflow/service/FieldActionsCacheService.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). (21)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • 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
  • 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
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (6)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (2)

50-77: LGTM!

The get, put, evict, and clear methods correctly delegate to the synchronized map with proper type handling via safeCast. The ValueWrapper pattern is correctly implemented.


79-90: LGTM!

The safeCast helper properly validates types at runtime and provides clear error messages on type mismatch.

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

85-97: LGTM!

The cache lookup pattern is correctly implemented. The shouldRewriteCachedActions flag is properly checked before cache lookup, and the closure evaluation/caching logic is sound.


177-186: LGTM!

Good defensive implementation: uses instanceof pattern matching, returns a defensive copy to prevent external modification of cache state, and handles the non-Map case gracefully.


188-209: LGTM!

The clear methods correctly delegate to the underlying cache, and getRequiredCache provides a fail-fast mechanism for catching configuration errors early.


32-43: LGTM!

Constructor correctly initializes dependencies via injection. The @Lazy annotation on setPetriNetService appropriately handles the circular dependency.

@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

@machacjozef: I'll rescan the entire PR and conduct a fresh comprehensive review of all the changes.

✅ Actions performed

Full review triggered.

@Retoocs Retoocs self-requested a review January 13, 2026 17:22
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

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

287-316: Inconsistent error messages across createCaseByIdentifier overloads.

The error message was updated on Line 290 to "does not have default version", but the other overloads (Lines 304 and 313) still use "does not exist". Since all three methods call getDefaultVersionByIdentifier and check the same condition, the messages should be consistent.

Proposed fix
@@ -301,7 +301,7 @@ public class WorkflowService implements IWorkflowService {
     public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, LoggedUser user, Map<String, String> params) {
         PetriNet net = petriNetService.getDefaultVersionByIdentifier(identifier);
         if (net == null) {
-            throw new IllegalArgumentException("Petri net with identifier [" + identifier + "] does not exist.");
+            throw new IllegalArgumentException("Petri net with identifier [" + identifier + "] does not have default version.");
         }
         return this.createCase(net.getStringId(), title, color, user, params);
     }
@@ -310,7 +310,7 @@ public class WorkflowService implements IWorkflowService {
     public CreateCaseEventOutcome createCaseByIdentifier(String identifier, String title, String color, LoggedUser user) {
         PetriNet net = petriNetService.getDefaultVersionByIdentifier(identifier);
         if (net == null) {
-            throw new IllegalArgumentException("Petri net with identifier [" + identifier + "] does not exist.");
+            throw new IllegalArgumentException("Petri net with identifier [" + identifier + "] does not have default version.");
         }
         return this.createCase(net.getStringId(), title, color, user);
     }
🤖 Fix all issues with AI agents
In
@application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java:
- Around line 71-78: The method reloadCachedGlobalFunctions currently skips
evicting cache when
petriNetService.getDefaultVersionByIdentifier(processIdentifier) returns null;
update reloadCachedGlobalFunctions so that it always calls
getRequiredCache(CacheMapKeys.GLOBAL_FUNCTIONS).evictIfPresent(processIdentifier)
before returning, and only calls cachePetriNetFunctions(petriNet) when petriNet
is non-null, ensuring stale entries are removed; reference
reloadCachedGlobalFunctions, petriNetService.getDefaultVersionByIdentifier,
getRequiredCache(CacheMapKeys.GLOBAL_FUNCTIONS).evictIfPresent(processIdentifier),
and cachePetriNetFunctions.
- Around line 80-83: The one-arg overload reloadCachedGlobalFunctions(PetriNet
petriNet) currently dereferences petriNet (petriNet.getIdentifier()) and can
throw NPE if petriNet is null; change it to mirror cachePetriNetFunctions by
adding a null check and delegating appropriately (e.g., if petriNet is null call
the String overload with null or otherwise return) so
reloadCachedGlobalFunctions handles null inputs safely.
- Around line 177-186: getGlobalFunctionsCache currently returns an empty map
silently if getRequiredCache(CacheMapKeys.GLOBAL_FUNCTIONS).getNativeCache() is
not a Map, which can hide misconfigurations; modify getGlobalFunctionsCache to
detect non-Map nativeCache and log a warning (including
CacheMapKeys.GLOBAL_FUNCTIONS and nativeCache.getClass() or the null value)
before returning Collections.emptyMap, while keeping the defensive copy logic
for the Map branch; use the class logger (e.g., the existing logger field) to
emit the warning so callers and operators can diagnose unexpected cache
implementations.

In
@nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java:
- Line 59: In Version.java update the Javadoc return description to remove the
malformed closing tag `</0>` and escape comparison operators using {@code}, e.g.
change the @return line in the Version class (compareTo method Javadoc) to
something like: "@return {@code 0} if the versions equal, {@code < 0} if this is
lower than other, {@code > 0} if this is higher than other" so the Javadoc
parses correctly and shows the operators as code.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e99c844 and 173592d.

📒 Files selected for processing (13)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/RunnerConfigurationProperties.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/workflow/service/FieldActionsCacheService.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/interfaces/IFieldActionsCacheService.java
  • application-engine/src/main/resources/application.yaml
  • docs/process/functions.md
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java
💤 Files with no reviewable changes (1)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/RunnerConfigurationProperties.java
🧰 Additional context used
🧠 Learnings (10)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 363
File: application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java:66-69
Timestamp: 2026-01-08T08:37:58.317Z
Learning: In the CacheConfigurationProperties class, the cache property for namespace/global functions is named `globalFunctions`, not `namespaceFunctions`. This property was renamed as part of the refactoring from namespace-scoped to global-scoped function caching.
📚 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/resources/application.yaml
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
  • docs/process/functions.md
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
📚 Learning: 2026-01-13T16:33:15.098Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 348
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java:4-4
Timestamp: 2026-01-13T16:33:15.098Z
Learning: In the com.netgrif.application.engine.objects.petrinet.domain package, prefer using wildcard imports and do not suggest replacing them with explicit imports during code reviews. This guideline applies to all Java files under that package (and subpackages) to reflect project-specific import conventions and reduce noise in imports.

Applied to files:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/version/Version.java
📚 Learning: 2026-01-08T08:37:58.317Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 363
File: application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java:66-69
Timestamp: 2026-01-08T08:37:58.317Z
Learning: In the CacheConfigurationProperties class, the cache property for namespace/global functions is named `globalFunctions`, not `namespaceFunctions`. This property was renamed as part of the refactoring from namespace-scoped to global-scoped function caching.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.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/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.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/main/java/com/netgrif/application/engine/workflow/service/WorkflowService.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/PetriNetService.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java
📚 Learning: 2026-01-13T16:33:15.098Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 348
File: application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.java:4-4
Timestamp: 2026-01-13T16:33:15.098Z
Learning: In the netgrif/application-engine repository, wildcard imports are preferred for the com.netgrif.application.engine.objects.petrinet.domain package. Do not suggest replacing wildcard imports with explicit imports for this package.

Applied to files:

  • 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
📚 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
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
  • application-engine/src/main/java/com/netgrif/application/engine/petrinet/service/interfaces/IPetriNetService.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/petrinet/service/PetriNetService.java
🧬 Code graph analysis (1)
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
  • CacheMapKeys (3-8)
⏰ 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). (42)
  • 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
  • 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
  • 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: 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
  • 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
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (21)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)

1-8: LGTM!

Clean utility class following standard patterns: final class with private constructor and public static constants. The constant names align with the cache configuration used elsewhere in this PR. Based on learnings, globalFunctions is the correct naming after the refactoring from namespace-scoped to global-scoped function caching.

docs/process/functions.md (1)

7-7: No issues.

Trailing whitespace removal; no semantic impact.

application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (1)

14-23: LGTM!

The cache implementation correctly uses Collections.synchronizedMap for thread safety and enforces type constraints via safeCast. The design appropriately wraps loader exceptions in Cache.ValueRetrievalException per Spring Cache contract, and computeIfAbsent is atomic on synchronized maps in Java 8+.

application-engine/src/main/resources/application.yaml (1)

119-121: Clarify the intent for exposing all management endpoints in /manage/**.

While exposing all Actuator endpoints can pose a security risk, the configuration in application.yaml has /manage/** explicitly listed in both server-patterns (line 88) and anonymous-exceptions (line 89), indicating this access is intentional by design. This same configuration already existed in the legacy property files and has no production override.

Before restricting endpoints to health,info,prometheus,caches as suggested, verify with the team whether:

  • This intentional anonymous access is required for operational monitoring or cache management systems
  • The isolated /manage/** path provides sufficient security isolation
  • Any production environments override this configuration (which would need to be added separately)

If unrestricted anonymous access to these endpoints is truly unintended, the suggested restriction is valid. If this is intentional operational design, the current configuration should be preserved.

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

4-4: LGTM!

The wildcard import for com.netgrif.application.engine.objects.petrinet.domain.* aligns with the repository's preferred import style for this package. Based on learnings, this is the expected convention.

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

8-11: LGTM!

Explicit imports for domain types are appropriate here.


636-636: LGTM!

The method call correctly aligns with the updated IFieldActionsCacheService interface, which now provides reloadCachedGlobalFunctions instead of reloadCachedFunctions.


637-645: Good handling of default version promotion.

The logic correctly promotes the latest version to default when the current default is deleted. The cache eviction at line 635 ensures getLatestVersionByIdentifier() returns fresh data from the database, excluding the just-deleted net.

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

24-25: Good migration from deprecated class.

Implementing CachingConfigurer directly instead of extending the deprecated CachingConfigurerSupport is the correct approach for Spring Framework 5.3+.


31-55: LGTM!

The cache manager implementation cleanly separates standard ConcurrentMapCache instances for Petri net caching from the specialized GenericMapCache instances for actions and functions. The size-limited caches for ACTIONS, FUNCTIONS, and GLOBAL_FUNCTIONS align with the PR's goal of making cache operations manageable via Actuator.

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

56-80: LGTM!

The new cache size properties are well-documented with sensible defaults (500) and proper validation (@Min(1)). The naming follows the established pattern, with globalFunctionsCacheSize correctly using the globalFunctions naming convention per learnings.


88-93: LGTM!

The getAllCaches() method correctly returns only the cache names for standard ConcurrentMapCache instances. The specialized caches (ACTIONS, FUNCTIONS, GLOBAL_FUNCTIONS) are handled separately via GenericMapCache in CacheConfiguration, so excluding them here prevents duplicate cache creation.

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

16-18: Breaking API change verified: method renamed and fully migrated.

The interface provides two overloads for reloadCachedGlobalFunctions (by ID and by PetriNet object), replacing the previous reloadCachedFunctions method. All usages have been migrated and both overloads are implemented correctly, with the PetriNet overload properly delegating to the String-based implementation.

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

32-43: Clean dependency injection pattern.

The constructor properly injects CacheConfigurationProperties and CacheManager, enabling integration with Spring's cache infrastructure. The lazy injection of petriNetService (lines 45-49) correctly avoids potential circular dependencies.


51-69: LGTM - Proper cache management with cleanup.

The method correctly handles the null case, uses evictIfPresent to clean up entries when no global functions exist, and properly integrates with the Spring Cache API.


85-97: LGTM - Correct cache retrieval pattern.

The method properly uses Cache.ValueWrapper to differentiate between cache miss (null wrapper) and cached value, with appropriate handling for the shouldRewriteCachedActions flag.


99-116: LGTM - Proper cache-aside pattern implementation.

The method correctly implements a cache-aside pattern, computing and storing CachedFunction on cache miss. Pre-allocating the ArrayList with the expected size is a good optimization.


118-138: LGTM - Robust batch caching with proper error isolation.

The pagination pattern is correct, and the per-net exception handling ensures a single failure doesn't prevent caching other PetriNets. The warning log includes the net ID for debugging.


140-153: Correct duplicate function signature detection.

The evaluation logic properly validates that no duplicate method signatures exist within the cached functions, throwing a clear exception message on violation.


188-201: LGTM - Clean cache clearing implementation.

All three clear methods correctly delegate to the respective cache's clear() method via getRequiredCache, ensuring fail-fast behavior if a cache is misconfigured.


203-209: Well-designed fail-fast helper.

The getRequiredCache helper provides consistent fail-fast behavior across all cache operations. The clear error message aids debugging when a cache is misconfigured.

Replaced synchronized map with ConcurrentHashMap in GenericMapCache for thread-safety and ensured proper caching logic by reordering conditionals in FieldActionsCacheService. These changes enhance performance and maintainability of the caching system.
Add a null check to prevent potential NullPointerException when calling reloadCachedGlobalFunctions. This ensures better stability and avoids unnecessary errors during execution.
@renczesstefan
Copy link
Member

renczesstefan commented Jan 13, 2026

@coderabbitai please rescan this PR

@coderabbitai
Copy link

coderabbitai bot commented Jan 13, 2026

@renczesstefan: I'll rescan the entire PR and conduct a fresh comprehensive review of all the changes.

✅ Actions performed

Full review triggered.

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In
@application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java:
- Around line 12-16: The class GenericMapCache<V> is annotated with @Slf4j but
never uses the generated logger; remove the unused @Slf4j annotation from the
class declaration to eliminate the unused-field warning and dead import; update
any imports to remove lombok.extern.slf4j.Slf4j if present and ensure the class
compiles unchanged (retain GenericMapCache, name, valueType, and map fields and
the implemented Cache interface).
- Around line 63-66: The put(Object key, Object value) method calls
map.put(String.valueOf(key), safeCast(value)) which will throw
NullPointerException when safeCast(value) is null because ConcurrentHashMap
disallows null values; either (A) explicitly reject nulls by validating value in
put and throw IllegalArgumentException (update put to check value == null and
log/throw) or (B) change the backing map to store a wrapper (e.g., Optional) and
adapt safeCast and get/put/remove methods to wrap on insert and unwrap on
retrieval so nulls can be cached safely; locate and update the put method,
safeCast helper, and any get/remove logic to keep behavior consistent.
- Around line 28-47: The get method currently fails to cache null loader results
(because ConcurrentHashMap.computeIfAbsent won't store null) and performs an
unchecked cast; change the caching to store a sentinel or Optional<V> wrapper so
null results are cached (e.g., wrap loader.call() into Optional.ofNullable(...)
or a private NULL_SENTINEL and store that in map inside the lambda used by
map.computeIfAbsent in get), update the retrieval logic to unwrap the stored
wrapper before returning, and add a @SuppressWarnings("unchecked") annotation
(on the get method or immediately before the unchecked cast) to silence the
unchecked cast warning related to the (T) value return; refer to the get method,
map.computeIfAbsent lambda, safeCast and Cache.ValueRetrievalException to locate
the changes.

In
@application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java:
- Around line 101-118: Replace the manual get-then-put race in
getCachedFunctions by using the cache's atomic compute-if-absent form: obtain
the cache via getRequiredCache(CacheMapKeys.FUNCTIONS) and for each Function
call functionsCache.get(function.getStringId(), () ->
CachedFunction.build(shell, function)) to retrieve or atomically create the
CachedFunction instead of using functionsCache.get(...) followed by
functionsCache.put(...); add the result to cachedFunctions (casting to
CachedFunction if necessary).
- Around line 87-99: The getCompiledAction method in FieldActionsCacheService
uses a check-then-act pattern (actionsCache.get then put) which can cause
redundant concurrent compilation; change it to use Cache.get(stringId, Callable)
to perform an atomic get-or-compute: build the closure with shell.evaluate("{->
" + action.getDefinition() + "}") inside the Callable and return that result,
keeping references to actionsCache, stringId, CacheMapKeys.ACTIONS and the
shell.evaluate call to locate where to replace the existing logic.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 173592d and 790b359.

📒 Files selected for processing (2)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 363
File: application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java:66-69
Timestamp: 2026-01-08T08:37:58.317Z
Learning: In the CacheConfigurationProperties class, the cache property for namespace/global functions is named `globalFunctions`, not `namespaceFunctions`. This property was renamed as part of the refactoring from namespace-scoped to global-scoped function caching.
📚 Learning: 2026-01-08T08:37:58.317Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 363
File: application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java:66-69
Timestamp: 2026-01-08T08:37:58.317Z
Learning: In the CacheConfigurationProperties class, the cache property for namespace/global functions is named `globalFunctions`, not `namespaceFunctions`. This property was renamed as part of the refactoring from namespace-scoped to global-scoped function caching.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.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/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.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/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.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/FieldActionsCacheService.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/workflow/service/FieldActionsCacheService.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). (11)
  • 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: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (8)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (2)

49-61: LGTM!

Both get overloads correctly implement the Spring Cache contract with proper null handling and type casting.


68-89: LGTM!

The evict, clear, and safeCast methods are correctly implemented with proper type checking.

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

32-43: LGTM!

Clean transition from custom map-based caching to Spring Cache abstractions. The constructor properly injects CacheConfigurationProperties and CacheManager, aligning with the PR's goal of integrating with Spring Boot Actuator's cache management endpoints.


51-69: LGTM!

The caching logic correctly handles the two cases: storing functions when present, and evicting the cache entry when the PetriNet has no global functions. The null check on line 53-55 provides appropriate early exit.


71-85: LGTM!

The null check on line 75 correctly prevents NPE when getDefaultVersionByIdentifier returns null. The evict-before-cache ordering on lines 74-77 ensures no window where stale cache data persists after a reload request, which aligns with the commit message about ensuring correct caching behavior.


120-140: LGTM!

Good resilience pattern with per-PetriNet exception handling that logs failures but continues processing the batch. The pagination approach correctly avoids loading all entities into memory.


205-211: LGTM!

Good defensive programming pattern. Throwing IllegalStateException for missing cache configuration provides clear, fast-fail behavior that will surface misconfiguration issues early rather than causing obscure NPEs downstream.


179-188: The native cache implementation guarantees a Map is always returned; the type check will never fail.

GenericMapCache.getNativeCache() returns Map.copyOf(map) (line 26 of GenericMapCache.java), which always succeeds and returns a Map. The instanceof check at line 182 will always pass, so the else branch returning Collections.emptyMap() is unreachable and the silent failure scenario cannot occur. No defensive logging is needed.

Likely an incorrect or invalid review comment.

Updated the constructor parameter and internal map initialization to use 'initialCapacity' instead of the ambiguous 'cacheSize'. This improves code clarity and better
@machacjozef
Copy link
Member

2026-01-13T18:22:37.9729165Z [INFO] Results:
2026-01-13T18:22:37.9729520Z [INFO]
2026-01-13T18:22:37.9729819Z [ERROR] Failures:
2026-01-13T18:22:37.9731128Z [ERROR] FunctionsTest.testNamespaceFunctionException:127 Unexpected exception type thrown, expected: <java.lang.NullPointerException> but was: <java.lang.ClassCastException>
2026-01-13T18:22:37.9733437Z [ERROR] FunctionsTest.testNewNetVersionMissingMethodException:145 Unexpected exception type thrown, expected: <java.lang.NullPointerException> but was: <java.lang.ClassCastException>
2026-01-13T18:22:37.9735777Z [ERROR] FunctionsTest.testProcessFunctionException:168 Unexpected exception type thrown, expected: <groovy.lang.MissingMethodException> but was: <java.lang.ClassCastException>
2026-01-13T18:22:37.9737482Z [ERROR] Errors:
2026-01-13T18:22:37.9738347Z [ERROR] FunctionsTest.testNamespaceMethodOverloading:222->testMethodOverloading:238 » ClassCast
2026-01-13T18:22:37.9739690Z [ERROR] FunctionsTest.testNamespaceUseCaseUpdate:209 » ClassCast Expected com.netgrif....
2026-01-13T18:22:37.9740848Z [ERROR] FunctionsTest.testNewVersionOfNamespaceFunction:179 » ClassCast Expected com.n...
2026-01-13T18:22:37.9741850Z [ERROR] FunctionsTest.testProcessFunctions:111 » ClassCast Expected com.netgrif.applic...
2026-01-13T18:22:37.9742463Z [INFO]
2026-01-13T18:22:37.9742787Z [ERROR] Tests run: 374, Failures: 3, Errors: 4, Skipped: 29

Added a null check in the GenericMapCache.put method to evict entries with null values instead of storing them. Updated JavaDoc in Version.java to fix a minor formatting issue.
Previously, the cache retrieval logic did not properly handle cases where cached objects were lists. This change ensures that the type check applies to the first element of a list, preventing potential `ClassCastException` issues.
@renczesstefan
Copy link
Member

Please review PR, and resolve trivial comments.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java`:
- Around line 83-94: The cache is mis-typed and safeCast incorrectly handles
lists: change the GLOBAL_FUNCTIONS cache to
GenericMapCache<List<CachedFunction>> and update FieldActionsCacheService usages
to store and retrieve List<CachedFunction> (so types align), and fix
GenericMapCache.safeCast(Object) to avoid calling getFirst() on an empty list
(use isEmpty()/get(0) or check size>0 before inspecting element) and only
perform element-type checks when the cache value type is the element type
(otherwise allow casting the entire List to V).
♻️ Duplicate comments (2)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (2)

13-17: Remove unused @Slf4j annotation.

The @Slf4j annotation generates a logger field, but no logging is performed anywhere in this class (e.g., in clear() or exception handling).


29-48: Add @SuppressWarnings("unchecked") for the unchecked cast.

Line 43 performs an unchecked cast from V to T. While safeCast ensures the value conforms to V, there's no compile-time guarantee that T is assignable from V. Add the suppression annotation to acknowledge this intentional cast.

🧹 Suggested fix
 `@Override`
+@SuppressWarnings("unchecked")
 public <T> T get(Object key, Callable<T> loader) {
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e297242 and 271c93f.

📒 Files selected for processing (1)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 363
File: application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java:66-69
Timestamp: 2026-01-08T08:38:06.491Z
Learning: In the CacheConfigurationProperties class, the cache property for namespace/global functions is named `globalFunctions`, not `namespaceFunctions`. This property was renamed as part of the refactoring from namespace-scoped to global-scoped function caching.
⏰ 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: Test
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
🔇 Additional comments (3)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (3)

25-27: LGTM!

The getNativeCache() implementation is now safe. Since put() evicts on null values (lines 66-68) and ConcurrentHashMap rejects null values, Map.copyOf() will not encounter null values.


50-62: LGTM!

Both get overloads correctly implement the Spring Cache contract: returning null on cache miss and properly wrapping/casting values on hit.


64-81: LGTM!

The put() method correctly handles null values by evicting the key, preventing ConcurrentHashMap NPE. The clear() method now clears in place, preserving native cache identity as recommended in past reviews.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Introduce an additional generic type parameter in `GenericMapCache` to ensure proper type handling for list elements. Update `safeCast` logic to validate element types in lists and modify related cache configurations accordingly. Remove redundant cache eviction from `FieldActionsCacheService`.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java`:
- Line 19: The import java.lang.annotation.ElementType in CacheConfiguration is
unused; remove that import line from the CacheConfiguration class so only
necessary imports remain (keep other imports such as
org.springframework.cache.annotation.EnableCaching and
org.springframework.context.annotation.Configuration intact).

In
`@application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java`:
- Around line 71-77: In reloadCachedGlobalFunctions, always evict the cache
entry for the given processIdentifier before calling
petriNetService.getDefaultVersionByIdentifier(processIdentifier) so stale
functions aren't left when the PetriNet is deleted; after eviction, fetch the
PetriNet and only call cachePetriNetFunctions(petriNet) if the fetched petriNet
is non-null. Ensure you use the class's existing cache-eviction method (invoke
it at the start of reloadCachedGlobalFunctions) then proceed with the current
null-check and repopulation logic.
♻️ Duplicate comments (2)
application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (2)

13-14: Remove unused @Slf4j annotation.

The @Slf4j annotation generates a logger field, but no logging is performed in this class.

🧹 Suggested fix
-@Slf4j
 public class GenericMapCache<V, E> implements Cache {

31-50: Add @SuppressWarnings("unchecked") for the unavoidable cast.

Line 45 performs an unchecked cast (T) value. While this is inherent to the Spring Cache contract where the caller asserts the type, adding the annotation documents the intentional cast.

🧹 Suggested fix
 `@Override`
+@SuppressWarnings("unchecked")
 public <T> T get(Object key, Callable<T> loader) {
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 271c93f and 7a57310.

📒 Files selected for processing (3)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 363
File: application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java:66-69
Timestamp: 2026-01-08T08:38:06.491Z
Learning: In the CacheConfigurationProperties class, the cache property for namespace/global functions is named `globalFunctions`, not `namespaceFunctions`. This property was renamed as part of the refactoring from namespace-scoped to global-scoped function caching.
📚 Learning: 2026-01-08T08:38:06.491Z
Learnt from: renczesstefan
Repo: netgrif/application-engine PR: 363
File: application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/CacheConfigurationProperties.java:66-69
Timestamp: 2026-01-08T08:38:06.491Z
Learning: In the CacheConfigurationProperties class, the cache property for namespace/global functions is named `globalFunctions`, not `namespaceFunctions`. This property was renamed as part of the refactoring from namespace-scoped to global-scoped function caching.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.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/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.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/configuration/CacheConfiguration.java
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.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/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.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/FieldActionsCacheService.java
🧬 Code graph analysis (1)
application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheMapKeys.java (1)
  • CacheMapKeys (3-8)
⏰ 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). (10)
  • 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
  • GitHub Check: task-list-completed
  • GitHub Check: Build
🔇 Additional comments (9)
application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java (1)

38-59: LGTM!

The cache configuration correctly defines type parameters for each specialized cache:

  • ACTIONS cache stores Closure values
  • FUNCTIONS cache stores CachedFunction values
  • GLOBAL_FUNCTIONS cache stores List<CachedFunction> with proper element type validation

The use of SimpleCacheManager to aggregate both generic ConcurrentMapCache instances and specialized GenericMapCache instances is a clean approach.

application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java (3)

27-29: LGTM!

The getNativeCache() implementation using Map.copyOf(map) is safe because:

  1. ConcurrentHashMap doesn't allow null values
  2. The put() method evicts the key instead of storing null (lines 68-70)

This ensures the backing map never contains null values, preventing NullPointerException from Map.copyOf().


52-83: LGTM!

The Cache interface methods are correctly implemented:

  • get(Object key) returns null on cache miss per Spring Cache contract
  • put() defensively evicts on null values, preventing ConcurrentHashMap NPE
  • clear() clears in-place, preserving native cache references

85-123: LGTM!

The safeCast method correctly handles:

  • Direct type matching via isInstance check
  • List-typed caches with element type validation
  • Empty lists without throwing NoSuchElementException (addressed from past review)

The element-type validation against only the first element is a reasonable performance tradeoff for homogeneous lists.

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

39-43: LGTM!

The constructor properly initializes dependencies, and the lazy setter for petriNetService (lines 45-49) appropriately breaks potential circular dependencies.


51-69: LGTM!

The method correctly:

  • Guards against null input
  • Caches functions when present
  • Evicts stale entries when no global functions exist (preventing stale cache data)

86-98: LGTM!

The method correctly caches compiled closures and properly reuses the wrapper variable instead of performing a redundant cache lookup (addressed from past review).


100-117: LGTM!

The method correctly handles cache lookups with proper null checks on ValueWrapper before calling .get().


204-210: LGTM!

The getRequiredCache helper provides fail-fast behavior with a clear error message when a cache is misconfigured. This centralizes null-checking and improves error diagnostics.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Ensure the global functions cache is cleared for the given process identifier before reloading. This prevents stale data from persisting and ensures updated functions are cached correctly.
The import for `ElementType` was unnecessary and has been removed to clean up the code. This change improves maintainability by keeping the imports list concise and relevant.
Reordered method logic to ensure cache eviction precedes PetriNet retrieval. This avoids potential redundant operations and ensures consistent caching behavior. No functional changes were introduced.
@renczesstefan
Copy link
Member

@coderabbitai I made the change that you wanted, can you please review your last request?

@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch recent review comments from coderabbitai on PR `#348`
gh pr view 348 --repo netgrif/application-engine --json comments,reviews --jq '.reviews[] | select(.author.login == "coderabbitai") | {state: .state, body: .body, submittedAt: .submittedAt}' | head -20

echo "---"
echo "Fetching review thread comments:"

# Get review comments (inline code comments)
gh api "/repos/netgrif/application-engine/pulls/348/comments" --jq '.[] | select(.user.login == "coderabbitai") | {path: .path, line: .line, body: .body, created_at: .created_at, updated_at: .updated_at}' | head -30

Length of output: 255969


@renczesstefan: Thank you for making the change! However, I don't have visibility into which specific request you addressed or what changes were made in the latest commit.

To help me review your changes effectively, could you please clarify:

  1. Which specific review comment were you addressing? (For example, was it about the unused import, cache size validation, or something else?)
  2. What files did you modify to address the request?

Alternatively, if you'd like me to conduct a fresh review of the latest changes, I can scan the most recent commits to see what was updated.

Would you prefer that I:

  • Review a specific change you made, or
  • Conduct a full re-scan of the recent commits?

🧠 Learnings used
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.

@renczesstefan
Copy link
Member

@coderabbitai can you please review once more and accept if everything is OK?

@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the GenericMapCache implementation to understand the safeCast logic
cat application-engine/src/main/java/com/netgrif/application/engine/configuration/GenericMapCache.java

Length of output: 3971


🏁 Script executed:

#!/bin/bash
# Read the FunctionsTest to understand what the tests expect
cat application-engine/src/test/java/com/netgrif/application/engine/petrinet/service/FunctionsTest.java

Length of output: 131


🏁 Script executed:

#!/bin/bash
# Check how the caches are configured in CacheConfiguration
cat application-engine/src/main/java/com/netgrif/application/engine/configuration/CacheConfiguration.java

Length of output: 2304


🏁 Script executed:

#!/bin/bash
# Check recent commits to see if fixes were attempted after the test failures
git log --oneline -10

Length of output: 641


🏁 Script executed:

#!/bin/bash
# Find the FunctionsTest file
fd -t f "FunctionsTest.java" --full-path

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check if there are any recent CI workflow runs or test results
gh run list --repo netgrif/application-engine --branch NAE-2182 --limit 5

Length of output: 614


🏁 Script executed:

#!/bin/bash
# Look for any test result files or CI logs
find . -name "TEST-*.xml" -o -name "surefire-reports" -type d 2>/dev/null | head -10

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check the FieldActionsCacheService to see how it uses the caches
cat application-engine/src/main/java/com/netgrif/application/engine/workflow/service/FieldActionsCacheService.java

Length of output: 7835


🏁 Script executed:

#!/bin/bash
# Check the details of the most recent successful CI run
gh run view 20987364303 --repo netgrif/application-engine --log | grep -A 20 "Tests run:" | head -30

Length of output: 8198


🏁 Script executed:

#!/bin/bash
# Check if any of the in-progress runs have completed
gh run list --repo netgrif/application-engine --branch NAE-2182 --limit 1 --json status,conclusion,databaseId

Length of output: 67


🏁 Script executed:

#!/bin/bash
# Get the test summary from the most recent completed run
gh run view 20987364303 --repo netgrif/application-engine --json conclusion,status,jobs --jq '.jobs[] | select(.name | contains("test") or contains("build")) | {name: .name, conclusion: .conclusion, status: .status}'

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Get more details about the successful run to see if it includes test results
gh run view 20987364303 --repo netgrif/application-engine --json conclusion,createdAt,headSha --jq '.'

Length of output: 112


🏁 Script executed:

#!/bin/bash
# Check what commit the successful run was on
git log --oneline --all | grep -A 5 -B 5 "$(gh run view 20987364303 --repo netgrif/application-engine --json headSha --jq -r '.headSha' | cut -c1-7)"

Length of output: 266189


🏁 Script executed:

#!/bin/bash
# Get the full test output from the successful run, focusing on FunctionsTest
gh run view 20987364303 --repo netgrif/application-engine --log | grep -E "(FunctionsTest|Tests run:|Failures:|Errors:)" | tail -50

Length of output: 9432


🏁 Script executed:

#!/bin/bash
# Check when the test failures were reported vs when fixes were committed
git log --oneline --since="2026-01-13" --until="2026-01-14"

Length of output: 1383


@renczesstefan Thanks for the ping — I re-checked the PR.

What I verified

  • CI status: The latest completed run on January 14, 2026 08:24 UTC (commit 271c93f) is green with “Tests run: 374, Failures: 0, Errors: 0, Skipped: 29.” The earlier FunctionsTest ClassCastException issues are no longer present.
  • GenericMapCache: The safeCast fix for List element types and exception wrapping in get(Object, Callable) look correct.
  • FieldActionsCacheService: Now consistently uses Spring Cache for actions/functions/global functions; eviction-then-reload for global functions is in place; getGlobalFunctionsCache returns a safe snapshot.
  • PetriNetService: Uses reloadCachedGlobalFunctions after delete, which aligns with the new cache semantics.

Non-blocking suggestion

  • application.yaml currently exposes Actuator endpoints with include: "*". Consider scoping exposure (e.g., to health,info,caches) or moving the wildcard to a dev/test profile to avoid overexposure in production. This isn’t a blocker for this PR.

Everything else looks good to me. Approving now.


🧠 Learnings used
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.
✅ Actions performed

Comments resolved and changes approved.

@machacjozef machacjozef merged commit e90f3e4 into release/7.0.0-rev9 Jan 14, 2026
6 of 7 checks passed
@machacjozef machacjozef deleted the NAE-2182 branch January 14, 2026 10:40
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 improvement A change that improves on an existing feature Large Medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants