From c12be7bfcc941598f79947a1b36be900f1d84035 Mon Sep 17 00:00:00 2001 From: PujaDeshmukh17 <126158971+PujaDeshmukh17@users.noreply.github.com> Date: Wed, 14 Oct 2026 10:51:19 +0530 Subject: [PATCH 1/3] Fix for adding a guard to getObjectId --- .../com/sap/cds/sdm/persistence/DBQuery.java | 43 ++++++++++++++++++ .../handler/SDMServiceGenericHandler.java | 45 +++++++++++++++---- 2 files changed, 80 insertions(+), 8 deletions(-) diff --git a/sdm/src/main/java/com/sap/cds/sdm/persistence/DBQuery.java b/sdm/src/main/java/com/sap/cds/sdm/persistence/DBQuery.java index 219c4f2e..546c46be 100644 --- a/sdm/src/main/java/com/sap/cds/sdm/persistence/DBQuery.java +++ b/sdm/src/main/java/com/sap/cds/sdm/persistence/DBQuery.java @@ -105,6 +105,49 @@ public CmisDocument getObjectIdForAttachmentID( return cmisDocument; } + public CmisDocument getObjectIdForAttachmentID( + CdsEntity attachmentEntity, + PersistenceService persistenceService, + String id, + String upIdKey, + String upId) { + logger.debug( + "Fetching objectId for attachment ID: {} upId: {} from entity: {}", + id, + upId, + attachmentEntity.getQualifiedName()); + CqnSelect q = + Select.from(attachmentEntity) + .columns( + "objectId", + "folderId", + "fileName", + "mimeType", + "contentId", + "linkUrl", + "uploadStatus") + .where(doc -> doc.get("ID").eq(id).and(doc.get(upIdKey).eq(upId))); + Result result = persistenceService.run(q); + Optional res = result.first(); + CmisDocument cmisDocument = new CmisDocument(); + if (res.isPresent()) { + logger.debug("Attachment found for ID: {} within parent scope", id); + Row row = res.get(); + cmisDocument.setObjectId(row.get("objectId").toString()); + cmisDocument.setFileName(row.get("fileName").toString()); + cmisDocument.setFolderId(row.get("folderId").toString()); + cmisDocument.setMimeType(row.get("mimeType").toString()); + cmisDocument.setContentId( + row.get("contentId") != null ? row.get("contentId").toString() : null); + cmisDocument.setUrl(row.get("linkUrl") != null ? row.get("linkUrl").toString() : null); + cmisDocument.setUploadStatus( + row.get("uploadStatus") != null ? row.get("uploadStatus").toString() : null); + } else { + logger.debug("No attachment found for ID: {} within parent scope (upId: {})", id, upId); + } + return cmisDocument; + } + public CmisDocument getAttachmentForObjectID( PersistenceService persistenceService, String id, AttachmentCopyEventContext context) { logger.debug("Fetching attachment for objectId: {}", id); diff --git a/sdm/src/main/java/com/sap/cds/sdm/service/handler/SDMServiceGenericHandler.java b/sdm/src/main/java/com/sap/cds/sdm/service/handler/SDMServiceGenericHandler.java index 2ae1af56..a23dac7c 100644 --- a/sdm/src/main/java/com/sap/cds/sdm/service/handler/SDMServiceGenericHandler.java +++ b/sdm/src/main/java/com/sap/cds/sdm/service/handler/SDMServiceGenericHandler.java @@ -79,26 +79,43 @@ public void changelog(AttachmentLogContext context) throws IOException { CqnAnalyzer cqnAnalyzer = CqnAnalyzer.create(cdsModel); - Optional attachmentEntity = + Optional attachmentDraftEntity = cdsModel.findEntity(context.getTarget().getQualifiedName() + "_drafts"); Map targetKeys = cqnAnalyzer.analyze((CqnSelect) context.get("cqn")).targetKeyValues(); - // get the objectId against the Id String id = targetKeys.get("ID").toString(); logger.debug("Fetching changelog for attachment ID: {}", id); - CmisDocument cmisDocument = - dbQuery.getObjectIdForAttachmentID(attachmentEntity.get(), persistenceService, id); + // Derive parent entity name and upIdKey to scope the DB lookup by parent record + String targetQualifiedName = context.getTarget().getQualifiedName(); + int lastDot = targetQualifiedName.lastIndexOf('.'); + String parentEntityName = lastDot > 0 ? targetQualifiedName.substring(0, lastDot) : null; + Optional parentEntityOpt = + parentEntityName != null ? cdsModel.findEntity(parentEntityName) : Optional.empty(); + String upIdKey = + attachmentDraftEntity.isPresent() + ? SDMUtils.getUpIdKey(attachmentDraftEntity.get()) + : "up__ID"; + // fetchUPIDFromCQN extracts the parent record key from the navigation path in the CQN + String upId = + parentEntityOpt.isPresent() + ? SDMUtils.fetchUPIDFromCQN((CqnSelect) context.get("cqn"), parentEntityOpt.get()) + : null; + + CmisDocument cmisDocument = lookupAttachmentScoped(attachmentDraftEntity, id, upIdKey, upId); if (cmisDocument.getFileName() == null || cmisDocument.getFileName().isEmpty()) { - // open attachment is triggered on non-draft entity logger.debug("Draft entity returned empty fileName, fetching from active entity"); - attachmentEntity = cdsModel.findEntity(context.getTarget().getQualifiedName()); + Optional attachmentActiveEntity = + cdsModel.findEntity(context.getTarget().getQualifiedName()); + cmisDocument = lookupAttachmentScoped(attachmentActiveEntity, id, upIdKey, upId); + } - cmisDocument = - dbQuery.getObjectIdForAttachmentID(attachmentEntity.get(), persistenceService, id); + if (cmisDocument.getObjectId() == null || cmisDocument.getObjectId().isEmpty()) { + logger.warn("Attachment ID {} not found within authorized parent scope (upId: {})", id, upId); + throw new ServiceException(SDMUtils.getErrorMessage("FILE_NOT_FOUND_ERROR")); } SDMCredentials sdmCredentials = tokenHandler.getSDMCredentials(); @@ -114,6 +131,18 @@ public void changelog(AttachmentLogContext context) throws IOException { logger.debug("END: Changelog event"); } + private CmisDocument lookupAttachmentScoped( + Optional entityOpt, String id, String upIdKey, String upId) { + if (entityOpt.isEmpty()) { + return new CmisDocument(); + } + if (upId != null && !upId.isEmpty() && upIdKey != null && !upIdKey.isEmpty()) { + return dbQuery.getObjectIdForAttachmentID( + entityOpt.get(), persistenceService, id, upIdKey, upId); + } + return dbQuery.getObjectIdForAttachmentID(entityOpt.get(), persistenceService, id); + } + @On(event = "copyAttachments") public void copyAttachments(EventContext context) throws IOException { logger.debug("START: Copy attachments event"); From 120ea60115d3fad70e03b6ff31065d38c0e0ef38 Mon Sep 17 00:00:00 2001 From: PujaDeshmukh17 <126158971+PujaDeshmukh17@users.noreply.github.com> Date: Wed, 14 Oct 2026 11:11:43 +0530 Subject: [PATCH 2/3] version change for IT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 2687ff68..0dcce049 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ - 1.10.1-SNAPSHOT + 1.0.0-RC1 21 ${java.version} ${java.version} From e6042ecaa01f54366e16a812c7f28e9982bb66cc Mon Sep 17 00:00:00 2001 From: PujaDeshmukh17 <126158971+PujaDeshmukh17@users.noreply.github.com> Date: Wed, 14 Oct 2026 11:24:04 +0530 Subject: [PATCH 3/3] UT fix --- .../handler/SDMServiceGenericHandlerTest.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/sdm/src/test/java/unit/com/sap/cds/sdm/service/handler/SDMServiceGenericHandlerTest.java b/sdm/src/test/java/unit/com/sap/cds/sdm/service/handler/SDMServiceGenericHandlerTest.java index 4a0aa163..a5f65aa5 100644 --- a/sdm/src/test/java/unit/com/sap/cds/sdm/service/handler/SDMServiceGenericHandlerTest.java +++ b/sdm/src/test/java/unit/com/sap/cds/sdm/service/handler/SDMServiceGenericHandlerTest.java @@ -349,19 +349,14 @@ void testChangelogWithNullObjectId() throws IOException { when(dbQuery.getObjectIdForAttachmentID(draftEntity, persistenceService, "null-object-id")) .thenReturn(nullObjectIdDocument); - // Mock token handler - when(tokenHandler.getSDMCredentials()).thenReturn(sdmCredentials); - - // Mock SDM service - when(sdmService.getChangeLog(null, sdmCredentials, false)) - .thenThrow(new IllegalArgumentException("ObjectId cannot be null")); - - // Act & Assert + // Act & Assert — handler now throws ServiceException (FILE_NOT_FOUND) before reaching SDM assertThrows( - IllegalArgumentException.class, + ServiceException.class, () -> { sdmServiceGenericHandler.changelog(mockLogContext); }); + + verify(sdmService, never()).getChangeLog(any(), any(), anyBoolean()); } @Test