Skip to content

Conversation

@renczesstefan
Copy link
Member

@renczesstefan renczesstefan commented Aug 19, 2025

Description

Updated bulk index with new Elasticsearch client configuration. Updated ElasticCase and ElasticTask ID.

Implements NAE-2168

Dependencies

No new dependencies were introduced

Third party dependencies

No new dependencies were introduced

Blocking Pull requests

There are no dependencies on other PR

How Has Been This Tested?

This was tested manually.

Test Configuration

Name Tested on
OS macOS Sequoia 15.3.1
Runtime Java 21
Dependency Manager Maven 3.9.9n
Framework version Spring Boot 3.2.5
Run parameters
Other configuration

Checklist:

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

Summary by CodeRabbit

  • New Features

    • Multiple Elasticsearch hosts with optional SSL and Basic/Bearer auth.
    • Admin bulk reindex endpoint with "index all", last-run filter and configurable case/task batch sizes.
    • New IndexParams payload for reindex control.
    • Improved LocalDateTime JSON handling (consistent ISO‑8601 serializer/deserializer).
  • Bug Fixes

    • Date/time handling standardized to midnight to avoid timestamp mismatches.
  • Refactor

    • Batched, Mongo-backed reindexing with improved bulk error handling and unified ID usage across indexing/search.

- Introduced `LocalDateTimeJsonSerializer` and `LocalDateTimeJsonDeserializer` for improved LocalDateTime handling.
- Enhanced bulk indexing in ElasticIndexService:
  - Added new methods for reindexing cases and tasks into Elasticsearch.
  - Introduced batch processing for efficient indexing with log monitoring.
  - Removed deprecated `bulkIndex(List, Class, String...)` method.
- Updated ElasticTask and IndexCaseEvent to replace `stringId` with `id` field, simplifying object mapping.
- Improved task and case processing for better compatibility with MongoDB and Elasticsearch.
- Updated task entity construction in response body for consistency.
Replaced try-with-resources for CloseableIterator with direct Iterator usage for reindexing cases and tasks. Enhanced error reporting in bulk indexing by including detailed failure messages. Minor adjustments to method calls for improved clarity and consistency.
Updated repository methods to use `id` instead of `stringId` for consistency and clarity. Adjusted associated service and task management logic to ensure compatibility with the updated repository interface.
Moved the ObjectMapper configuration from ElasticsearchConfiguration to ElasticIndexService. This improves encapsulation by localizing the mapping logic within the service, reducing unnecessary bean exposure.
Introduced a custom JsonpMapper to handle Java 8 `LocalDateTime` serialization and deserialization using Jackson. This ensures proper JSON handling of datetime objects in Elasticsearch operations.
@coderabbitai
Copy link

coderabbitai bot commented Aug 19, 2025

Walkthrough

Migrate identifier usage from stringId/mongoId to id across domains, repos, services, events, mappings and tests; add multi-host SSL/token/basic-auth Elasticsearch client and Jackson-backed JsonpMapper; add millisecond-safe LocalDateTime (de)serializers; implement Mongo-driven, batched bulk reindex API and controller endpoint.

Changes

Cohort / File(s) Summary
Elasticsearch client configuration
application-engine/src/main/java/com/netgrif/application/engine/configuration/ElasticsearchConfiguration.java
Build ClientConfiguration from a sanitized list of URLs, enable SSL when configured, support basic auth or bearer-token header, provide a Jackson-backed JsonpMapper with JavaTimeModule and custom LocalDateTime (de)serializers; added helper methods and @NotNull annotations.
Configuration properties
application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/DataConfigurationProperties.java
ElasticsearchProperties.urlList<String>; added ssl, username, password, token; removed searchPort; added nested BatchProperties with caseBatchSize/taskBatchSize and validation.
Indexing interface & service (bulk reindex)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticIndexService.java, application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticIndexService.java
Replace old list-based bulkIndex with void bulkIndex(boolean indexAll, LocalDateTime lastRun, Integer caseBatchSize, Integer taskBatchSize); remove Lombok constructor in favor of explicit constructor; add Mongo-driven paged reindex for Case and Task, Bulk API usage with recursive split-on-failure handling, and ObjectMapper configured for LocalDateTime.
Reindex task & controller + params DTO
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ReindexingTask.java, application-engine/src/main/java/com/netgrif/application/engine/elastic/web/ElasticController.java, application-engine/src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/IndexParams.java
ReindexingTask delegates to IElasticIndexService.bulkIndex; controller adds setIndexService and new ADMIN endpoint bulkReindex(IndexParams); new IndexParams DTO carries indexAll, case/task batch sizes, lastRun.
Repository & domain id migration
application-engine/src/main/java/com/netgrif/application/engine/elastic/domain/*Repository.java, nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/*
Remove stringId/mongoId fields and relevant getters in domain objects; repositories convert findByStringIdfindById (Optional) and rename deleteAllByStringIddeleteAllById; some count methods refactored to use id.
Services & queue manager (id migration)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/*.java
Switch service flows (index, delete, search) from stringIdid; use findById Optional upsert patterns; delete duplicates via deleteAllById; update logs and executor keys to use getId().
Mapping / date handling
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java, application-engine/src/test/groovy/.../DataSearchRequestTest.groovy
Change LocalDate/Date conversions to use LocalTime.MIDNIGHT instead of NOON; tests adjusted accordingly.
Serializers
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/serializer/LocalDateTimeJsonSerializer.java, .../LocalDateTimeJsonDeserializer.java
Add serializer formatting "yyyy-MM-dd'T'HH:mm:ss.SSS" and tolerant deserializer accepting optional milliseconds; used by Jackson JsonpMapper.
Spring adapter mappings
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/*
Remove overridden getStringId()/getMongoId() methods (inherit from base); add getCaseValue() exposing List<String> mapped as Text; align Spring Elasticsearch mappings with id-based model.
Events & response bodies
nae-object-library/.../events/*, application-engine/.../workflow/web/responsebodies/Task.java
Event messages and Task _id construction switched to use getId() instead of getStringId().
Tests & queries
application-engine/src/test/groovy/...
Many tests updated to use id/getId/setId; search queries changed from stringIdid (ReindexTest, FilterImportExportTest, etc.).
Menu process query
application-engine/src/main/resources/petriNets/engine-processes/menu/menu_item.xml
Query NOT clause now excludes by id:%s instead of stringId:%s.
CaseField / caseValue
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java, nae-spring-core-adapter/.../CaseField.java
Model gains caseValue List; adapter exposes getCaseValue() mapped as Text.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Admin as Admin Client
  participant API as ElasticController
  participant IndexSvc as IElasticIndexService
  participant Mongo as MongoDB
  participant Map as Mapping Services
  participant ES as Elasticsearch

  Admin->>API: POST /elastic/bulkReindex {IndexParams}
  API->>IndexSvc: bulkIndex(indexAll,lastRun,caseBatchSize,taskBatchSize)

  rect rgb(245,248,255)
  note over IndexSvc: Case reindex (paged via Mongo)
  IndexSvc->>Mongo: Query Cases (page by lastModified)
  Mongo-->>IndexSvc: Case batch
  IndexSvc->>Map: map Cases -> ElasticCase
  Map-->>IndexSvc: ElasticCase list
  IndexSvc->>ES: Bulk index cases (BulkRequest)
  ES-->>IndexSvc: BulkResponse
  IndexSvc->>IndexSvc: on partial failures -> split & retry
  end

  rect rgb(245,255,245)
  note over IndexSvc: Task reindex (batched by caseIds)
  IndexSvc->>Mongo: Query Tasks (by caseIds)
  Mongo-->>IndexSvc: Task batch
  IndexSvc->>Map: map Tasks -> ElasticTask
  Map-->>IndexSvc: ElasticTask list
  IndexSvc->>ES: Bulk index tasks (BulkRequest)
  ES-->>IndexSvc: BulkResponse (check/split on failures)
  end

  IndexSvc-->>API: Completed
  API-->>Admin: 200 OK / message
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch NAE-2168

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai bot added improvement A change that improves on an existing feature Large labels Aug 19, 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: 10

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/elastic/service/ElasticTaskQueueManager.java (1)

38-45: Validate and key by entity ID; current null-check can miss null id and cause runtime errors

You’re guarding on task.getTask().getTaskId(), but later use task.getId() for repository operations. If id is null but taskId is not, repository.findById(null) will throw. Prefer validating (and queuing by) the entity id to align with the rest of the changes.

Apply:

-        if (task.getTask().getTaskId() == null) {
+        if (task.getTask().getId() == null) {
             throw new IllegalArgumentException("Task id cannot be null");
         }
-
-        String taskId = task.getTaskId();
+        String taskId = task.getTask().getId();
         log.debug("Scheduling operation for task: {}", taskId);

If the queue is intentionally keyed by a different business key, keep the queue key as-is, but still validate task.getTask().getId() for null to prevent IllegalArgumentException from downstream repository calls.

🧹 Nitpick comments (27)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java (2)

20-20: If you keep the field, make it null-safe and immutable

The current initialization uses Arrays.asList, which returns a fixed-size, array-backed list and does not handle a null source. Prefer an unmodifiable list and guard against nulls.

Apply this diff:

-        this.caseValue = Arrays.asList(fullTextValue);
+        this.caseValue = (fullTextValue == null) ? null : List.of(fullTextValue);

Notes:

  • If any element in fullTextValue can be null, List.of will throw NPE. In that case, use:
    this.caseValue = (fullTextValue == null) ? null : Arrays.stream(fullTextValue).filter(Objects::nonNull).toList();
    (and add import java.util.Objects;)
  • Consider suppressing Lombok’s public setter for caseValue to avoid external mutation:
    @Setter(AccessLevel.NONE) on the field (and import lombok.Setter and lombok.AccessLevel).

8-8: Import cleanup opportunity

If you adopt List.of(...) as suggested on Line 20 (or remove the field entirely), Arrays becomes unused. Please remove this import to keep the file tidy.

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

28-32: Expose mapping for caseValue — LGTM; consider adding a keyword subfield for exact matches/aggregations

The override and @Field(type = Text) mapping for a List<String> is correct for analyzed full-text queries.

If you also need exact matching, sorting, or aggregations on caseValue, consider a multifield mapping with a keyword subfield (keeps analyzed text while enabling exact queries):

Example (outside the selected range):

import org.springframework.data.elasticsearch.annotations.MultiField;
import org.springframework.data.elasticsearch.annotations.InnerField;
import static org.springframework.data.elasticsearch.annotations.FieldType.Keyword;

@Override
@MultiField(
    mainField = @Field(type = Text),
    otherFields = {
        @InnerField(suffix = "keyword", type = Keyword)
    }
)
public List<String> getCaseValue() {
    return super.getCaseValue();
}

This will produce caseValue (text) and caseValue.keyword for exact match and aggregations without incurring fielddata costs on text.

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

1-18: Preserve fractional precision and align with standard ISO format

Using a fixed SSS pattern truncates to milliseconds. To stay symmetric with typical parsing (optional fractional seconds) and avoid precision loss, prefer ISO formatter (variable fraction digits). Minor: add a null guard (Jackson usually skips nulls, but harmless).

Apply:

-    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");
+    // Use ISO_LOCAL_DATE_TIME to preserve fractional precision when present and keep symmetry with parsing
+    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
@@
-    public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
-        gen.writeString(FORMATTER.format(value));
+    public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
+        if (value == null) {
+            gen.writeNull();
+            return;
+        }
+        gen.writeString(FORMATTER.format(value));
application-engine/src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/IndexParams.java (1)

13-26: Add validation to prevent zero/negative batch sizes and document null-as-default semantics

Currently, clients can submit 0 or negative values, which can break batching logic or cause degenerate loops. If null is intended to mean "use server-configured defaults" (from DataConfigurationProperties), keep Integer but validate when provided.

Proposed change:

 package com.netgrif.application.engine.elastic.web.requestbodies;

 import lombok.Data;
+import jakarta.validation.constraints.Min;

 /**
  * Represents the parameters to configure the indexing operation.
  * This class allows customization of batch sizes for cases and tasks,
  * as well as the option to index all data.
  */
 @Data
 public class IndexParams {

     /**
      * Determines whether to index all available data. Default is {@code false}.
      */
     private boolean indexAll = false;

     /**
-     * Specifies the batch size for cases during indexing. Default is {@code 5000}.
+     * Specifies the batch size for cases during indexing. Default is {@code 5000}.
+     * If null, the server-side default is used. When provided, must be >= 1.
      */
-    private Integer caseBatchSize = 5000;
+    @Min(1)
+    private Integer caseBatchSize = 5000;

     /**
-     * Specifies the batch size for tasks during indexing. Default is {@code 20000}.
+     * Specifies the batch size for tasks during indexing. Default is {@code 20000}.
+     * If null, the server-side default is used. When provided, must be >= 1.
      */
-    private Integer taskBatchSize = 20000;
+    @Min(1)
+    private Integer taskBatchSize = 20000;
 }
application-engine/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticCaseRepository.java (1)

10-12: Clarify and disambiguate your custom deleteAllById

  • Rename the parameter in
    long countByIdAndLastModified(String stringId, long lastUpdated);
    to
    - long countByIdAndLastModified(String stringId, long lastUpdated);
    + long countByIdAndLastModified(String id, long lastUpdated);
  • Your custom void deleteAllById(String id) currently overloads
    CrudRepository.deleteAllById(Iterable<ID>) and may confuse readers. You have two options:
    • Rename it to void deleteAllByIdEquals(String id) so it clearly targets the entity’s id field.
    • Or add a Javadoc explaining “Deletes all documents whose id property equals the given value.”

Call-site locations you’ll need to update if you rename the method:

  • ElasticTaskQueueManager.java (repository.deleteAllById(task.getId()))
  • ElasticCaseService.java (repository.deleteAllById(caseId) and repository.deleteAllById(useCase.getId()))
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (2)

89-91: Ensure creationDateSortable matches the truncated creationDate

You truncate creationDate to milliseconds but compute creationDateSortable from the original (potentially higher-precision) timestamp. Use the truncated value for consistency across fields.

-        creationDate = useCase.getCreationDate().truncatedTo(ChronoUnit.MILLIS);
-        creationDateSortable = Timestamp.valueOf(useCase.getCreationDate()).getTime();
+        creationDate = useCase.getCreationDate().truncatedTo(ChronoUnit.MILLIS);
+        creationDateSortable = Timestamp.valueOf(creationDate).getTime();

109-111: Null-safe version increment

version is a Long. Using version++ will NPE if it is null. Guard the first increment.

-        version++;
+        version = (version == null ? 0L : version) + 1L;

If version is guaranteed non-null by upstream code, please confirm; otherwise this guard avoids sporadic NPEs during updates.

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

160-166: Doc nit: url is now multi-host; update comment

The field is now a List. Adjust the comment to avoid confusion.

-        /**
-         * Hostname for the Elasticsearch server.
-         */
+        /**
+         * Hostnames or base URLs for the Elasticsearch cluster (multi-host support).
+         */

302-339: maxPoolSize default does not track customized size at runtime

maxPoolSize is initialized as size * 10 at field init time; if size is overridden via configuration, maxPoolSize won’t recompute. Either document that it’s an independent property or compute it lazily/at init if not explicitly set.

Option A (document-only): Update the javadoc to clarify it’s an independent configurable property.

Option B (auto-compute when unset):

         @Data
         public static class ExecutorProperties {
             private int size = 50;
             private int timeout = 5;
-            private int maxPoolSize = size * 10;
+            private Integer maxPoolSize; // null means compute from size
             private boolean allowCoreThreadTimeOut = true;
             private int keepAliveSeconds = 30;
             private String threadNamePrefix = "ElasticTaskExecutor-";
+
+            @PostConstruct
+            void init() {
+                if (maxPoolSize == null) {
+                    maxPoolSize = size * 10;
+                }
+            }
         }

162-166: Clarify auth precedence and invalid combinations (token vs username/password)

Given both token and username/password are supported, define precedence (e.g., token first) and validate that mutually exclusive combinations are not used simultaneously.

Option: add a simple validation hook in ElasticsearchProperties:

         @PostConstruct
         public void init() {
+            if (token != null && (username != null || password != null)) {
+                throw new IllegalArgumentException("Configure either token or username/password, not both.");
+            }
             indexSettings.putIfAbsent("max_result_window", 10000000);
             mappingSettings.putIfAbsent("date_detection", false);
             ...
         }

If your ElasticsearchConfiguration already handles this precedence, please confirm and ignore this suggestion.

Also applies to: 177-191

application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ReindexTest.groovy (1)

87-87: Switch to id looks correct; consider idiomatic Groovy string interpolation

Good move aligning the query with the new id field. Minor readability nit: Groovy interpolation avoids manual concatenation and escaping.

Apply this diff for readability:

-            request.query = "id:\"" + it.getStringId() + "\""
+            request.query = "id:\"${it.getStringId()}\""
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskService.java (1)

153-153: Use fully-qualified method reference to avoid class ambiguity

There are two ElasticTask types in play (adapter vs. objects). Using the simple name in the method reference risks confusion and accidental binding. Fully-qualify the method reference to the adapter class to make intent explicit and reduce the chance of future breakage.

Apply this diff to make the reference unambiguous:

-            taskPage = taskService.findAllById(indexedTasks.get().map(ElasticTask::getId).collect(Collectors.toList()));
+            taskPage = taskService.findAllById(
+                indexedTasks.get()
+                    .map(com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask::getId)
+                    .collect(Collectors.toList())
+            );

If you want to go one step further and eliminate the raw cast above, I can propose a typed variant for the unwrapping as a follow-up.

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

81-81: ID migration aligns with the ES shift

Initializing id from task.getStringId() is consistent with the repo-wide move to id. Note there is also a taskId field set to the same value (Line 83); if duplication isn’t required by downstream consumers/mappings, consider consolidating later to avoid divergence.

application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java (1)

260-265: MIDNIGHT normalization is consistent with test updates

Switching Date-only indexing to midnight is coherent with the tests and prevents the Noon skew. No functional issues spotted.

If this normalization is used elsewhere, consider extracting a small helper to avoid drift:

  • Example: private static LocalDateTime atStartOfDay(LocalDate date) { return LocalDateTime.of(date, LocalTime.MIDNIGHT); }
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (2)

63-66: Avoid potential NPE on queue.poll()

Objects.requireNonNull(queue.poll()) can theoretically NPE under contention. Mirror the safer pattern used in scheduleNextTask().

-            elasticTaskExecutor.submit(Objects.requireNonNull(queue.poll()));
+            Runnable first = queue.poll();
+            if (first != null) {
+                elasticTaskExecutor.submit(first);
+            } else {
+                activeTasks.remove(taskId);
+            }

129-135: Duplicate handling: align logs with ID and keep stacktrace in error logging

  • Log after reindex should include the id for consistency.
  • Preserve stacktrace in the error log.
-            repository.save((com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask) task);
-            log.debug("[{}]: Task \"{}\" indexed", task.getCaseId(), task.getTitle());
+            repository.save((com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask) task);
+            log.debug("[{}]: Task \"{}\" [{}] indexed", task.getCaseId(), task.getTitle(), task.getId());
         } catch (RuntimeException e) {
-            log.error("Elastic executor was killed before finish: {}", e.getMessage());
+            log.error("Elastic executor was killed before finish", e);
         }
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticIndexService.java (2)

8-8: Time type choice: prefer Instant over LocalDateTime

Unless you explicitly need locale-specific wall time, Instant avoids timezone and DST ambiguity in distributed systems.


17-17: Document bulkIndex parameter nullability and consider a parameter object

To make the API contract explicit (call sites in ReindexingTask.java and ElasticController.java already pass null for these args), annotate the bulkIndex signature:

• In application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticIndexService.java:

+ import org.springframework.lang.Nullable;
@@
-    void bulkIndex(boolean indexAll, LocalDateTime lastRun, Integer caseBatchSize, Integer taskBatchSize);
+    void bulkIndex(boolean indexAll,
+                   @Nullable LocalDateTime lastRun,
+                   @Nullable Integer caseBatchSize,
+                   @Nullable Integer taskBatchSize);

(Optional) Propagate the same @Nullable annotations to the @Override in ElasticIndexService.java to keep documentation consistent.

(Optional refactor) Introduce a params object to group these arguments and improve readability:

public record BulkIndexParams(
    boolean indexAll,
    @Nullable LocalDateTime lastRun,
    @Nullable Integer caseBatchSize,
    @Nullable Integer taskBatchSize
) {}

void bulkIndex(BulkIndexParams params);
application-engine/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticTaskRepository.java (1)

14-14: Parameter name is misleading; it’s an id, not a taskId

Minor clarity fix: rename the parameter to id to match the property in the derived query and the rest of the codebase.

-    void deleteAllById(String taskId);
+    void deleteAllById(String id);
application-engine/src/main/java/com/netgrif/application/engine/elastic/web/ElasticController.java (1)

100-105: Fix off-by-one page calculation

When count is a multiple of page size, this adds an extra empty page. Use ceiling division.

-                long numOfPages = (count / elasticsearchProperties.getReindexExecutor().getSize()) + 1;
+                long size = elasticsearchProperties.getReindexExecutor().getSize();
+                long numOfPages = (count + size - 1) / size;
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java (1)

104-121: Upsert-by-id path looks good; optionally publish event in duplicate path as well

The Optional-based upsert is correct. Consider publishing the IndexCaseEvent in the duplicate reindex path to keep downstream listeners consistent.

             } catch (InvalidDataAccessApiUsageException ignored) {
                 log.debug("[" + useCase.getId() + "]: Case \"" + useCase.getTitle() + "\" has duplicates, will be reindexed");
                 repository.deleteAllById(useCase.getId());
                 repository.save((com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticCase) useCase);
-                log.debug("[" + useCase.getId() + "]: Case \"" + useCase.getTitle() + "\" indexed");
+                log.debug("[" + useCase.getId() + "]: Case \"" + useCase.getTitle() + "\" indexed");
+                publisher.publishEvent(new IndexCaseEvent(useCase));
             }
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/serializer/LocalDateTimeJsonDeserializer.java (2)

15-20: Broaden fraction parsing to nanos for better interoperability

Parsing only MILLI_OF_SECOND (1–3 digits) is strict; upstream sources may send micro/nanosecond precision. Switching to NANO_OF_SECOND (0–9) keeps compatibility with the existing serializer while accepting a wider range.

Apply this diff:

-    private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
-            .appendPattern("yyyy-MM-dd'T'HH:mm:ss")
-            .optionalStart()
-            .appendFraction(ChronoField.MILLI_OF_SECOND, 1, 3, true)
-            .optionalEnd()
-            .toFormatter();
+    private static final DateTimeFormatter FORMATTER = new DateTimeFormatterBuilder()
+            .appendPattern("yyyy-MM-dd'T'HH:mm:ss")
+            .optionalStart()
+            .appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true)
+            .optionalEnd()
+            .toFormatter();

23-29: Trim input to avoid spurious parse failures on whitespace

Defensive trim of the incoming string prevents failures on values like "2025-08-19T10:01:02.123 " and keeps null/empty handling intact.

Apply this diff:

-        String value = p.getValueAsString();
-        if (value == null || value.isEmpty()) {
+        String value = p.getValueAsString();
+        if (value == null) {
             return null;
         }
-        return LocalDateTime.parse(value, FORMATTER);
+        value = value.trim();
+        if (value.isEmpty()) {
+            return null;
+        }
+        return LocalDateTime.parse(value, FORMATTER);
application-engine/src/main/java/com/netgrif/application/engine/configuration/ElasticsearchConfiguration.java (1)

61-83: Validate multi-host inputs early

Consider failing fast if elasticsearchProperties.getUrl() is null/empty to avoid a confusing client configuration error downstream. Optional, since it's environment/config guarded.

application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticIndexService.java (2)

617-623: ObjectMapper configuration is fine; consider reusing it

The configured objectMapper is not used elsewhere in this class. Either remove it or reuse it (e.g., in parseAnalysisSettings()) for consistency.

If you want to reuse it in parseAnalysisSettings(), change that method to use this.objectMapper instead of instantiating a new one.


527-542: Improve bulk failure diagnostics

The thrown ElasticsearchException doesn’t carry failedDocuments; the message suggests it does. Consider attaching IDs and reasons to the exception metadata or logging them before throwing to aid troubleshooting.

📜 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 ee37ab4 and 30ca813.

📒 Files selected for processing (28)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/ElasticsearchConfiguration.java (2 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/DataConfigurationProperties.java (6 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticCaseRepository.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticTaskRepository.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java (3 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticIndexService.java (4 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (3 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskService.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ReindexingTask.java (6 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/interfaces/IElasticIndexService.java (2 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/web/ElasticController.java (5 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/IndexParams.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/Task.java (1 hunks)
  • application-engine/src/main/resources/petriNets/engine-processes/menu/menu_item.xml (1 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/elastic/DataSearchRequestTest.groovy (1 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ReindexTest.groovy (1 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy (1 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java (1 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (2 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticTask.java (1 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/serializer/LocalDateTimeJsonDeserializer.java (1 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/serializer/LocalDateTimeJsonSerializer.java (1 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/event/events/task/IndexTaskEvent.java (1 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/event/events/workflow/IndexCaseEvent.java (1 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/CaseField.java (2 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (0 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticTask.java (0 hunks)
💤 Files with no reviewable changes (2)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticTask.java
🧰 Additional context used
🧬 Code Graph Analysis (5)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java (3)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/DataField.java (1)
  • Data (10-29)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/FieldWithAllowedNetsField.java (1)
  • Data (10-26)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/CaseField.java (1)
  • NoArgsConstructor (10-33)
application-engine/src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/IndexParams.java (1)
application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/DataConfigurationProperties.java (10)
  • Data (28-465)
  • Data (116-121)
  • Data (126-149)
  • Data (154-392)
  • Data (302-339)
  • Data (341-353)
  • Data (355-367)
  • Data (375-391)
  • Data (401-447)
  • Data (455-464)
application-engine/src/main/java/com/netgrif/application/engine/configuration/ElasticsearchConfiguration.java (2)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/serializer/LocalDateTimeJsonDeserializer.java (1)
  • LocalDateTimeJsonDeserializer (13-30)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/serializer/LocalDateTimeJsonSerializer.java (1)
  • LocalDateTimeJsonSerializer (11-18)
application-engine/src/main/java/com/netgrif/application/engine/elastic/web/ElasticController.java (1)
application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/MessageResource.java (1)
  • MessageResource (10-31)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticIndexService.java (2)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/serializer/LocalDateTimeJsonDeserializer.java (1)
  • LocalDateTimeJsonDeserializer (13-30)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/serializer/LocalDateTimeJsonSerializer.java (1)
  • LocalDateTimeJsonSerializer (11-18)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: Build
🔇 Additional comments (16)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/CaseField.java (1)

6-7: Import addition is appropriate

Importing java.util.List is required for the new getter signature.

application-engine/src/main/java/com/netgrif/application/engine/workflow/web/responsebodies/Task.java (1)

111-117: Switched to id-based ProcessResourceId — looks correct

Using entity.getId() here aligns with the repo-wide migration away from stringId. No functional issues spotted.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/event/events/task/IndexTaskEvent.java (1)

18-19: LGTM: event message now uses canonical id

Message generation via task.getId() matches the ID unification done elsewhere.

application-engine/src/main/resources/petriNets/engine-processes/menu/menu_item.xml (1)

115-116: LGTM: ES query updated to id

Replacing stringId with id in the NOT clause is consistent with the new mapping and avoids accidental hits on the current case.

nae-object-library/src/main/java/com/netgrif/application/engine/objects/event/events/workflow/IndexCaseEvent.java (1)

19-20: LGTM: switched to id-based message

The event message now references elasticCase.getId(), aligning with the ID migration across the codebase.

application-engine/src/test/groovy/com/netgrif/application/engine/elastic/DataSearchRequestTest.groovy (1)

151-151: Updated expectation to MIDNIGHT matches new mapping

Changing to LocalTime.MIDNIGHT aligns with the mapping change in ElasticCaseMappingService#transformDateField. Looks good.

application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (1)

115-127: Upsert by id via Optional is consistent with the ID migration

Lookup by id, update-or-insert, and logging with the id is aligned with the new approach. Casting to the adapter entity is consistent with the repository type.

application-engine/src/main/java/com/netgrif/application/engine/elastic/web/ElasticController.java (1)

78-81: Setter injection for indexService is fine

Wires the new IElasticIndexService dependency cleanly and matches existing style in this controller.

application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseService.java (2)

89-91: Delete by id aligns with the ID migration

Switching to repository.deleteAllById(caseId) is consistent and correct.


146-147: Mapping search result IDs to workflowService is consistent

Using ElasticCase.getId() here is aligned with repository/entity changes.

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

85-96: Custom JsonpMapper wiring looks good

Jackson mapper setup with JavaTimeModule and custom LocalDateTime (de)serializers matches the new ES client and date handling strategy.


98-105: Auth precedence is clear

Credential precedence over token is explicit and acceptable. No changes needed.


64-68: Confirm host:port format for connectedTo(...)
ClientConfiguration.connectedTo(...) expects entries in the form hostname:port (no http:// or https:// prefix). While sanitizeUrls(...) will append your searchPort when an entry has no “:”, it treats any string containing “:” (for example http://my-es) as already valid. Please verify that in all your deployments:

  • The elasticsearch.url property supplies plain hostnames (optionally with ports), not full URLs.
  • If you must support full URLs, update sanitizeUrls to strip the protocol (e.g. u.replaceFirst("^https?://", "")) before appending the port.
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ReindexingTask.java (2)

77-79: Good delegation to the new bulk indexer

Switching the scheduled job to elasticIndexService.bulkIndex(false, lastRun, null, null) aligns with the new batching flow and centralizes reindexing logic.


91-96: Verify ID semantics in countByIdAndLastModified(...)

You pass aCase.getStringId() to a method renamed to countByIdAndLastModified(...). If the repository now strictly uses the canonical id, confirm getStringId() matches the document ID used in ES. If the domain Case exposes getId() as the canonical identifier now, switch to that to avoid false negatives on staleness checks.

Would you like me to scan ElasticCaseRepository and Case to confirm the expected type and source of the ID and propose a patch if needed?

application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticIndexService.java (1)

328-352: Bulk indexing entrypoint fits the new model

Defaults for batch sizes, safe time windowing (after.minusMinutes(2)), and counting before streaming are reasonable. Nice.

Replaced usages of `stringId` with `id` across the codebase for consistency. Removed the `stringId` field and updated related methods, constructors, and tests to use `id`. This change improves clarity and aligns the ID handling logic with standard practices.
Replaced the `javax.validation.Valid` import with `jakarta.validation.Valid` to align with the newer Jakarta API standards. This ensures compatibility with updated dependencies and maintains consistency across the project.
Added @ToString.Exclude annotation to `username`, `password`, and `token` fields in `DataConfigurationProperties` to prevent sensitive information from being exposed in the `toString()` method. This ensures better protection of sensitive credentials in log output or debugging scenarios.
Replaced manual iterators with try-with-resources for streamlined resource management using Stream API. Adjusted batch processing logic to improve safety and clarity. This enhances maintainability and reduces resource leakage risks.
Introduced a new `lastRun` parameter in `IndexParams` to specify a starting point based on the `lastModifiedDate` of cases. Updated `ElasticController` to pass this parameter to the `bulkIndex` method, enabling more targeted reindexing operations.
Updated the repository call to use the correct parameter `task.getId()` instead of `task.getTaskId()`. This ensures that tasks are properly identified and removed, preventing potential errors during task deletion.
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: 7

Caution

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

⚠️ Outside diff range comments (2)
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleTest.groovy (1)

93-97: Fix variable shadowing: netId field remains null

You assign a local variable instead of the class field, which explains the disabled test complaining about a null id. Assign to the field.

Apply this diff:

-        String netId = net.getNet().getStringId()
+        this.netId = net.getNet().getStringId()
application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy (1)

160-169: Bug: mutating the wrong instance inside the executor (data race / incorrect test setup)

Inside both executor blocks you set the title on the outer task instead of the per-iteration taskParallel. This introduces shared-state races and invalidates the assertion intent.

Apply these fixes:

-                    task.setTitle(new I18nString("START" + index))
+                    taskParallel.setTitle(new I18nString("START" + index))

and

-                    task.setTitle(new I18nString("START"))
+                    taskParallel.setTitle(new I18nString("START"))

Given this correction, also align the assertion in the second test to the title you actually set:

-                    assert result.getTitle().getDefaultValue().equals("TestTask"+ index)
+                    assert result.getTitle().getDefaultValue().equals("START")

Also applies to: 244-253

🧹 Nitpick comments (13)
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleTest.groovy (1)

172-173: Align task identifier with ID migration

The test still reads taskId from 'stringId'. If tasks now expose 'id', this will fail silently and make subsequent calls break.

Proposed update (verify API payload first):

-        taskId = response?._embedded?.tasks?.find { it.title == title }?.stringId
+        taskId = response?._embedded?.tasks?.find { it.title == title }?.id

If the API still returns 'stringId' for tasks, consider updating the backend representation for consistency with cases, or leave this as-is.

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

159-167: Clarify docs: support multiple hosts/URLs and pluralize name if kept long-term

The field is a List but the Javadoc says “Hostname.” Either adjust the wording to “hosts/URLs” or consider renaming to 'urls' in a future breaking change window.

Apply this doc-only diff:

-        /**
-         * Hostname for the Elasticsearch server.
-         */
-        private List<String> url = List.of("localhost");
+        /**
+         * Hosts/URLs for the Elasticsearch cluster nodes (e.g., ["localhost", "es1:9200", "https://es2:9200"]).
+         */
+        private List<String> url = List.of("localhost");
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (1)

80-82: Avoid generating a random id in no-args constructor

Constructors used by mappers/deserializers shouldn’t mutate identity. Generating an ObjectId here can cause unintended inserts or upserts.

Apply this diff:

-    public ElasticCase() {
-        this.id = new ObjectId().toString();
-    }
+    public ElasticCase() {
+        // Intentionally left blank: id is provided by source Case or deserializer
+    }
application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy (3)

194-194: Avoid conflating taskId with id; use a realistic Mongo-like id

Per the migration, ElasticTask.id should carry the Mongo Task ObjectId, not the business taskId. Using "TestTask" here can mask mismatches and make reindex behavior non-representative.

Add a stable Mongo-like id in this test and reuse it:

Groovy (outside the selected range, right before setting the id):

def mongoId = new org.bson.types.ObjectId().toHexString()

Then update this line:

-        task.setId("TestTask")
+        task.setId(mongoId)

207-207: Keep id consistent with the “Mongo ObjectId” contract

For the parallel task in the same scenario, set the same Mongo-like id so upserts target a single ES document.

-                    taskParallel.setId("TestTask")
+                    taskParallel.setId(mongoId)

Note: mongoId is defined once in the test method as shown in the previous comment.


250-250: Prefer ObjectId-shaped values over synthesized keys for ElasticTask.id

If each parallel task is meant to be a different ES document, generate proper ObjectId-shaped ids to mirror production.

-                    taskParallel.setId("TestTask"+index)
+                    taskParallel.setId(new org.bson.types.ObjectId().toHexString())
application-engine/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticPetriNetRepository.java (1)

19-19: Method name collides with CrudRepository semantics; prefer using built-in deleteById

Spring Data already provides CrudRepository.deleteById(ID) and deleteAllById(Iterable). Having a custom deleteAllById(String) is confusing and overlaps with the built-in name that expects an Iterable.

Options:

  • Remove this method and use the inherited deleteById(String id).
  • If you truly need a derived delete query by field, rename to avoid confusion (e.g., deleteAllByIdIs(String id)), but for ES doc id the built-in deleteById is the right choice.

At minimum, update the Javadoc to drop the “string ID” legacy wording:

-    /**
-     * Deletes all {@link ElasticPetriNet} entities with the given string ID.
-     *
-     * @param id the string ID of the {@link ElasticPetriNet} entities to delete
-     */
+    /**
+     * Deletes the {@link ElasticPetriNet} document with the given id (ES _id).
+     *
+     * @param id the document id to delete
+     */
-    void deleteAllById(String id);
+    // Prefer using inherited CrudRepository#deleteById(String id)
nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (1)

29-31: Method signature likely not overriding the base-class update; fully qualify and annotate

Because this class name collides with the base type, the parameter here resolves to the adapter type, creating an overload instead of overriding the base-class method. Recommend explicitly overriding the base method to avoid overload confusion and subtle bugs in dynamic dispatch.

Apply this diff:

-    public void update(ElasticCase useCase) {
-        super.update(useCase);
-    }
+    @Override
+    public void update(com.netgrif.application.engine.objects.elastic.domain.ElasticCase useCase) {
+        super.update(useCase);
+    }
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java (1)

66-76: Guard the downcast to the adapter type to avoid ClassCastException

repository.save((adapter.ElasticPetriNet) net) assumes callers always pass the adapter subtype. If any caller provides the base type (objects.elastic.domain.ElasticPetriNet), this will throw at runtime.

  • Either change the service/interface to accept the adapter subtype and remove the cast, or guard the cast and fail fast with a clear log.

Suggested localized fix:

-        Optional<com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticPetriNet> elasticPetriNetOptional = repository.findById(net.getId());
-        if (elasticPetriNetOptional.isEmpty()) {
-            repository.save((com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticPetriNet) net);
+        Optional<com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticPetriNet> elasticPetriNetOptional =
+                repository.findById(net.getId());
+        if (elasticPetriNetOptional.isEmpty()) {
+            if (net instanceof com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticPetriNet adapterNet) {
+                repository.save(adapterNet);
+            } else {
+                log.error("Expected adapter ElasticPetriNet, got [{}]. Skipping index.", net.getClass().getName());
+                return;
+            }
         } else {
             com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticPetriNet elasticNet = elasticPetriNetOptional.get();
             elasticNet.update(net);
             repository.save(elasticNet);
         }

Optionally, if feasible, update IElasticPetriNetService#index to accept the adapter type to enforce this at compile time.

application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticIndexService.java (4)

339-346: Make the 2-minute skew guard configurable

The after.minusMinutes(2) guard is reasonable to avoid clock skew/race conditions, but hardcoding it reduces flexibility and makes testing harder.

Externalize this offset into DataConfigurationProperties.ElasticsearchProperties.batch.skewGuardSeconds (or similar) and reference it here. If you want, I can draft the config property and wiring.


492-520: Improve failure logging and split message; consider catching IO separately

The recursive split approach is fine. The log message “Dividing the requirement.” is unclear. Also, bulk(...) can throw checked IO-related exceptions depending on the transport.

Apply this diff for clearer logs:

-            log.warn("Dividing the requirement.");
+            log.warn("Bulk failed for {} ops; splitting in half and retrying.", operations.size());

Optional: add a separate catch for IOException (if your transport throws it) to provide a more actionable message about connectivity/timeouts.


531-547: Use response.errors() and derive status from failing items

Relying on getFirst() may pick a successful item’s status. The client exposes response.errors() and per-item statuses. Aggregate accurately and throw with a representative status.

Apply this diff:

-    private void checkForBulkUpdateFailure(BulkResponse response) {
-        Map<String, String> failedDocuments = new HashMap<>();
-        response.items().forEach(item -> {
-            if (item.error() != null) {
-                failedDocuments.put(item.id(), item.error().reason());
-            }
-        });
-
-        if (!failedDocuments.isEmpty()) {
-            String message = "Bulk indexing has failures. Use ElasticsearchException.getFailedDocuments() for details [" + failedDocuments.values() + "]";
-            throw new ElasticsearchException(message,
-                    ErrorResponse.of(builder -> builder
-                            .error(ErrorCause.of(errorCauseBuilder -> errorCauseBuilder.reason(message)))
-                            .status(response.items().getFirst().status())));
-        }
-    }
+    private void checkForBulkUpdateFailure(BulkResponse response) {
+        if (!response.errors()) {
+            return;
+        }
+
+        Map<String, String> failedDocuments = new LinkedHashMap<>();
+        response.items().forEach(item -> {
+            if (item.error() != null) {
+                failedDocuments.put(item.id(), item.error().reason());
+            }
+        });
+
+        if (!failedDocuments.isEmpty()) {
+            String message = "Bulk indexing has failures: " + failedDocuments;
+            int status = response.items().stream()
+                    .filter(i -> i.error() != null)
+                    .map(i -> i.status() == null ? 500 : i.status())
+                    .findFirst()
+                    .orElse(500);
+            throw new ElasticsearchException(message,
+                    ErrorResponse.of(builder -> builder
+                            .error(ErrorCause.of(ecb -> ecb.reason(message)))
+                            .status(status)));
+        }
+    }

621-627: Configured ObjectMapper is unused; either wire into the ES client or remove

You create and configure an ObjectMapper (with JavaTimeModule and custom LocalDateTime serializers), but never use it. The Elasticsearch Java client uses its own mapper. If you intend to control date serialization (e.g., yyyy-MM-dd'T'HH:mm:ss.SSS), you must provide this mapper when building the client transport (JacksonJsonpMapper).

  • If ES client is built elsewhere (e.g., in a configuration class), inject a preconfigured ElasticsearchClient that uses:
    • new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper))
  • Otherwise, remove the field and method to avoid confusion.

Would you like me to draft the configuration snippet to build the client with the custom mapper?

📜 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 30ca813 and 90c1112.

📒 Files selected for processing (18)
  • application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/DataConfigurationProperties.java (6 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/domain/ElasticPetriNetRepository.java (1 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticIndexService.java (4 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java (3 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (3 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/web/ElasticController.java (5 hunks)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/IndexParams.java (1 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/auth/WorkflowAuthorizationServiceTest.groovy (3 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy (3 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/insurance/mvc/InsuranceTest.groovy (1 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleTest.groovy (1 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy (1 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (2 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java (1 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticTask.java (1 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (1 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticPetriNet.java (1 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticTask.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticTask.java
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/web/requestbodies/IndexParams.java
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/web/ElasticController.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticTask.java
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.600Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2025-08-19T20:07:15.600Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.600Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

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

Applied to files:

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

Applied to files:

  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
🧬 Code Graph Analysis (1)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticIndexService.java (4)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/serializer/LocalDateTimeJsonDeserializer.java (1)
  • LocalDateTimeJsonDeserializer (13-30)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/serializer/LocalDateTimeJsonSerializer.java (1)
  • LocalDateTimeJsonSerializer (11-18)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java (1)
  • Slf4j (28-327)
application-engine/src/main/java/com/netgrif/application/engine/startup/runner/ElasticsearchRunner.java (1)
  • Slf4j (16-66)
⏰ 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 (12)
application-engine/src/main/java/com/netgrif/application/engine/configuration/properties/DataConfigurationProperties.java (2)

277-279: Good addition: Batch sizes with validation for bulk indexing

Introducing BatchProperties with @min validations is a solid foundation for tunable, safe batching.

Also applies to: 373-395


174-176: Keep searchPort—it’s still in use
The searchPort property is referenced in several places and shouldn’t be removed:

  • DashboardService.java (line 21): used to build the Elasticsearch query URL
  • ElasticsearchConfiguration.java (line 108): used in sanitizeUrls to append a default port when none is provided
  • application.yaml (line 19): defines the default value

Since these usages rely on searchPort, it must remain.

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

91-93: LGTM: Truncate creationDate to milliseconds

This aligns with ES date precision and avoids noisy diffs.


84-86: Case#getId() is undefined in the workflow domain Case class

The Case class (nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/Case.java) only provides getStringId() and has no getId() method. Applying the suggested change will break compilation. Before replacing any calls to getStringId(), add a getId() alias in the Case class, for example:

// in Case.java
public String getId() {
    return getStringId();
}

Likely an incorrect or invalid review comment.

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

22-24: Adding an explicit no-arg constructor is correct here

Subclasses that declare other constructors won’t get a synthetic no-arg constructor; this ensures Spring Data/serializers can instantiate the class. LGTM.

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

41-43: Mapping id from PetriNet.stringId — LGTM

Assigning this.id = net.getStringId() matches the migration to use the Mongo id as the ES doc id.

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

21-23: No-args constructor is a good addition for Spring Data/Jackson

Explicit default constructor helps frameworks and avoids Lombok coupling. Looks good.

application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticPetriNetService.java (3)

78-82: Duplicate handling path is sound

Catching InvalidDataAccessApiUsageException, deleting by ID, and re-saving is a pragmatic recovery for duplicate index state. Logging also references the new ID model consistently.


94-96: Removal by ID aligns with the ID migration

Using deleteAllById(id) matches the standardized ID usage. LGTM.


124-125: Search mapping: switched to ID correctly

Collecting PetriNet IDs via ElasticPetriNet::getId before fetching domain objects is consistent with the migration away from stringId.

application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticIndexService.java (2)

399-406: Task batching page math and streaming look good

Uses ceiling division and try-with-resources on the Mongo stream. Matches the fixes applied to case batching.


370-379: No ID mismatch — keep aCase.getStringId()

Task.caseId is a String that holds the Case string id (ProcessResourceId full id), so using aCase.getStringId() to query tasks is correct. Evidence:

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/Task.java — private String caseId; (getter/setter present).
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/workflow/domain/ProcessResourceId.java — toString()/getFullId() returns the composite "-" used by Case.getStringId().
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticIndexService.java — collects caseIds.add(aCase.getStringId()) and queries Tasks with Criteria.where("caseId").in(caseIds).
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticTask.java — maps this.caseId = task.getCaseId().
  • Multiple call sites use Case.getStringId() when querying tasks (e.g. FilterImportExportService, MenuImportExportService, TaskRepository.findAllByCaseId / findByCaseIdIn).

Conclusion: the original concern is incorrect — no change required.

Likely an incorrect or invalid review comment.

Previously, the task removal process logged and deleted tasks using inconsistent ID references (`getId` vs. `getTaskId`). This change ensures consistent usage of `getTaskId` for both logging and deletion, preventing potential errors and improving clarity.
Replaced the deprecated `getId` method with direct `.id` field access for better readability and consistency. This update ensures compatibility with the current data structure and cleans up test code syntax.
Removed explicit no-argument constructors across Elastic* domain objects and replaced them with the @NoArgsConstructor annotation for cleaner and more consistent code. Additionally, adjusted test cases to properly initialize task IDs where required and updated instance checks to reflect repository changes.
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

Caution

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

⚠️ Outside diff range comments (7)
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleTest.groovy (3)

90-97: Fix variable shadowing that leaves netId field null (cause of the Disabled test)

In before(), the local String netId = ... shadows the class field netId. The class field remains null, which leads to an invalid payload (netId: null) and the IllegalArgumentException noted in the @disabled reason.

Apply this diff to set the class field and use it consistently:

-        String netId = net.getNet().getStringId()
+        this.netId = net.getNet().getStringId()
 
-        def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin])
-        def processRoles = userProcessRoleRepository.findAllByProcessId(netId)
+        def auths = importHelper.createAuthorities(["user": Authority.user, "admin": Authority.admin])
+        def processRoles = userProcessRoleRepository.findAllByProcessId(this.netId)

After this change, consider removing the @disabled annotation.


171-173: Update test assertions to use id instead of removed stringId

The migration removed stringId from task payloads—tests must be updated to use .id. We found two occurrences:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleTest.groovy:172
  • application-engine/src/test/groovy/com/netgrif/application/engine/insurance/mvc/InsuranceTest.groovy:287

Replace in both files:

-        taskId = response?._embedded?.tasks?.find { it.title == title }?.stringId
+        taskId = response?._embedded?.tasks?.find { it.title == title }?.id

After updating, scan for any remaining stringId usages in tests:

rg -nP --type groovy '\._embedded\?.tasks\?.*stringId'

141-147: Replace deprecated MediaType.APPLICATION_JSON_UTF8_VALUE with APPLICATION_JSON_VALUE

Spring Framework 6 (Spring Boot 3.x) no longer provides MediaType.APPLICATION_JSON_UTF8_VALUE. Update all occurrences in ProcessRoleTest.groovy to use MediaType.APPLICATION_JSON_VALUE so the tests compile.

Locations:

  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleTest.groovy
    • Lines 141–147
    • Lines 161–167

Apply this diff in both places:

-                .contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
+                .contentType(MediaType.APPLICATION_JSON_VALUE)
application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy (3)

165-173: Bug: Mutating the wrong instance inside parallel section (task vs taskParallel)

Within the worker, you instantiate taskParallel but then set fields on the outer task. You subsequently schedule indexing for taskParallel, which is missing id, taskId, and title. This breaks isolation and makes the test nondeterministic.

Apply this diff:

-                    ElasticTask taskParallel = new com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask()
-                    task.setId("TestTask" + index)
-                    task.setTaskId("TestTask" + index)
-                    task.setTitle(new I18nString("START" + index))
-                    taskParallel.setProcessId("TestProcess")
-                    Future<ElasticTask> resultFuture = elasticTaskService.scheduleTaskIndexing(taskParallel)
+                    ElasticTask taskParallel = new com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask()
+                    taskParallel.setId("TestTask" + index)
+                    taskParallel.setTaskId("TestTask" + index)
+                    taskParallel.setTitle(new I18nString("START" + index))
+                    taskParallel.setProcessId("TestProcess")
+                    Future<ElasticTask> resultFuture = elasticTaskService.scheduleTaskIndexing(taskParallel)

207-215: Same bug: set fields on taskParallel, not task

Repeat of the above issue in reindexTaskTest().

Apply this diff:

-                    ElasticTask taskParallel = new com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask()
-                    task.setId("TestTask" + index)
-                    task.setTaskId("TestTask" + index)
-                    task.setTitle(new I18nString("START" + index))
-                    Future<ElasticTask> resultFuture = elasticTaskService.scheduleTaskIndexing(taskParallel)
+                    ElasticTask taskParallel = new com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask()
+                    taskParallel.setId("TestTask" + index)
+                    taskParallel.setTaskId("TestTask" + index)
+                    taskParallel.setTitle(new I18nString("START" + index))
+                    Future<ElasticTask> resultFuture = elasticTaskService.scheduleTaskIndexing(taskParallel)

250-257: Two issues: wrong instance mutated and inconsistent assertion

  • Fields are set on task instead of taskParallel.
  • The assertion expects title "TestTask${index}" but you set title to "START".

Apply this diff:

-                    ElasticTask taskParallel = new com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask()
-                    task.setId("TestTask" + index)
-                    task.setTaskId("TestTask" + index)
-                    task.setTitle(new I18nString("START"))
-                    Future<ElasticTask> resultFuture = elasticTaskService.scheduleTaskIndexing(taskParallel)
+                    ElasticTask taskParallel = new com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask()
+                    taskParallel.setId("TestTask" + index)
+                    taskParallel.setTaskId("TestTask" + index)
+                    taskParallel.setTitle(new I18nString("START"))
+                    Future<ElasticTask> resultFuture = elasticTaskService.scheduleTaskIndexing(taskParallel)

And either fix the expectation to match title:

-                    assert result.getTitle().getDefaultValue().equals("TestTask"+ index)
+                    assert result.getTitle().getDefaultValue().equals("START")

Or, if the intent was to assert identity, prefer checking taskId:

-                    assert result.getTitle().getDefaultValue().equals("TestTask"+ index)
+                    assert result.getTaskId().equals("TestTask" + index)
nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (1)

109-110: Prevent NPE when incrementing version in ElasticCase.update

The version field is declared as Long and isn’t initialized in the constructor, so version++ will throw a NullPointerException on the first call.

• File: nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
Line 109, replace:

    version++;

with:

    version = (version == null) ? 1L : version + 1L;

• Optional: if version is meant to mirror your domain Case version rather than act as an index-local counter, initialize it in the ElasticCase(Case useCase) constructor (e.g. this.version = useCase.getVersion()) and adjust your increment logic to reflect domain semantics.

♻️ Duplicate comments (1)
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy (1)

134-134: Post-delete ES check corrected to assert absence — LGTM

The assertion now correctly verifies that the ES document is absent after delete. This addresses the earlier inversion and aligns with the Optional-based repository API.

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

132-136: Replace fixed sleep with polling to de-flake ES eventual consistency

Fixed sleeps are brittle and slow. Poll for the ES condition with a short timeout to make the test more reliable and faster.

Apply this diff:

         petriNetService.deletePetriNet(testNet.stringId, superCreator.getLoggedSuper())
         assert petriNetRepository.count() == processCount
-        Thread.sleep(5000)
-        assert elasticPetriNetRepository.findById(testNet.stringId).isEmpty()
+        awaitTrue("Elastic doc deleted") { elasticPetriNetRepository.findById(testNet.stringId).isEmpty() }

Add this helper method inside the test class (e.g., near the bottom):

private static void awaitTrue(String description, Closure<Boolean> condition, long timeoutMs = 10_000L, long pollMs = 100L) {
    long deadline = System.currentTimeMillis() + timeoutMs
    while (System.currentTimeMillis() < deadline) {
        if (condition.call()) return
        Thread.sleep(pollMs)
    }
    assert false : "Timed out waiting for condition: ${description}"
}

142-149: Prefer Assertions.assertThrows over manual try/catch

Cleaner, asserts the exception type, and keeps the test focused.

-        boolean exceptionThrown = false
-        try {
-            petriNetService.get(new ObjectId(testNet.stringId))
-        } catch (IllegalArgumentException e) {
-            exceptionThrown = true
-            assert e.message.contains(testNet.stringId)
-        }
-        assert exceptionThrown
+        def ex = org.junit.jupiter.api.Assertions.assertThrows(IllegalArgumentException) {
+            petriNetService.get(new ObjectId(testNet.stringId))
+        }
+        assert ex.message?.contains(testNet.stringId)

42-42: Provide a reason for @disabled to aid future re-enablement

Attaching context (e.g., ticket ID, flakiness reason) helps maintainers know when/how to re-enable.

-@Disabled
+@Disabled("Flaky due to ES eventual consistency; re-enable after delete propagation is deterministic (NAE-2168)")
application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy (1)

159-181: Shut down ExecutorService to avoid thread leakage in tests

The fixed thread pool isn't shut down, which can leak threads and affect subsequent tests.

Apply this pattern at the end of each test using executorService:

         latch.await(5, TimeUnit.SECONDS)
+        executorService.shutdownNow()

And in the test with 50s await:

         latch.await(50, TimeUnit.SECONDS)
+        executorService.shutdownNow()

Also applies to: 201-223, 243-265

📜 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 fbaafc3 and 4850288.

📒 Files selected for processing (10)
  • application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy (8 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/insurance/mvc/InsuranceTest.groovy (1 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/domain/roles/ProcessRoleTest.groovy (1 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy (1 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java (2 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java (1 hunks)
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticTask.java (1 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java (0 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticPetriNet.java (0 hunks)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticTask.java (0 hunks)
💤 Files with no reviewable changes (3)
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticCase.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticPetriNet.java
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticTask.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • application-engine/src/test/groovy/com/netgrif/application/engine/insurance/mvc/InsuranceTest.groovy
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticTask.java
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.600Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2025-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/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy
📚 Learning: 2025-08-19T20:07:15.600Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.600Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

  • application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • 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 (4)
application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy (1)

114-131: Confirmed — keep using stringId (no test change required)

Search shows the project still exposes and relies on stringId as the PetriNet identifier and the Elastic mapping is populated from it, so the test's use of testNet.stringId is correct.

  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/petrinet/domain/PetriNet.java — public String getStringId() (returns _id.toString())
  • nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticPetriNet.java — constructor sets this.id = net.getStringId()
  • nae-spring-core-adapter/src/main/java/com/netgrif/application/engine/adapter/spring/elastic/domain/ElasticPetriNet.java — @id on getId() which returns super.getId() (mapped to the same value)
  • application-engine/src/test/groovy/com/netgrif/application/engine/petrinet/service/PetriNetServiceTest.groovy — testNet.stringId usage is consistent with current domain API (many other tests still reference .stringId)

No changes needed to the test unless the domain API is intentionally changed in a future commit to use a different identifier (e.g., migrating callers to getId()).

application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy (1)

96-99: LGTM on migrating to setId(...)

The switch from setStringId(...) to setId(...) aligns with the new identity model and keeps the tests consistent with the updated ElasticTask API.

Also applies to: 130-134, 153-157, 196-199, 239-242

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

82-82: LGTM on canonicalizing to id = useCase.getStringId()

This is consistent with the ID consolidation (single id field representing the Case's Mongo/Object ID).


89-89: Good: truncate creationDate to milliseconds

Aligns with ES date precision and avoids mapper round-trip drift.

Updated the null check and logging to use the correct method `getId()` instead of `getTaskId()`. This ensures proper validation and accurate debug logs for scheduled tasks.
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

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/elastic/service/ElasticTaskQueueManager.java (1)

88-90: Do not clear the active flag before scheduling next; also use the same id key for queue operations

Clearing activeTasks before polling the next task allows a racing scheduleOperation to spin up a second worker for the same key. Let scheduleNextTask clear the flag only when the queue is empty, and pass the same key used during enqueueing.

Apply this diff:

-            activeTasks.remove(task.getTaskId());
-            scheduleNextTask(task.getTaskId());
+            // Keep active flag until the queue drains; scheduleNextTask clears it when empty.
+            scheduleNextTask(task.getTask().getId());
♻️ Duplicate comments (1)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (1)

140-144: Delete by canonical document id (task.getId), not taskId; align with repository API and PR migration to id

This repeats a previously raised concern. With id now being the canonical unique identifier, delete by id to avoid accidental multi-doc deletes and to be consistent with indexTaskWorker’s duplicate cleanup.

Apply this diff:

-        log.debug("Remove task [{}] in thread [{}]", task.getTaskId(), Thread.currentThread().getName());
+        log.debug("Remove task [{}] in thread [{}]", task.getId(), Thread.currentThread().getName());
@@
-            log.debug("[{}]: Task \"{}\" [{}] removed", task.getCaseId(), task.getTitle(), task.getTaskId());
-            return repository.deleteAllByTaskId(task.getTaskId());
+            log.debug("[{}]: Task \"{}\" [{}] removed", task.getCaseId(), task.getTitle(), task.getId());
+            repository.deleteAllById(task.getId());
+            return task;
🧹 Nitpick comments (2)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (2)

55-61: Log the actual exception and avoid string concatenation in logs

Use parameterized logging and pass the throwable so stack traces are preserved.

Apply this diff:

-        } catch (Exception e) {
-            log.error("Queue error:" + e.getMessage());
-            throw e;
-        }
+        } catch (Exception e) {
+            log.error("Queue error while scheduling operation for task {}", taskId, e);
+            throw e;
+        }

72-91: Unify logging key in processTask and avoid mixing id sources

Optional refactor: derive a single key once and use it for logs and queue lifecycle to reduce drift between id and taskId.

Example:

private ElasticTask processTask(ElasticTaskJob task) {
    final String key = task.getTask().getId();
    try {
        log.debug("Processing task: {}", key);
        switch (task.getJobType()) {
            case INDEX:
                return indexTaskWorker(task.getTask());
            case REMOVE:
                return removeTaskWorker(task.getTask());
            default:
                log.warn("Unknown job type for task: {}", key);
                throw new IllegalArgumentException("Unknown job type: " + task.getJobType());
        }
    } catch (Exception e) {
        log.error("Error processing task {}: {}", key, e.getMessage(), e);
        throw e;
    } finally {
        // see fix on Lines 88-90 to avoid clearing active prematurely
        scheduleNextTask(key);
    }
}
📜 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 4850288 and 108c089.

📒 Files selected for processing (1)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (4 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.600Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
⏰ 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). (3)
  • GitHub Check: task-list-completed
  • GitHub Check: task-list-completed
  • GitHub Check: Build
🔇 Additional comments (1)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (1)

16-16: LGTM: Optional import aligns with repository.findById usage

The move to Optional improves null-safety and reads better with the new repository API.

Replace redundant `getTask().getId()` calls with `getTaskId()` for clarity and simplify task identification. Update related test cases to reflect changes and ensure consistency.
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

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/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy (1)

251-257: Fix variable usage and assertion mismatch in parallel test.

  • Same bug: you mutate task instead of taskParallel.
  • Title is set to "START" (no index), but the assertion expects "TestTask" + index. Align these.
-                    task.setId("TestTask" + index)
-                    task.setTaskId("TestTask" + index)
-                    task.setTitle(new I18nString("START"))
+                    taskParallel.setId("TestTask" + index)
+                    taskParallel.setTaskId("TestTask" + index)
+                    taskParallel.setTitle(new I18nString("START" + index))
                     Future<ElasticTask> resultFuture = elasticTaskService.scheduleTaskIndexing(taskParallel)
                     ElasticTask result = resultFuture.get()
                     assert result != null
-                    assert result.getTitle().getDefaultValue().equals("TestTask"+ index)
+                    assert result.getTitle().getDefaultValue().equals("START" + index)
🧹 Nitpick comments (3)
application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy (3)

95-103: Use a single ElasticTask type consistently (adapter vs. objects).

You’re constructing com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask while the imported ElasticTask refers to com.netgrif.application.engine.objects.elastic.domain.ElasticTask. This is confusing and risks type mismatches. Prefer aligning the declared type with the constructed one (apply similarly in other tests).

Apply this change locally in this spot (and mirror it in the other test methods that instantiate the adapter class):

-ElasticTask task = new com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask()
+com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask task = new com.netgrif.application.engine.adapter.spring.elastic.domain.ElasticTask()

Note: The move from stringId to id is correct per the migration; just ensure all API points expect/return the adapter type where used.


139-146: CountDownLatch is not wired; this is a timed sleep and may be flaky.

latch is never decremented, so await just waits for a timeout. Prefer polling the repository or awaiting a completion signal from the service to reduce flakiness.

Example replacement for these lines:

-CountDownLatch latch = new CountDownLatch(1)
-elasticTaskService.removeByPetriNetId("Process")
-latch.await(10, TimeUnit.SECONDS)
-ElasticTask deletedTask = elasticTaskRepository.findByTaskId("TestTask1")
+elasticTaskService.removeByPetriNetId("Process")
+long deadline = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(10)
+while (System.currentTimeMillis() < deadline) {
+    if (elasticTaskRepository.findByTaskId("TestTask1") == null) {
+        break
+    }
+    Thread.sleep(50)
+}
+ElasticTask deletedTask = elasticTaskRepository.findByTaskId("TestTask1")

239-241: Nit: consider setting taskId alongside id for consistency across tests.

Not strictly required here, but keeping both identifiers initialized uniformly avoids surprises if downstream logic queries by taskId.

📜 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 108c089 and f141ee9.

📒 Files selected for processing (2)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (4 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy (7 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.600Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2025-08-19T20:07:15.600Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.600Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.

Applied to files:

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

Applied to files:

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

130-132: LGTM: id/taskId initialization is consistent with the migration.

Initializing both id and taskId to the same value aligns with the new identity model in tests.


153-155: LGTM: base record’s id initialization is aligned with id-based identity.


196-199: LGTM: id initialization before reindexing is fine.

Retoocs
Retoocs previously approved these changes Aug 20, 2025
Enhanced logging to include task ID during indexing after duplicate cleanup. Corrected variable assignments and made minor adjustments in test cases to fix and align task ID consistency for better clarity and reliability.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

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

⚠️ Outside diff range comments (2)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (2)

133-136: Don’t swallow runtime failures — propagate so futures complete exceptionally

Catching RuntimeException and returning null masks failures. Let processTask handle the error and complete the Future exceptionally.

-        } catch (RuntimeException e) {
-            log.error("Elastic executor was killed before finish: {}", e.getMessage());
-        }
+        } catch (RuntimeException e) {
+            log.error("Elastic executor was killed before finish: {}", e.getMessage(), e);
+            throw e;
+        }

139-147: Removal worker should also propagate runtime failures

Same rationale as index path: don’t mask remove errors by returning task.

-        } catch (RuntimeException e) {
-            log.error("Elastic executor was killed before finish: {}", e.getMessage());
-        }
-        return task;
+        } catch (RuntimeException e) {
+            log.error("Elastic executor was killed before finish: {}", e.getMessage(), e);
+            throw e;
+        }

Note: processTask will catch and rethrow, and the caller future will complete exceptionally as expected.

♻️ Duplicate comments (1)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (1)

142-143: Confirm delete key: id vs taskId (prior discussion) and align logs accordingly

Given the PR-wide migration to id and the earlier resolution that task IDs are globally unique now, please confirm whether removal should target repository.deleteAllById(task.getId()) instead of deleteAllByTaskId(task.getTaskId()). The current log also references taskId.

If the canonical identifier is id, consider:

-            log.debug("[{}]: Task \"{}\" [{}] removed", task.getCaseId(), task.getTitle(), task.getTaskId());
-            return repository.deleteAllByTaskId(task.getTaskId());
+            log.debug("[{}]: Task \"{}\" [{}] removed", task.getCaseId(), task.getTitle(), task.getId());
+            return repository.deleteAllById(task.getId());

To verify repository capabilities and current usages:

#!/bin/bash
# Show ElasticTaskRepository signatures for delete methods
rg -nP -C3 'interface\s+ElasticTaskRepository\b|deleteAllBy(TaskId|Id)\s*\(' --type=java

# Call sites of deleteAllByTaskId vs deleteAllById
rg -nP -C3 '\bdeleteAllByTaskId\s*\(' --type=java
rg -nP -C3 '\bdeleteAllById\s*\(' --type=java
🧹 Nitpick comments (5)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (5)

55-61: Improve error logging to include stacktrace

Concatenating the message drops the stacktrace. Log the exception object.

Apply:

-            log.error("Queue error:" + e.getMessage());
+            log.error("Queue error: {}", e.getMessage(), e);

115-117: Align start-of-index log with canonical id

Other logs in this method use task.getId(); use it here too for traceability.

-        log.debug("Indexing task [{}] in thread [{}]", task.getTaskId(), Thread.currentThread().getName());
+        log.debug("Indexing task [{}] in thread [{}]", task.getId(), Thread.currentThread().getName());

150-176: Prefer using scheduleOperation() for removals to preserve single-worker-per-key semantics

By calling processTask(job) directly via supplyAsync, you bypass the per-task queue and activeTasks gating. Using scheduleOperation keeps sequencing guarantees consistent with INDEX jobs.

-            CompletableFuture<ElasticTask> removeJobFuture = CompletableFuture.supplyAsync(() -> processTask(job), elasticTaskExecutor);
+            Future<ElasticTask> removeJobFuture = scheduleOperation(job);

The timeout loop with removeJobFuture.get(waitTime, TimeUnit.SECONDS) can stay unchanged.


109-113: Minor: drop throws InterruptedException and consider graceful shutdown wait

The method doesn’t throw, and you might want to await termination if shutdown ordering matters at shutdown.

-    public void destroy() throws InterruptedException {
+    public void destroy() {
         log.info("Shutting down ElasticTaskQueueManager");
         elasticTaskExecutor.shutdown();
     }

Optional: if you need to wait for completion, consider awaiting the underlying pool termination.


88-104: Redundant activeTasks.remove in scheduleNextTask()’s else branch

activeTasks is already removed in processTask’s finally before calling scheduleNextTask. The extra removal is redundant but harmless.

-            } else {
-                activeTasks.remove(taskId);
-                taskQueues.remove(taskId);
-            }
+            } else {
+                taskQueues.remove(taskId);
+            }
📜 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 f141ee9 and 30def80.

📒 Files selected for processing (2)
  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (4 hunks)
  • application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy (8 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • application-engine/src/test/groovy/com/netgrif/application/engine/elastic/ElasticTaskTest.groovy
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/test/groovy/com/netgrif/application/engine/filters/FilterImportExportTest.groovy:341-341
Timestamp: 2025-08-19T20:07:15.600Z
Learning: In the Elasticsearch migration, the stringId property was removed from ElasticCase and ElasticTask, with the id property now containing the MongoDB case/task object ID directly. This consolidation eliminates redundancy and simplifies the data model.
📚 Learning: 2025-08-20T07:27:02.645Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java:38-45
Timestamp: 2025-08-20T07:27:02.645Z
Learning: When reviewing ElasticTaskQueueManager changes, task.getTask().getId() returns the document identifier while task.getTaskId() returns the business task identifier. The queue operations should use consistent identifiers throughout the lifecycle (scheduling, processing, cleanup).

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java
📚 Learning: 2025-08-20T07:24:22.531Z
Learnt from: renczesstefan
PR: netgrif/application-engine#339
File: application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java:38-45
Timestamp: 2025-08-20T07:24:22.531Z
Learning: When renczesstefan indicates a conversation is resolved after conflicting guidance, respect their decision and don't push for further changes.

Applied to files:

  • application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: 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 (4)
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticTaskQueueManager.java (4)

16-16: LGTM: Optional import matches new repository API usage

Adding Optional is appropriate for repository.findById() usage below.


119-127: Use of Optional and update/insert flow looks correct

findById/update/save path is sound and aligns with the id-centric repository API.


128-133: Duplicate-cleanup path now assigns saved entity — good; keep id-centric logging

Nice fix assigning repository.save(...) to elasticTask and logging with id. This prevents returning null and keeps logs consistent.


38-43: Verified: Business taskId queue keying is correct and repository methods align

  • scheduleOperation uses task.getTaskId() as the queue key
  • removeTaskWorker calls repository.deleteAllByTaskId(task.getTaskId()) for cleanup
  • indexTaskWorker uses repository.findById(task.getId()) / deleteAllById(task.getId()) on the document ID
  • ElasticTaskRepository declares exactly these methods (findByTaskId, deleteAllByTaskId, findById, deleteAllById) and no conflicting usages exist

All checks pass—no changes needed.

@renczesstefan renczesstefan requested a review from Retoocs August 20, 2025 13:00
@machacjozef machacjozef merged commit ef2f7c1 into release/7.0.0-rev6 Aug 20, 2025
6 of 7 checks passed
@machacjozef machacjozef deleted the NAE-2168 branch August 20, 2025 15:35
@coderabbitai coderabbitai bot mentioned this pull request Sep 6, 2025
18 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement A change that improves on an existing feature Large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants