Skip to content

Commit dde83af

Browse files
authored
Merge pull request #343 from cap-java/copyAttachmentsFixForProjectionEntities
Fixing copyAttachments api for Projection entities
2 parents 534e233 + 3ee6619 commit dde83af

File tree

13 files changed

+692
-115
lines changed

13 files changed

+692
-115
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,13 @@ private SDMConstants() {
8585
public static final String FAILED_TO_FETCH_UP_ID = "Failed to fetch up_id";
8686
public static final String FAILED_TO_FETCH_FACET =
8787
"Invalid facet format, unable to extract required information.";
88+
public static final String PARENT_ENTITY_NOT_FOUND_ERROR = "Unable to find parent entity: %s";
89+
public static final String COMPOSITION_NOT_FOUND_ERROR =
90+
"Unable to find composition '%s' in entity: %s";
91+
public static final String TARGET_ATTACHMENT_ENTITY_NOT_FOUND_ERROR =
92+
"Unable to find target attachment entity: %s";
93+
public static final String INVALID_FACET_FORMAT_ERROR =
94+
"Invalid facet format. Expected: Service.Entity.Composition, got: %s";
8895

8996
public static String nameConstraintMessage(
9097
List<String> fileNameWithRestrictedCharacters, String operation) {

sdm/src/main/java/com/sap/cds/sdm/model/CopyAttachmentInput.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@
33
import java.util.List;
44

55
/**
6-
* The class {@link CopyAttachmentInput} is used to store the input for creating an attachment.
6+
* The class {@link CopyAttachmentInput} is used to store the input for copying attachments. This
7+
* model supports both regular entities and projection entities by using facet-based navigation.
78
*
8-
* @param upId The keys for the attachment entity
9-
* @param facet
10-
* @param objectIds
9+
* @param upId The key of the parent entity instance
10+
* @param facet The full facet path (e.g., "Service.Entity.composition") that will be internally
11+
* parsed to determine parent entity and composition name
12+
* @param objectIds The list of attachment object IDs to be copied
1113
*/
1214
public record CopyAttachmentInput(String upId, String facet, List<String> objectIds) {}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package com.sap.cds.sdm.model;
2+
3+
import com.sap.cds.sdm.service.handler.AttachmentCopyEventContext;
4+
import java.util.List;
5+
6+
/**
7+
* Parameter object for copyAttachmentsToSDM method to reduce parameter count and improve code
8+
* maintainability.
9+
*/
10+
public class CopyAttachmentsRequest {
11+
private final AttachmentCopyEventContext context;
12+
private final List<String> objectIds;
13+
private final String folderId;
14+
private final String repositoryId;
15+
private final SDMCredentials sdmCredentials;
16+
private final Boolean isSystemUser;
17+
private final boolean folderExists;
18+
19+
private CopyAttachmentsRequest(Builder builder) {
20+
this.context = builder.context;
21+
this.objectIds = builder.objectIds;
22+
this.folderId = builder.folderId;
23+
this.repositoryId = builder.repositoryId;
24+
this.sdmCredentials = builder.sdmCredentials;
25+
this.isSystemUser = builder.isSystemUser;
26+
this.folderExists = builder.folderExists;
27+
}
28+
29+
// Getters
30+
public AttachmentCopyEventContext getContext() {
31+
return context;
32+
}
33+
34+
public List<String> getObjectIds() {
35+
return objectIds;
36+
}
37+
38+
public String getFolderId() {
39+
return folderId;
40+
}
41+
42+
public String getRepositoryId() {
43+
return repositoryId;
44+
}
45+
46+
public SDMCredentials getSdmCredentials() {
47+
return sdmCredentials;
48+
}
49+
50+
public Boolean getIsSystemUser() {
51+
return isSystemUser;
52+
}
53+
54+
public boolean isFolderExists() {
55+
return folderExists;
56+
}
57+
58+
public static Builder builder() {
59+
return new Builder();
60+
}
61+
62+
public static class Builder {
63+
private AttachmentCopyEventContext context;
64+
private List<String> objectIds;
65+
private String folderId;
66+
private String repositoryId;
67+
private SDMCredentials sdmCredentials;
68+
private Boolean isSystemUser;
69+
private boolean folderExists;
70+
71+
public Builder context(AttachmentCopyEventContext context) {
72+
this.context = context;
73+
return this;
74+
}
75+
76+
public Builder objectIds(List<String> objectIds) {
77+
this.objectIds = objectIds;
78+
return this;
79+
}
80+
81+
public Builder folderId(String folderId) {
82+
this.folderId = folderId;
83+
return this;
84+
}
85+
86+
public Builder repositoryId(String repositoryId) {
87+
this.repositoryId = repositoryId;
88+
return this;
89+
}
90+
91+
public Builder sdmCredentials(SDMCredentials sdmCredentials) {
92+
this.sdmCredentials = sdmCredentials;
93+
return this;
94+
}
95+
96+
public Builder isSystemUser(Boolean isSystemUser) {
97+
this.isSystemUser = isSystemUser;
98+
return this;
99+
}
100+
101+
public Builder folderExists(boolean folderExists) {
102+
this.folderExists = folderExists;
103+
return this;
104+
}
105+
106+
public CopyAttachmentsRequest build() {
107+
return new CopyAttachmentsRequest(this);
108+
}
109+
}
110+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package com.sap.cds.sdm.model;
2+
3+
import java.util.List;
4+
5+
/**
6+
* Parameter object for createDraftEntries method to reduce parameter count and improve code
7+
* maintainability.
8+
*/
9+
public class CreateDraftEntriesRequest {
10+
private final List<List<String>> attachmentsMetadata;
11+
private final List<CmisDocument> populatedDocuments;
12+
private final String parentEntity;
13+
private final String compositionName;
14+
private final String upID;
15+
private final String upIdKey;
16+
private final String repositoryId;
17+
private final String folderId;
18+
19+
private CreateDraftEntriesRequest(Builder builder) {
20+
this.attachmentsMetadata = builder.attachmentsMetadata;
21+
this.populatedDocuments = builder.populatedDocuments;
22+
this.parentEntity = builder.parentEntity;
23+
this.compositionName = builder.compositionName;
24+
this.upID = builder.upID;
25+
this.upIdKey = builder.upIdKey;
26+
this.repositoryId = builder.repositoryId;
27+
this.folderId = builder.folderId;
28+
}
29+
30+
// Getters
31+
public List<List<String>> getAttachmentsMetadata() {
32+
return attachmentsMetadata;
33+
}
34+
35+
public List<CmisDocument> getPopulatedDocuments() {
36+
return populatedDocuments;
37+
}
38+
39+
public String getParentEntity() {
40+
return parentEntity;
41+
}
42+
43+
public String getCompositionName() {
44+
return compositionName;
45+
}
46+
47+
public String getUpID() {
48+
return upID;
49+
}
50+
51+
public String getUpIdKey() {
52+
return upIdKey;
53+
}
54+
55+
public String getRepositoryId() {
56+
return repositoryId;
57+
}
58+
59+
public String getFolderId() {
60+
return folderId;
61+
}
62+
63+
public static Builder builder() {
64+
return new Builder();
65+
}
66+
67+
public static class Builder {
68+
private List<List<String>> attachmentsMetadata;
69+
private List<CmisDocument> populatedDocuments;
70+
private String parentEntity;
71+
private String compositionName;
72+
private String upID;
73+
private String upIdKey;
74+
private String repositoryId;
75+
private String folderId;
76+
77+
public Builder attachmentsMetadata(List<List<String>> attachmentsMetadata) {
78+
this.attachmentsMetadata = attachmentsMetadata;
79+
return this;
80+
}
81+
82+
public Builder populatedDocuments(List<CmisDocument> populatedDocuments) {
83+
this.populatedDocuments = populatedDocuments;
84+
return this;
85+
}
86+
87+
public Builder parentEntity(String parentEntity) {
88+
this.parentEntity = parentEntity;
89+
return this;
90+
}
91+
92+
public Builder compositionName(String compositionName) {
93+
this.compositionName = compositionName;
94+
return this;
95+
}
96+
97+
public Builder upID(String upID) {
98+
this.upID = upID;
99+
return this;
100+
}
101+
102+
public Builder upIdKey(String upIdKey) {
103+
this.upIdKey = upIdKey;
104+
return this;
105+
}
106+
107+
public Builder repositoryId(String repositoryId) {
108+
this.repositoryId = repositoryId;
109+
return this;
110+
}
111+
112+
public Builder folderId(String folderId) {
113+
this.folderId = folderId;
114+
return this;
115+
}
116+
117+
public CreateDraftEntriesRequest build() {
118+
return new CreateDraftEntriesRequest(this);
119+
}
120+
}
121+
}

sdm/src/main/java/com/sap/cds/sdm/persistence/DBQuery.java

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
import com.sap.cds.ql.Update;
88
import com.sap.cds.ql.cqn.CqnSelect;
99
import com.sap.cds.ql.cqn.CqnUpdate;
10+
import com.sap.cds.reflect.CdsAssociationType;
11+
import com.sap.cds.reflect.CdsElement;
1012
import com.sap.cds.reflect.CdsEntity;
13+
import com.sap.cds.reflect.CdsModel;
1114
import com.sap.cds.sdm.constants.SDMConstants;
1215
import com.sap.cds.sdm.model.CmisDocument;
1316
import com.sap.cds.sdm.service.handler.AttachmentCopyEventContext;
17+
import com.sap.cds.services.ServiceException;
1418
import com.sap.cds.services.persistence.PersistenceService;
1519
import java.util.*;
1620

@@ -65,31 +69,66 @@ public CmisDocument getObjectIdForAttachmentID(
6569

6670
public CmisDocument getAttachmentForObjectID(
6771
PersistenceService persistenceService, String id, AttachmentCopyEventContext context) {
68-
Optional<CdsEntity> attachmentEntity = context.getModel().findEntity(context.getFacet());
72+
73+
// Use the new API to resolve the target attachment entity
74+
String parentEntity = context.getParentEntity();
75+
String compositionName = context.getCompositionName();
76+
CdsModel model = context.getModel();
77+
78+
// Find the parent entity
79+
Optional<CdsEntity> optionalParentEntity = model.findEntity(parentEntity);
80+
if (optionalParentEntity.isEmpty()) {
81+
throw new ServiceException(
82+
String.format(SDMConstants.PARENT_ENTITY_NOT_FOUND_ERROR, parentEntity));
83+
}
84+
85+
// Find the composition element in the parent entity
86+
Optional<CdsElement> compositionElement =
87+
optionalParentEntity.get().findElement(compositionName);
88+
if (compositionElement.isEmpty() || !compositionElement.get().getType().isAssociation()) {
89+
throw new ServiceException(
90+
String.format(SDMConstants.COMPOSITION_NOT_FOUND_ERROR, compositionName, parentEntity));
91+
}
92+
93+
// Get the target entity of the composition
94+
CdsAssociationType assocType = (CdsAssociationType) compositionElement.get().getType();
95+
String targetEntityName = assocType.getTarget().getQualifiedName();
96+
97+
// Find the target attachment entity
98+
Optional<CdsEntity> attachmentEntity = model.findEntity(targetEntityName);
99+
if (attachmentEntity.isEmpty()) {
100+
throw new ServiceException(
101+
String.format(SDMConstants.TARGET_ATTACHMENT_ENTITY_NOT_FOUND_ERROR, targetEntityName));
102+
}
103+
104+
// Search in active entity first
69105
CqnSelect q =
70106
Select.from(attachmentEntity.get())
71107
.columns("linkUrl", "type")
72108
.where(doc -> doc.get("objectId").eq(id));
73109
Result result = persistenceService.run(q);
74110
Optional<Row> res = result.first();
111+
75112
CmisDocument cmisDocument = new CmisDocument();
76113
if (res.isPresent()) {
77114
Row row = res.get();
78115
cmisDocument.setType(row.get("type") != null ? row.get("type").toString() : null);
79116
cmisDocument.setUrl(row.get("linkUrl") != null ? row.get("linkUrl").toString() : null);
80117
} else {
81118
// Check in draft table as well
82-
attachmentEntity = context.getModel().findEntity(context.getFacet() + "_drafts");
83-
q =
84-
Select.from(attachmentEntity.get())
85-
.columns("linkUrl", "type")
86-
.where(doc -> doc.get("objectId").eq(id));
87-
result = persistenceService.run(q);
88-
res = result.first();
89-
if (res.isPresent()) {
90-
Row row = res.get();
91-
cmisDocument.setType(row.get("type") != null ? row.get("type").toString() : null);
92-
cmisDocument.setUrl(row.get("linkUrl") != null ? row.get("linkUrl").toString() : null);
119+
Optional<CdsEntity> attachmentDraftEntity = model.findEntity(targetEntityName + "_drafts");
120+
if (attachmentDraftEntity.isPresent()) {
121+
q =
122+
Select.from(attachmentDraftEntity.get())
123+
.columns("linkUrl", "type")
124+
.where(doc -> doc.get("objectId").eq(id));
125+
result = persistenceService.run(q);
126+
res = result.first();
127+
if (res.isPresent()) {
128+
Row row = res.get();
129+
cmisDocument.setType(row.get("type") != null ? row.get("type").toString() : null);
130+
cmisDocument.setUrl(row.get("linkUrl") != null ? row.get("linkUrl").toString() : null);
131+
}
93132
}
94133
}
95134
return cmisDocument;

sdm/src/main/java/com/sap/cds/sdm/service/RegisterService.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,13 @@ public interface RegisterService extends Service {
77
String SDM_NAME = "SDMAttachmentService$Default";
88
String EVENT_COPY_ATTACHMENT = "COPY_ATTACHMENT";
99

10+
/**
11+
* Copies attachments using the facet-based approach. This method supports both regular entities
12+
* and projection entities by internally parsing the facet to determine parent entity and
13+
* composition name.
14+
*
15+
* @param input The copy attachment input containing facet and object IDs
16+
* @param isSystemUser Whether to use system user flow
17+
*/
1018
public void copyAttachments(CopyAttachmentInput input, boolean isSystemUser);
1119
}

0 commit comments

Comments
 (0)