Skip to content

Commit

Permalink
#888 fix product update validation with slug sku gtin (#908)
Browse files Browse the repository at this point in the history
* #888 Fix product update validation with slug, sku, and gtin

* #888 Reformat

* #888 Fix checkstyle

* #888 Refactor & fix gtin validation to only check when not empty

* #888 Refactor validation to use findAllById for retrieving variant details

* #888 Refactor interface naming convention
  • Loading branch information
mochacr0 authored Aug 20, 2024
1 parent 6dd9958 commit 7206d1a
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public interface ProductRepository extends JpaRepository<Product, Long> {

Optional<Product> findBySlugAndIsPublishedTrue(String slug);

boolean existsBySlugAndIsPublishedTrue(String slug);
Optional<Product> findByGtinAndIsPublishedTrue(String gtin);

Optional<Product> findBySkuAndIsPublishedTrue(String sku);

@Query(value = "SELECT p FROM Product p WHERE LOWER(p.name) LIKE %:productName% "
+ "AND (p.brand.name IN :brandName OR (:brandName is null OR :brandName = '')) "
Expand All @@ -37,10 +39,6 @@ Page<Product> getProductsWithFilter(@Param("productName") String productName,

List<Product> findAllByIdIn(List<Long> productIds);

boolean existsByGtinAndIsPublishedTrue(String gtin);

boolean existsBySkuAndIsPublishedTrue(String sku);

@Query(value = "FROM Product p WHERE p.isFeatured = TRUE "
+ "AND p.isVisibleIndividually = TRUE "
+ "AND p.isPublished = TRUE ORDER BY p.lastModifiedOn DESC")
Expand Down
104 changes: 54 additions & 50 deletions product/src/main/java/com/yas/product/service/ProductService.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,15 @@
import com.yas.product.viewmodel.product.ProductListGetVm;
import com.yas.product.viewmodel.product.ProductListVm;
import com.yas.product.viewmodel.product.ProductPostVm;
import com.yas.product.viewmodel.product.ProductProperties;
import com.yas.product.viewmodel.product.ProductPutVm;
import com.yas.product.viewmodel.product.ProductQuantityPostVm;
import com.yas.product.viewmodel.product.ProductQuantityPutVm;
import com.yas.product.viewmodel.product.ProductSaveVm;
import com.yas.product.viewmodel.product.ProductSlugGetVm;
import com.yas.product.viewmodel.product.ProductThumbnailGetVm;
import com.yas.product.viewmodel.product.ProductThumbnailVm;
import com.yas.product.viewmodel.product.ProductVariationGetVm;
import com.yas.product.viewmodel.product.ProductVariationPostVm;
import com.yas.product.viewmodel.product.ProductVariationPutVm;
import com.yas.product.viewmodel.product.ProductsGetVm;
import com.yas.product.viewmodel.productattribute.ProductAttributeGroupGetVm;
Expand Down Expand Up @@ -170,59 +171,73 @@ public ProductListGetVm getProductsWithFilter(int pageNo, int pageSize, String p
);
}

private boolean isProductWithSlugAvailable(String slug) {
return productRepository.existsBySlugAndIsPublishedTrue(slug);
}

private boolean isProductWithSkuAvailable(String sku) {
return productRepository.existsBySkuAndIsPublishedTrue(sku);
}

private boolean isProductWithGtinAvailable(String gtin) {
return productRepository.existsByGtinAndIsPublishedTrue(gtin);
private void checkPropertyExists(String propertyValue, Product existingProduct, Function<String,
Optional<Product>> finder, String errorCode) {
finder.apply(propertyValue).ifPresent(foundProduct -> {
if (existingProduct == null || !foundProduct.getId().equals(existingProduct.getId())) {
throw new DuplicatedException(errorCode, propertyValue);
}
});
}

private void validateIfProductWithSkuOrGtinOrSlugExist(String slug,
String gtin,
String sku) {
if (isProductWithSlugAvailable(slug)) {
throw new DuplicatedException(Constants.ErrorCode.SLUG_ALREADY_EXISTED_OR_DUPLICATED, slug);
}

if (isProductWithGtinAvailable(gtin)) {
throw new DuplicatedException(Constants.ErrorCode.GTIN_ALREADY_EXISTED_OR_DUPLICATED, gtin);
}

if (isProductWithSkuAvailable(sku)) {
throw new DuplicatedException(Constants.ErrorCode.SKU_ALREADY_EXISTED_OR_DUPLICATED, sku);
private void validateExistingProductProperties(ProductProperties productProperties, Product existingProduct) {
checkPropertyExists(productProperties.slug(), existingProduct,
productRepository::findBySlugAndIsPublishedTrue, Constants.ErrorCode.SLUG_ALREADY_EXISTED_OR_DUPLICATED);
// only check gtin when it's not empty
if (StringUtils.isNotEmpty(productProperties.gtin())) {
checkPropertyExists(productProperties.gtin(), existingProduct,
productRepository::findByGtinAndIsPublishedTrue,
Constants.ErrorCode.GTIN_ALREADY_EXISTED_OR_DUPLICATED);
}
checkPropertyExists(productProperties.sku(), existingProduct,
productRepository::findBySkuAndIsPublishedTrue, Constants.ErrorCode.SKU_ALREADY_EXISTED_OR_DUPLICATED);
}

private void validateProductVariationDuplicates(List<ProductVariationPostVm> variations) {
Set<String> seenSlugs = new HashSet<>();
Set<String> seenSkus = new HashSet<>();
Set<String> seenGtins = new HashSet<>();
for (ProductVariationPostVm variation : variations) {
private void validateProductVariationDuplicates(ProductSaveVm productSaveVm) {
Set<String> seenSlugs = new HashSet<>(Collections.singletonList(productSaveVm.slug()));
Set<String> seenSkus = new HashSet<>(Collections.singletonList(productSaveVm.sku()));
Set<String> seenGtins = new HashSet<>(Collections.singletonList(productSaveVm.gtin()));
for (ProductProperties variation : productSaveVm.variations()) {
if (!seenSlugs.add(variation.slug())) {
throw new DuplicatedException(Constants.ErrorCode.SLUG_ALREADY_EXISTED_OR_DUPLICATED, variation.slug());
}

if (!seenGtins.add(variation.gtin())) {
// only check gtin when it's not empty
if (StringUtils.isNotEmpty(variation.gtin()) && !seenGtins.add(variation.gtin())) {
throw new DuplicatedException(Constants.ErrorCode.GTIN_ALREADY_EXISTED_OR_DUPLICATED, variation.gtin());
}

if (!seenSkus.add(variation.sku())) {
throw new DuplicatedException(Constants.ErrorCode.SKU_ALREADY_EXISTED_OR_DUPLICATED, variation.sku());
}
}
}

private void validateProductVm(ProductSaveVm productSaveVm, Product existingProduct) {
// validate the properties of the main product
validateExistingProductProperties(productSaveVm, existingProduct);

// validate whether the product and its variations contain duplicate properties
validateProductVariationDuplicates(productSaveVm);

// validate whether the variations contain properties that already exist
List<Long> variationIds = productSaveVm.variations().stream()
.map(ProductProperties::id)
.filter(Objects::nonNull).toList();

Map<Long, Product> existingVariationsMap = productRepository.findAllById(variationIds).stream()
.collect(Collectors.toMap(Product::getId, Function.identity()));

for (ProductProperties variation : productSaveVm.variations()) {
Product existingVariation = existingVariationsMap.get(variation.id());
validateExistingProductProperties(variation, existingVariation);
}
}

private void validateProductVm(ProductSaveVm productSaveVm) {
validateProductVm(productSaveVm, null);
}

public ProductGetDetailVm createProduct(ProductPostVm productPostVm) {
validateIfProductWithSkuOrGtinOrSlugExist(
productPostVm.slug(),
productPostVm.gtin(),
productPostVm.sku()
);
validateProductVm(productPostVm);

Product mainProduct = Product.builder()
.name(productPostVm.name())
Expand Down Expand Up @@ -274,15 +289,8 @@ public ProductGetDetailVm createProduct(ProductPostVm productPostVm) {
if (CollectionUtils.isNotEmpty(productPostVm.variations())
&& CollectionUtils.isNotEmpty(productPostVm.productOptionValues())) {
List<ProductImage> allProductVariantImageList = new ArrayList<>();
validateProductVariationDuplicates(productPostVm.variations());
List<Product> productVariants = productPostVm.variations().stream()
.map(variation -> {
validateIfProductWithSkuOrGtinOrSlugExist(
variation.slug(),
variation.gtin(),
variation.sku()
);

Product productVariant = Product.builder()
.name(variation.name())
.thumbnailMediaId(variation.thumbnailMediaId())
Expand Down Expand Up @@ -338,6 +346,8 @@ public void updateProduct(long productId, ProductPutVm productPutVm) {
Product product = productRepository.findById(productId).orElseThrow(()
-> new NotFoundException(Constants.ErrorCode.PRODUCT_NOT_FOUND, productId));

validateProductVm(productPutVm, product);

setProductBrand(productPutVm.brandId(), product);

List<ProductCategory> productCategoryList = setProductCategories(productPutVm.categoryIds(), product);
Expand Down Expand Up @@ -461,12 +471,6 @@ public void updateProductFromVm(ProductPutVm productPutVm, Product product) {
}

private Product convertProductVariant(Product product, ProductVariationPutVm variation) {
validateIfProductWithSkuOrGtinOrSlugExist(
variation.slug(),
variation.gtin(),
variation.sku()
);

return Product.builder()
.name(variation.name())
.thumbnailMediaId(variation.thumbnailMediaId())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ public record ProductPostVm(
List<ProductVariationPostVm> variations,
List<ProductOptionValuePostVm> productOptionValues,
List<Long> relatedProductIds,
Long taxClassId) {
Long taxClassId) implements ProductSaveVm {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.yas.product.viewmodel.product;

public interface ProductProperties {
Long id();

String slug();

String sku();

String gtin();
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,6 @@ public record ProductPutVm(
List<ProductVariationPutVm> variations,
List<ProductOptionValuePutVm> productOptionValues,
List<Long> relatedProductIds,
Long taxClassId) {
Long taxClassId) implements ProductSaveVm {
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.yas.product.viewmodel.product;

import java.util.List;

public interface ProductSaveVm extends ProductProperties {
List<? extends ProductProperties> variations();

@Override
default Long id() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,10 @@ public record ProductVariationPostVm(
Double price,
Long thumbnailMediaId,
List<Long> productImageIds
) {
) implements ProductProperties {

@Override
public Long id() {
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ public record ProductVariationPutVm(
Double price,
Long thumbnailMediaId,
List<Long> productImageIds
) {
) implements ProductProperties {
}

0 comments on commit 7206d1a

Please sign in to comment.