Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
7c9123f
Merge branch 'develop' of https://github.com/cap-java/sdm into develop
deepikaSingh2711 Sep 30, 2025
51c9389
Merge remote-tracking branch 'origin/develop' into revertDiscardLink
deepikaSingh2711 Nov 10, 2025
a506492
added revert discrad draft for edit link
deepikaSingh2711 Nov 10, 2025
7467d26
gemini review fix
deepikaSingh2711 Nov 10, 2025
8c7edbd
Ut's
deepikaSingh2711 Nov 13, 2025
546fb31
Merge branch 'develop' into revertDiscardLink
deepikaSingh2711 Nov 13, 2025
b0203ac
formatting correction
deepikaSingh2711 Nov 13, 2025
6fe2575
Merge branch 'revertDiscardLink' of https://github.com/cap-java/sdm i…
deepikaSingh2711 Nov 13, 2025
8175eaf
format correction
deepikaSingh2711 Nov 13, 2025
9169910
resolved PR comments
deepikaSingh2711 Nov 14, 2025
20367e7
pom.xml
deepikaSingh2711 Nov 14, 2025
03da33e
addressed github comments
deepikaSingh2711 Nov 14, 2025
6386f7c
formatting correction
deepikaSingh2711 Nov 14, 2025
ae3f7f1
removed unused parameter
deepikaSingh2711 Nov 14, 2025
65d6168
formatting correction
deepikaSingh2711 Nov 14, 2025
30262a6
updated pom.xml
deepikaSingh2711 Nov 14, 2025
993f1c7
changes
deepikaSingh2711 Nov 14, 2025
1ea1468
formatting
deepikaSingh2711 Nov 14, 2025
f9c14c5
Merge branch 'develop' into revertDiscardLink
rashmiangadi05 Nov 17, 2025
dd17d7d
Merge branch 'develop' into revertDiscardLink
deepikaSingh2711 Nov 17, 2025
dd67e39
added UT's for coverage
deepikaSingh2711 Nov 17, 2025
56f9a35
Merge branch 'revertDiscardLink' of https://github.com/cap-java/sdm i…
deepikaSingh2711 Nov 17, 2025
8d5c350
format correction
deepikaSingh2711 Nov 17, 2025
95383c9
UT
deepikaSingh2711 Nov 17, 2025
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 @@ -12,6 +12,7 @@ private SDMConstants() {
}

public static final String REPOSITORY_ID = System.getenv("REPOSITORY_ID");
public static final String MIMETYPE_INTERNET_SHORTCUT = "application/internet-shortcut";
public static final String SYSTEM_USER = "system-internal";
public static final String DESTINATION_EXCEPTION =
"Unable to get the destination for sdm service binding";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sap.cds.Result;
import com.sap.cds.Row;
import com.sap.cds.feature.attachments.service.AttachmentService;
import com.sap.cds.ql.Insert;
import com.sap.cds.ql.Select;
import com.sap.cds.ql.Update;
import com.sap.cds.ql.cqn.CqnAnalyzer;
import com.sap.cds.ql.cqn.CqnSelect;
Expand All @@ -16,6 +18,7 @@
import com.sap.cds.reflect.CdsModel;
import com.sap.cds.sdm.constants.SDMConstants;
import com.sap.cds.sdm.handler.TokenHandler;
import com.sap.cds.sdm.handler.applicationservice.helper.AttachmentsHandlerUtils;
import com.sap.cds.sdm.model.*;
import com.sap.cds.sdm.persistence.DBQuery;
import com.sap.cds.sdm.service.DocumentUploadService;
Expand All @@ -25,8 +28,10 @@
import com.sap.cds.services.EventContext;
import com.sap.cds.services.ServiceException;
import com.sap.cds.services.cds.ApplicationService;
import com.sap.cds.services.draft.DraftCancelEventContext;
import com.sap.cds.services.draft.DraftService;
import com.sap.cds.services.handler.EventHandler;
import com.sap.cds.services.handler.annotations.Before;
import com.sap.cds.services.handler.annotations.On;
import com.sap.cds.services.handler.annotations.ServiceName;
import com.sap.cds.services.persistence.PersistenceService;
Expand Down Expand Up @@ -96,6 +101,180 @@ public void edit(EventContext context) throws IOException {
editLink(context);
}

@Before(event = DraftService.EVENT_DRAFT_CANCEL)
public void handleDraftDiscardForLinks(DraftCancelEventContext context) throws IOException {
Comment thread
rishikunnath2747 marked this conversation as resolved.
CdsEntity parentDraftEntity = context.getTarget();
CqnAnalyzer analyzer = CqnAnalyzer.create(context.getModel());
Map<String, Object> parentKeys = analyzer.analyze(context.getCqn()).rootKeys();
String parentEntityName = parentDraftEntity.getQualifiedName().replace("_drafts", "");

Optional<CdsEntity> parentActiveEntityOpt = context.getModel().findEntity(parentEntityName);
Map<String, String> compositionPathMapping =
parentActiveEntityOpt
.map(
cdsEntity ->
AttachmentsHandlerUtils.getAttachmentPathMapping(
context.getModel(), cdsEntity, persistenceService))
.orElse(new HashMap<>());

for (Map.Entry<String, String> entry : compositionPathMapping.entrySet()) {
String attachmentCompositionDefinition = entry.getKey();
revertLinksForComposition(context, parentKeys, attachmentCompositionDefinition);
}
revertNestedEntityLinks(context);
}

private void revertNestedEntityLinks(DraftCancelEventContext context) throws IOException {

CdsEntity parentDraftEntity = context.getTarget();
String parentEntityName = parentDraftEntity.getQualifiedName().replace("_drafts", "");
Optional<CdsEntity> parentActiveEntityOpt = context.getModel().findEntity(parentEntityName);

if (parentActiveEntityOpt.isPresent()) {
CdsEntity parentActiveEntity = parentActiveEntityOpt.get();

parentActiveEntity
.compositions()
.forEach(
composition -> {
try {
processNestedEntityComposition(context, composition);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}

private void processNestedEntityComposition(
DraftCancelEventContext context, CdsElement composition) throws IOException {

CdsAssociationType associationType = (CdsAssociationType) composition.getType();
String targetEntityName = associationType.getTarget().getQualifiedName();
String draftTargetEntityName = targetEntityName + "_drafts";

Optional<CdsEntity> nestedDraftEntity = context.getModel().findEntity(draftTargetEntityName);

if (nestedDraftEntity.isPresent()) {
Map<String, String> nestedAttachmentMapping =
AttachmentsHandlerUtils.getAttachmentPathMapping(
context.getModel(), associationType.getTarget(), persistenceService);

if (nestedAttachmentMapping.isEmpty()) {
return;
}

Result nestedRecords =
persistenceService.run(
Select.from(nestedDraftEntity.get())
.columns("ID")
.where(e -> e.get("IsActiveEntity").eq(false)));

for (Row nestedRecord : nestedRecords) {
Object nestedEntityId = nestedRecord.get("ID");

Map<String, Object> nestedEntityKeys = new HashMap<>();
nestedEntityKeys.put("ID", nestedEntityId);
nestedEntityKeys.put("IsActiveEntity", false);

for (Map.Entry<String, String> entry : nestedAttachmentMapping.entrySet()) {
String attachmentPath = entry.getKey();
revertLinksForComposition(context, nestedEntityKeys, attachmentPath);
}
}
}
}

private void revertLinksForComposition(
DraftCancelEventContext context,
Map<String, Object> parentKeys,
String attachmentCompositionDefinition)
throws IOException {

CdsModel model = context.getModel();
String draftEntityName = attachmentCompositionDefinition + "_drafts";
CdsEntity draftEntity = model.findEntity(draftEntityName).get();
CdsEntity activeEntity = model.findEntity(attachmentCompositionDefinition).get();

String upIdKey = getUpIdKey(draftEntity);
String parentKeyName = upIdKey.replaceFirst("^up__", "");
Object parentId = parentKeys.get(parentKeyName);

CqnSelect selectDraftLinks =
Select.from(draftEntity)
.where(
a ->
a.get(upIdKey)
.eq(parentId)
.and(a.get("mimeType").eq(SDMConstants.MIMETYPE_INTERNET_SHORTCUT))
.and(a.get("IsActiveEntity").eq(false)));

Result draftLinks = persistenceService.run(selectDraftLinks);
SDMCredentials sdmCredentials = tokenHandler.getSDMCredentials();
Boolean isSystemUser = context.getUserInfo().isSystemUser();

for (Row draftLinkRow : draftLinks) {
Map<String, Object> draftLink = new HashMap<>();
draftLink.put("ID", draftLinkRow.get("ID"));
draftLink.put("linkUrl", draftLinkRow.get("linkUrl"));
draftLink.put("objectId", draftLinkRow.get("objectId"));
draftLink.put("fileName", draftLinkRow.get("fileName"));
String attachmentId = (String) draftLink.get("ID");
String draftLinkUrl = (String) draftLink.get("linkUrl");
String objectId = (String) draftLink.get("objectId");
String filename = (String) draftLink.get("fileName");

String originalUrl =
getOriginalUrlFromActiveTable(activeEntity, attachmentId, parentId, upIdKey);

if (originalUrl != null && !originalUrl.equals(draftLinkUrl)) {
revertLinkInSDM(objectId, filename, originalUrl, sdmCredentials, isSystemUser);
}
}
}

private String getOriginalUrlFromActiveTable(
CdsEntity activeEntity, String attachmentId, Object parentId, String upIdKey) {
CqnSelect selectActiveLink =
Select.from(activeEntity)
.columns("linkUrl")
.where(
a ->
a.get("ID")
.eq(attachmentId)
.and(a.get(upIdKey).eq(parentId))
.and(a.get("IsActiveEntity").eq(true))
.and(a.get("mimeType").eq(SDMConstants.MIMETYPE_INTERNET_SHORTCUT)));

Result activeResult = persistenceService.run(selectActiveLink);

if (activeResult.rowCount() > 0) {
Row activeRow = activeResult.single();
String originalUrl =
activeRow.get("linkUrl") != null ? activeRow.get("linkUrl").toString() : null;
return originalUrl;
} else {
return null;
}
}

private void revertLinkInSDM(
String objectId,
String filename,
String originalUrl,
SDMCredentials sdmCredentials,
Boolean isSystemUser)
throws IOException {

CmisDocument cmisDocToRevert = new CmisDocument();
cmisDocToRevert.setObjectId(objectId);
cmisDocToRevert.setFileName(filename);

cmisDocToRevert.setUrl(originalUrl);
cmisDocToRevert.setRepositoryId(SDMConstants.REPOSITORY_ID);
sdmService.editLink(cmisDocToRevert, sdmCredentials, isSystemUser);
}

@On(event = "openAttachment")
public void openAttachment(AttachmentReadContext context) throws Exception {
CdsModel cdsModel = context.getModel();
Expand All @@ -115,7 +294,7 @@ public void openAttachment(AttachmentReadContext context) throws Exception {
cmisDocument =
dbQuery.getObjectIdForAttachmentID(attachmentEntity.get(), persistenceService, id);
}
if (cmisDocument.getMimeType().equalsIgnoreCase("application/internet-shortcut")) {
if (cmisDocument.getMimeType().equalsIgnoreCase(SDMConstants.MIMETYPE_INTERNET_SHORTCUT)) {
context.setResult(cmisDocument.getUrl());
} else {
context.setResult("None");
Expand Down Expand Up @@ -169,7 +348,7 @@ private void createLink(EventContext context) throws IOException {
CmisDocument cmisDocument = new CmisDocument();
cmisDocument.setFolderId(folderId);
cmisDocument.setFileName(filenameInRequest);
cmisDocument.setMimeType("application/internet-shortcut");
cmisDocument.setMimeType(SDMConstants.MIMETYPE_INTERNET_SHORTCUT);
cmisDocument.setRepositoryId(repositoryId);
cmisDocument.setUrl(context.get("url").toString());

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package unit.com.sap.cds.sdm.service.handler;

import static com.sap.cds.sdm.constants.SDMConstants.ATTACHMENT_MAXCOUNT_ERROR_MSG;
import static org.junit.Assert.assertNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
Expand All @@ -16,6 +19,9 @@
import com.sap.cds.Result;
import com.sap.cds.Row;
import com.sap.cds.feature.attachments.generated.cds4j.sap.attachments.MediaData;
import com.sap.cds.feature.attachments.service.model.service.AttachmentModificationResult;
import com.sap.cds.feature.attachments.service.model.service.CreateAttachmentInput;
import com.sap.cds.feature.attachments.service.model.service.MarkAsDeletedInput;
import com.sap.cds.feature.attachments.service.model.servicehandler.AttachmentCreateEventContext;
import com.sap.cds.feature.attachments.service.model.servicehandler.AttachmentMarkAsDeletedEventContext;
import com.sap.cds.feature.attachments.service.model.servicehandler.AttachmentReadEventContext;
Expand All @@ -30,10 +36,12 @@
import com.sap.cds.sdm.handler.TokenHandler;
import com.sap.cds.sdm.handler.applicationservice.helper.AttachmentsHandlerUtils;
import com.sap.cds.sdm.model.CmisDocument;
import com.sap.cds.sdm.model.CopyAttachmentInput;
import com.sap.cds.sdm.model.RepoValue;
import com.sap.cds.sdm.model.SDMCredentials;
import com.sap.cds.sdm.persistence.DBQuery;
import com.sap.cds.sdm.service.DocumentUploadService;
import com.sap.cds.sdm.service.SDMAttachmentsService;
import com.sap.cds.sdm.service.SDMService;
import com.sap.cds.sdm.service.SDMServiceImpl;
import com.sap.cds.sdm.service.handler.SDMAttachmentsServiceHandler;
Expand All @@ -50,6 +58,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.Instant;
import java.util.*;
import java.util.stream.Stream;
import org.json.JSONObject;
Expand Down Expand Up @@ -547,6 +556,64 @@ public void testCreateNonVersionedDIVirus() throws IOException {
}
}

@Test
void testCopyAttachments_invalidFacetFormat() {
SDMAttachmentsService service = new SDMAttachmentsService();
CopyAttachmentInput input = mock(CopyAttachmentInput.class);
when(input.facet()).thenReturn("invalidfacet");
when(input.upId()).thenReturn("upId");
when(input.objectIds()).thenReturn(List.of("obj1"));
Exception ex =
assertThrows(
IllegalArgumentException.class,
() -> {
service.copyAttachments(input, false);
});
assertTrue(ex.getMessage().contains("Invalid facet format"));
}

@Test
void testReadAttachment_emitsContext() {
SDMAttachmentsService service = spy(new SDMAttachmentsService());
doNothing().when(service).emit(any());
InputStream result = service.readAttachment("docId");
assertNull(result);
}

@Test
void testCreateAttachment_emitsContextAndReturnsResult() {
SDMAttachmentsService service = spy(new SDMAttachmentsService());
doNothing().when(service).emit(any());
CreateAttachmentInput input = mock(CreateAttachmentInput.class);
MediaData mediaData = MediaData.create();
Comment thread
deepikaSingh2711 marked this conversation as resolved.
Dismissed
when(input.attachmentIds()).thenReturn(new HashMap<>());
when(input.attachmentEntity()).thenReturn(mock(com.sap.cds.reflect.CdsEntity.class));
when(input.fileName()).thenReturn("file.txt");
when(input.mimeType()).thenReturn("text/plain");
when(input.content()).thenReturn(new ByteArrayInputStream(new byte[0]));
AttachmentModificationResult result = service.createAttachment(input);
assertNotNull(result);
}

@Test
void testMarkAttachmentAsDeleted_emitsContext() {
SDMAttachmentsService service = spy(new SDMAttachmentsService());
doNothing().when(service).emit(any());
MarkAsDeletedInput input = mock(MarkAsDeletedInput.class);
when(input.contentId()).thenReturn("docId");
UserInfo userInfo = mock(UserInfo.class);
when(userInfo.getName()).thenReturn("user");
when(input.userInfo()).thenReturn(userInfo);
service.markAttachmentAsDeleted(input);
}

@Test
void testRestoreAttachment_emitsContext() {
SDMAttachmentsService service = spy(new SDMAttachmentsService());
doNothing().when(service).emit(any());
service.restoreAttachment(Instant.now());
}

@Test
public void testCreateNonVersionedDIOther() throws IOException {
// Initialization of mocks and setup
Expand Down
Loading
Loading