Skip to content

Commit b3f940b

Browse files
authored
Make use of improved event handler APIs (#541)
- Typed queries - Typed entity references - More possibilities for return types
1 parent 229674e commit b3f940b

File tree

9 files changed

+166
-213
lines changed

9 files changed

+166
-213
lines changed

srv/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@
235235
<basePackage>cds.gen</basePackage>
236236
<strictSetters>true</strictSetters>
237237
<interfacesForAspects>true</interfacesForAspects>
238+
<linkedInterfaces>true</linkedInterfaces>
238239
</configuration>
239240
</execution>
240241
</executions>

srv/src/main/java/my/bookshop/RatingCalculator.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,18 @@
22

33
import static cds.gen.my.bookshop.Bookshop_.BOOKS;
44

5+
import cds.gen.my.bookshop.Books;
6+
import cds.gen.my.bookshop.Reviews;
7+
import com.sap.cds.Result;
8+
import com.sap.cds.ql.Select;
9+
import com.sap.cds.ql.Update;
10+
import com.sap.cds.services.persistence.PersistenceService;
511
import java.math.BigDecimal;
612
import java.math.RoundingMode;
713
import java.util.OptionalDouble;
814
import java.util.stream.Stream;
9-
1015
import org.springframework.stereotype.Component;
1116

12-
import com.sap.cds.Result;
13-
import com.sap.cds.ql.Select;
14-
import com.sap.cds.ql.Update;
15-
import com.sap.cds.services.persistence.PersistenceService;
16-
17-
import cds.gen.my.bookshop.Books;
18-
import cds.gen.my.bookshop.Reviews;
19-
2017
/**
2118
* Takes care of calculating the average rating of a book based on its review
2219
* ratings.
@@ -34,8 +31,8 @@ public class RatingCalculator {
3431
* Initializes the ratings for all existing books based on their reviews.
3532
*/
3633
public void initBookRatings() {
37-
Result result = db.run(Select.from(BOOKS).columns(b -> b.ID()));
38-
for (Books book : result.listOf(Books.class)) {
34+
var result = db.run(Select.from(BOOKS).columns(b -> b.ID()));
35+
for (Books book : result) {
3936
setBookRating(book.getId());
4037
}
4138
}

srv/src/main/java/my/bookshop/handlers/AdminServiceAddressHandler.java

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,13 @@
22

33
import static cds.gen.adminservice.AdminService_.ADDRESSES;
44

5-
import java.time.Duration;
6-
import java.util.Optional;
7-
import java.util.stream.Stream;
8-
9-
import org.slf4j.Logger;
10-
import org.slf4j.LoggerFactory;
11-
import org.springframework.beans.factory.annotation.Qualifier;
12-
import org.springframework.stereotype.Component;
13-
14-
import com.sap.cds.Result;
5+
import cds.gen.adminservice.Addresses;
6+
import cds.gen.adminservice.Addresses_;
7+
import cds.gen.adminservice.AdminService_;
8+
import cds.gen.adminservice.Orders;
9+
import cds.gen.api_business_partner.ApiBusinessPartner;
10+
import cds.gen.api_business_partner.ApiBusinessPartner_;
11+
import cds.gen.api_business_partner.BusinessPartnerChangedContext;
1512
import com.sap.cds.ql.CQL;
1613
import com.sap.cds.ql.Insert;
1714
import com.sap.cds.ql.Predicate;
@@ -33,15 +30,14 @@
3330
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceConfiguration;
3431
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceConfiguration.TimeLimiterConfiguration;
3532
import com.sap.cloud.sdk.cloudplatform.resilience.ResilienceDecorator;
36-
37-
import cds.gen.adminservice.Addresses;
38-
import cds.gen.adminservice.Addresses_;
39-
import cds.gen.adminservice.AdminService_;
40-
import cds.gen.adminservice.Orders;
41-
import cds.gen.api_business_partner.ApiBusinessPartner;
42-
import cds.gen.api_business_partner.ApiBusinessPartner_;
43-
import cds.gen.api_business_partner.BusinessPartnerChangedContext;
33+
import java.time.Duration;
34+
import java.util.Optional;
35+
import java.util.stream.Stream;
4436
import my.bookshop.MessageKeys;
37+
import org.slf4j.Logger;
38+
import org.slf4j.LoggerFactory;
39+
import org.springframework.beans.factory.annotation.Qualifier;
40+
import org.springframework.stereotype.Component;
4541

4642
/**
4743
* Custom handler for the Admin Service Addresses, which come from a remote S/4 System
@@ -107,13 +103,13 @@ public void patchAddressId(EventContext context, Stream<Orders> orders) {
107103

108104
orders.filter(o -> o.getShippingAddressId() != null).forEach(order -> {
109105
String addressId = order.getShippingAddressId();
110-
Result replica = db.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner).and(a.ID().eq(addressId))));
106+
var replica = db.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner).and(a.ID().eq(addressId))));
111107
// check if the address was not yet replicated
112108
if(replica.rowCount() < 1) {
113109
logger.info("Replicating Address '{}' from S/4 service", addressId);
114110
Addresses remoteAddress = bupa.run(Select.from(ADDRESSES)
115111
.where(a -> a.businessPartner().eq(businessPartner).and(a.ID().eq(addressId))))
116-
.single(Addresses.class);
112+
.single();
117113

118114
remoteAddress.setTombstone(false);
119115
db.run(Insert.into(ADDRESSES).entry(remoteAddress));
@@ -128,15 +124,15 @@ public void updateBusinessPartnerAddresses(BusinessPartnerChangedContext context
128124
String businessPartner = context.getData().getBusinessPartner();
129125

130126
// fetch affected entries from local replicas
131-
Result replicas = db.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner)));
127+
var replicas = db.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner)));
132128
if(replicas.rowCount() > 0) {
133129
logger.info("Updating Addresses for BusinessPartner '{}'", businessPartner);
134130
// fetch changed data from S/4 -> might be less than local due to deletes
135-
Result remoteAddresses = bupa.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner)));
131+
var remoteAddresses = bupa.run(Select.from(ADDRESSES).where(a -> a.businessPartner().eq(businessPartner)));
136132
// update replicas or add tombstone if external address was deleted
137-
replicas.streamOf(Addresses.class).forEach(rep -> {
133+
replicas.stream().forEach(rep -> {
138134
Optional<Addresses> matching = remoteAddresses
139-
.streamOf(Addresses.class)
135+
.stream()
140136
.filter(ext -> ext.getId().equals(rep.getId()))
141137
.findFirst();
142138

srv/src/main/java/my/bookshop/handlers/AdminServiceAuditHandler.java

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,9 @@
22

33
import static cds.gen.adminservice.AdminService_.ORDERS;
44

5-
import java.util.Arrays;
6-
import java.util.Optional;
7-
import java.util.stream.Stream;
8-
9-
import org.apache.commons.lang3.StringUtils;
10-
import org.springframework.stereotype.Component;
11-
5+
import cds.gen.adminservice.AdminService_;
6+
import cds.gen.adminservice.Orders;
7+
import cds.gen.adminservice.Orders_;
128
import com.sap.cds.ql.Select;
139
import com.sap.cds.ql.cqn.CqnDelete;
1410
import com.sap.cds.services.EventContext;
@@ -24,10 +20,11 @@
2420
import com.sap.cds.services.handler.annotations.Before;
2521
import com.sap.cds.services.handler.annotations.ServiceName;
2622
import com.sap.cds.services.persistence.PersistenceService;
27-
28-
import cds.gen.adminservice.AdminService_;
29-
import cds.gen.adminservice.Orders;
30-
import cds.gen.adminservice.Orders_;
23+
import java.util.Arrays;
24+
import java.util.Optional;
25+
import java.util.stream.Stream;
26+
import org.apache.commons.lang3.StringUtils;
27+
import org.springframework.stereotype.Component;
3128

3229
/**
3330
* A custom handler that creates AuditLog messages.
@@ -74,7 +71,7 @@ public void beforeUpdateOrUpsertOrder(EventContext context, Stream<Orders> order
7471
});
7572
}
7673

77-
@Before(event = { CqnService.EVENT_DELETE }, entity = { Orders_.CDS_NAME })
74+
@Before(entity = { Orders_.CDS_NAME })
7875
public void beforeDelete(CdsDeleteEventContext context) {
7976
// prepare a select statement to read old currency code
8077
Select<?> ordersSelect = toSelect(context.getCqn());
@@ -95,11 +92,11 @@ private void auditCfgChange(final Action action, final ConfigChange cfgChange, E
9592

9693
private Optional<Orders> readOldOrders(String ordersId) {
9794
// prepare a select statement to read old order number
98-
Select<Orders_> ordersSelect = Select.from(ORDERS).columns(Orders_::OrderNo)
95+
var ordersSelect = Select.from(ORDERS).columns(Orders_::OrderNo)
9996
.where(o -> o.ID().eq(ordersId).and(o.IsActiveEntity().eq(true)));
10097

10198
// read old orders from DB
102-
return this.db.run(ordersSelect).first(Orders.class);
99+
return this.db.run(ordersSelect).first();
103100
}
104101

105102
private static ConfigChange createConfigChange(Orders orders, Orders oldOrders) {

srv/src/main/java/my/bookshop/handlers/AdminServiceHandler.java

Lines changed: 47 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
11
package my.bookshop.handlers;
22

33
import static cds.gen.adminservice.AdminService_.ORDERS;
4-
import static cds.gen.adminservice.AdminService_.ORDER_ITEMS;
54
import static cds.gen.my.bookshop.Bookshop_.BOOKS;
65

7-
import java.io.BufferedReader;
8-
import java.io.IOException;
9-
import java.io.InputStream;
10-
import java.io.InputStreamReader;
11-
import java.math.BigDecimal;
12-
import java.util.ArrayList;
13-
import java.util.Arrays;
14-
import java.util.List;
15-
import java.util.UUID;
16-
import java.util.function.Supplier;
17-
import java.util.stream.Stream;
18-
import org.springframework.stereotype.Component;
19-
import com.sap.cds.Result;
6+
import cds.gen.adminservice.AdminService;
7+
import cds.gen.adminservice.AdminService_;
8+
import cds.gen.adminservice.Books;
9+
import cds.gen.adminservice.BooksAddToOrderContext;
10+
import cds.gen.adminservice.BooksCovers;
11+
import cds.gen.adminservice.Books_;
12+
import cds.gen.adminservice.OrderItems;
13+
import cds.gen.adminservice.OrderItems_;
14+
import cds.gen.adminservice.Orders;
15+
import cds.gen.adminservice.Upload;
16+
import cds.gen.adminservice.Upload_;
17+
import cds.gen.my.bookshop.Bookshop_;
2018
import com.sap.cds.ql.Select;
2119
import com.sap.cds.ql.Update;
2220
import com.sap.cds.ql.Upsert;
@@ -37,20 +35,18 @@
3735
import com.sap.cds.services.handler.annotations.ServiceName;
3836
import com.sap.cds.services.messages.Messages;
3937
import com.sap.cds.services.persistence.PersistenceService;
40-
41-
import cds.gen.adminservice.AdminService;
42-
import cds.gen.adminservice.AdminService_;
43-
import cds.gen.adminservice.Books;
44-
import cds.gen.adminservice.BooksAddToOrderContext;
45-
import cds.gen.adminservice.BooksCovers;
46-
import cds.gen.adminservice.Books_;
47-
import cds.gen.adminservice.OrderItems;
48-
import cds.gen.adminservice.OrderItems_;
49-
import cds.gen.adminservice.Orders;
50-
import cds.gen.adminservice.Upload;
51-
import cds.gen.adminservice.Upload_;
52-
import cds.gen.my.bookshop.Bookshop_;
38+
import java.io.BufferedReader;
39+
import java.io.IOException;
40+
import java.io.InputStream;
41+
import java.io.InputStreamReader;
42+
import java.math.BigDecimal;
43+
import java.util.ArrayList;
44+
import java.util.Arrays;
45+
import java.util.List;
46+
import java.util.UUID;
47+
import java.util.stream.Stream;
5348
import my.bookshop.MessageKeys;
49+
import org.springframework.stereotype.Component;
5450

5551
/**
5652
* Custom business logic for the "Admin Service" (see admin-service.cds)
@@ -100,11 +96,11 @@ public void beforeCreateOrder(Stream<Orders> orders, EventContext context) {
10096
// calculate the actual quantity difference
10197
// FIXME this should handle book changes, currently only quantity changes are handled
10298
int diffQuantity = quantity - db.run(Select.from(Bookshop_.ORDER_ITEMS).columns(i -> i.quantity()).byId(orderItem.getId()))
103-
.first(OrderItems.class).map(i -> i.getQuantity()).orElse(0);
99+
.first().map(i -> i.getQuantity()).orElse(0);
104100

105101
// check if enough books are available
106-
Result result = db.run(Select.from(BOOKS).columns(b -> b.ID(), b -> b.stock(), b -> b.price()).byId(bookId));
107-
result.first(Books.class).ifPresent(book -> {
102+
var result = db.run(Select.from(BOOKS).columns(b -> b.ID(), b -> b.stock(), b -> b.price()).byId(bookId));
103+
result.first().ifPresent(book -> {
108104
if (book.getStock() < diffQuantity) {
109105
// Tip: you can have localized messages and use parameters in your messages
110106
messages.error(MessageKeys.BOOK_REQUIRE_STOCK, book.getStock())
@@ -128,51 +124,43 @@ public void beforeCreateOrder(Stream<Orders> orders, EventContext context) {
128124
});
129125
}
130126

131-
/**
127+
/*
132128
* Calculate the total order value preview when editing an order item
133-
*
134-
* @param context
135-
* @param orderItem
136129
*/
137-
@Before(event = DraftService.EVENT_DRAFT_PATCH)
138-
public void patchOrderItems(DraftPatchEventContext context, OrderItems orderItem) {
130+
@Before
131+
public void patchOrderItems(DraftPatchEventContext context, OrderItems_ ref, OrderItems orderItem) {
139132
// check if quantity or book was updated
140133
Integer quantity = orderItem.getQuantity();
141134
String bookId = orderItem.getBookId();
142-
String orderItemId = orderItem.getId();
143-
BigDecimal amount = calculateAmountInDraft(orderItemId, quantity, bookId);
135+
BigDecimal amount = calculateAmountInDraft(ref, quantity, bookId);
144136
if (amount != null) {
145137
orderItem.setAmount(amount);
146138
}
147139
}
148140

149-
/**
141+
/*
150142
* Calculate the total order value preview when deleting an order item from the order
151-
*
152-
* @param context
153143
*/
154-
@Before(event = DraftService.EVENT_DRAFT_CANCEL, entity = OrderItems_.CDS_NAME)
155-
public void cancelOrderItems(DraftCancelEventContext context) {
156-
String orderItemId = (String) analyzer.analyze(context.getCqn()).targetKeys().get(OrderItems.ID);
157-
if(orderItemId != null) {
158-
calculateAmountInDraft(orderItemId, 0, null);
144+
@Before
145+
public void cancelOrderItems(DraftCancelEventContext context, OrderItems_ ref) {
146+
if(ref.asRef().targetSegment().filter().isPresent()) {
147+
calculateAmountInDraft(ref, 0, null);
159148
}
160149
}
161150

162-
private BigDecimal calculateAmountInDraft(String orderItemId, Integer newQuantity, String newBookId) {
151+
private BigDecimal calculateAmountInDraft(OrderItems_ ref, Integer newQuantity, String newBookId) {
163152
Integer quantity = newQuantity;
164153
String bookId = newBookId;
165154
if (quantity == null && bookId == null) {
166155
return null; // nothing changed
167156
}
168157

169158
// get the order item that was updated (to get access to the book price, quantity and order total)
170-
Result result = adminService.run(Select.from(ORDER_ITEMS)
159+
var result = adminService.run(Select.from(ref)
171160
.columns(o -> o.quantity(), o -> o.amount(),
172161
o -> o.book().expand(b -> b.ID(), b -> b.price()),
173-
o -> o.parent().expand(p -> p.ID(), p -> p.total()))
174-
.where(o -> o.ID().eq(orderItemId).and(o.IsActiveEntity().eq(false))));
175-
OrderItems itemToPatch = result.first(OrderItems.class).orElseThrow(notFound(MessageKeys.ORDERITEM_MISSING));
162+
o -> o.parent().expand(p -> p.ID(), p -> p.total())));
163+
OrderItems itemToPatch = result.single();
176164
BigDecimal bookPrice = null;
177165

178166
// fallback to existing values
@@ -191,9 +179,8 @@ private BigDecimal calculateAmountInDraft(String orderItemId, Integer newQuantit
191179

192180
// get the price of the updated book ID
193181
if(bookPrice == null) {
194-
result = db.run(Select.from(BOOKS).byId(bookId).columns(b -> b.price()));
195-
Books book = result.first(Books.class).orElseThrow(notFound(MessageKeys.BOOK_MISSING));
196-
bookPrice = book.getPrice();
182+
var bookResult = db.run(Select.from(BOOKS).byId(bookId).columns(b -> b.price()));
183+
bookPrice = bookResult.single().getPrice();
197184
}
198185

199186
// update the amount of the order item
@@ -215,9 +202,9 @@ private BigDecimal calculateAmountInDraft(String orderItemId, Integer newQuantit
215202
* @param context
216203
*/
217204
@On(entity = Books_.CDS_NAME)
218-
public void addBookToOrder(BooksAddToOrderContext context) {
205+
public Orders addBookToOrder(BooksAddToOrderContext context) {
219206
String orderId = context.getOrderId();
220-
List<Orders> orders = adminService.run(Select.from(ORDERS).columns(o -> o._all(), o -> o.Items().expand()).where(o -> o.ID().eq(orderId))).listOf(Orders.class);
207+
List<Orders> orders = adminService.run(Select.from(ORDERS).columns(o -> o._all(), o -> o.Items().expand()).where(o -> o.ID().eq(orderId))).list();
221208
Orders order = orders.stream().filter(p -> p.getIsActiveEntity()).findFirst().orElse(null);
222209

223210
// check that the order with given ID exists and is not in draft-mode
@@ -241,9 +228,9 @@ public void addBookToOrder(BooksAddToOrderContext context) {
241228
newItem.setQuantity(context.getQuantity());
242229
order.getItems().add(newItem);
243230

244-
Orders updatedOrder = adminService.run(Update.entity(ORDERS).data(order)).single(Orders.class);
231+
Orders updatedOrder = adminService.run(Update.entity(ORDERS).data(order)).single();
245232
messages.success(MessageKeys.BOOK_ADDED_ORDER);
246-
context.setResult(updatedOrder);
233+
return updatedOrder;
247234
}
248235

249236
/**
@@ -260,7 +247,7 @@ public Upload getUploadSingleton() {
260247
* @param csv
261248
*/
262249
@On
263-
public void addBooksViaCsv(CdsUpdateEventContext context, Upload upload) {
250+
public List<Upload> addBooksViaCsv(CdsUpdateEventContext context, Upload upload) {
264251
InputStream is = upload.getCsv();
265252
if (is != null) {
266253
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
@@ -287,7 +274,7 @@ public void addBooksViaCsv(CdsUpdateEventContext context, Upload upload) {
287274
throw new ServiceException(ErrorStatuses.SERVER_ERROR, MessageKeys.BOOK_IMPORT_INVALID_CSV, e);
288275
}
289276
}
290-
context.setResult(Arrays.asList(upload));
277+
return Arrays.asList(upload);
291278
}
292279

293280
@Before(event = {CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE, DraftService.EVENT_DRAFT_NEW, DraftService.EVENT_DRAFT_PATCH})
@@ -296,10 +283,6 @@ public void restoreCoversUpId(CqnStructuredTypeRef ref, BooksCovers cover) {
296283
cover.setUpId((String) analyzer.analyze(ref).rootKeys().get(Books.ID));
297284
}
298285

299-
private Supplier<ServiceException> notFound(String message) {
300-
return () -> new ServiceException(ErrorStatuses.NOT_FOUND, message);
301-
}
302-
303286
private BigDecimal defaultZero(BigDecimal decimal) {
304287
return decimal == null ? BigDecimal.valueOf(0) : decimal;
305288
}

0 commit comments

Comments
 (0)