Skip to content

Commit f5df78a

Browse files
committed
Change ActiveEntityModifier such that it flattens the delete requests
Then they can be correctly transformed into select statements in the DraftCancelAttachmentsHandler
1 parent 6a541e8 commit f5df78a

File tree

4 files changed

+83
-26
lines changed

4 files changed

+83
-26
lines changed

cds-feature-attachments/src/main/java/com/sap/cds/feature/attachments/handler/draftservice/ActiveEntityModifier.java

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,20 @@
1717
import org.slf4j.LoggerFactory;
1818

1919
/**
20-
* The class is used to modify the following values in a given {@link CqnStructuredTypeRef}:
20+
* A CQL modifier that transforms entity references for draft/active entity handling.
21+
*
22+
* <p>This modifier flattens complex entity references by removing nested references and creating a
23+
* new CQN statement for the specified {@code fullEntityName}. It performs the following
24+
* transformations:
2125
*
2226
* <ul>
23-
* <li>{@code isActiveEntity}
24-
* <li>{@code fullEntityName}
27+
* <li>Removes nested references and creates a new entity reference for {@code fullEntityName}
28+
* <li>Preserves the filter from the last segment of the original {@link CqnStructuredTypeRef}
29+
* <li>Adds an {@code IsActiveEntity} filter with the specified boolean value
2530
* </ul>
31+
*
32+
* <p>This is primarily used in draft service scenarios to transform queries between draft entities
33+
* (IsActiveEntity = false) and active entities (IsActiveEntity = true).
2634
*/
2735
class ActiveEntityModifier implements Modifier {
2836

@@ -45,13 +53,27 @@ public CqnStructuredTypeRef ref(CqnStructuredTypeRef original) {
4553
rootSegment,
4654
isActiveEntity,
4755
fullEntityName);
48-
rootSegment.id(fullEntityName);
4956

50-
Modifier modifier = new ActiveEntityModifier(isActiveEntity, fullEntityName);
51-
for (RefSegment segment : ref.segments()) {
52-
segment.filter(segment.filter().map(filter -> CQL.copy(filter, modifier)).orElse(null));
53-
}
54-
return ref.build();
57+
// Get the filter from the last segment:
58+
// Get the last segment with targetSegment, then an Optional<CqnPredicate> with filter()
59+
// which is then unwrapped to CqnPredicate or null by orElse(null).
60+
CqnPredicate lastSegmentFilter = original.targetSegment().filter().orElse(null);
61+
62+
// Create an IsActiveEntity filter
63+
CqnPredicate isActiveEntityFilter = CQL.get(Drafts.IS_ACTIVE_ENTITY).eq(isActiveEntity);
64+
65+
// Combine with original filter if it exists
66+
CqnPredicate combinedFilter =
67+
lastSegmentFilter != null
68+
? CQL.and(lastSegmentFilter, isActiveEntityFilter)
69+
: isActiveEntityFilter;
70+
71+
// Apply any additional modifications (like replacing other IsActiveEntity references)
72+
// This calls the comparison() method below for each comparison in the filter
73+
CqnPredicate modifiedFilter = CQL.copy(combinedFilter, this);
74+
75+
// Create a new entity reference with the modified filter
76+
return CQL.entity(fullEntityName).filter(modifiedFilter).asRef();
5577
}
5678

5779
@Override

cds-feature-attachments/src/main/java/com/sap/cds/feature/attachments/handler/draftservice/DraftCancelAttachmentsHandler.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ private Validator buildDeleteContentValidator(
108108
// Unfortunately, context.getEvent() does not return a reliable value in this case.
109109
private boolean isWhereEmpty(DraftCancelEventContext context) {
110110
System.out.println(context.getCqn().where());
111-
return true; // context.getCqn().where().isEmpty();
111+
return context.getCqn().where().isEmpty();
112112
}
113113

114114
// This function checks if the given entity is of type Attachments
@@ -127,16 +127,13 @@ private boolean hasAttachmentAssociations(CdsEntity entity) {
127127
private List<Attachments> readAttachments(
128128
DraftCancelEventContext context, CdsStructuredType entity, boolean isActiveEntity) {
129129
logger.debug(
130-
"Reading attachments for entity {} (isActiveEntity={})",
131-
entity.getName(),
132-
isActiveEntity);
130+
"Reading attachments for entity {} (isActiveEntity={})", entity.getName(), isActiveEntity);
133131
logger.debug("Original CQN: {}", context.getCqn());
134-
CqnDelete cqnInactiveEntity =
132+
CqnDelete modifiedCQN =
135133
CQL.copy(
136134
context.getCqn(), new ActiveEntityModifier(isActiveEntity, entity.getQualifiedName()));
137-
logger.debug("Modified CQN: {}", cqnInactiveEntity);
138-
return attachmentsReader.readAttachments(
139-
context.getModel(), (CdsEntity) entity, cqnInactiveEntity);
135+
logger.debug("Modified CQN: {}", modifiedCQN);
136+
return attachmentsReader.readAttachments(context.getModel(), (CdsEntity) entity, modifiedCQN);
140137
}
141138

142139
private List<Attachments> getCondensedActiveAttachments(

cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/draftservice/ActiveEntityModifierTest.java

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,28 @@ void activeEntityReplacedToTrue() {
3838
}
3939

4040
@Test
41-
void entityNameReplaced() {
41+
void entityNameReplacedAndActiveEntity() {
4242
var select = Select.from(RootTable_.class).where(root -> root.IsActiveEntity().eq(true));
4343

4444
var result = CQL.copy(select, new ActiveEntityModifier(true, RootTable_.CDS_NAME + "_draft"));
4545

46-
assertThat(result.toString()).contains("{\"ref\":[\"unit.test.TestService.RootTable_draft\"]}");
46+
// Expects the entity to have an IsActiveEntity filter in the reference
47+
assertThat(result.toString())
48+
.contains(
49+
"{\"id\":\"unit.test.TestService.RootTable_draft\",\"where\":[{\"ref\":[\"IsActiveEntity\"]},\"=\",{\"val\":true}]}");
4750
}
4851

4952
@Test
50-
void nothingReplaced() {
53+
void entityNameNotReplacedAndActiveEntity() {
5154
var select = Select.from(RootTable_.class).where(root -> root.IsActiveEntity().eq(true));
5255

5356
var result = CQL.copy(select, new ActiveEntityModifier(true, RootTable_.CDS_NAME));
5457

55-
assertThat(result).hasToString(select.toString());
58+
// Expects the entity to have an IsActiveEntity filter in the reference even when entity name
59+
// doesn't change
60+
assertThat(result.toString())
61+
.contains(
62+
"{\"id\":\"unit.test.TestService.RootTable\",\"where\":[{\"ref\":[\"IsActiveEntity\"]},\"=\",{\"val\":true}]}");
5663
}
5764

5865
@Test
@@ -95,4 +102,27 @@ void onlyRefActiveEntityIsReplaced() {
95102
assertThat(result.toString()).contains("{\"val\":false},\"=\",{\"ref\":[\"IsActiveEntity\"]}");
96103
assertThat(result.toString()).contains("{\"val\":true},\"=\",{\"ref\":[\"HasActiveEntity\"]}");
97104
}
105+
106+
@Test
107+
void combinesNonIsActiveEntityFilterWithIsActiveEntityFilter() {
108+
// Create query with a filter on the last/target segment
109+
CqnSelect original =
110+
Select.from(
111+
CQL.entity(RootTable_.CDS_NAME)
112+
.filter(CQL.get("title").eq("Some Title")) // Filter on entity reference
113+
);
114+
115+
ActiveEntityModifier modifier = new ActiveEntityModifier(true, RootTable_.CDS_NAME);
116+
117+
var result = CQL.copy(original, modifier);
118+
119+
// Should contain both the original title filter and IsActiveEntity filter
120+
assertThat(result.toString()).contains("\"title\"");
121+
assertThat(result.toString()).contains("\"Some Title\"");
122+
assertThat(result.toString()).contains("\"IsActiveEntity\"");
123+
assertThat(result.toString()).contains("\"val\":true");
124+
125+
// The key assertion: should contain AND operation combining both filters
126+
assertThat(result.toString()).contains("\"and\"");
127+
}
98128
}

cds-feature-attachments/src/test/java/com/sap/cds/feature/attachments/handler/draftservice/DraftCancelAttachmentsHandlerTest.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,11 @@ void attachmentReaderCorrectCalled() {
109109
CdsEntity target = eventContext.getTarget();
110110
verify(attachmentsReader)
111111
.readAttachments(eq(runtime.getCdsModel()), eq(target), deleteArgumentCaptor.capture());
112-
CqnDelete originDelete = deleteArgumentCaptor.getValue();
113-
assertThat(originDelete.toJson()).isEqualTo(delete.toJson());
112+
// Check if the modified CqnDelete that is passed to readAttachments looks correct
113+
CqnDelete modifiedCQN = deleteArgumentCaptor.getValue();
114+
assertThat(modifiedCQN.toJson())
115+
.isEqualTo(
116+
"{\"DELETE\":{\"from\":{\"ref\":[{\"id\":\"unit.test.TestService.Attachment\",\"where\":[{\"ref\":[\"IsActiveEntity\"]},\"=\",{\"val\":true}]}]}}}");
114117

115118
deleteArgumentCaptor = ArgumentCaptor.forClass(CqnDelete.class);
116119
CdsEntity siblingTarget = target.getTargetOf(Drafts.SIBLING_ENTITY);
@@ -133,8 +136,11 @@ void attachmentReaderCorrectCalledForEntityWithAttachmentAssociations() {
133136
CdsEntity target = eventContext.getTarget();
134137
verify(attachmentsReader)
135138
.readAttachments(eq(runtime.getCdsModel()), eq(target), deleteArgumentCaptor.capture());
136-
CqnDelete originDelete = deleteArgumentCaptor.getValue();
137-
assertThat(originDelete.toJson()).isEqualTo(delete.toJson());
139+
// Check if the modified CqnDelete that is passed to readAttachments looks correct
140+
CqnDelete modifiedCQN = deleteArgumentCaptor.getValue();
141+
assertThat(modifiedCQN.toJson())
142+
.isEqualTo(
143+
"{\"DELETE\":{\"from\":{\"ref\":[{\"id\":\"unit.test.TestService.RootTable\",\"where\":[{\"ref\":[\"IsActiveEntity\"]},\"=\",{\"val\":true}]}]}}}");
138144

139145
deleteArgumentCaptor = ArgumentCaptor.forClass(CqnDelete.class);
140146
CdsEntity siblingTarget = target.getTargetOf(Drafts.SIBLING_ENTITY);
@@ -162,7 +168,9 @@ void modifierCalledWithCorrectEntitiesIfDraftIsInContext() {
162168
.readAttachments(
163169
eq(runtime.getCdsModel()), eq(siblingTarget), deleteArgumentCaptor.capture());
164170
CqnDelete siblingDelete = deleteArgumentCaptor.getValue();
165-
assertThat(siblingDelete.toJson()).isEqualTo(delete.toJson());
171+
assertThat(siblingDelete.toJson())
172+
.isEqualTo(
173+
"{\"DELETE\":{\"from\":{\"ref\":[{\"id\":\"unit.test.TestService.Attachment\",\"where\":[{\"ref\":[\"IsActiveEntity\"]},\"=\",{\"val\":true}]}]}}}");
166174
}
167175

168176
@Test

0 commit comments

Comments
 (0)