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
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
</developers>

<properties>
<revision>1.10.1-SNAPSHOT</revision>
<revision>1.0.0-RC1</revision>
<java.version>21</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
Expand Down
43 changes: 43 additions & 0 deletions sdm/src/main/java/com/sap/cds/sdm/persistence/DBQuery.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<Row> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,26 +79,43 @@ public void changelog(AttachmentLogContext context) throws IOException {

CqnAnalyzer cqnAnalyzer = CqnAnalyzer.create(cdsModel);

Optional<CdsEntity> attachmentEntity =
Optional<CdsEntity> attachmentDraftEntity =
cdsModel.findEntity(context.getTarget().getQualifiedName() + "_drafts");

Map<String, Object> 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<CdsEntity> 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<CdsEntity> 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();
Expand All @@ -114,6 +131,18 @@ public void changelog(AttachmentLogContext context) throws IOException {
logger.debug("END: Changelog event");
}

private CmisDocument lookupAttachmentScoped(
Optional<CdsEntity> 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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading