Skip to content

Commit 1d89c4e

Browse files
committed
Implements Product Composite Service.
- Implement service API and integration component. - Changed spring boot version from 2.3.0M3 to 2.3.0.M2 to avoid message java.lang.UnsupportedOperationException: This feature requires ASM8_EXPERIMENTAL when using JDK 14 record feature.
1 parent 3d58057 commit 1d89c4e

File tree

7 files changed

+283
-3
lines changed

7 files changed

+283
-3
lines changed

product-composite-service/src/main/java/com/siriusxi/ms/store/pcs/ProductCompositeServiceApplication.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.context.annotation.Bean;
6+
import org.springframework.context.annotation.ComponentScan;
7+
import org.springframework.web.client.RestTemplate;
58

69
@SpringBootApplication
10+
@ComponentScan("com.siriusxi.ms.store")
711
public class ProductCompositeServiceApplication {
812

13+
@Bean
14+
RestTemplate restTemplate() {
15+
return new RestTemplate();
16+
}
17+
918
public static void main(String[] args) {
1019
SpringApplication.run(ProductCompositeServiceApplication.class, args);
1120
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.siriusxi.ms.store.pcs.controller;
2+
3+
import com.siriusxi.ms.store.api.composite.product.ProductCompositeService;
4+
import com.siriusxi.ms.store.api.composite.product.dto.ProductAggregate;
5+
import com.siriusxi.ms.store.api.composite.product.dto.RecommendationSummary;
6+
import com.siriusxi.ms.store.api.composite.product.dto.ReviewSummary;
7+
import com.siriusxi.ms.store.api.composite.product.dto.ServiceAddresses;
8+
import com.siriusxi.ms.store.api.core.product.Product;
9+
import com.siriusxi.ms.store.api.core.recommendation.Recommendation;
10+
import com.siriusxi.ms.store.api.core.review.Review;
11+
import com.siriusxi.ms.store.pcs.services.ProductCompositeIntegration;
12+
import com.siriusxi.ms.store.util.exceptions.NotFoundException;
13+
import com.siriusxi.ms.store.util.http.ServiceUtil;
14+
import org.springframework.beans.factory.annotation.Autowired;
15+
import org.springframework.web.bind.annotation.RestController;
16+
17+
import java.util.List;
18+
import java.util.stream.Collectors;
19+
20+
@RestController
21+
public class ProductCompositeServiceImpl implements ProductCompositeService {
22+
23+
private final ServiceUtil serviceUtil;
24+
private final ProductCompositeIntegration integration;
25+
26+
@Autowired
27+
public ProductCompositeServiceImpl(ServiceUtil serviceUtil, ProductCompositeIntegration integration) {
28+
this.serviceUtil = serviceUtil;
29+
this.integration = integration;
30+
}
31+
32+
@Override
33+
public ProductAggregate getProduct(int productId) {
34+
35+
Product product = integration.getProduct(productId);
36+
if (product == null) throw new NotFoundException("No product found for productId: " + productId);
37+
38+
List<Recommendation> recommendations = integration.getRecommendations(productId);
39+
40+
List<Review> reviews = integration.getReviews(productId);
41+
42+
return createProductAggregate(product, recommendations, reviews, serviceUtil.getServiceAddress());
43+
}
44+
45+
private ProductAggregate createProductAggregate(Product product, List<Recommendation> recommendations,
46+
List<Review> reviews, String serviceAddress) {
47+
48+
// 1. Setup product info
49+
int productId = product.getProductId();
50+
String name = product.getName();
51+
int weight = product.getWeight();
52+
53+
// 2. Copy summary recommendation info, if available
54+
List<RecommendationSummary> recommendationSummaries = (recommendations == null) ? null :
55+
recommendations.stream()
56+
.map(r -> new RecommendationSummary(r.getRecommendationId(), r.getAuthor(), r.getRate()))
57+
.collect(Collectors.toList());
58+
59+
// 3. Copy summary review info, if available
60+
List<ReviewSummary> reviewSummaries = (reviews == null) ? null :
61+
reviews.stream()
62+
.map(r -> new ReviewSummary(r.reviewId(), r.author(), r.subject()))
63+
.collect(Collectors.toList());
64+
65+
// 4. Create info regarding the involved microservices addresses
66+
String productAddress = product.getServiceAddress();
67+
String reviewAddress = (reviews != null && !reviews.isEmpty()) ? reviews.get(0).serviceAddress() : "";
68+
String recommendationAddress = (recommendations != null && !recommendations.isEmpty()) ?
69+
recommendations.get(0).getServiceAddress() : "";
70+
ServiceAddresses serviceAddresses = new ServiceAddresses(
71+
serviceAddress,
72+
productAddress,
73+
reviewAddress,
74+
recommendationAddress);
75+
76+
return new ProductAggregate(
77+
productId,
78+
name,
79+
weight,
80+
recommendationSummaries,
81+
reviewSummaries,
82+
serviceAddresses);
83+
}
84+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package com.siriusxi.ms.store.pcs.services;
2+
3+
import com.siriusxi.ms.store.api.core.product.Product;
4+
import com.siriusxi.ms.store.api.core.product.ProductService;
5+
import com.siriusxi.ms.store.api.core.recommendation.Recommendation;
6+
import com.siriusxi.ms.store.api.core.recommendation.RecommendationService;
7+
import com.siriusxi.ms.store.api.core.review.Review;
8+
import com.siriusxi.ms.store.api.core.review.ReviewService;
9+
10+
import com.fasterxml.jackson.databind.ObjectMapper;
11+
import lombok.extern.log4j.Log4j2;
12+
import org.springframework.beans.factory.annotation.Autowired;
13+
import org.springframework.beans.factory.annotation.Value;
14+
import org.springframework.core.ParameterizedTypeReference;
15+
import org.springframework.stereotype.Component;
16+
import org.springframework.web.client.HttpClientErrorException;
17+
import org.springframework.web.client.RestTemplate;
18+
19+
import com.siriusxi.ms.store.util.exceptions.InvalidInputException;
20+
import com.siriusxi.ms.store.util.exceptions.NotFoundException;
21+
import com.siriusxi.ms.store.util.http.HttpErrorInfo;
22+
23+
import java.io.IOException;
24+
import java.util.ArrayList;
25+
import java.util.List;
26+
27+
import static java.lang.String.*;
28+
import static org.springframework.http.HttpMethod.GET;
29+
30+
@Log4j2
31+
@Component
32+
public class ProductCompositeIntegration
33+
implements
34+
ProductService,
35+
RecommendationService,
36+
ReviewService {
37+
38+
private final RestTemplate restTemplate;
39+
private final ObjectMapper mapper;
40+
41+
private final String productServiceUrl;
42+
private final String recommendationServiceUrl;
43+
private final String reviewServiceUrl;
44+
45+
@Autowired
46+
public ProductCompositeIntegration(
47+
RestTemplate restTemplate,
48+
ObjectMapper mapper,
49+
50+
@Value("${app.product-service.host}") String productServiceHost,
51+
@Value("${app.product-service.port}") int productServicePort,
52+
53+
@Value("${app.recommendation-service.host}") String recommendationServiceHost,
54+
@Value("${app.recommendation-service.port}") int recommendationServicePort,
55+
56+
@Value("${app.review-service.host}") String reviewServiceHost,
57+
@Value("${app.review-service.port}") int reviewServicePort
58+
) {
59+
60+
this.restTemplate = restTemplate;
61+
this.mapper = mapper;
62+
63+
var http = "http://";
64+
productServiceUrl = http.concat(productServiceHost).concat(":").concat(valueOf(productServicePort))
65+
.concat("/product/");
66+
recommendationServiceUrl = http.concat(recommendationServiceHost).concat(":")
67+
.concat(valueOf(recommendationServicePort)).concat("/recommendation?productId=");
68+
reviewServiceUrl = http.concat(reviewServiceHost).concat(":").concat(valueOf(reviewServicePort))
69+
.concat("/review?productId=");
70+
}
71+
72+
@Override
73+
public Product getProduct(int productId) {
74+
75+
try {
76+
String url = productServiceUrl + productId;
77+
log.debug("Will call getProduct API on URL: {}", url);
78+
79+
Product product = restTemplate.getForObject(url, Product.class);
80+
log.debug("Found a product with id: {}", product != null ? product.getProductId() : "No Product found!!");
81+
82+
return product;
83+
84+
} catch (HttpClientErrorException ex) {
85+
86+
switch (ex.getStatusCode()) {
87+
case NOT_FOUND -> throw new NotFoundException(getErrorMessage(ex));
88+
case UNPROCESSABLE_ENTITY -> throw new InvalidInputException(getErrorMessage(ex));
89+
default -> {
90+
log.warn("Got a unexpected HTTP error: {}, will rethrow it", ex.getStatusCode());
91+
log.warn("Error body: {}", ex.getResponseBodyAsString());
92+
throw ex;
93+
}
94+
}
95+
}
96+
}
97+
98+
@Override
99+
public List<Recommendation> getRecommendations(int productId) {
100+
101+
try {
102+
String url = recommendationServiceUrl + productId;
103+
104+
log.debug("Will call getRecommendations API on URL: {}", url);
105+
List<Recommendation> recommendations = restTemplate
106+
.exchange(url, GET, null,
107+
new ParameterizedTypeReference<List<Recommendation>>() {})
108+
.getBody();
109+
110+
log.debug("Found {} recommendations for a product with id: {}",
111+
recommendations != null ? recommendations.size() : "{No Recommendations}",
112+
productId);
113+
114+
return recommendations;
115+
116+
} catch (Exception ex) {
117+
log.warn("Got an exception while requesting recommendations, return zero recommendations: {}",
118+
ex.getMessage());
119+
120+
return new ArrayList<>();
121+
}
122+
}
123+
124+
@Override
125+
public List<Review> getReviews(int productId) {
126+
127+
try {
128+
String url = reviewServiceUrl + productId;
129+
130+
log.debug("Will call getReviews API on URL: {}", url);
131+
List<Review> reviews = restTemplate.exchange(
132+
url,
133+
GET,
134+
null,
135+
new ParameterizedTypeReference<List<Review>>() {})
136+
.getBody();
137+
138+
log.debug("Found {} reviews for a product with id: {}",
139+
reviews != null ? reviews.size() : "{No Reviews}",
140+
productId);
141+
142+
return reviews;
143+
144+
} catch (Exception ex) {
145+
log.warn("Got an exception while requesting reviews, return zero reviews: {}", ex.getMessage());
146+
return new ArrayList<>();
147+
}
148+
}
149+
150+
private String getErrorMessage(HttpClientErrorException ex) {
151+
try {
152+
return mapper.readValue(ex.getResponseBodyAsString(), HttpErrorInfo.class).message();
153+
} catch (IOException ioex) {
154+
return ex.getMessage();
155+
}
156+
}
157+
}

product-composite-service/src/main/resources/application.yaml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,21 @@ spring:
33
name: product-search
44

55
server:
6-
port: 9080
6+
port: 9080
7+
8+
logging:
9+
level:
10+
root: INFO
11+
com.siriusxi.ms:.store: DEBUG
12+
13+
# Custom configurations
14+
app:
15+
product-service:
16+
host: localhost
17+
port: 9081
18+
recommendation-service:
19+
host: localhost
20+
port: 9082
21+
review-service:
22+
host: localhost
23+
port: 9083
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
spring:
22
application:
3-
name: recommend-service
3+
name: recommendation-service
44

55
server:
66
port: 9082

review-service/src/main/java/com/siriusxi/ms/store/revs/ReviewServiceApplication.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.context.annotation.ComponentScan;
56

67
@SpringBootApplication
78
public class ReviewServiceApplication {

store-chassis/pom.xml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
<parent>
66
<groupId>org.springframework.boot</groupId>
77
<artifactId>spring-boot-starter-parent</artifactId>
8-
<version>2.3.0.M3</version>
8+
<!-- FIXME change this version to 2.3.0.M4 that fixes ASM8_EXPERIMENTAL when using JDK 14 record feature -->
9+
<version>2.3.0.M2</version>
910
<relativePath/> <!-- lookup parent from repository -->
1011
</parent>
1112

@@ -79,6 +80,17 @@
7980
<scope>test</scope>
8081
</dependency>
8182

83+
<dependency>
84+
<groupId>com.siriusxi.ms.store</groupId>
85+
<artifactId>store-api</artifactId>
86+
<version>1.0-SNAPSHOT</version>
87+
</dependency>
88+
<dependency>
89+
<groupId>com.siriusxi.ms.store</groupId>
90+
<artifactId>store-utils</artifactId>
91+
<version>1.0-SNAPSHOT</version>
92+
</dependency>
93+
8294
</dependencies>
8395

8496
<build>

0 commit comments

Comments
 (0)