Skip to content

Commit 974990d

Browse files
committed
Implement Product Service
- Add Rest implementation. - Add Product service tests. - Add Product composite service tests.
1 parent 1d89c4e commit 974990d

File tree

8 files changed

+205
-6
lines changed

8 files changed

+205
-6
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
public class ProductCompositeServiceApplication {
1212

1313
@Bean
14-
RestTemplate restTemplate() {
14+
RestTemplate newRestClient() {
1515
return new RestTemplate();
1616
}
1717

Original file line numberDiff line numberDiff line change
@@ -1,13 +1,102 @@
11
package com.siriusxi.ms.store.pcs;
22

3+
import com.siriusxi.ms.store.api.core.product.Product;
4+
import com.siriusxi.ms.store.api.core.recommendation.Recommendation;
5+
import com.siriusxi.ms.store.api.core.review.Review;
6+
import com.siriusxi.ms.store.pcs.services.ProductCompositeIntegration;
7+
import com.siriusxi.ms.store.util.exceptions.InvalidInputException;
8+
import com.siriusxi.ms.store.util.exceptions.NotFoundException;
9+
import org.junit.jupiter.api.BeforeAll;
310
import org.junit.jupiter.api.Test;
411
import org.springframework.boot.test.context.SpringBootTest;
512

6-
@SpringBootTest
13+
import org.springframework.beans.factory.annotation.Autowired;
14+
import org.springframework.boot.test.mock.mockito.MockBean;
15+
import org.springframework.test.web.reactive.server.WebTestClient;
16+
17+
import static java.util.Collections.singletonList;
18+
import static org.mockito.Mockito.when;
19+
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
20+
import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
21+
import static org.springframework.http.MediaType.APPLICATION_JSON;
22+
23+
@SpringBootTest(webEnvironment=RANDOM_PORT)
724
class ProductCompositeServiceApplicationTests {
825

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+
974
@Test
10-
void contextLoads() {
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);
11100
}
12101

13102
}

product-service/src/main/java/com/siriusxi/ms/store/ps/ProductServiceApplication.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 ProductServiceApplication {
810

911
public static void main(String[] args) {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.siriusxi.ms.store.ps.controller;
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.util.exceptions.InvalidInputException;
6+
import com.siriusxi.ms.store.util.exceptions.NotFoundException;
7+
import com.siriusxi.ms.store.util.http.ServiceUtil;
8+
import lombok.extern.log4j.Log4j2;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.web.bind.annotation.RestController;
11+
12+
@RestController
13+
@Log4j2
14+
public class ProductServiceImpl implements ProductService {
15+
16+
private final ServiceUtil serviceUtil;
17+
18+
@Autowired
19+
public ProductServiceImpl(ServiceUtil serviceUtil) {
20+
this.serviceUtil = serviceUtil;
21+
}
22+
23+
@Override
24+
public Product getProduct(int productId) {
25+
log.debug("/product returns the found product for productId={}", productId);
26+
27+
if (productId < 1) throw new InvalidInputException("Invalid productId: " + productId);
28+
29+
if (productId == 13) throw new NotFoundException("No product found for productId: " + productId);
30+
31+
return new Product(productId, "name-" + productId, 123, serviceUtil.getServiceAddress());
32+
}
33+
}

product-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: 9081
7+
8+
logging:
9+
level:
10+
root: INFO
11+
com.siriusxi.ms:.store: DEBUG

product-service/src/test/java/com/siriusxi/ms/store/ps/ProductServiceApplicationTests.java

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,79 @@
22

33
import org.junit.jupiter.api.Test;
44
import org.springframework.boot.test.context.SpringBootTest;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.test.web.reactive.server.WebTestClient;
57

6-
@SpringBootTest
8+
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
9+
import static org.springframework.http.HttpStatus.BAD_REQUEST;
10+
import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY;
11+
import static org.springframework.http.MediaType.APPLICATION_JSON;
12+
13+
@SpringBootTest(webEnvironment=RANDOM_PORT)
714
class ProductServiceApplicationTests {
815

16+
@Autowired
17+
private WebTestClient client;
18+
19+
@Test
20+
public void getProductById() {
21+
22+
int productId = 1;
23+
24+
client.get()
25+
.uri("/product/" + productId)
26+
.accept(APPLICATION_JSON)
27+
.exchange()
28+
.expectStatus().isOk()
29+
.expectHeader().contentType(APPLICATION_JSON)
30+
.expectBody()
31+
.jsonPath("$.productId").isEqualTo(productId);
32+
}
33+
34+
@Test
35+
public void getProductInvalidParameterString() {
36+
37+
client.get()
38+
.uri("/product/no-integer")
39+
.accept(APPLICATION_JSON)
40+
.exchange()
41+
.expectStatus().isEqualTo(BAD_REQUEST)
42+
.expectHeader().contentType(APPLICATION_JSON)
43+
.expectBody()
44+
.jsonPath("$.path").isEqualTo("/product/no-integer")
45+
.jsonPath("$.message").isEqualTo("Type mismatch.");
46+
}
47+
948
@Test
10-
void contextLoads() {
49+
public void getProductNotFound() {
50+
51+
int productIdNotFound = 13;
52+
53+
client.get()
54+
.uri("/product/" + productIdNotFound)
55+
.accept(APPLICATION_JSON)
56+
.exchange()
57+
.expectStatus().isNotFound()
58+
.expectHeader().contentType(APPLICATION_JSON)
59+
.expectBody()
60+
.jsonPath("$.path").isEqualTo("/product/" + productIdNotFound)
61+
.jsonPath("$.message").isEqualTo("No product found for productId: " + productIdNotFound);
62+
}
63+
64+
@Test
65+
public void getProductInvalidParameterNegativeValue() {
66+
67+
int productIdInvalid = -1;
68+
69+
client.get()
70+
.uri("/product/" + productIdInvalid)
71+
.accept(APPLICATION_JSON)
72+
.exchange()
73+
.expectStatus().isEqualTo(UNPROCESSABLE_ENTITY)
74+
.expectHeader().contentType(APPLICATION_JSON)
75+
.expectBody()
76+
.jsonPath("$.path").isEqualTo("/product/" + productIdInvalid)
77+
.jsonPath("$.message").isEqualTo("Invalid productId: " + productIdInvalid);
1178
}
1279

1380
}

store-api/src/main/java/com/siriusxi/ms/store/api/core/product/Product.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package com.siriusxi.ms.store.api.core.product;
22

3+
import lombok.AllArgsConstructor;
34
import lombok.Data;
45
import lombok.NoArgsConstructor;
5-
import lombok.Setter;
66

77
@Data
88
@NoArgsConstructor(force = true)
9+
@AllArgsConstructor
910
public class Product {
1011
private final int productId;
1112
private final String name;

store-api/src/main/java/com/siriusxi/ms/store/api/core/recommendation/Recommendation.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package com.siriusxi.ms.store.api.core.recommendation;
22

3+
import lombok.AllArgsConstructor;
34
import lombok.Data;
45
import lombok.NoArgsConstructor;
56

67
@Data
78
@NoArgsConstructor(force = true)
9+
@AllArgsConstructor
810
public class Recommendation {
911
private final int productId;
1012
private final int recommendationId;

0 commit comments

Comments
 (0)