Skip to content

Commit b4810c3

Browse files
hospelAudriusPutysMohammadIqbalAD
authored
BENCH-173: showing product ids in category response (#93)
* showing product ids in category response * rebase * BENCH-196 merge statuses added * BENCH-196 name updated * formatting updated * Defined API responses for product HTTP requests * Fixed 500 Error when getProductByID * Removed all Error Handling in Controller Methods * Updated mediaTypes for error responses * Fixed errors thrown by Checkstyle * rebase * rebase Co-authored-by: AudriusPutys <[email protected]> Co-authored-by: AudriusPutys <[email protected]> Co-authored-by: MohammadIqbalAD <[email protected]>
1 parent 7ea8044 commit b4810c3

File tree

8 files changed

+152
-22
lines changed

8 files changed

+152
-22
lines changed

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import com.answerdigital.answerking.model.Category;
55
import com.answerdigital.answerking.request.AddCategoryRequest;
66
import com.answerdigital.answerking.request.UpdateCategoryRequest;
7+
import com.answerdigital.answerking.response.CategoryResponse;
78
import com.answerdigital.answerking.service.CategoryService;
89
import io.swagger.v3.oas.annotations.Operation;
910
import io.swagger.v3.oas.annotations.media.Content;
@@ -49,7 +50,7 @@ public CategoryController(final CategoryService categoryService) {
4950
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) })
5051
})
5152
@PostMapping
52-
public ResponseEntity<Category> addCategory(@Valid @RequestBody final AddCategoryRequest categoryRequest) {
53+
public ResponseEntity<CategoryResponse> addCategory(@Valid @RequestBody final AddCategoryRequest categoryRequest) {
5354
return new ResponseEntity<>(categoryService.addCategory(categoryRequest), HttpStatus.CREATED);
5455
}
5556

@@ -59,8 +60,8 @@ public ResponseEntity<Category> addCategory(@Valid @RequestBody final AddCategor
5960
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Category.class)) })
6061
})
6162
@GetMapping
62-
public ResponseEntity<Collection<Category>> getAllCategories() {
63-
final Set<Category> categories = categoryService.findAll();
63+
public ResponseEntity<Collection<CategoryResponse>> getAllCategories() {
64+
final Set<CategoryResponse> categories = categoryService.findAll();
6465
return new ResponseEntity<>(categories, categories.isEmpty() ? HttpStatus.NO_CONTENT : HttpStatus.OK);
6566
}
6667

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,18 @@
33
import com.answerdigital.answerking.model.Category;
44
import com.answerdigital.answerking.request.AddCategoryRequest;
55
import com.answerdigital.answerking.request.UpdateCategoryRequest;
6+
import com.answerdigital.answerking.response.CategoryResponse;
67
import org.mapstruct.Mapper;
78
import org.mapstruct.Mapping;
89
import org.mapstruct.MappingTarget;
910
import java.time.ZonedDateTime;
1011
import java.time.format.DateTimeFormatter;
1112
import java.time.temporal.ChronoUnit;
1213
import java.time.ZoneOffset;
14+
import java.util.stream.Collectors;
1315

14-
@Mapper(componentModel = "spring", imports = {DateTimeFormatter.class, ZoneOffset.class, ZonedDateTime.class, ChronoUnit.class})
16+
@Mapper(componentModel = "spring",
17+
imports = {DateTimeFormatter.class, ZoneOffset.class, ZonedDateTime.class, ChronoUnit.class, Collectors.class})
1518
public interface CategoryMapper {
1619

1720
@Mapping(target = "createdOn", expression = "java( ZonedDateTime.now(ZoneOffset.UTC)" +
@@ -26,4 +29,9 @@ public interface CategoryMapper {
2629
".truncatedTo( ChronoUnit.SECONDS )" +
2730
".format( DateTimeFormatter.ofPattern( \"yyyy-MM-dd HH:mm:ss\" ) ) )")
2831
Category updateRequestToCategory(@MappingTarget Category category, UpdateCategoryRequest updateCategoryRequest);
32+
33+
@Mapping(target = "productIds",
34+
expression = "java(category.getProducts().stream().map(product -> product.getId()).collect(Collectors.toList()) )")
35+
CategoryResponse convertCategoryEntityToCategoryResponse(Category category);
36+
2937
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public class Category {
5656

5757
private boolean retired;
5858

59-
@ManyToMany(fetch = FetchType.LAZY)
59+
@ManyToMany(fetch = FetchType.EAGER)
6060
@JoinTable(
6161
name = "product_category",
6262
joinColumns = {@JoinColumn(name = "category_id")},
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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 javax.validation.constraints.NotBlank;
10+
import javax.validation.constraints.Pattern;
11+
import java.util.List;
12+
13+
@Data
14+
@Builder
15+
@NoArgsConstructor
16+
@AllArgsConstructor
17+
public class CategoryResponse {
18+
19+
private Long id;
20+
21+
@NotBlank
22+
@Pattern(regexp = "^[a-zA-Z\s-]*",
23+
message = "Category name must only contain letters, spaces and dashes")
24+
private String name;
25+
26+
@NotBlank
27+
@Pattern(regexp = "^[a-zA-Z\s.,!?0-9-']*",
28+
message = "Category description can only contain letters, numbers, spaces and !?-.,' punctuation")
29+
private String description;
30+
31+
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
32+
private List<Long> productIds;
33+
34+
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
35+
private String createdOn;
36+
37+
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
38+
private String lastUpdated;
39+
40+
private boolean retired;
41+
42+
}

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,15 @@
1010
import com.answerdigital.answerking.repository.CategoryRepository;
1111
import com.answerdigital.answerking.request.AddCategoryRequest;
1212
import com.answerdigital.answerking.request.UpdateCategoryRequest;
13+
import com.answerdigital.answerking.response.CategoryResponse;
1314
import org.mapstruct.factory.Mappers;
1415
import org.springframework.beans.factory.annotation.Autowired;
1516
import org.springframework.stereotype.Service;
1617
import org.springframework.transaction.annotation.Propagation;
1718
import org.springframework.transaction.annotation.Transactional;
1819

1920
import java.util.Set;
21+
import java.util.stream.Collectors;
2022

2123
@Service
2224
public class CategoryService {
@@ -33,22 +35,27 @@ public CategoryService(final ProductService productService,
3335
this.categoryRepository = categoryRepository;
3436
}
3537

36-
public Category addCategory(final AddCategoryRequest categoryRequest) {
38+
public CategoryResponse addCategory(final AddCategoryRequest categoryRequest) {
3739
if (categoryRepository.existsByName(categoryRequest.name())) {
3840
throw new NameUnavailableException(String.format("A category named '%s' already exists", categoryRequest.name()));
3941
}
4042

4143
final Category newCategory = categoryMapper.addRequestToCategory(categoryRequest);
42-
return categoryRepository.save(newCategory);
44+
final var category = categoryRepository.save(newCategory);
45+
return categoryMapper.convertCategoryEntityToCategoryResponse(category);
4346
}
4447

4548
public Category findById(final Long categoryId) {
4649
return categoryRepository.findById(categoryId)
4750
.orElseThrow(() -> new NotFoundException(String.format("Category with ID %d does not exist.", categoryId)));
4851
}
4952

50-
public Set<Category> findAll() {
51-
return categoryRepository.findAll();
53+
public Set<CategoryResponse> findAll() {
54+
final var categorySet = categoryRepository.findAll();
55+
return categorySet
56+
.stream()
57+
.map(categoryMapper::convertCategoryEntityToCategoryResponse)
58+
.collect(Collectors.toSet());
5259
}
5360

5461
public Category updateCategory(final UpdateCategoryRequest updateCategoryRequest, final Long id) {

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.answerdigital.answerking.model.Category;
44
import com.answerdigital.answerking.request.AddCategoryRequest;
55
import com.answerdigital.answerking.request.UpdateCategoryRequest;
6+
import com.answerdigital.answerking.response.CategoryResponse;
67
import com.answerdigital.answerking.service.CategoryService;
78
import com.fasterxml.jackson.databind.ObjectMapper;
89
import org.junit.jupiter.api.Test;
@@ -65,14 +66,19 @@ void removeProductFromCategoryTest() throws Exception {
6566
@Test
6667
void addCategoryTest() throws Exception {
6768
ObjectMapper mapper = new ObjectMapper();
69+
70+
final String testDate = ZonedDateTime.now( ZoneId.of( "Etc/UTC" ) )
71+
.truncatedTo( ChronoUnit.SECONDS )
72+
.format( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" ) );
6873
final var addCategoryRequest = new AddCategoryRequest("random name", "random description");
69-
final var category = new Category("random name", "random description");
74+
final var categoryResponse = CategoryResponse.builder()
75+
.name(addCategoryRequest.name())
76+
.description(addCategoryRequest.description())
77+
.createdOn(testDate)
78+
.build();
7079
final var categoryRequest = "{\"name\": \"random name\",\"description\": \"random description\"}";
71-
final String testDate = ZonedDateTime.now( ZoneId.of( "Etc/UTC" ) )
72-
.truncatedTo( ChronoUnit.SECONDS )
73-
.format( DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" ) );
7480

75-
doReturn(category).when(categoryService).addCategory(addCategoryRequest);
81+
doReturn(categoryResponse).when(categoryService).addCategory(addCategoryRequest);
7682
final var response = mvc.perform(post("/categories")
7783
.content(categoryRequest)
7884
.contentType(MediaType.APPLICATION_JSON))
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.answerdigital.answerking.response;
2+
3+
import com.answerdigital.answerking.mapper.CategoryMapper;
4+
import com.answerdigital.answerking.mapper.CategoryMapperImpl;
5+
import com.answerdigital.answerking.model.Category;
6+
import com.answerdigital.answerking.model.Product;
7+
import org.junit.jupiter.api.Test;
8+
import org.junit.runner.RunWith;
9+
import org.springframework.beans.factory.annotation.Autowired;
10+
import org.springframework.boot.test.context.SpringBootTest;
11+
import org.springframework.test.context.junit4.SpringRunner;
12+
import java.math.BigDecimal;
13+
import java.util.HashSet;
14+
15+
import static org.junit.jupiter.api.Assertions.*;
16+
17+
@RunWith(SpringRunner.class)
18+
@SpringBootTest(classes = {CategoryMapperImpl.class, CategoryMapper.class, Product.class, Category.class})
19+
class CategoryResponseTest {
20+
21+
@Autowired
22+
CategoryMapper categoryMapper;
23+
24+
private static final Long CATEGORY_ID = 1L;
25+
private static final Long PRODUCT_ID = 1L;
26+
27+
@Test
28+
void categoryMapperToMapCategoryResponseTest() {
29+
30+
final var product = Product.builder()
31+
.id(PRODUCT_ID)
32+
.name("Coca cola")
33+
.description("This is a coke")
34+
.price(BigDecimal.valueOf(2.00d))
35+
.retired(true)
36+
.build();
37+
final var categoryName = "Drinks";
38+
final var categoryDesc = "Our selection of drinks";
39+
40+
final var category = Category.builder()
41+
.name(categoryName)
42+
.description(categoryDesc)
43+
.id(CATEGORY_ID)
44+
.products(new HashSet<>())
45+
.build();
46+
category.addProduct(product);
47+
48+
final var categoryResponse = categoryMapper.convertCategoryEntityToCategoryResponse(category);
49+
50+
assertNotNull(categoryResponse);
51+
assertEquals(PRODUCT_ID, categoryResponse.getProductIds().get(0));
52+
assertEquals(CATEGORY_ID, categoryResponse.getId());
53+
assertEquals(categoryName, categoryResponse.getName());
54+
assertEquals(categoryDesc, categoryResponse.getDescription());
55+
56+
}
57+
58+
}

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,10 @@
1919
import org.mockito.Mock;
2020
import org.mockito.junit.jupiter.MockitoExtension;
2121

22+
import java.util.HashSet;
2223
import java.util.Optional;
2324

24-
import static org.junit.jupiter.api.Assertions.assertEquals;
25-
import static org.junit.jupiter.api.Assertions.assertFalse;
26-
import static org.junit.jupiter.api.Assertions.assertThrows;
25+
import static org.junit.jupiter.api.Assertions.*;
2726
import static org.mockito.ArgumentMatchers.anyLong;
2827
import static org.mockito.ArgumentMatchers.anyString;
2928
import static org.mockito.Mockito.any;
@@ -58,15 +57,19 @@ void tearDown() {
5857
void testAddCategory() {
5958
// given
6059
AddCategoryRequest addCategoryRequest = new AddCategoryRequest("Drinks", "Our selection of drinks");
61-
Category expectedResponse = new Category("Drinks", "Our selection of drinks");
60+
final var expectedResponse = new Category("Drinks", "Our selection of drinks");
6261

6362
// when
6463
doReturn(false).when(categoryRepository).existsByName(anyString());
6564
doReturn(expectedResponse).when(categoryRepository).save(any(Category.class));
66-
Category response = categoryService.addCategory(addCategoryRequest);
65+
final var categoryResponse = categoryService.addCategory(addCategoryRequest);
6766

6867
// then
69-
assertEquals(expectedResponse, response);
68+
assertAll(
69+
() -> assertEquals(expectedResponse.getId(), categoryResponse.getId()),
70+
() -> assertEquals(expectedResponse.getName(), categoryResponse.getName()),
71+
() -> assertEquals(expectedResponse.getDescription(), categoryResponse.getDescription())
72+
);
7073
verify(categoryRepository).existsByName(anyString());
7174
verify(categoryRepository).save(any(Category.class));
7275
}
@@ -182,8 +185,13 @@ void testAddProductToCategoryThatIsAlreadyInCategory() {
182185
Product product = new Product("Coca cola", "This is a coke", BigDecimal.valueOf(2.00d), true);
183186
product.setId(PRODUCT_ID);
184187

185-
Category category = new Category("Drinks", "Our selection of drinks");
186-
category.setId(CATEGORY_ID);
188+
final var category = Category.builder()
189+
.name("Drinks")
190+
.description("Our selection of drinks")
191+
.id(CATEGORY_ID)
192+
.products(new HashSet<>())
193+
.build();
194+
187195
category.addProduct(product);
188196

189197
// when

0 commit comments

Comments
 (0)