Skip to content

Commit 53c3ae8

Browse files
committed
Merge branch 'main' into BENCH-181
# Conflicts: # src/main/java/com/answerdigital/answerking/mapper/ProductMapper.java # src/main/java/com/answerdigital/answerking/model/Product.java # src/main/java/com/answerdigital/answerking/response/ProductResponse.java # src/test/java/com/answerdigital/answerking/service/ProductServiceTest.java
2 parents 153bcd7 + f8d4d51 commit 53c3ae8

31 files changed

+349
-166
lines changed

src/main/java/com/answerdigital/answerking/config/SecurityConfig.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ public InMemoryUserDetailsManager userDetailsManager() {
4949
return new InMemoryUserDetailsManager(
5050
List.of("paul", "john", "ringo", "george")
5151
.stream()
52-
.map(user -> {
53-
return User.withUsername(user)
54-
.password(COMMON_PASSWORD)
55-
.authorities(COMMON_ROLE)
56-
.build();
57-
}).toList()
52+
.map(user ->
53+
User.withUsername(user)
54+
.password(COMMON_PASSWORD)
55+
.authorities(COMMON_ROLE)
56+
.build()
57+
).toList()
5858
);
5959
}
6060

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

Lines changed: 13 additions & 0 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.CategoryRequest;
66
import com.answerdigital.answerking.response.CategoryResponse;
7+
import com.answerdigital.answerking.response.ProductResponse;
78
import com.answerdigital.answerking.service.CategoryService;
89
import io.swagger.v3.oas.annotations.Operation;
910
import io.swagger.v3.oas.annotations.media.Content;
@@ -76,6 +77,18 @@ public ResponseEntity<Category> getCategoryById(@PathVariable @NotNull final Lon
7677
return new ResponseEntity<>(categoryService.findById(categoryId), HttpStatus.OK);
7778
}
7879

80+
@Operation(summary = "Get all products in a category.")
81+
@ApiResponses(value = {
82+
@ApiResponse(responseCode = "200", description = "When all the products have been returned.",
83+
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = Category.class)) }),
84+
@ApiResponse(responseCode = "404", description = "When the category with the given id does not exist.",
85+
content = { @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)) })
86+
})
87+
@GetMapping("/{categoryId}/products")
88+
public ResponseEntity<Collection<ProductResponse>> fetchProductsByCategory(@PathVariable @NotNull final Long categoryId) {
89+
return new ResponseEntity<>(categoryService.findProductsByCategoryId(categoryId), HttpStatus.OK);
90+
}
91+
7992
@Operation(summary = "Add product to a category.")
8093
@ApiResponses(value = {
8194
@ApiResponse(responseCode = "200", description = "Add product to a category.",

src/main/java/com/answerdigital/answerking/exception/util/RestResponseEntityExceptionHandler.java

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

33
import com.answerdigital.answerking.exception.AnswerKingException;
44
import com.answerdigital.answerking.exception.custom.ValidationException;
5-
import com.answerdigital.answerking.exception.generic.BadRequestException;
65
import com.answerdigital.answerking.exception.generic.InternalServerErrorException;
76
import org.springframework.http.HttpStatus;
87
import org.springframework.http.ResponseEntity;
8+
import org.springframework.validation.FieldError;
99
import org.springframework.web.bind.MethodArgumentNotValidException;
1010
import org.springframework.web.bind.annotation.ExceptionHandler;
1111
import org.springframework.web.bind.annotation.RestControllerAdvice;
@@ -15,9 +15,11 @@
1515
import javax.validation.ConstraintViolationException;
1616
import java.util.ArrayList;
1717
import java.util.Collection;
18+
import java.util.Collections;
1819
import java.util.HashMap;
1920
import java.util.Map;
2021
import java.util.Objects;
22+
import java.util.stream.Collectors;
2123
import java.util.stream.StreamSupport;
2224

2325
@RestControllerAdvice
@@ -28,14 +30,17 @@ public ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException(
2830
final MethodArgumentNotValidException exception,
2931
final HttpServletRequest request) {
3032

31-
String detail;
32-
try {
33-
detail = Objects.requireNonNull(exception.getFieldError()).getDefaultMessage();
34-
} catch (NullPointerException nullPointerException) {
35-
detail = "Unknown error - MethodArgumentNotValidException was thrown with no default message";
36-
}
33+
final Map<String, Collection<String>> errorsMap =
34+
exception.getBindingResult().getFieldErrors()
35+
.stream()
36+
.collect(Collectors.toMap(FieldError::getField,
37+
FieldError -> new ArrayList<>(Collections.singletonList(FieldError.getDefaultMessage())),
38+
(mainList, newList) -> {
39+
mainList.addAll(newList);
40+
return mainList;
41+
}));
3742

38-
final ErrorResponse response = new ErrorResponse(new BadRequestException(detail), request);
43+
final ErrorResponse response = new ValidationErrorResponse(new ValidationException(errorsMap), request);
3944
return new ResponseEntity<>(response, HttpStatus.BAD_REQUEST);
4045
}
4146

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public interface CategoryMapper {
2020
@Mapping(target = "lastUpdated", expression = "java(DateTimeUtility.getDateTimeAsString())")
2121
Category updateRequestToCategory(@MappingTarget Category category, CategoryRequest updateCategoryRequest);
2222

23-
@Mapping(target = "productIds",
23+
@Mapping(target = "products",
2424
expression = "java(category.getProducts().stream().map(product -> product.getId()).collect(Collectors.toList()) )")
2525
CategoryResponse convertCategoryEntityToCategoryResponse(Category category);
2626

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

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,16 @@
66
import org.mapstruct.Mapper;
77
import org.mapstruct.Mapping;
88
import org.mapstruct.MappingTarget;
9+
import java.util.List;
910

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

2016
Product updateRequestToProduct(@MappingTarget Product product, ProductRequest productRequest);
2117

22-
@Mapping(target = "categories",
23-
expression = "java(product.getCategories().stream().map(category -> category.getId()).collect(Collectors.toList()) )")
18+
@Mapping(target = "categories", expression = "java(List.of(product.getCategory().getId()))")
2419
ProductResponse convertProductEntityToProductResponse(Product product);
20+
2521
}

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

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@
99
import lombok.NoArgsConstructor;
1010
import lombok.Setter;
1111

12-
import javax.persistence.Entity;
12+
import javax.persistence.OneToMany;
1313
import javax.persistence.FetchType;
14+
import javax.persistence.Entity;
1415
import javax.persistence.GeneratedValue;
1516
import javax.persistence.GenerationType;
1617
import javax.persistence.Id;
17-
import javax.persistence.JoinColumn;
18-
import javax.persistence.JoinTable;
19-
import javax.persistence.ManyToMany;
2018
import javax.validation.constraints.NotBlank;
2119
import javax.validation.constraints.Pattern;
2220
import java.util.HashSet;
@@ -54,11 +52,7 @@ public class Category {
5452

5553
private boolean retired;
5654

57-
@ManyToMany(fetch = FetchType.EAGER)
58-
@JoinTable(
59-
name = "product_category",
60-
joinColumns = {@JoinColumn(name = "category_id")},
61-
inverseJoinColumns = {@JoinColumn(name = "product_id")})
55+
@OneToMany(mappedBy="category", fetch = FetchType.EAGER)
6256
@JsonIgnore
6357
private Set<Product> products = new HashSet<>();
6458

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@
88
import lombok.NoArgsConstructor;
99
import lombok.Setter;
1010

11-
import javax.persistence.CascadeType;
1211
import javax.persistence.Column;
12+
import javax.persistence.PreRemove;
13+
import javax.persistence.CascadeType;
14+
import javax.persistence.OneToMany;
15+
import javax.persistence.ManyToOne;
16+
import javax.persistence.JoinColumn;
17+
import javax.persistence.GenerationType;
1318
import javax.persistence.Entity;
14-
import javax.persistence.FetchType;
1519
import javax.persistence.GeneratedValue;
16-
import javax.persistence.GenerationType;
1720
import javax.persistence.Id;
18-
import javax.persistence.ManyToMany;
19-
import javax.persistence.OneToMany;
20-
import javax.persistence.PreRemove;
2121
import javax.persistence.Table;
2222
import java.math.BigDecimal;
2323
import java.math.RoundingMode;
@@ -47,8 +47,9 @@ public class Product {
4747

4848
private boolean retired;
4949

50-
@ManyToMany(fetch = FetchType.EAGER, mappedBy = "products")
51-
private Set<Category> categories = new HashSet<>();
50+
@ManyToOne
51+
@JoinColumn(name="category_id", nullable=false)
52+
private Category category;
5253

5354
@OneToMany(mappedBy = "product", cascade = CascadeType.ALL)
5455
private Set<LineItem> lineItems = new HashSet<>();
@@ -57,13 +58,12 @@ public Product(final String name, final String description, final BigDecimal pri
5758
this.name = name;
5859
this.description = description;
5960
this.price = price;
60-
this.categories = new HashSet<>();
6161
this.retired = isRetired;
6262
}
6363

6464
@PreRemove
6565
private void removeProductsFromCategories() {
66-
categories.forEach(category -> category.removeProduct(this));
66+
category.removeProduct(this);
6767
}
6868

6969
public BigDecimal getPrice() {

src/main/java/com/answerdigital/answerking/repository/CategoryRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.answerdigital.answerking.model.Category;
44
import org.springframework.data.repository.PagingAndSortingRepository;
55
import org.springframework.stereotype.Repository;
6-
76
import java.util.Set;
87

98
@Repository
@@ -13,4 +12,5 @@ public interface CategoryRepository extends PagingAndSortingRepository<Category,
1312
boolean existsByNameAndIdIsNot(String name, Long id);
1413

1514
Set<Category> findAll();
15+
1616
}

src/main/java/com/answerdigital/answerking/repository/ProductRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ public interface ProductRepository extends PagingAndSortingRepository<Product, L
1313
boolean existsByNameAndIdIsNot(String name, Long id);
1414

1515
List<Product> findAll();
16+
17+
List<Product> findProductsByCategoryId(Long categoryId);
18+
1619
}

src/main/java/com/answerdigital/answerking/response/CategoryResponse.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
import lombok.Builder;
66
import lombok.Data;
77
import lombok.NoArgsConstructor;
8-
9-
import javax.validation.constraints.NotBlank;
10-
import javax.validation.constraints.Pattern;
118
import java.util.List;
129

1310
@Data
@@ -16,27 +13,23 @@
1613
@AllArgsConstructor
1714
public class CategoryResponse {
1815

16+
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
1917
private Long id;
2018

21-
@NotBlank
22-
@Pattern(regexp = "^[a-zA-Z\s-]*",
23-
message = "Category name must only contain letters, spaces and dashes")
2419
private String name;
2520

26-
@NotBlank
27-
@Pattern(regexp = "^[a-zA-Z\s.,!?0-9-']*",
28-
message = "Category description can only contain letters, numbers, spaces and !?-.,' punctuation")
2921
private String description;
3022

3123
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
32-
private List<Long> productIds;
24+
private List<Long> products;
3325

3426
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
3527
private String createdOn;
3628

3729
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
3830
private String lastUpdated;
3931

32+
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
4033
private boolean retired;
4134

4235
}

0 commit comments

Comments
 (0)