Skip to content

Commit 56fee73

Browse files
Merge branch 'regulationLogs' of https://github.com/cap-java/sdm into regulationLogs
2 parents 4bd39e4 + 9f7aae9 commit 56fee73

4 files changed

Lines changed: 1953 additions & 4 deletions

File tree

‎sdm/src/main/java/com/sap/cds/sdm/constants/SDMConstants.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ private SDMConstants() {
1212
}
1313

1414
public static final String REPOSITORY_ID = System.getenv("REPOSITORY_ID");
15+
public static final String MIMETYPE_INTERNET_SHORTCUT = "application/internet-shortcut";
1516
public static final String SYSTEM_USER = "system-internal";
1617
public static final String DESTINATION_EXCEPTION =
1718
"Unable to get the destination for sdm service binding";

‎sdm/src/main/java/com/sap/cds/sdm/service/handler/SDMServiceGenericHandler.java‎

Lines changed: 181 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
import com.fasterxml.jackson.databind.JsonNode;
66
import com.fasterxml.jackson.databind.ObjectMapper;
77
import com.sap.cds.Result;
8+
import com.sap.cds.Row;
89
import com.sap.cds.feature.attachments.service.AttachmentService;
910
import com.sap.cds.ql.Insert;
11+
import com.sap.cds.ql.Select;
1012
import com.sap.cds.ql.Update;
1113
import com.sap.cds.ql.cqn.CqnAnalyzer;
1214
import com.sap.cds.ql.cqn.CqnSelect;
@@ -16,6 +18,7 @@
1618
import com.sap.cds.reflect.CdsModel;
1719
import com.sap.cds.sdm.constants.SDMConstants;
1820
import com.sap.cds.sdm.handler.TokenHandler;
21+
import com.sap.cds.sdm.handler.applicationservice.helper.AttachmentsHandlerUtils;
1922
import com.sap.cds.sdm.model.*;
2023
import com.sap.cds.sdm.persistence.DBQuery;
2124
import com.sap.cds.sdm.service.DocumentUploadService;
@@ -25,8 +28,10 @@
2528
import com.sap.cds.services.EventContext;
2629
import com.sap.cds.services.ServiceException;
2730
import com.sap.cds.services.cds.ApplicationService;
31+
import com.sap.cds.services.draft.DraftCancelEventContext;
2832
import com.sap.cds.services.draft.DraftService;
2933
import com.sap.cds.services.handler.EventHandler;
34+
import com.sap.cds.services.handler.annotations.Before;
3035
import com.sap.cds.services.handler.annotations.On;
3136
import com.sap.cds.services.handler.annotations.ServiceName;
3237
import com.sap.cds.services.persistence.PersistenceService;
@@ -96,6 +101,180 @@ public void edit(EventContext context) throws IOException {
96101
editLink(context);
97102
}
98103

104+
@Before(event = DraftService.EVENT_DRAFT_CANCEL)
105+
public void handleDraftDiscardForLinks(DraftCancelEventContext context) throws IOException {
106+
CdsEntity parentDraftEntity = context.getTarget();
107+
CqnAnalyzer analyzer = CqnAnalyzer.create(context.getModel());
108+
Map<String, Object> parentKeys = analyzer.analyze(context.getCqn()).rootKeys();
109+
String parentEntityName = parentDraftEntity.getQualifiedName().replace("_drafts", "");
110+
111+
Optional<CdsEntity> parentActiveEntityOpt = context.getModel().findEntity(parentEntityName);
112+
Map<String, String> compositionPathMapping =
113+
parentActiveEntityOpt
114+
.map(
115+
cdsEntity ->
116+
AttachmentsHandlerUtils.getAttachmentPathMapping(
117+
context.getModel(), cdsEntity, persistenceService))
118+
.orElse(new HashMap<>());
119+
120+
for (Map.Entry<String, String> entry : compositionPathMapping.entrySet()) {
121+
String attachmentCompositionDefinition = entry.getKey();
122+
revertLinksForComposition(context, parentKeys, attachmentCompositionDefinition);
123+
}
124+
revertNestedEntityLinks(context);
125+
}
126+
127+
private void revertNestedEntityLinks(DraftCancelEventContext context) throws IOException {
128+
129+
CdsEntity parentDraftEntity = context.getTarget();
130+
String parentEntityName = parentDraftEntity.getQualifiedName().replace("_drafts", "");
131+
Optional<CdsEntity> parentActiveEntityOpt = context.getModel().findEntity(parentEntityName);
132+
133+
if (parentActiveEntityOpt.isPresent()) {
134+
CdsEntity parentActiveEntity = parentActiveEntityOpt.get();
135+
136+
parentActiveEntity
137+
.compositions()
138+
.forEach(
139+
composition -> {
140+
try {
141+
processNestedEntityComposition(context, composition);
142+
} catch (IOException e) {
143+
throw new RuntimeException(e);
144+
}
145+
});
146+
}
147+
}
148+
149+
private void processNestedEntityComposition(
150+
DraftCancelEventContext context, CdsElement composition) throws IOException {
151+
152+
CdsAssociationType associationType = (CdsAssociationType) composition.getType();
153+
String targetEntityName = associationType.getTarget().getQualifiedName();
154+
String draftTargetEntityName = targetEntityName + "_drafts";
155+
156+
Optional<CdsEntity> nestedDraftEntity = context.getModel().findEntity(draftTargetEntityName);
157+
158+
if (nestedDraftEntity.isPresent()) {
159+
Map<String, String> nestedAttachmentMapping =
160+
AttachmentsHandlerUtils.getAttachmentPathMapping(
161+
context.getModel(), associationType.getTarget(), persistenceService);
162+
163+
if (nestedAttachmentMapping.isEmpty()) {
164+
return;
165+
}
166+
167+
Result nestedRecords =
168+
persistenceService.run(
169+
Select.from(nestedDraftEntity.get())
170+
.columns("ID")
171+
.where(e -> e.get("IsActiveEntity").eq(false)));
172+
173+
for (Row nestedRecord : nestedRecords) {
174+
Object nestedEntityId = nestedRecord.get("ID");
175+
176+
Map<String, Object> nestedEntityKeys = new HashMap<>();
177+
nestedEntityKeys.put("ID", nestedEntityId);
178+
nestedEntityKeys.put("IsActiveEntity", false);
179+
180+
for (Map.Entry<String, String> entry : nestedAttachmentMapping.entrySet()) {
181+
String attachmentPath = entry.getKey();
182+
revertLinksForComposition(context, nestedEntityKeys, attachmentPath);
183+
}
184+
}
185+
}
186+
}
187+
188+
private void revertLinksForComposition(
189+
DraftCancelEventContext context,
190+
Map<String, Object> parentKeys,
191+
String attachmentCompositionDefinition)
192+
throws IOException {
193+
194+
CdsModel model = context.getModel();
195+
String draftEntityName = attachmentCompositionDefinition + "_drafts";
196+
CdsEntity draftEntity = model.findEntity(draftEntityName).get();
197+
CdsEntity activeEntity = model.findEntity(attachmentCompositionDefinition).get();
198+
199+
String upIdKey = getUpIdKey(draftEntity);
200+
String parentKeyName = upIdKey.replaceFirst("^up__", "");
201+
Object parentId = parentKeys.get(parentKeyName);
202+
203+
CqnSelect selectDraftLinks =
204+
Select.from(draftEntity)
205+
.where(
206+
a ->
207+
a.get(upIdKey)
208+
.eq(parentId)
209+
.and(a.get("mimeType").eq(SDMConstants.MIMETYPE_INTERNET_SHORTCUT))
210+
.and(a.get("IsActiveEntity").eq(false)));
211+
212+
Result draftLinks = persistenceService.run(selectDraftLinks);
213+
SDMCredentials sdmCredentials = tokenHandler.getSDMCredentials();
214+
Boolean isSystemUser = context.getUserInfo().isSystemUser();
215+
216+
for (Row draftLinkRow : draftLinks) {
217+
Map<String, Object> draftLink = new HashMap<>();
218+
draftLink.put("ID", draftLinkRow.get("ID"));
219+
draftLink.put("linkUrl", draftLinkRow.get("linkUrl"));
220+
draftLink.put("objectId", draftLinkRow.get("objectId"));
221+
draftLink.put("fileName", draftLinkRow.get("fileName"));
222+
String attachmentId = (String) draftLink.get("ID");
223+
String draftLinkUrl = (String) draftLink.get("linkUrl");
224+
String objectId = (String) draftLink.get("objectId");
225+
String filename = (String) draftLink.get("fileName");
226+
227+
String originalUrl =
228+
getOriginalUrlFromActiveTable(activeEntity, attachmentId, parentId, upIdKey);
229+
230+
if (originalUrl != null && !originalUrl.equals(draftLinkUrl)) {
231+
revertLinkInSDM(objectId, filename, originalUrl, sdmCredentials, isSystemUser);
232+
}
233+
}
234+
}
235+
236+
private String getOriginalUrlFromActiveTable(
237+
CdsEntity activeEntity, String attachmentId, Object parentId, String upIdKey) {
238+
CqnSelect selectActiveLink =
239+
Select.from(activeEntity)
240+
.columns("linkUrl")
241+
.where(
242+
a ->
243+
a.get("ID")
244+
.eq(attachmentId)
245+
.and(a.get(upIdKey).eq(parentId))
246+
.and(a.get("IsActiveEntity").eq(true))
247+
.and(a.get("mimeType").eq(SDMConstants.MIMETYPE_INTERNET_SHORTCUT)));
248+
249+
Result activeResult = persistenceService.run(selectActiveLink);
250+
251+
if (activeResult.rowCount() > 0) {
252+
Row activeRow = activeResult.single();
253+
String originalUrl =
254+
activeRow.get("linkUrl") != null ? activeRow.get("linkUrl").toString() : null;
255+
return originalUrl;
256+
} else {
257+
return null;
258+
}
259+
}
260+
261+
private void revertLinkInSDM(
262+
String objectId,
263+
String filename,
264+
String originalUrl,
265+
SDMCredentials sdmCredentials,
266+
Boolean isSystemUser)
267+
throws IOException {
268+
269+
CmisDocument cmisDocToRevert = new CmisDocument();
270+
cmisDocToRevert.setObjectId(objectId);
271+
cmisDocToRevert.setFileName(filename);
272+
273+
cmisDocToRevert.setUrl(originalUrl);
274+
cmisDocToRevert.setRepositoryId(SDMConstants.REPOSITORY_ID);
275+
sdmService.editLink(cmisDocToRevert, sdmCredentials, isSystemUser);
276+
}
277+
99278
@On(event = "openAttachment")
100279
public void openAttachment(AttachmentReadContext context) throws Exception {
101280
CdsModel cdsModel = context.getModel();
@@ -115,7 +294,7 @@ public void openAttachment(AttachmentReadContext context) throws Exception {
115294
cmisDocument =
116295
dbQuery.getObjectIdForAttachmentID(attachmentEntity.get(), persistenceService, id);
117296
}
118-
if (cmisDocument.getMimeType().equalsIgnoreCase("application/internet-shortcut")) {
297+
if (cmisDocument.getMimeType().equalsIgnoreCase(SDMConstants.MIMETYPE_INTERNET_SHORTCUT)) {
119298
context.setResult(cmisDocument.getUrl());
120299
} else {
121300
context.setResult("None");
@@ -169,7 +348,7 @@ private void createLink(EventContext context) throws IOException {
169348
CmisDocument cmisDocument = new CmisDocument();
170349
cmisDocument.setFolderId(folderId);
171350
cmisDocument.setFileName(filenameInRequest);
172-
cmisDocument.setMimeType("application/internet-shortcut");
351+
cmisDocument.setMimeType(SDMConstants.MIMETYPE_INTERNET_SHORTCUT);
173352
cmisDocument.setRepositoryId(repositoryId);
174353
cmisDocument.setUrl(context.get("url").toString());
175354

‎sdm/src/test/java/unit/com/sap/cds/sdm/service/handler/SDMAttachmentsServiceHandlerTest.java‎

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package unit.com.sap.cds.sdm.service.handler;
22

33
import static com.sap.cds.sdm.constants.SDMConstants.ATTACHMENT_MAXCOUNT_ERROR_MSG;
4+
import static org.junit.Assert.assertNull;
45
import static org.junit.jupiter.api.Assertions.assertEquals;
56
import static org.junit.jupiter.api.Assertions.assertFalse;
7+
import static org.junit.jupiter.api.Assertions.assertNotNull;
68
import static org.junit.jupiter.api.Assertions.assertThrows;
79
import static org.junit.jupiter.api.Assertions.assertTrue;
810
import static org.mockito.ArgumentMatchers.any;
11+
import static org.mockito.ArgumentMatchers.anyBoolean;
912
import static org.mockito.ArgumentMatchers.anyList;
1013
import static org.mockito.ArgumentMatchers.anyString;
1114
import static org.mockito.ArgumentMatchers.eq;
@@ -16,6 +19,9 @@
1619
import com.sap.cds.Result;
1720
import com.sap.cds.Row;
1821
import com.sap.cds.feature.attachments.generated.cds4j.sap.attachments.MediaData;
22+
import com.sap.cds.feature.attachments.service.model.service.AttachmentModificationResult;
23+
import com.sap.cds.feature.attachments.service.model.service.CreateAttachmentInput;
24+
import com.sap.cds.feature.attachments.service.model.service.MarkAsDeletedInput;
1925
import com.sap.cds.feature.attachments.service.model.servicehandler.AttachmentCreateEventContext;
2026
import com.sap.cds.feature.attachments.service.model.servicehandler.AttachmentMarkAsDeletedEventContext;
2127
import com.sap.cds.feature.attachments.service.model.servicehandler.AttachmentReadEventContext;
@@ -30,10 +36,12 @@
3036
import com.sap.cds.sdm.handler.TokenHandler;
3137
import com.sap.cds.sdm.handler.applicationservice.helper.AttachmentsHandlerUtils;
3238
import com.sap.cds.sdm.model.CmisDocument;
39+
import com.sap.cds.sdm.model.CopyAttachmentInput;
3340
import com.sap.cds.sdm.model.RepoValue;
3441
import com.sap.cds.sdm.model.SDMCredentials;
3542
import com.sap.cds.sdm.persistence.DBQuery;
3643
import com.sap.cds.sdm.service.DocumentUploadService;
44+
import com.sap.cds.sdm.service.SDMAttachmentsService;
3745
import com.sap.cds.sdm.service.SDMService;
3846
import com.sap.cds.sdm.service.SDMServiceImpl;
3947
import com.sap.cds.sdm.service.handler.SDMAttachmentsServiceHandler;
@@ -50,6 +58,7 @@
5058
import java.io.ByteArrayInputStream;
5159
import java.io.IOException;
5260
import java.io.InputStream;
61+
import java.time.Instant;
5362
import java.util.*;
5463
import java.util.stream.Stream;
5564
import org.json.JSONObject;
@@ -547,6 +556,64 @@ public void testCreateNonVersionedDIVirus() throws IOException {
547556
}
548557
}
549558

559+
@Test
560+
void testCopyAttachments_invalidFacetFormat() {
561+
SDMAttachmentsService service = new SDMAttachmentsService();
562+
CopyAttachmentInput input = mock(CopyAttachmentInput.class);
563+
when(input.facet()).thenReturn("invalidfacet");
564+
when(input.upId()).thenReturn("upId");
565+
when(input.objectIds()).thenReturn(List.of("obj1"));
566+
Exception ex =
567+
assertThrows(
568+
IllegalArgumentException.class,
569+
() -> {
570+
service.copyAttachments(input, false);
571+
});
572+
assertTrue(ex.getMessage().contains("Invalid facet format"));
573+
}
574+
575+
@Test
576+
void testReadAttachment_emitsContext() {
577+
SDMAttachmentsService service = spy(new SDMAttachmentsService());
578+
doNothing().when(service).emit(any());
579+
InputStream result = service.readAttachment("docId");
580+
assertNull(result);
581+
}
582+
583+
@Test
584+
void testCreateAttachment_emitsContextAndReturnsResult() {
585+
SDMAttachmentsService service = spy(new SDMAttachmentsService());
586+
doNothing().when(service).emit(any());
587+
CreateAttachmentInput input = mock(CreateAttachmentInput.class);
588+
MediaData mediaData = MediaData.create();
589+
when(input.attachmentIds()).thenReturn(new HashMap<>());
590+
when(input.attachmentEntity()).thenReturn(mock(com.sap.cds.reflect.CdsEntity.class));
591+
when(input.fileName()).thenReturn("file.txt");
592+
when(input.mimeType()).thenReturn("text/plain");
593+
when(input.content()).thenReturn(new ByteArrayInputStream(new byte[0]));
594+
AttachmentModificationResult result = service.createAttachment(input);
595+
assertNotNull(result);
596+
}
597+
598+
@Test
599+
void testMarkAttachmentAsDeleted_emitsContext() {
600+
SDMAttachmentsService service = spy(new SDMAttachmentsService());
601+
doNothing().when(service).emit(any());
602+
MarkAsDeletedInput input = mock(MarkAsDeletedInput.class);
603+
when(input.contentId()).thenReturn("docId");
604+
UserInfo userInfo = mock(UserInfo.class);
605+
when(userInfo.getName()).thenReturn("user");
606+
when(input.userInfo()).thenReturn(userInfo);
607+
service.markAttachmentAsDeleted(input);
608+
}
609+
610+
@Test
611+
void testRestoreAttachment_emitsContext() {
612+
SDMAttachmentsService service = spy(new SDMAttachmentsService());
613+
doNothing().when(service).emit(any());
614+
service.restoreAttachment(Instant.now());
615+
}
616+
550617
@Test
551618
public void testCreateNonVersionedDIOther() throws IOException {
552619
// Initialization of mocks and setup

0 commit comments

Comments
 (0)