Skip to content

Commit aae2abc

Browse files
committed
BENCH-181 list of categories added to Product Response
1 parent a109dd6 commit aae2abc

File tree

7 files changed

+91
-27
lines changed

7 files changed

+91
-27
lines changed

src/main/java/com/answerdigital/answerking/controller/ProductController.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.answerdigital.answerking.exception.util.ErrorResponse;
44
import com.answerdigital.answerking.model.Product;
55
import com.answerdigital.answerking.request.ProductRequest;
6+
import com.answerdigital.answerking.response.ProductResponse;
67
import com.answerdigital.answerking.service.ProductService;
78
import io.swagger.v3.oas.annotations.Operation;
89
import io.swagger.v3.oas.annotations.media.Content;
@@ -45,8 +46,8 @@ public ProductController(final ProductService productService) {
4546
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Product.class)) })
4647
})
4748
@GetMapping
48-
public ResponseEntity<List<Product>> getAllProducts() {
49-
final List<Product> products = productService.findAll();
49+
public ResponseEntity<List<ProductResponse>> getAllProducts() {
50+
final List<ProductResponse> products = productService.findAll();
5051
return new ResponseEntity<>(products, products.isEmpty() ? HttpStatus.NO_CONTENT : HttpStatus.OK);
5152
}
5253

@@ -59,8 +60,8 @@ public ResponseEntity<List<Product>> getAllProducts() {
5960
schema = @Schema(implementation = ErrorResponse.class)) })
6061
})
6162
@GetMapping(path = "/{id}")
62-
public ResponseEntity<Product> getProductById(@Valid @PathVariable @NotNull final Long id) {
63-
return new ResponseEntity<>(productService.findById(id), HttpStatus.OK);
63+
public ResponseEntity<ProductResponse> getProductById(@Valid @PathVariable @NotNull final Long id) {
64+
return new ResponseEntity<>(productService.findByIdResponse(id), HttpStatus.OK);
6465
}
6566

6667
@Operation(summary = "Create a new product.")
@@ -72,7 +73,7 @@ public ResponseEntity<Product> getProductById(@Valid @PathVariable @NotNull fina
7273
schema = @Schema(implementation = ErrorResponse.class)) })
7374
})
7475
@PostMapping
75-
public ResponseEntity<Product> addProduct(@Valid @RequestBody final ProductRequest productRequest) {
76+
public ResponseEntity<ProductResponse> addProduct(@Valid @RequestBody final ProductRequest productRequest) {
7677
return new ResponseEntity<>(productService.addNewProduct(productRequest), HttpStatus.CREATED);
7778
}
7879

@@ -88,7 +89,7 @@ public ResponseEntity<Product> addProduct(@Valid @RequestBody final ProductReque
8889
schema = @Schema(implementation = ErrorResponse.class)) })
8990
})
9091
@PutMapping("/{id}")
91-
public ResponseEntity<Product> updateProduct(@PathVariable @NotNull final Long id,
92+
public ResponseEntity<ProductResponse> updateProduct(@PathVariable @NotNull final Long id,
9293
@Valid @RequestBody final ProductRequest productRequest) {
9394
return new ResponseEntity<>(productService.updateProduct(id, productRequest), HttpStatus.OK);
9495
}
@@ -105,7 +106,7 @@ public ResponseEntity<Product> updateProduct(@PathVariable @NotNull final Long i
105106
schema = @Schema(implementation = ErrorResponse.class)) })
106107
})
107108
@DeleteMapping("/{id}")
108-
public ResponseEntity<Product> retireProduct(@PathVariable @NotNull final Long id) {
109+
public ResponseEntity<ProductResponse> retireProduct(@PathVariable @NotNull final Long id) {
109110
return new ResponseEntity<>(productService.retireProduct(id), HttpStatus.OK);
110111
}
111112
}

src/main/java/com/answerdigital/answerking/mapper/ProductMapper.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,24 @@
22

33
import com.answerdigital.answerking.model.Product;
44
import com.answerdigital.answerking.request.ProductRequest;
5+
import com.answerdigital.answerking.response.ProductResponse;
56
import org.mapstruct.Mapper;
67
import org.mapstruct.Mapping;
78
import org.mapstruct.MappingTarget;
89

9-
@Mapper(componentModel = "spring")
10+
import java.util.Collections;
11+
import java.util.stream.Collectors;
12+
13+
@Mapper(componentModel = "spring",
14+
imports = {Collectors.class, Collections.class})
1015
public interface ProductMapper {
1116
@Mapping(target = "retired", constant = "false")
17+
@Mapping(target = "categories", expression = "java(Collections.EMPTY_SET)")
1218
Product addRequestToProduct(ProductRequest productRequest);
1319

1420
Product updateRequestToProduct(@MappingTarget Product product, ProductRequest productRequest);
21+
22+
@Mapping(target = "categories",
23+
expression = "java(product.getCategories().stream().map(category -> category.getId()).collect(Collectors.toList()) )")
24+
ProductResponse convertProductEntityToProductResponse(Product product);
1525
}

src/main/java/com/answerdigital/answerking/model/Product.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,10 @@ public class Product {
4848
@Column(precision = 12, scale = 2)
4949
private BigDecimal price;
5050

51-
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
5251
private boolean retired;
5352

5453
@JsonIgnore
55-
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "products")
54+
@ManyToMany(fetch = FetchType.EAGER, mappedBy = "products")
5655
private Set<Category> categories = new HashSet<>();
5756

5857
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.answerdigital.answerking.response;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
9+
import java.math.BigDecimal;
10+
import java.util.List;
11+
12+
@Data
13+
@Builder
14+
@NoArgsConstructor
15+
@AllArgsConstructor
16+
public class ProductResponse {
17+
18+
private Long id;
19+
20+
private String name;
21+
22+
private String description;
23+
24+
private BigDecimal price;
25+
26+
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
27+
private List<Long> categories;
28+
29+
private boolean retired;
30+
}

src/main/java/com/answerdigital/answerking/service/ProductService.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@
77
import com.answerdigital.answerking.model.Product;
88
import com.answerdigital.answerking.repository.ProductRepository;
99
import com.answerdigital.answerking.request.ProductRequest;
10+
import com.answerdigital.answerking.response.ProductResponse;
1011
import org.mapstruct.factory.Mappers;
1112
import org.springframework.beans.factory.annotation.Autowired;
1213
import org.springframework.stereotype.Service;
14+
1315
import java.util.List;
16+
import java.util.stream.Collectors;
1417

1518
@Service
1619
public class ProductService {
@@ -24,25 +27,37 @@ public ProductService(final ProductRepository productRepository) {
2427
this.productRepository = productRepository;
2528
}
2629

27-
public Product addNewProduct(final ProductRequest productRequest) {
30+
public ProductResponse addNewProduct(final ProductRequest productRequest) {
2831
if (productRepository.existsByName(productRequest.name())) {
2932
throw new NameUnavailableException(String.format("A Product named '%s' already exists", productRequest.name()));
3033
}
3134

3235
final Product newProduct = productMapper.addRequestToProduct(productRequest);
33-
return productRepository.save(newProduct);
36+
final Product savedProduct = productRepository.save(newProduct);
37+
return productMapper.convertProductEntityToProductResponse(savedProduct);
3438
}
3539

3640
public Product findById(final Long productId) {
3741
return productRepository.findById(productId)
3842
.orElseThrow(() -> new NotFoundException(String.format("Product with ID %d does not exist.", productId)));
3943
}
4044

41-
public List<Product> findAll() {
42-
return productRepository.findAll();
45+
public ProductResponse findByIdResponse(final Long productId) {
46+
final Product product = productRepository.findById(productId)
47+
.orElseThrow(() -> new NotFoundException(String.format("Product with ID %d does not exist.", productId)));
48+
49+
return productMapper.convertProductEntityToProductResponse(product);
50+
}
51+
52+
public List<ProductResponse> findAll() {
53+
final List<Product> productsList = productRepository.findAll();
54+
return productsList
55+
.stream()
56+
.map(productMapper::convertProductEntityToProductResponse)
57+
.collect(Collectors.toList());
4358
}
4459

45-
public Product updateProduct(final Long productId, final ProductRequest productRequest) {
60+
public ProductResponse updateProduct(final Long productId, final ProductRequest productRequest) {
4661
productRepository.findById(productId)
4762
.orElseThrow(() -> new NotFoundException(String.format("Product with ID %d does not exist.", productId)));
4863

@@ -51,15 +66,17 @@ public Product updateProduct(final Long productId, final ProductRequest productR
5166
}
5267

5368
final Product updatedProduct = productMapper.updateRequestToProduct(findById(productId), productRequest);
54-
return productRepository.save(updatedProduct);
69+
final Product savedProduct = productRepository.save(updatedProduct);
70+
return productMapper.convertProductEntityToProductResponse(savedProduct);
5571
}
5672

57-
public Product retireProduct(final Long productId) {
73+
public ProductResponse retireProduct(final Long productId) {
5874
final Product product = findById(productId);
5975
if (product.isRetired()) {
6076
throw new RetirementException(String.format("The product with ID %d is already retired", productId));
6177
}
6278
product.setRetired(true);
63-
return productRepository.save(product);
79+
final Product savedProduct = productRepository.save(product);
80+
return productMapper.convertProductEntityToProductResponse(savedProduct);
6481
}
6582
}

src/test/java/com/answerdigital/answerking/controller/ProductControllerTest.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.answerdigital.answerking.controller;
22

3-
import com.answerdigital.answerking.model.Product;
3+
import com.answerdigital.answerking.response.ProductResponse;
44
import com.answerdigital.answerking.service.ProductService;
55
import com.fasterxml.jackson.databind.ObjectMapper;
66
import org.junit.jupiter.api.BeforeEach;
@@ -20,12 +20,14 @@
2020
import org.springframework.test.web.servlet.ResultActions;
2121
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
2222
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
23+
2324
import java.math.BigDecimal;
25+
import java.util.ArrayList;
2426
import java.util.List;
2527

2628
import static org.junit.jupiter.api.Assertions.assertEquals;
27-
import static org.junit.jupiter.api.Assertions.assertTrue;
2829
import static org.junit.jupiter.api.Assertions.assertFalse;
30+
import static org.junit.jupiter.api.Assertions.assertTrue;
2931
import static org.mockito.ArgumentMatchers.any;
3032
import static org.mockito.ArgumentMatchers.eq;
3133
import static org.mockito.BDDMockito.given;
@@ -49,16 +51,17 @@ class ProductControllerTest {
4951
@Autowired
5052
private MockMvc mvc;
5153

52-
private Product product;
54+
private ProductResponse product;
5355

5456
@BeforeEach
5557
public void generateProduct() {
56-
product = Product.builder()
58+
product = ProductResponse.builder()
5759
.id(55L)
5860
.name("test")
5961
.description("testDes")
6062
.price(BigDecimal.valueOf(2.99))
6163
.retired(false)
64+
.categories(new ArrayList<>())
6265
.build();
6366
}
6467

@@ -92,7 +95,7 @@ void getAllProductsReturnNoContentIfEmpty() throws Exception {
9295
@Test
9396
void getProductByIdReturnsOkStatusIfExist() throws Exception {
9497
//given
95-
when(productService.findById(55L)).thenReturn(product);
98+
when(productService.findByIdResponse(55L)).thenReturn(product);
9699
//when
97100
ResultActions actualPerformResult = mvc.perform(get("/products/{id}", 55L)).andExpect(status().isOk());
98101
ObjectMapper mapper = new ObjectMapper();

src/test/java/com/answerdigital/answerking/service/ProductServiceTest.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.answerdigital.answerking.model.Product;
77
import com.answerdigital.answerking.repository.ProductRepository;
88
import com.answerdigital.answerking.request.ProductRequest;
9+
import com.answerdigital.answerking.response.ProductResponse;
910
import org.junit.jupiter.api.BeforeEach;
1011
import org.junit.jupiter.api.Test;
1112
import org.junit.jupiter.api.extension.ExtendWith;
@@ -14,12 +15,14 @@
1415
import org.mockito.junit.jupiter.MockitoExtension;
1516

1617
import java.math.BigDecimal;
18+
import java.util.HashSet;
1719
import java.util.List;
1820
import java.util.Optional;
1921

2022
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2123
import static org.junit.jupiter.api.Assertions.assertEquals;
2224
import static org.junit.jupiter.api.Assertions.assertFalse;
25+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
2326
import static org.junit.jupiter.api.Assertions.assertThrows;
2427
import static org.mockito.Mockito.any;
2528
import static org.mockito.Mockito.anyLong;
@@ -46,6 +49,7 @@ public void generateProduct() {
4649
.description("testDes")
4750
.price(BigDecimal.valueOf(2.99))
4851
.retired(false)
52+
.categories(new HashSet<>())
4953
.build();
5054
productRequest = ProductRequest.builder()
5155
.name("test")
@@ -60,7 +64,7 @@ void addNewProductReturnsProductObjectSuccessfully() {
6064
when(productRepository.save(any())).thenReturn(product);
6165
when(productRepository.existsByName(any())).thenReturn(false);
6266
//when
63-
Product actualAddNewProductResult = productService.addNewProduct(
67+
ProductResponse actualAddNewProductResult = productService.addNewProduct(
6468
productRequest);
6569
//then
6670
assertEquals(product.getName(), actualAddNewProductResult.getName());
@@ -105,7 +109,7 @@ void getAllProductsReturnListOfProductObjects() {
105109
//given
106110
when(productRepository.findAll()).thenReturn(List.of(product));
107111
//when
108-
List<Product> actualResult = productService.findAll();
112+
List<ProductResponse> actualResult = productService.findAll();
109113
//then
110114
assertFalse(actualResult.isEmpty());
111115
assertEquals(actualResult.get(0).getName(), product.getName());
@@ -120,7 +124,7 @@ void updateProductReturnsProductObjectSuccessfully() {
120124
when(productRepository.findById(anyLong())).thenReturn(Optional.ofNullable(product));
121125

122126
//when
123-
Product actualAddNewProductResult = productService.updateProduct(12L,
127+
ProductResponse actualAddNewProductResult = productService.updateProduct(12L,
124128
productRequest);
125129

126130
//then
@@ -159,7 +163,7 @@ void testRetireProduct() {
159163
.thenReturn(product);
160164

161165
// then
162-
assertEquals(product, productService.retireProduct(PRODUCT_ID));
166+
assertNotEquals(product.isRetired(), productService.retireProduct(PRODUCT_ID).isRetired());
163167
verify(productRepository).findById(anyLong());
164168
verify(productRepository).save(any(Product.class));
165169
}

0 commit comments

Comments
 (0)