Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,9 @@ protected Optional<DataField> transformDataField(String fieldId, Case useCase) {
}
Set mapValues = optValues.get();
Map<String, I18nString> options = this.getFieldOptions(multichoiceMap, netField);
List<Map.Entry<String, I18nString>> values = new ArrayList<>();
List<Map.Entry<String, Collection<String>>> values = new ArrayList<>();
for (String key : (Set<String>) mapValues) {
I18nString selectedValue = options.get(key);
values.add(new AbstractMap.SimpleEntry<>(key, selectedValue != null ? selectedValue : new I18nString("")));
values.add(new AbstractMap.SimpleEntry<>(key, I18nStringUtils.collectTranslations(options.get(key))));
}
Comment on lines +100 to 103
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the file and the complete method context
cat -n application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java | sed -n '80,110p'

Repository: netgrif/application-engine

Length of output: 2089


🏁 Script executed:

# Search for I18nStringUtils.collectTranslations implementation
find . -type f -name "*.java" | xargs grep -l "I18nStringUtils" | head -5

Repository: netgrif/application-engine

Length of output: 292


🏁 Script executed:

# Look for the I18nStringUtils class definition
fd -type f -name "I18nStringUtils.java"

Repository: netgrif/application-engine

Length of output: 240


🏁 Script executed:

# Read the I18nStringUtils file to see the collectTranslations implementation
cat -n nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/I18nStringUtils.java

Repository: netgrif/application-engine

Length of output: 786


🏁 Script executed:

# Search for getFieldOptions implementation
rg -nA10 "getFieldOptions" application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java

Repository: netgrif/application-engine

Length of output: 1830


🏁 Script executed:

# Search for tests related to transformMultichoiceMapField
fd -type f -name "*.java" | xargs grep -l "transformMultichoiceMapField\|ElasticCaseMappingService" | grep -i test

Repository: netgrif/application-engine

Length of output: 240


🏁 Script executed:

# Search for tests of ElasticCaseMappingService
find . -type f -name "*.java" -path "*/test/*" | xargs grep -l "ElasticCaseMappingService" 2>/dev/null | head -3

Repository: netgrif/application-engine

Length of output: 52


🏁 Script executed:

# Search for any validation or warnings about missing keys
rg -n "mapValues|options\.get" application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java -B2 -A2

Repository: netgrif/application-engine

Length of output: 1025


🏁 Script executed:

# Check if there's any logging or warning in the surrounding code
rg -n "logger|log\.|warn|error" application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java | head -20

Repository: netgrif/application-engine

Length of output: 1771


🏁 Script executed:

# Check the git history of this specific method to understand its evolution
git log --oneline -n 20 -- application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java

Repository: netgrif/application-engine

Length of output: 150


Inconsistent error handling: missing options keys are silently converted to empty translations.

The code at line 98 does not validate that all keys from mapValues exist in the options map. While I18nStringUtils.collectTranslations(null) handles null gracefully by returning an empty list, this pattern is inconsistent with the error handling established elsewhere in this class. Other methods (e.g., lines 163, 180, 194) log errors when encountering data inconsistencies. Consider either adding validation to ensure all keys are present in the options map, or logging a warning when options.get(key) returns null to make data issues visible during debugging.

🤖 Prompt for AI Agents
In
application-engine/src/main/java/com/netgrif/application/engine/elastic/service/ElasticCaseMappingService.java
around lines 96-99, the code blindly calls options.get(key) and passes the
result to I18nStringUtils.collectTranslations, silently producing empty
translations for missing keys; change this to first check
options.containsKey(key) and if the key is missing log a warning (including the
missing key and identifying context such as the parent map or document id), then
either continue with an explicit empty translation list or handle it
consistently with the class’s error strategy (throw or collect an error),
ensuring missing keys are visible in logs rather than silently ignored.

return Optional.of(new com.netgrif.application.engine.adapter.spring.elastic.domain.MapField(values));
}
Expand Down Expand Up @@ -138,8 +137,7 @@ protected Optional<DataField> transformStringCollectionField(com.netgrif.applica
(com.netgrif.application.engine.objects.workflow.domain.DataField enumMap, EnumerationMapField netField) {
Map<String, I18nString> options = this.getFieldOptions(enumMap, netField);
String selectedKey = (String) enumMap.getValue();
I18nString selectedValue = options.get(selectedKey);
return Optional.of(new com.netgrif.application.engine.adapter.spring.elastic.domain.MapField(new AbstractMap.SimpleEntry<>(selectedKey, selectedValue != null ? selectedValue : new I18nString(""))));
return Optional.of(new com.netgrif.application.engine.adapter.spring.elastic.domain.MapField(new AbstractMap.SimpleEntry<>(selectedKey, I18nStringUtils.collectTranslations(options.get(selectedKey)))));
}

private Map<String, I18nString> getFieldOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
Comment on lines 4 to 7
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Remove unused import.

lombok.NonNull is imported but not used in this class.

 import lombok.Data;
 import lombok.EqualsAndHashCode;
-import lombok.NonNull;
 import lombok.ToString;
📝 Committable suggestion

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

Suggested change
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NonNull;
import lombok.ToString;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
🤖 Prompt for AI Agents
In
nae-object-library/src/main/java/com/netgrif/application/engine/objects/auth/dto/AuthPrincipalDto.java
around lines 3 to 6, the import lombok.NonNull is unused; remove that import
line and keep only the required lombok imports (Data, EqualsAndHashCode,
ToString) so there are no unused imports.


import java.io.Serial;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ public abstract class ElasticCase implements Serializable {

private String authorUsername;

private List<ImmediateField> immediateData;

private Map<String, DataField> dataSet;

private Set<String> taskIds;
Expand Down Expand Up @@ -81,6 +79,8 @@ public abstract class ElasticCase implements Serializable {

private Map<String, String> tags;

private NodeInfo nodeInfo;

Comment on lines +82 to +83
Copy link

@coderabbitai coderabbitai bot Feb 5, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find where nodeInfo is set/used
echo "=== Searching for nodeInfo usage patterns ==="
rg -n 'nodeInfo' --type=java -C2

Repository: netgrif/application-engine

Length of output: 760


🏁 Script executed:

#!/bin/bash
# Find where NodeInfo is instantiated
echo "=== Searching for NodeInfo instantiation ==="
rg -n 'new NodeInfo\(' --type=java -C3

Repository: netgrif/application-engine

Length of output: 114


🏁 Script executed:

#!/bin/bash
# Search for setNodeInfo method calls
echo "=== Searching for setNodeInfo calls ==="
rg -n 'setNodeInfo' --type=java -C2

Repository: netgrif/application-engine

Length of output: 109


🏁 Script executed:

#!/bin/bash
# Find NodeInfo class definition
echo "=== Searching for NodeInfo class definition ==="
rg -n 'class NodeInfo|interface NodeInfo' --type=java -C3

Repository: netgrif/application-engine

Length of output: 1019


🏁 Script executed:

#!/bin/bash
# Check ElasticCase for getter/setter methods related to nodeInfo
echo "=== Searching for nodeInfo getter/setter in ElasticCase ==="
rg -n 'getNodeInfo|setNodeInfo|nodeInfo' nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java -C1

Repository: netgrif/application-engine

Length of output: 171


🏁 Script executed:

#!/bin/bash
# Search for any assignment to nodeInfo field
echo "=== Searching for nodeInfo field assignments ==="
rg -n 'this\.nodeInfo\s*=' --type=java -C2

Repository: netgrif/application-engine

Length of output: 118


🏁 Script executed:

#!/bin/bash
# Look at the complete ElasticCase file around the nodeInfo field and constructor
echo "=== ElasticCase constructor and nodeInfo field context ==="
cat -n nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java | sed -n '78,140p'

Repository: netgrif/application-engine

Length of output: 4641


Initialize nodeInfo in the constructor and update it in the update() method.

The nodeInfo field is declared (line 82) but never initialized in the constructor (lines 84-111) or updated in the update() method (lines 113-135). Unlike all other fields in this class which are explicitly initialized and copied, nodeInfo will remain null. Either this field should be initialized from the source ElasticCase like other fields, or justify why it requires different handling.

🤖 Prompt for AI Agents
In
`@nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/ElasticCase.java`
around lines 82 - 83, The nodeInfo field in class ElasticCase is declared but
never initialized or copied; update the constructor (the ElasticCase(...)
initializer that sets other fields like id, title, etc.) to initialize
this.nodeInfo from the source ElasticCase (e.g., this.nodeInfo =
source.getNodeInfo() or new NodeInfo(source.getNodeInfo()) if a defensive copy
is needed) and also modify the update(...) method to set/replace nodeInfo the
same way when copying fields from another ElasticCase (ensure you use the same
copying semantics used for other complex fields in this class to maintain
immutability/defensive copying).

Copy link
Contributor

Choose a reason for hiding this comment

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

@Kovy95 shouldnt be it at least updated?

Copy link

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

public ElasticCase(Case useCase) {
id = useCase.getStringId();
lastModified = Timestamp.valueOf(useCase.getLastModified()).getTime();
Expand Down Expand Up @@ -108,7 +108,6 @@ public ElasticCase(Case useCase) {
users = deepCopy(useCase.getUsers());
userRefs = deepCopy(useCase.getUserRefs());
dataSet = new HashMap<>();
immediateData = useCase.getImmediateData() == null ? Collections.emptyList() : useCase.getImmediateData().stream().map(ImmediateField::new).collect(Collectors.toList());
}

public void update(ElasticCase useCase) {
Expand All @@ -133,9 +132,6 @@ public void update(ElasticCase useCase) {
dataSet = useCase.getDataSet() == null ? new HashMap<>() : useCase.getDataSet().entrySet().stream()
.filter(entry -> entry.getValue() != null)
.collect(Collectors.toMap(Map.Entry::getKey, entry -> CopyConstructorUtil.copy(entry.getValue().getClass(), entry.getValue())));
immediateData = useCase.getImmediateData() == null ? new ArrayList<>() : useCase.getImmediateData().stream()
.map(field -> new ImmediateField(field.getStringId(), new I18nString(field.getName()), field.getType()))
.collect(Collectors.toList());
}

private static Map<String, Map<String, Boolean>> deepCopy(Map<String, Map<String, Boolean>> map) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public abstract class I18nField extends TextField {
public abstract class I18nField extends DataField {

public String[] keyValue;

Expand All @@ -26,16 +26,15 @@ public I18nField(I18nField field) {
}

public I18nField(Set<String> keys, Set<String> values, Map<String, String> translations) {
super(new String[0]);
super(values.toArray(new String[0]));
this.keyValue = keys.toArray(new String[0]);
this.textValue = values.toArray(new String[0]);
this.translations = translations;
}

@Override
public Object getValue() {
if (textValue != null && textValue.length > 0) {
return new I18nString(textValue[0], translations);
if (fulltextValue != null && fulltextValue.length > 0) {
return new I18nString(fulltextValue[0], translations);
}
Comment on lines 35 to 38
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's read the I18nField.java file to see its full structure
find . -type f -name "I18nField.java" | head -5

Repository: netgrif/application-engine

Length of output: 402


🏁 Script executed:

# Search for fulltextValue usage across the codebase to understand the pattern
rg -n "fulltextValue" --type java | head -30

Repository: netgrif/application-engine

Length of output: 2581


🏁 Script executed:

# Let's specifically look at CaseField.java since the learning mentions it has a related pattern
find . -type f -name "CaseField.java" | head -3

Repository: netgrif/application-engine

Length of output: 402


🏁 Script executed:

# Read the I18nField.java file in elastic domain
cat -n ./nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/I18nField.java

Repository: netgrif/application-engine

Length of output: 1606


🏁 Script executed:

# Read the CaseField.java file to understand the separate field pattern
cat -n ./nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/CaseField.java

Repository: netgrif/application-engine

Length of output: 1133


🏁 Script executed:

# Check how I18nField is populated/instantiated
rg -n "new I18nField|I18nField(" -A 5 --type java | head -50

Repository: netgrif/application-engine

Length of output: 168


🏁 Script executed:

# Fix regex syntax to search for I18nField instantiation
rg "I18nField\(" -A 3 --type java | head -50

Repository: netgrif/application-engine

Length of output: 6140


🏁 Script executed:

# Find concrete implementations/subclasses of I18nField
rg "extends I18nField|class.*I18nField" --type java

Repository: netgrif/application-engine

Length of output: 783


🏁 Script executed:

# Search for tests related to I18nField
find . -type f -name "*I18nField*Test*.java" -o -name "*Test*I18nField*.java"

Repository: netgrif/application-engine

Length of output: 52


🏁 Script executed:

# Check DataField to see how it handles single vs multi-value
cat -n ./nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/DataField.java

Repository: netgrif/application-engine

Length of output: 1267


🏁 Script executed:

# Check if I18nField has abstract methods that might clarify intent
cat -n ./nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/I18nField.java

Repository: netgrif/application-engine

Length of output: 1606


🏁 Script executed:

# Search for concrete implementations of the elastic I18nField
rg "class.*extends.*I18nField" -A 3 --type java | grep -A 3 "elastic"

Repository: netgrif/application-engine

Length of output: 685


🏁 Script executed:

# Check how I18nField is actually used in the codebase (elastic domain variant)
rg "I18nField\(.*Set" --type java -A 2 | head -20

Repository: netgrif/application-engine

Length of output: 1058


🏁 Script executed:

# Look for actual tests of the elastic domain module
find ./nae-object-library -type f -name "*Test*.java" | xargs grep -l "I18nField" 2>/dev/null | head -5

Repository: netgrif/application-engine

Length of output: 52


Address cardinality mismatch between constructor design and getValue() implementation.

The constructor accepts Set<String> keys and values (supporting multiple entries) and stores them in parallel arrays, but getValue() returns only fulltextValue[0]. This creates a design inconsistency: either enforce single-value semantics in the constructor or return all values. The parallel keyValue array suggests each index should correspond to a value, making the silent truncation of additional entries problematic. Consider enforcing single-value semantics explicitly (fail fast) or returning a multi-value representation like CaseField does with its separate caseValue field.

🤖 Prompt for AI Agents
In
`@nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/I18nField.java`
around lines 35 - 38, I18nField's getValue() currently returns only
fulltextValue[0], but the constructor accepts Set<String> inputs and populates
fulltextValue and keyValue as parallel arrays, causing a cardinality mismatch;
either enforce single-value semantics in the constructor (in I18nField's
constructor validate that fulltextValue/keyValue sets contain exactly one
element and throw an IllegalArgumentException if not) or change getValue() to
return a multi-value representation (e.g., return a collection or map of
keys->values, or a list of I18nString instances) so that each index in
fulltextValue/keyValue is preserved; update usages of getValue() accordingly to
handle the chosen representation and keep translations consistent.

return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.netgrif.application.engine.objects.elastic.domain;

import com.netgrif.application.engine.objects.petrinet.domain.I18nString;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
Expand All @@ -11,34 +10,22 @@
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public abstract class MapField extends TextField {
public abstract class MapField extends DataField {

public String[] keyValue;
public Map<String, I18nString> keyValueTranslations;

public MapField(MapField field) {
super(field);
this.keyValue = field.keyValue == null ? null : Arrays.copyOf(field.keyValue, field.keyValue.length);
this.keyValueTranslations = field.keyValueTranslations == null ? null
: field.keyValueTranslations.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, entry -> new I18nString(entry.getValue())));
public MapField(Map.Entry<String, Collection<String>> valuePair) {
this(List.of(valuePair));
}

public MapField(Map.Entry<String, I18nString> valueTranslationPair) {
this(List.of(valueTranslationPair));
}

public MapField(List<Map.Entry<String, I18nString>> valueTranslationPairs) {
public MapField(List<Map.Entry<String, Collection<String>>> valuePairs) {
super(new String[0]);
this.keyValue = new String[valueTranslationPairs.size()];
this.keyValue = new String[valuePairs.size()];
List<String> values = new ArrayList<>();
this.keyValueTranslations = new HashMap<>();
for (int i = 0; i < valueTranslationPairs.size(); i++) {
this.keyValue[i] = valueTranslationPairs.get(i).getKey();
values.addAll(I18nStringUtils.collectTranslations(valueTranslationPairs.get(i).getValue()));
this.keyValueTranslations.put(valueTranslationPairs.get(i).getKey(), valueTranslationPairs.get(i).getValue());
for (int i = 0; i < valuePairs.size(); i++) {
keyValue[i] = valuePairs.get(i).getKey();
values.addAll(valuePairs.get(i).getValue());
Comment on lines +21 to +27
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Null-safe aggregation of entry values to prevent NPEs.

values.addAll(valuePairs.get(i).getValue()) will throw if any entry carries a null collection. Consider guarding or defaulting to an empty collection to keep indexing resilient.

🔧 Proposed fix
         for (int i = 0; i < valuePairs.size(); i++) {
             keyValue[i] = valuePairs.get(i).getKey();
-            values.addAll(valuePairs.get(i).getValue());
+            Collection<String> entryValues = valuePairs.get(i).getValue();
+            if (entryValues != null) {
+                values.addAll(entryValues);
+            }
         }
🤖 Prompt for AI Agents
In
`@nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/MapField.java`
around lines 21 - 27, In the MapField constructor the call
values.addAll(valuePairs.get(i).getValue()) can NPE if an entry's value is null;
update the constructor (MapField(List<Map.Entry<String, Collection<String>>>
valuePairs)) to null-safe aggregate by retrieving the entry value into a local
variable, defaulting to Collections.emptyList() (or new ArrayList<>()) when
null, then addAll that safe collection to values while still populating
keyValue[i]; ensure keyValue and values sizing logic remains unchanged.

}
this.textValue = values.toArray(new String[0]);
this.fulltextValue = values.toArray(new String[0]);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.netgrif.application.engine.objects.elastic.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serial;
import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class NodeInfo implements Serializable {

@Serial
private static final long serialVersionUID = 7454569786379865L;

private String nodeHost;
private int nodePort;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.netgrif.application.engine.objects.elastic.domain;


import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
Expand All @@ -10,7 +9,7 @@
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public abstract class StringCollectionField extends TextField {
public abstract class StringCollectionField extends DataField {

public String[] collectionValue;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package com.netgrif.application.engine.adapter.spring.elastic.domain;

import com.netgrif.application.engine.objects.petrinet.domain.I18nString;
import lombok.NoArgsConstructor;
import org.springframework.data.elasticsearch.annotations.Field;

import java.util.Collection;
import java.util.List;
import java.util.Map;
Expand All @@ -13,15 +11,11 @@
@NoArgsConstructor
public class MapField extends com.netgrif.application.engine.objects.elastic.domain.MapField {

public MapField(MapField field) {
super(field);
}

public MapField(Map.Entry<String, I18nString> valuePair) {
public MapField(Map.Entry<String, Collection<String>> valuePair) {
super(valuePair);
}

public MapField(List<Map.Entry<String, I18nString>> valuePairs) {
public MapField(List<Map.Entry<String, Collection<String>>> valuePairs) {
super(valuePairs);
}

Expand All @@ -35,9 +29,4 @@ public String[] getFulltextValue() {
public String[] getKeyValue() {
return super.getKeyValue();
}

@Field(type = Flattened, index = false)
public Map<String, I18nString> getKeyValueTranslations() {
return super.keyValueTranslations;
}
}
Loading