Skip to content

Commit ad32733

Browse files
committed
Implement Recommendation & Review Services
- Add services Rest implementation. - Add services tests. - Some refactoring.
1 parent 974990d commit ad32733

File tree

16 files changed

+360
-91
lines changed

16 files changed

+360
-91
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import com.siriusxi.ms.store.api.core.product.Product;
99
import com.siriusxi.ms.store.api.core.recommendation.Recommendation;
1010
import com.siriusxi.ms.store.api.core.review.Review;
11-
import com.siriusxi.ms.store.pcs.services.ProductCompositeIntegration;
11+
import com.siriusxi.ms.store.pcs.integration.ProductCompositeIntegration;
1212
import com.siriusxi.ms.store.util.exceptions.NotFoundException;
1313
import com.siriusxi.ms.store.util.http.ServiceUtil;
1414
import org.springframework.beans.factory.annotation.Autowired;
@@ -32,12 +32,12 @@ public ProductCompositeServiceImpl(ServiceUtil serviceUtil, ProductCompositeInte
3232
@Override
3333
public ProductAggregate getProduct(int productId) {
3434

35-
Product product = integration.getProduct(productId);
35+
var product = integration.getProduct(productId);
3636
if (product == null) throw new NotFoundException("No product found for productId: " + productId);
3737

38-
List<Recommendation> recommendations = integration.getRecommendations(productId);
38+
var recommendations = integration.getRecommendations(productId);
3939

40-
List<Review> reviews = integration.getReviews(productId);
40+
var reviews = integration.getReviews(productId);
4141

4242
return createProductAggregate(product, recommendations, reviews, serviceUtil.getServiceAddress());
4343
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.siriusxi.ms.store.pcs.services;
1+
package com.siriusxi.ms.store.pcs.integration;
22

33
import com.siriusxi.ms.store.api.core.product.Product;
44
import com.siriusxi.ms.store.api.core.product.ProductService;
@@ -27,8 +27,8 @@
2727
import static java.lang.String.*;
2828
import static org.springframework.http.HttpMethod.GET;
2929

30-
@Log4j2
3130
@Component
31+
@Log4j2
3232
public class ProductCompositeIntegration
3333
implements
3434
ProductService,

product-composite-service/src/test/java/com/siriusxi/ms/store/pcs/ProductCompositeServiceApplicationTests.java

Lines changed: 79 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
import com.siriusxi.ms.store.api.core.product.Product;
44
import com.siriusxi.ms.store.api.core.recommendation.Recommendation;
55
import com.siriusxi.ms.store.api.core.review.Review;
6-
import com.siriusxi.ms.store.pcs.services.ProductCompositeIntegration;
6+
import com.siriusxi.ms.store.pcs.integration.ProductCompositeIntegration;
77
import com.siriusxi.ms.store.util.exceptions.InvalidInputException;
88
import com.siriusxi.ms.store.util.exceptions.NotFoundException;
9-
import org.junit.jupiter.api.BeforeAll;
9+
import org.junit.jupiter.api.BeforeEach;
1010
import org.junit.jupiter.api.Test;
11-
import org.springframework.boot.test.context.SpringBootTest;
12-
1311
import org.springframework.beans.factory.annotation.Autowired;
12+
import org.springframework.boot.test.context.SpringBootTest;
1413
import org.springframework.boot.test.mock.mockito.MockBean;
1514
import org.springframework.test.web.reactive.server.WebTestClient;
1615

@@ -20,83 +19,83 @@
2019
import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
2120
import static org.springframework.http.MediaType.APPLICATION_JSON;
2221

23-
@SpringBootTest(webEnvironment=RANDOM_PORT)
22+
@SpringBootTest(webEnvironment = RANDOM_PORT)
2423
class ProductCompositeServiceApplicationTests {
2524

26-
private static final int PRODUCT_ID_OK = 1;
27-
private static final int PRODUCT_ID_NOT_FOUND = 2;
28-
private static final int PRODUCT_ID_INVALID = 3;
29-
30-
@Autowired
31-
private WebTestClient client;
32-
33-
@MockBean
34-
private static ProductCompositeIntegration compositeIntegration;
35-
36-
@BeforeAll
37-
static void setUp() {
38-
39-
when(compositeIntegration.getProduct(PRODUCT_ID_OK)).
40-
thenReturn(new Product(PRODUCT_ID_OK, "name", 1, "mock-address"));
41-
42-
when(compositeIntegration.getRecommendations(PRODUCT_ID_OK)).
43-
thenReturn(singletonList(new Recommendation(PRODUCT_ID_OK,
44-
1, "author",
45-
1, "content", "mock address")));
46-
47-
when(compositeIntegration.getReviews(PRODUCT_ID_OK)).
48-
thenReturn(singletonList(new Review(PRODUCT_ID_OK, 1,
49-
"author", "subject", "content",
50-
"mock address")));
51-
52-
when(compositeIntegration.getProduct(PRODUCT_ID_NOT_FOUND))
53-
.thenThrow(new NotFoundException("NOT FOUND: " + PRODUCT_ID_NOT_FOUND));
54-
55-
when(compositeIntegration.getProduct(PRODUCT_ID_INVALID))
56-
.thenThrow(new InvalidInputException("INVALID: " + PRODUCT_ID_INVALID));
57-
}
58-
59-
@Test
60-
public void getProductById() {
61-
62-
client.get()
63-
.uri("/product-composite/" + PRODUCT_ID_OK)
64-
.accept(APPLICATION_JSON)
65-
.exchange()
66-
.expectStatus().isOk()
67-
.expectHeader().contentType(APPLICATION_JSON)
68-
.expectBody()
69-
.jsonPath("$.productId").isEqualTo(PRODUCT_ID_OK)
70-
.jsonPath("$.recommendations.length()").isEqualTo(1)
71-
.jsonPath("$.reviews.length()").isEqualTo(1);
72-
}
73-
74-
@Test
75-
public void getProductNotFound() {
76-
77-
client.get()
78-
.uri("/product-composite/" + PRODUCT_ID_NOT_FOUND)
79-
.accept(APPLICATION_JSON)
80-
.exchange()
81-
.expectStatus().isNotFound()
82-
.expectHeader().contentType(APPLICATION_JSON)
83-
.expectBody()
84-
.jsonPath("$.path").isEqualTo("/product-composite/" + PRODUCT_ID_NOT_FOUND)
85-
.jsonPath("$.message").isEqualTo("NOT FOUND: " + PRODUCT_ID_NOT_FOUND);
86-
}
87-
88-
@Test
89-
public void getProductInvalidInput() {
90-
91-
client.get()
92-
.uri("/product-composite/" + PRODUCT_ID_INVALID)
93-
.accept(APPLICATION_JSON)
94-
.exchange()
95-
.expectStatus().isEqualTo(UNPROCESSABLE_ENTITY)
96-
.expectHeader().contentType(APPLICATION_JSON)
97-
.expectBody()
98-
.jsonPath("$.path").isEqualTo("/product-composite/" + PRODUCT_ID_INVALID)
99-
.jsonPath("$.message").isEqualTo("INVALID: " + PRODUCT_ID_INVALID);
100-
}
25+
private static final int PRODUCT_ID_OK = 1;
26+
private static final int PRODUCT_ID_NOT_FOUND = 2;
27+
private static final int PRODUCT_ID_INVALID = 3;
28+
29+
@Autowired
30+
private WebTestClient client;
31+
32+
@MockBean
33+
private ProductCompositeIntegration compositeIntegration;
34+
35+
@BeforeEach
36+
void setUp() {
37+
38+
when(compositeIntegration.getProduct(PRODUCT_ID_OK)).
39+
thenReturn(new Product(PRODUCT_ID_OK, "name", 1, "mock-address"));
40+
41+
when(compositeIntegration.getRecommendations(PRODUCT_ID_OK)).
42+
thenReturn(singletonList(new Recommendation(PRODUCT_ID_OK,
43+
1, "author",
44+
1, "content", "mock address")));
45+
46+
when(compositeIntegration.getReviews(PRODUCT_ID_OK)).
47+
thenReturn(singletonList(new Review(PRODUCT_ID_OK, 1,
48+
"author", "subject", "content",
49+
"mock address")));
50+
51+
when(compositeIntegration.getProduct(PRODUCT_ID_NOT_FOUND))
52+
.thenThrow(new NotFoundException("NOT FOUND: " + PRODUCT_ID_NOT_FOUND));
53+
54+
when(compositeIntegration.getProduct(PRODUCT_ID_INVALID))
55+
.thenThrow(new InvalidInputException("INVALID: " + PRODUCT_ID_INVALID));
56+
}
57+
58+
@Test
59+
public void getProductById() {
60+
61+
client.get()
62+
.uri("/product-composite/" + PRODUCT_ID_OK)
63+
.accept(APPLICATION_JSON)
64+
.exchange()
65+
.expectStatus().isOk()
66+
.expectHeader().contentType(APPLICATION_JSON)
67+
.expectBody()
68+
.jsonPath("$.productId").isEqualTo(PRODUCT_ID_OK)
69+
.jsonPath("$.recommendations.length()").isEqualTo(1)
70+
.jsonPath("$.reviews.length()").isEqualTo(1);
71+
}
72+
73+
@Test
74+
public void getProductNotFound() {
75+
76+
client.get()
77+
.uri("/product-composite/" + PRODUCT_ID_NOT_FOUND)
78+
.accept(APPLICATION_JSON)
79+
.exchange()
80+
.expectStatus().isNotFound()
81+
.expectHeader().contentType(APPLICATION_JSON)
82+
.expectBody()
83+
.jsonPath("$.path").isEqualTo("/product-composite/" + PRODUCT_ID_NOT_FOUND)
84+
.jsonPath("$.message").isEqualTo("NOT FOUND: " + PRODUCT_ID_NOT_FOUND);
85+
}
86+
87+
@Test
88+
public void getProductInvalidInput() {
89+
90+
client.get()
91+
.uri("/product-composite/" + PRODUCT_ID_INVALID)
92+
.accept(APPLICATION_JSON)
93+
.exchange()
94+
.expectStatus().isEqualTo(UNPROCESSABLE_ENTITY)
95+
.expectHeader().contentType(APPLICATION_JSON)
96+
.expectBody()
97+
.jsonPath("$.path").isEqualTo("/product-composite/" + PRODUCT_ID_INVALID)
98+
.jsonPath("$.message").isEqualTo("INVALID: " + PRODUCT_ID_INVALID);
99+
}
101100

102101
}

recommendation-service/src/main/java/com/siriusxi/ms/store/rs/RecommendationServiceApplication.java

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

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

67
@SpringBootApplication
8+
@ComponentScan("com.siriusxi.ms.store")
79
public class RecommendationServiceApplication {
810

911
public static void main(String[] args) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.siriusxi.ms.store.rs.controller;
2+
3+
import com.siriusxi.ms.store.api.core.recommendation.Recommendation;
4+
import com.siriusxi.ms.store.api.core.recommendation.RecommendationService;
5+
import com.siriusxi.ms.store.util.exceptions.InvalidInputException;
6+
import com.siriusxi.ms.store.util.http.ServiceUtil;
7+
import lombok.extern.log4j.Log4j2;
8+
import org.springframework.beans.factory.annotation.Autowired;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
import java.util.ArrayList;
12+
import java.util.List;
13+
14+
@RestController
15+
@Log4j2
16+
public class RecommendationServiceImpl implements RecommendationService {
17+
18+
private final ServiceUtil serviceUtil;
19+
20+
@Autowired
21+
public RecommendationServiceImpl(ServiceUtil serviceUtil) {
22+
this.serviceUtil = serviceUtil;
23+
}
24+
25+
@Override
26+
public List<Recommendation> getRecommendations(int productId) {
27+
28+
if (productId < 1) throw new InvalidInputException("Invalid productId: " + productId);
29+
30+
if (productId == 113) {
31+
log.debug("No recommendations found for productId: {}", productId);
32+
return new ArrayList<>();
33+
}
34+
35+
List<Recommendation> list = new ArrayList<>();
36+
list.add(new Recommendation(productId, 1, "Author 1", 1, "Content 1", serviceUtil.getServiceAddress()));
37+
list.add(new Recommendation(productId, 2, "Author 2", 2, "Content 2", serviceUtil.getServiceAddress()));
38+
list.add(new Recommendation(productId, 3, "Author 3", 3, "Content 3", serviceUtil.getServiceAddress()));
39+
40+
log.debug("/recommendation response size: {}", list.size());
41+
42+
return list;
43+
}
44+
}

recommendation-service/src/main/resources/application.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,8 @@ spring:
44

55
server:
66
port: 9082
7+
8+
logging:
9+
level:
10+
root: INFO
11+
com.siriusxi.ms:.store: DEBUG
Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,95 @@
11
package com.siriusxi.ms.store.rs;
22

33
import org.junit.jupiter.api.Test;
4+
import org.springframework.beans.factory.annotation.Autowired;
45
import org.springframework.boot.test.context.SpringBootTest;
56

6-
@SpringBootTest
7+
import org.springframework.test.web.reactive.server.WebTestClient;
8+
9+
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
10+
import static org.springframework.http.HttpStatus.BAD_REQUEST;
11+
import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
12+
import static org.springframework.http.MediaType.APPLICATION_JSON;
13+
14+
@SpringBootTest(webEnvironment=RANDOM_PORT)
715
class RecommendationServiceApplicationTests {
816

17+
@Autowired
18+
private WebTestClient client;
19+
20+
@Test
21+
public void getRecommendationsByProductId() {
22+
23+
int productId = 1;
24+
25+
client.get()
26+
.uri("/recommendation?productId=" + productId)
27+
.accept(APPLICATION_JSON)
28+
.exchange()
29+
.expectStatus().isOk()
30+
.expectHeader().contentType(APPLICATION_JSON)
31+
.expectBody()
32+
.jsonPath("$.length()").isEqualTo(3)
33+
.jsonPath("$[0].productId").isEqualTo(productId);
34+
}
35+
36+
@Test
37+
public void getRecommendationsMissingParameter() {
38+
39+
client.get()
40+
.uri("/recommendation")
41+
.accept(APPLICATION_JSON)
42+
.exchange()
43+
.expectStatus().isEqualTo(BAD_REQUEST)
44+
.expectHeader().contentType(APPLICATION_JSON)
45+
.expectBody()
46+
.jsonPath("$.path").isEqualTo("/recommendation")
47+
.jsonPath("$.message").isEqualTo("Required int parameter 'productId' is not present");
48+
}
49+
950
@Test
10-
void contextLoads() {
51+
public void getRecommendationsInvalidParameter() {
52+
53+
client.get()
54+
.uri("/recommendation?productId=no-integer")
55+
.accept(APPLICATION_JSON)
56+
.exchange()
57+
.expectStatus().isEqualTo(BAD_REQUEST)
58+
.expectHeader().contentType(APPLICATION_JSON)
59+
.expectBody()
60+
.jsonPath("$.path").isEqualTo("/recommendation")
61+
.jsonPath("$.message").isEqualTo("Type mismatch.");
62+
}
63+
64+
@Test
65+
public void getRecommendationsNotFound() {
66+
67+
int productIdNotFound = 113;
68+
69+
client.get()
70+
.uri("/recommendation?productId=" + productIdNotFound)
71+
.accept(APPLICATION_JSON)
72+
.exchange()
73+
.expectStatus().isOk()
74+
.expectHeader().contentType(APPLICATION_JSON)
75+
.expectBody()
76+
.jsonPath("$.length()").isEqualTo(0);
77+
}
78+
79+
@Test
80+
public void getRecommendationsInvalidParameterNegativeValue() {
81+
82+
int productIdInvalid = -1;
83+
84+
client.get()
85+
.uri("/recommendation?productId=" + productIdInvalid)
86+
.accept(APPLICATION_JSON)
87+
.exchange()
88+
.expectStatus().isEqualTo(UNPROCESSABLE_ENTITY)
89+
.expectHeader().contentType(APPLICATION_JSON)
90+
.expectBody()
91+
.jsonPath("$.path").isEqualTo("/recommendation")
92+
.jsonPath("$.message").isEqualTo("Invalid productId: " + productIdInvalid);
1193
}
1294

1395
}

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
@@ -5,6 +5,7 @@
55
import org.springframework.context.annotation.ComponentScan;
66

77
@SpringBootApplication
8+
@ComponentScan("com.siriusxi.ms.store")
89
public class ReviewServiceApplication {
910

1011
public static void main(String[] args) {

0 commit comments

Comments
 (0)