diff --git a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/RestResponseExceptionHandler.java b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/RestResponseExceptionHandler.java index 344c46ece8..449a901b9e 100644 --- a/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/RestResponseExceptionHandler.java +++ b/application-engine/src/main/java/com/netgrif/application/engine/workflow/web/RestResponseExceptionHandler.java @@ -21,10 +21,7 @@ public class RestResponseExceptionHandler extends ResponseEntityExceptionHandler private static final Logger log = LoggerFactory.getLogger(RestResponseExceptionHandler.class); @Override - protected ResponseEntity handleHttpMessageNotWritable(HttpMessageNotWritableException exception, - HttpHeaders headers, - HttpStatusCode status, - WebRequest request) { + protected ResponseEntity handleHttpMessageNotWritable(HttpMessageNotWritableException exception, HttpHeaders headers, HttpStatusCode status, WebRequest request) { try { Throwable cause = exception.getCause(); if (!(cause instanceof JsonMappingException jme)) { @@ -33,20 +30,38 @@ protected ResponseEntity handleHttpMessageNotWritable(HttpMessageNotWrit } List path = jme.getPath(); + + if (log.isTraceEnabled()) { + log.trace("JSON write failed (cause). msg={} | pathRef={}", + jme.getOriginalMessage(), + jme.getPathReference(), + jme); + + tracePathAll(path); + } + + if (log.isDebugEnabled()) { + for (int i = 0; i < path.size(); i++) { + log.debug("Reference[{}]: {}", i, path.get(i)); + } + } + if (path.size() > 3) { Object fieldFrom = path.getLast().getFrom(); + log.debug("Field of class [{}] from: {}", fieldFrom == null ? "null" : fieldFrom.getClass(), fieldFrom); Object caseFrom = path.get(path.size() - 3).getFrom(); + log.debug("Case of class [{}] from: {}", caseFrom == null ? "null" : caseFrom.getClass(), caseFrom); if (fieldFrom instanceof Field field && caseFrom instanceof Case useCase) { log.debug("[{}] Could not parse value of field [{}], value [{}] | path={}", useCase.getStringId(), field.getStringId(), field.getValue(), jme.getPathReference()); } else { - log.error("JSON write failed: {} | path={}", - jme.getOriginalMessage(), jme.getPathReference(), jme); + log.error("JSON write failed: {} | path={} | details={}", + jme.getOriginalMessage(), jme.getPathReference(), describePath(path), jme); } } else { - log.error("JSON write failed: {} | path={}", - jme.getOriginalMessage(), jme.getPathReference(), jme); + log.error("JSON write failed because of path is smaller than 3: {} | path={} | details={}", + jme.getOriginalMessage(), jme.getPathReference(), describePath(path), jme); } } catch (Exception e) { @@ -54,4 +69,46 @@ protected ResponseEntity handleHttpMessageNotWritable(HttpMessageNotWrit } return super.handleHttpMessageNotWritable(exception, headers, status, request); } + + private static String describePath(List path) { + if (path == null || path.isEmpty()) return ""; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < path.size(); i++) { + JsonMappingException.Reference ref = path.get(i); + Object from = ref.getFrom(); + + if (i > 0) sb.append(" | "); + + sb.append(i).append(":"); + sb.append(from == null ? "null" : from.getClass().getSimpleName()); + + if (ref.getFieldName() != null) sb.append(".").append(ref.getFieldName()); + if (ref.getIndex() >= 0) sb.append("[").append(ref.getIndex()).append("]"); + + sb.append(" (").append(ref).append(")"); + } + return sb.toString(); + } + + private static void tracePathAll(List path) { + if (path == null || path.isEmpty()) { + log.trace("[JSON_WRITE][PATH] "); + return; + } + + for (int i = 0; i < path.size(); i++) { + JsonMappingException.Reference ref = path.get(i); + Object from = ref.getFrom(); + + String where = (ref.getFieldName() != null ? "." + ref.getFieldName() : "") + + (ref.getIndex() >= 0 ? "[" + ref.getIndex() + "]" : ""); + + log.trace("[JSON_WRITE][PATH] idx={} fromType={}{} ref={} from={}", + i, + (from == null ? "null" : from.getClass().getName()), + where, + ref, + from); + } + } } \ No newline at end of file diff --git a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/MapField.java b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/MapField.java index 2b7ea8ec87..599d224bf6 100644 --- a/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/MapField.java +++ b/nae-object-library/src/main/java/com/netgrif/application/engine/objects/elastic/domain/MapField.java @@ -13,6 +13,8 @@ @EqualsAndHashCode(callSuper = true) public abstract class MapField extends TextField { + public static final String NONE_OPTION_KEY = "none"; + protected List keyValue; protected Map keyValueTranslations; @@ -21,7 +23,10 @@ public MapField(MapField field) { this.keyValue = field.keyValue == null ? null : new ArrayList<>(field.keyValue); this.keyValueTranslations = field.keyValueTranslations == null ? null : field.keyValueTranslations.entrySet().stream() - .collect(Collectors.toMap(Map.Entry::getKey, entry -> new I18nString(entry.getValue()))); + .collect(Collectors.toMap(entry -> + resolveTranslationPairKey(entry.getKey()), + entry -> new I18nString(entry.getValue()), + (existing, replacement) -> replacement, LinkedHashMap::new)); } public MapField(Map.Entry valueTranslationPair) { @@ -34,11 +39,15 @@ public MapField(List> valueTranslationPairs) { } List values = new ArrayList<>(); this.keyValue = new ArrayList<>(); - this.keyValueTranslations = new HashMap<>(); + this.keyValueTranslations = new LinkedHashMap<>(); for (Map.Entry valueTranslationPair : valueTranslationPairs) { - this.keyValue.add(valueTranslationPair.getKey()); + String key = resolveTranslationPairKey(valueTranslationPair.getKey()); + this.keyValue.add(key); values.addAll(I18nStringUtils.collectTranslations(valueTranslationPair.getValue())); - this.keyValueTranslations.put(valueTranslationPair.getKey(), valueTranslationPair.getValue()); + this.keyValueTranslations.put( + key, + valueTranslationPair.getValue() == null ? null : new I18nString(valueTranslationPair.getValue()) + ); } this.textValue = values; this.fulltextValue = values; @@ -52,4 +61,8 @@ public Object getValue() { } return null; } + + private String resolveTranslationPairKey(String key) { + return key == null || key.isBlank() ? NONE_OPTION_KEY : key; + } }