From 153e19ee665a325a46e7bb3f6c552ae93333e13e Mon Sep 17 00:00:00 2001 From: Rashmi Angadi Date: Mon, 7 Sep 2026 10:50:30 +0530 Subject: [PATCH] Nexus File Download issue because of uploading status in Saved state --- pom.xml | 2 +- .../SDMReadAttachmentsHandler.java | 2 +- .../common/SDMApplicationHandlerHelper.java | 21 ++++++- .../com/sap/cds/sdm/utilities/SDMUtils.java | 57 +++++++++++++------ 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/pom.xml b/pom.xml index ab4efefd2..43e0f514f 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ - 1.10.2 + 1.10.3.1-SNAPSHOT 21 ${java.version} ${java.version} diff --git a/sdm/src/main/java/com/sap/cds/sdm/handler/applicationservice/SDMReadAttachmentsHandler.java b/sdm/src/main/java/com/sap/cds/sdm/handler/applicationservice/SDMReadAttachmentsHandler.java index 241e28531..c203f9a8e 100644 --- a/sdm/src/main/java/com/sap/cds/sdm/handler/applicationservice/SDMReadAttachmentsHandler.java +++ b/sdm/src/main/java/com/sap/cds/sdm/handler/applicationservice/SDMReadAttachmentsHandler.java @@ -133,7 +133,7 @@ public void processBefore(CdsReadEventContext context) throws IOException { return; } setErrorMessagesInCache(context); - if (context.getTarget().getAnnotationValue(SDMConstants.ANNOTATION_IS_MEDIA_DATA, false)) { + if (SDMApplicationHandlerHelper.isMediaEntity(context.getTarget())) { try { // update the uploadStatus of all blank attachments with success this is for existing // attachments diff --git a/sdm/src/main/java/com/sap/cds/sdm/handler/common/SDMApplicationHandlerHelper.java b/sdm/src/main/java/com/sap/cds/sdm/handler/common/SDMApplicationHandlerHelper.java index 5d8d692d3..a4b251567 100644 --- a/sdm/src/main/java/com/sap/cds/sdm/handler/common/SDMApplicationHandlerHelper.java +++ b/sdm/src/main/java/com/sap/cds/sdm/handler/common/SDMApplicationHandlerHelper.java @@ -13,14 +13,31 @@ public final class SDMApplicationHandlerHelper { private static final String ANNOTATION_IS_MEDIA_DATA = "_is_media_data"; /** - * Checks if the entity is a media entity. A media entity is an entity that is annotated with the - * annotation "_is_media_data". + * Checks if the entity is a media entity. A media entity is one that carries the "_is_media_data" + * annotation (set by the CAP attachments plugin on DB-backed attachment entities), or — as a + * fallback for service-layer-only draft entities whose annotation may not be propagated — one + * that has both the SDM-specific "objectId" element and the "content" element that is + * characteristic of the sap.attachments.Attachments aspect. * * @param baseEntity The entity to check * @return true if the entity is a media entity, false otherwise */ public static boolean isMediaEntity(CdsStructuredType baseEntity) { boolean isMedia = baseEntity.getAnnotationValue(ANNOTATION_IS_MEDIA_DATA, false); + if (!isMedia) { + // Fallback for service-layer-only entities (e.g. SupplierBidTermValuesServiceEntity + // attachments) whose inline Composition of Attachments does not receive the + // _is_media_data annotation at runtime. Presence of both "objectId" (SDM-specific + // extension) and "content" (core MediaData field) is a reliable structural signal. + isMedia = + baseEntity.findElement("objectId").isPresent() + && baseEntity.findElement("content").isPresent(); + if (isMedia) { + logger.debug( + "Entity {} identified as media entity via structural fallback (objectId + content)", + baseEntity.getQualifiedName()); + } + } logger.debug("Entity {} isMediaEntity: {}", baseEntity.getQualifiedName(), isMedia); return isMedia; } diff --git a/sdm/src/main/java/com/sap/cds/sdm/utilities/SDMUtils.java b/sdm/src/main/java/com/sap/cds/sdm/utilities/SDMUtils.java index bbe5b9c15..89572bd5b 100644 --- a/sdm/src/main/java/com/sap/cds/sdm/utilities/SDMUtils.java +++ b/sdm/src/main/java/com/sap/cds/sdm/utilities/SDMUtils.java @@ -398,9 +398,9 @@ public static Map getSecondaryPropertiesWithInvalidDefinition( title = titleAnnotation.get().getValue().toString(); } else { title = element.getName(); /* - * This is in case the user has not specified a title for the column in the cds - * file (which is optional) - */ + * This is in case the user has not specified a title for the column in the cds + * file (which is optional) + */ } invalidProperties.put(key, title); } @@ -570,7 +570,19 @@ private static void retrieveAnnotations(CdsElement cdsElement, AttachmentInfo at } private static List getKeyElementNames(CdsEntity entity) { - return entity.elements().filter(CdsElement::isKey).map(CdsElement::getName).toList(); + return entity + .elements() + .filter(CdsElement::isKey) + .map( + e -> { + String name = e.getName(); + // _drafts entity key columns have an "up__" prefix (e.g. up__bidUUID, + // up__bookItem_ID). + // The CQN where clause uses the bare field names (bidUUID, bookItem_ID). + // Strip the prefix so the contains-check in fetchUPIDFromCQN matches correctly. + return name.startsWith("up__") ? name.substring(4) : name; + }) + .toList(); } /** @@ -586,26 +598,37 @@ public static String fetchUPIDFromCQN(CqnSelect select, CdsEntity parentEntity) String upID = null; ObjectMapper mapper = new ObjectMapper(); JsonNode root = mapper.readTree(select.toString()); + logger.debug( + "fetchUPIDFromCQN - CQN from.ref: {}", root.path("SELECT").path("from").path("ref")); JsonNode refArray = root.path("SELECT").path("from").path("ref"); - JsonNode secondLast = refArray.get(refArray.size() - 2); - JsonNode whereArray; - if (secondLast != null) { - whereArray = secondLast.path("where"); - } else { - whereArray = refArray; + // Get the actual key field names from the parent entity + List keyElementNames = getKeyElementNames(parentEntity); + + // Scan ref nodes from right to left (excluding the last, which is the attachment entity + // itself) to find the first node that has a where clause containing a matching key. + // This handles deep navigation paths like: + // SourcingEvents(id=...) / items(id=...) / terms(id=...) / initialSlice / itemValue / + // attachments + // where intermediate nodes (initialSlice, itemValue) have no where clause. + JsonNode whereArray = null; + for (int r = refArray.size() - 2; r >= 0; r--) { + JsonNode refNode = refArray.get(r); + if (refNode == null) continue; + JsonNode candidate = refNode.path("where"); + if (candidate != null && !candidate.isMissingNode() && candidate.size() > 0) { + whereArray = candidate; + break; + } } - // If where condition is not present or empty, return null (valid scenario for - // select without - // filter) - if (whereArray == null || whereArray.isMissingNode() || whereArray.size() == 0) { + // If no ref node with a where clause was found, return null (valid scenario for + // select without filter) + if (whereArray == null) { + logger.debug("END: fetchUPIDFromCQN - upID: null (no where clause found in ref chain)"); return null; } - // Get the actual key field names from the parent entity - List keyElementNames = getKeyElementNames(parentEntity); - for (int i = 0; i < whereArray.size(); i++) { JsonNode node = whereArray.get(i);