diff --git a/.gitignore b/.gitignore
index 33223716..b9b8044e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,4 @@ dist/
# Experimental
xxx_*
xxx/*
+local-messaging.txt
diff --git a/db/schema.cds b/db/schema.cds
index 2b633022..c89ad3ca 100644
--- a/db/schema.cds
+++ b/db/schema.cds
@@ -16,6 +16,7 @@ entity Travel : managed {
EndDate : Date;
BookingFee : Decimal(16, 3);
TotalPrice : Decimal(16, 3) @readonly;
+ AvgReviewRating: Decimal;
CurrencyCode : Currency;
Description : String(1024);
TravelStatus : Association to TravelStatus @readonly;
@@ -37,6 +38,7 @@ annotate Travel with @(
});
+
entity Booking : managed {
key BookingUUID : UUID;
BookingID : Integer @Core.Computed;
@@ -89,3 +91,11 @@ entity TravelStatus : CodeList {
createDeleteHidden: Boolean;
insertDeleteRestriction: Boolean; // = NOT createDeleteHidden
}
+
+entity TravelReview : managed {
+ key RatingUUID : UUID;
+ TravelID : Integer;
+ Rating : Integer @assert.range: [ 0, 5 ];
+ Email : String;
+ Comment : String;
+}
\ No newline at end of file
diff --git a/local-messaging.txt b/local-messaging.txt
new file mode 100644
index 00000000..c6d9e2f7
--- /dev/null
+++ b/local-messaging.txt
@@ -0,0 +1 @@
+sap.cap.reviews.review.changed {"data":{"subject":42,"count":1,"rating":5}}
diff --git a/pom.xml b/pom.xml
index d8446638..aefe81a1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -112,9 +112,6 @@
3.5.0
-
project.artifactId
[^_]+
diff --git a/srv/post_review.http b/srv/post_review.http
new file mode 100644
index 00000000..9533fb21
--- /dev/null
+++ b/srv/post_review.http
@@ -0,0 +1,11 @@
+POST http://localhost:8080/reviewer/TravelReview HTTP/1.1
+Authorization: Basic YXV0aGVudGljYXRlZDo=
+content-type: application/json
+
+{
+ "RatingUUID": "D5055A5D-8EED-47E3-AB38-6D646120F9DD",
+ "Rating" : 4,
+ "Email" : "john@doe.com",
+ "Comment" : "awesome",
+ "TravelID" : 4132
+}
\ No newline at end of file
diff --git a/srv/review-service.cds b/srv/review-service.cds
new file mode 100644
index 00000000..7771052e
--- /dev/null
+++ b/srv/review-service.cds
@@ -0,0 +1,13 @@
+using { sap.fe.cap.travel as my } from '../db/schema';
+
+service ReviewService @(path:'/reviewer') {
+
+ entity TravelReview as projection on my.TravelReview;
+
+ @topic: 'sap.cap.reviews.review.changed'
+ event Reviewed : {
+ subject : Integer;
+ count : Integer;
+ rating : Decimal;
+ }
+}
\ No newline at end of file
diff --git a/srv/src/main/java/com/sap/cap/sflight/processor/CreationHandler.java b/srv/src/main/java/com/sap/cap/sflight/processor/CreationHandler.java
index e2824f69..a7a9ff33 100644
--- a/srv/src/main/java/com/sap/cap/sflight/processor/CreationHandler.java
+++ b/srv/src/main/java/com/sap/cap/sflight/processor/CreationHandler.java
@@ -11,7 +11,7 @@
import com.sap.cds.ql.Select;
import com.sap.cds.ql.Update;
-import com.sap.cds.services.cds.CdsService;
+import com.sap.cds.services.cds.CqnService;
import com.sap.cds.services.draft.DraftService;
import com.sap.cds.services.handler.EventHandler;
import com.sap.cds.services.handler.annotations.Before;
@@ -42,7 +42,7 @@ public CreationHandler(PersistenceService persistenceService, DraftService draft
this.draftService = draftService;
}
- @Before(event = { CdsService.EVENT_CREATE, CdsService.EVENT_UPDATE, DraftService.EVENT_DRAFT_CREATE}, entity = Travel_.CDS_NAME)
+ @Before(event = { CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE, DraftService.EVENT_DRAFT_CREATE}, entity = Travel_.CDS_NAME)
public void setBookingDateIfNotProvided(final Travel travel) {
if (travel.getBeginDate() == null) {
travel.setBeginDate(LocalDate.now());
@@ -81,7 +81,7 @@ public void saveComputedValues(DraftActivateContext ctx) {
});
}
- @Before(event = { CdsService.EVENT_CREATE, CdsService.EVENT_UPDATE }, entity = Travel_.CDS_NAME)
+ @Before(event = { CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE }, entity = Travel_.CDS_NAME)
public void checkTravelEndDateIsAfterBeginDate(Travel travel) {
if (travel.getBeginDate() != null && travel.getEndDate() != null) {
@@ -97,7 +97,7 @@ public void checkTravelEndDateIsAfterBeginDate(Travel travel) {
}
}
- @Before(event = CdsService.EVENT_CREATE, entity = Travel_.CDS_NAME)
+ @Before(event = CqnService.EVENT_CREATE, entity = Travel_.CDS_NAME)
public void calculateTravelIdBeforeCreation(final Travel travel) {
if (travel.getTravelID() == null || travel.getTravelID() == 0) {
Select maxIdSelect = Select.from(TravelService_.TRAVEL).columns(e -> e.TravelID().max().as(MAX_ID));
@@ -107,7 +107,7 @@ public void calculateTravelIdBeforeCreation(final Travel travel) {
}
}
- @Before(event = { CdsService.EVENT_CREATE, CdsService.EVENT_UPDATE, }, entity = Travel_.CDS_NAME)
+ @Before(event = { CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE, }, entity = Travel_.CDS_NAME)
public void fillBookingIdsBeforeCreationAndUpdate(final Travel travel) {
if (travel.getToBooking() != null) {
addBookingIds(travel);
@@ -152,7 +152,7 @@ private void addBookingIds(Travel travel) {
}
}
- @Before(event = CdsService.EVENT_CREATE, entity = Travel_.CDS_NAME)
+ @Before(event = CqnService.EVENT_CREATE, entity = Travel_.CDS_NAME)
public void initialTravelStatus(final Travel travel) {
TravelStatus travelStatus = TravelStatus.create();
travelStatus.setCode("O");
diff --git a/srv/src/main/java/com/sap/cap/sflight/processor/RecalculatePriceHandler.java b/srv/src/main/java/com/sap/cap/sflight/processor/RecalculatePriceHandler.java
index 043f8aab..7fc22890 100644
--- a/srv/src/main/java/com/sap/cap/sflight/processor/RecalculatePriceHandler.java
+++ b/srv/src/main/java/com/sap/cap/sflight/processor/RecalculatePriceHandler.java
@@ -19,7 +19,6 @@
import com.sap.cds.ql.cqn.CqnUpdate;
import com.sap.cds.services.ErrorStatuses;
import com.sap.cds.services.ServiceException;
-import com.sap.cds.services.cds.CdsService;
import com.sap.cds.services.cds.CqnService;
import com.sap.cds.services.draft.DraftService;
import com.sap.cds.services.handler.EventHandler;
@@ -94,7 +93,7 @@ private static BigDecimal calculateTotalPriceForTravel(CqnService db, String tra
return bookingFee.add(flightPriceSum).add(supplementPriceSum);
}
- @After(event = {CdsService.EVENT_UPDATE, CdsService.EVENT_CREATE}, entity = Travel_.CDS_NAME)
+ @After(event = {CqnService.EVENT_UPDATE, CqnService.EVENT_CREATE}, entity = Travel_.CDS_NAME)
public void calculateNewTotalPriceForActiveTravel(Travel travel) {
/*
@@ -149,7 +148,7 @@ public void recalculateTravelPriceIfPriceWasUpdated(final BookingSupplement book
private BigDecimal calculateAndPatchNewTotalPriceForDraft(final String travelUUID) {
BigDecimal totalPrice = calculateTotalPriceForTravel(draftService, travelUUID, false);
- Map map = new HashMap();
+ Map map = new HashMap<>();
map.put(Travel.TRAVEL_UUID, travelUUID);
map.put(Travel.TOTAL_PRICE, totalPrice);
CqnUpdate update = Update.entity(TRAVEL).data(map);
diff --git a/srv/src/main/java/com/sap/cap/sflight/processor/TravelReviewChangedHandler.java b/srv/src/main/java/com/sap/cap/sflight/processor/TravelReviewChangedHandler.java
new file mode 100644
index 00000000..e5e97d6c
--- /dev/null
+++ b/srv/src/main/java/com/sap/cap/sflight/processor/TravelReviewChangedHandler.java
@@ -0,0 +1,49 @@
+package com.sap.cap.sflight.processor;
+
+import static cds.gen.travelservice.Travel.AVG_REVIEW_RATING;
+import static cds.gen.travelservice.TravelService_.TRAVEL;
+
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import com.sap.cds.ql.Update;
+import com.sap.cds.services.cds.CqnService;
+import com.sap.cds.services.handler.EventHandler;
+import com.sap.cds.services.handler.annotations.On;
+import com.sap.cds.services.handler.annotations.ServiceName;
+
+import cds.gen.reviewservice.ReviewService_;
+import cds.gen.reviewservice.ReviewedContext;
+import cds.gen.travelservice.TravelService_;
+
+@Component
+@ServiceName(TravelService_.CDS_NAME)
+public class TravelReviewChangedHandler implements EventHandler {
+
+ private static final Logger logger = LoggerFactory.getLogger(TravelReviewChangedHandler.class);
+
+ private final CqnService travelService;
+
+ public TravelReviewChangedHandler(CqnService travelService) {
+ this.travelService = travelService;
+ }
+
+ @On(service = ReviewService_.CDS_NAME)
+ public void onReviewChanged(ReviewedContext context) {
+
+ BigDecimal rating = context.getData().getRating();
+ Integer travelId = context.getData().getSubject();
+
+ Map updateFilter = new HashMap<>();
+ updateFilter.put("TravelID", travelId);
+
+ travelService.run(Update.entity(TRAVEL, t -> t.matching(updateFilter)).data(AVG_REVIEW_RATING, rating));
+ logger.info("Successfully updated travel with travelId {} with a new average rating of {}.", travelId, rating);
+ }
+
+}
diff --git a/srv/src/main/java/com/sap/cap/sflight/reviewer/ReviewHandler.java b/srv/src/main/java/com/sap/cap/sflight/reviewer/ReviewHandler.java
new file mode 100644
index 00000000..049c9d9c
--- /dev/null
+++ b/srv/src/main/java/com/sap/cap/sflight/reviewer/ReviewHandler.java
@@ -0,0 +1,52 @@
+package com.sap.cap.sflight.reviewer;
+
+import java.math.BigDecimal;
+
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.stereotype.Component;
+
+import com.sap.cds.Row;
+import com.sap.cds.ql.Select;
+import com.sap.cds.services.cds.CqnService;
+import com.sap.cds.services.handler.EventHandler;
+import com.sap.cds.services.handler.annotations.After;
+import com.sap.cds.services.handler.annotations.ServiceName;
+
+import cds.gen.reviewservice.ReviewService_;
+import cds.gen.reviewservice.Reviewed;
+import cds.gen.reviewservice.ReviewedContext;
+import cds.gen.reviewservice.TravelReview;
+import cds.gen.reviewservice.TravelReview_;
+
+@Component
+@ServiceName(ReviewService_.CDS_NAME)
+public class ReviewHandler implements EventHandler {
+
+ private final CqnService reviewService;
+
+ public ReviewHandler(@Qualifier(ReviewService_.CDS_NAME) CqnService reviewService) {
+ this.reviewService = reviewService;
+ }
+
+ @After(event = { CqnService.EVENT_CREATE, CqnService.EVENT_UPDATE, CqnService.EVENT_UPSERT,
+ CqnService.EVENT_DELETE }, entity = TravelReview_.CDS_NAME)
+ public void afterReviewWrite(TravelReview travelReview) {
+
+ Select averageRatingQuery = Select.from(ReviewService_.TRAVEL_REVIEW)
+ .columns(t -> t.Rating().average().as("average"), t -> t.Rating().countDistinct().as("reviewCount"))
+ .where(t -> t.TravelID().eq(travelReview.getTravelID()));
+
+ Row resultRow = reviewService.run(averageRatingQuery).single();
+
+
+ Reviewed reviewed = Reviewed.create();
+ reviewed.setCount(Integer.valueOf(resultRow.get("reviewCount").toString()));
+ reviewed.setRating(BigDecimal.valueOf(5L));
+ reviewed.setSubject(travelReview.getTravelID());
+
+ ReviewedContext reviewedContext = ReviewedContext.create();
+ reviewedContext.setData(reviewed);
+
+ reviewService.emit(reviewedContext);
+ }
+}
diff --git a/srv/src/main/resources/application.yml b/srv/src/main/resources/application.yml
index 73986e38..f830063d 100644
--- a/srv/src/main/resources/application.yml
+++ b/srv/src/main/resources/application.yml
@@ -7,6 +7,7 @@ management:
spring:
profiles: default
web.resources.static-locations: "file:./app"
+logging.level.com.sap.cds.messaging: DEBUG
cds:
security.mock.users:
- name: rose
@@ -24,9 +25,14 @@ cds:
odata-v4:
endpoint:
path: "/"
+ messaging:
+ services:
+ - name: "messaging"
+ kind: "file-based-messaging"
+ binding: "local-messaging.txt"
server:
- port: 4004
+ port: 8080
---
spring: