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: