Skip to content

Commit e81f977

Browse files
authored
Merge pull request #105 from AnswerConsulting/BENCH-181
BENCH-181 list of categories added to Product Response
2 parents f8d4d51 + 867f1fc commit e81f977

File tree

7 files changed

+60
-53
lines changed

7 files changed

+60
-53
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@
158158
<version>3.2.0</version>
159159
<configuration>
160160
<configLocation>checkstyle.xml</configLocation>
161-
<includeTestSourceDirectory>false</includeTestSourceDirectory>
161+
<includeTestSourceDirectory>true</includeTestSourceDirectory>
162162
</configuration>
163163

164164
<executions>

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

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

33
import com.answerdigital.answerking.exception.util.ErrorResponse;
4-
import com.answerdigital.answerking.model.Product;
54
import com.answerdigital.answerking.request.ProductRequest;
5+
import com.answerdigital.answerking.response.ProductResponse;
66
import com.answerdigital.answerking.service.ProductService;
77
import io.swagger.v3.oas.annotations.Operation;
88
import io.swagger.v3.oas.annotations.media.Content;
@@ -42,44 +42,44 @@ public ProductController(final ProductService productService) {
4242
@Operation(summary = "Get all products.")
4343
@ApiResponses(value = {
4444
@ApiResponse(responseCode = "200", description = "When all the products have been returned.",
45-
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Product.class)) })
45+
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ProductResponse.class)) })
4646
})
4747
@GetMapping
48-
public ResponseEntity<List<Product>> getAllProducts() {
49-
final List<Product> products = productService.findAll();
48+
public ResponseEntity<List<ProductResponse>> getAllProducts() {
49+
final List<ProductResponse> products = productService.findAll();
5050
return new ResponseEntity<>(products, products.isEmpty() ? HttpStatus.NO_CONTENT : HttpStatus.OK);
5151
}
5252

5353
@Operation(summary = "Get a single product.")
5454
@ApiResponses(value = {
5555
@ApiResponse(responseCode = "200", description = "When the product with the provided id has been found.",
56-
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Product.class)) }),
56+
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ProductResponse.class)) }),
5757
@ApiResponse(responseCode = "404", description = "When the product with the given id does not exist.",
5858
content = { @Content(mediaType = "application/problem+json",
5959
schema = @Schema(implementation = ErrorResponse.class)) })
6060
})
6161
@GetMapping(path = "/{id}")
62-
public ResponseEntity<Product> getProductById(@Valid @PathVariable @NotNull final Long id) {
63-
return new ResponseEntity<>(productService.findById(id), HttpStatus.OK);
62+
public ResponseEntity<ProductResponse> getProductById(@Valid @PathVariable @NotNull final Long id) {
63+
return new ResponseEntity<>(productService.findByIdResponse(id), HttpStatus.OK);
6464
}
6565

6666
@Operation(summary = "Create a new product.")
6767
@ApiResponses(value = {
6868
@ApiResponse(responseCode = "201", description = "When the product has been created.",
69-
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Product.class)) }),
69+
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ProductResponse.class)) }),
7070
@ApiResponse(responseCode = "400", description = "When invalid parameters are provided.",
7171
content = { @Content(mediaType = "application/problem+json",
7272
schema = @Schema(implementation = ErrorResponse.class)) })
7373
})
7474
@PostMapping
75-
public ResponseEntity<Product> addProduct(@Valid @RequestBody final ProductRequest productRequest) {
75+
public ResponseEntity<ProductResponse> addProduct(@Valid @RequestBody final ProductRequest productRequest) {
7676
return new ResponseEntity<>(productService.addNewProduct(productRequest), HttpStatus.CREATED);
7777
}
7878

7979
@Operation(summary = "Update an existing product.")
8080
@ApiResponses(value = {
8181
@ApiResponse(responseCode = "200", description = "When the product has been updated.",
82-
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Product.class)) }),
82+
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ProductResponse.class)) }),
8383
@ApiResponse(responseCode = "400", description = "When invalid parameters are provided.",
8484
content = { @Content(mediaType = "application/problem+json",
8585
schema = @Schema(implementation = ErrorResponse.class)) }),
@@ -88,15 +88,15 @@ public ResponseEntity<Product> addProduct(@Valid @RequestBody final ProductReque
8888
schema = @Schema(implementation = ErrorResponse.class)) })
8989
})
9090
@PutMapping("/{id}")
91-
public ResponseEntity<Product> updateProduct(@PathVariable @NotNull final Long id,
91+
public ResponseEntity<ProductResponse> updateProduct(@PathVariable @NotNull final Long id,
9292
@Valid @RequestBody final ProductRequest productRequest) {
9393
return new ResponseEntity<>(productService.updateProduct(id, productRequest), HttpStatus.OK);
9494
}
9595

9696
@Operation(summary = "Retire an existing product.")
9797
@ApiResponses(value = {
9898
@ApiResponse(responseCode = "200", description = "When the product has been retired.",
99-
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Product.class)) }),
99+
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ProductResponse.class)) }),
100100
@ApiResponse(responseCode = "404", description = "When the product with the given id does not exist.",
101101
content = { @Content(mediaType = "application/problem+json",
102102
schema = @Schema(implementation = ErrorResponse.class)) }),
@@ -105,7 +105,7 @@ public ResponseEntity<Product> updateProduct(@PathVariable @NotNull final Long i
105105
schema = @Schema(implementation = ErrorResponse.class)) })
106106
})
107107
@DeleteMapping("/{id}")
108-
public ResponseEntity<Product> retireProduct(@PathVariable @NotNull final Long id) {
108+
public ResponseEntity<ProductResponse> retireProduct(@PathVariable @NotNull final Long id) {
109109
return new ResponseEntity<>(productService.retireProduct(id), HttpStatus.OK);
110110
}
111111
}

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

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

3-
import com.fasterxml.jackson.annotation.JsonIgnore;
43
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
5-
import com.fasterxml.jackson.annotation.JsonProperty;
64
import lombok.AccessLevel;
75
import lombok.AllArgsConstructor;
86
import lombok.Builder;
@@ -38,7 +36,6 @@
3836
public class Product {
3937
@Id
4038
@GeneratedValue(strategy = GenerationType.IDENTITY)
41-
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
4239
private Long id;
4340

4441
private String name;
@@ -48,15 +45,13 @@ public class Product {
4845
@Column(precision = 12, scale = 2)
4946
private BigDecimal price;
5047

51-
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
5248
private boolean retired;
5349

5450
@ManyToOne
5551
@JoinColumn(name="category_id", nullable=false)
5652
private Category category;
5753

5854
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
59-
@JsonIgnore
6055
private Set<LineItem> lineItems = new HashSet<>();
6156

6257
public Product(final String name, final String description, final BigDecimal price, final boolean isRetired) {
Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
package com.answerdigital.answerking.response;
22

3-
import com.fasterxml.jackson.annotation.JsonProperty;
43
import lombok.AllArgsConstructor;
54
import lombok.Builder;
65
import lombok.Data;
76
import lombok.NoArgsConstructor;
87

9-
import javax.validation.constraints.NotNull;
10-
import javax.validation.constraints.DecimalMin;
11-
import javax.validation.constraints.Digits;
12-
import javax.validation.constraints.NotBlank;
13-
import javax.validation.constraints.Pattern;
148
import java.math.BigDecimal;
159
import java.util.List;
1610

@@ -20,26 +14,15 @@
2014
@AllArgsConstructor
2115
public class ProductResponse {
2216

23-
@NotBlank
2417
private Long id;
2518

26-
@Pattern(regexp = "^[a-zA-Z\s-]*",
27-
message = "Product name must only contain letters, spaces and dashes")
2819
private String name;
2920

30-
@NotBlank
31-
@Pattern(regexp = "^[a-zA-Z\s.,!?0-9-']*",
32-
message = "Product description can only contain letters, numbers, spaces and !?-.,' punctuation")
3321
private String description;
3422

35-
@Digits(integer = 12, fraction = 2, message = "Product price is invalid")
36-
@DecimalMin(value = "0.0", inclusive = false, message = "Product price cannot be less than 0")
37-
@NotNull
3823
private BigDecimal price;
3924

4025
private List<Long> categories;
4126

42-
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
4327
private boolean retired;
44-
4528
}

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

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
import org.mapstruct.factory.Mappers;
1212
import org.springframework.beans.factory.annotation.Autowired;
1313
import org.springframework.stereotype.Service;
14+
1415
import java.util.List;
16+
import java.util.stream.Collectors;
1517

1618
@Service
1719
public class ProductService {
@@ -24,25 +26,37 @@ public ProductService(final ProductRepository productRepository) {
2426
this.productRepository = productRepository;
2527
}
2628

27-
public Product addNewProduct(final ProductRequest productRequest) {
29+
public ProductResponse addNewProduct(final ProductRequest productRequest) {
2830
if (productRepository.existsByName(productRequest.name())) {
2931
throw new NameUnavailableException(String.format("A Product named '%s' already exists", productRequest.name()));
3032
}
3133

3234
final Product newProduct = productMapper.addRequestToProduct(productRequest);
33-
return productRepository.save(newProduct);
35+
final Product savedProduct = productRepository.save(newProduct);
36+
return productMapper.convertProductEntityToProductResponse(savedProduct);
3437
}
3538

3639
public Product findById(final Long productId) {
3740
return productRepository.findById(productId)
3841
.orElseThrow(() -> new NotFoundException(String.format("Product with ID %d does not exist.", productId)));
3942
}
4043

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

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

@@ -51,16 +65,18 @@ public Product updateProduct(final Long productId, final ProductRequest productR
5165
}
5266

5367
final Product updatedProduct = productMapper.updateRequestToProduct(findById(productId), productRequest);
54-
return productRepository.save(updatedProduct);
68+
final Product savedProduct = productRepository.save(updatedProduct);
69+
return productMapper.convertProductEntityToProductResponse(savedProduct);
5570
}
5671

57-
public Product retireProduct(final Long productId) {
72+
public ProductResponse retireProduct(final Long productId) {
5873
final Product product = findById(productId);
5974
if (product.isRetired()) {
6075
throw new RetirementException(String.format("The product with ID %d is already retired", productId));
6176
}
6277
product.setRetired(true);
63-
return productRepository.save(product);
78+
final Product savedProduct = productRepository.save(product);
79+
return productMapper.convertProductEntityToProductResponse(savedProduct);
6480
}
6581

6682
public List<ProductResponse> findProductsByCategoryId(final Long categoryId) {

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

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

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

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import com.answerdigital.answerking.exception.custom.NameUnavailableException;
44
import com.answerdigital.answerking.exception.custom.RetirementException;
55
import com.answerdigital.answerking.exception.generic.NotFoundException;
6+
import com.answerdigital.answerking.model.Category;
67
import com.answerdigital.answerking.model.Product;
78
import com.answerdigital.answerking.repository.ProductRepository;
89
import com.answerdigital.answerking.request.ProductRequest;
10+
import com.answerdigital.answerking.response.ProductResponse;
911
import org.junit.jupiter.api.BeforeEach;
1012
import org.junit.jupiter.api.Test;
1113
import org.junit.jupiter.api.extension.ExtendWith;
@@ -20,6 +22,7 @@
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;
@@ -43,12 +46,18 @@ class ProductServiceTest {
4346

4447
@BeforeEach
4548
public void generateProduct() {
49+
final Category category = Category.builder()
50+
.name("test")
51+
.description("categoryDesc")
52+
.id(1L)
53+
.build();
4654
product = Product.builder()
4755
.id(PRODUCT_ID)
4856
.name("test")
4957
.description("testDes")
5058
.price(BigDecimal.valueOf(2.99))
5159
.retired(false)
60+
.category(category)
5261
.build();
5362
productRequest = ProductRequest.builder()
5463
.name("test")
@@ -63,7 +72,7 @@ void addNewProductReturnsProductObjectSuccessfully() {
6372
when(productRepository.save(any())).thenReturn(product);
6473
when(productRepository.existsByName(any())).thenReturn(false);
6574
//when
66-
final Product actualAddNewProductResult = productService.addNewProduct(
75+
final ProductResponse actualAddNewProductResult = productService.addNewProduct(
6776
productRequest);
6877
//then
6978
assertEquals(product.getName(), actualAddNewProductResult.getName());
@@ -108,7 +117,7 @@ void getAllProductsReturnListOfProductObjects() {
108117
//given
109118
when(productRepository.findAll()).thenReturn(List.of(product));
110119
//when
111-
final List<Product> actualResult = productService.findAll();
120+
final List<ProductResponse> actualResult = productService.findAll();
112121
//then
113122
assertFalse(actualResult.isEmpty());
114123
assertEquals(actualResult.get(0).getName(), product.getName());
@@ -123,7 +132,8 @@ void updateProductReturnsProductObjectSuccessfully() {
123132
when(productRepository.findById(anyLong())).thenReturn(Optional.ofNullable(product));
124133

125134
//when
126-
final Product actualAddNewProductResult = productService.updateProduct(12L, productRequest);
135+
final ProductResponse actualAddNewProductResult = productService.updateProduct(12L,
136+
productRequest);
127137

128138
//then
129139
assertEquals(product.getName(), actualAddNewProductResult.getName());
@@ -161,7 +171,7 @@ void testRetireProduct() {
161171
.thenReturn(product);
162172

163173
// then
164-
assertEquals(product, productService.retireProduct(PRODUCT_ID));
174+
assertNotEquals(product.isRetired(), productService.retireProduct(PRODUCT_ID).isRetired());
165175
verify(productRepository).findById(anyLong());
166176
verify(productRepository).save(any(Product.class));
167177
}

0 commit comments

Comments
 (0)