Skip to content

Commit

Permalink
MARP-1094 Refactor product table (#227)
Browse files Browse the repository at this point in the history
  • Loading branch information
nntthuy-axonivy authored Nov 12, 2024
1 parent a72ed0f commit a0e4988
Show file tree
Hide file tree
Showing 33 changed files with 766 additions and 457 deletions.
23 changes: 23 additions & 0 deletions marketplace-migration/marketplace_script_SNAPSHOT.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
function moveProductFieldsAndCleanUp() {
const products = db.getCollection("Product").find({}, {
_id: 1,
installationCount: 1,
synchronizedInstallationCount: 1,
customOrder: 1
}).toArray();

if (products.length > 0) {
db.getCollection("ProductMarketplaceData").insertMany(products);
print("Fields successfully moved to ProductMarketplaceData.");
} else {
print("No fields to move.");
}

const result = db.getCollection("Product").updateMany(
{},
{ $unset: { installationCount: "", synchronizedInstallationCount: "", customOrder: "" } }
);
print(`Fields removed from ${result.modifiedCount} documents in the Product collection.`);
}

moveProductFieldsAndCleanUp();
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ public class EntityConstants {
public static final String IMAGE = "Image";
public static final String MAVEN_ARTIFACT_VERSION = "MavenArtifactVersion";
public static final String EXTERNAL_DOCUMENT_META = "ExternalDocumentMeta";
public static final String PRODUCT_MARKETPLACE_DATA = "ProductMarketplaceData";
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
public class MongoDBConstants {
public static final String ID = "_id";
public static final String PRODUCT_COLLECTION = "Product";
public static final String PRODUCT_MARKETPLACE_COLLECTION = "ProductMarketplaceData";
public static final String MARKETPLACE_DATA = "marketplaceData";
public static final String MARKETPLACE_DATA_CUSTOM_ORDER = "marketplaceData.customOrder";
public static final String INSTALLATION_COUNT = "InstallationCount";
public static final String SYNCHRONIZED_INSTALLATION_COUNT = "SynchronizedInstallationCount";
public static final String PRODUCT_ID = "productId";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ public class RequestMappingConstants {
public static final String VERSIONS_BY_ID = "/{id}/versions";
public static final String PRODUCT_BY_ID = "/product/{id}";
public static final String PRODUCT_RATING_BY_ID = "/product/{id}/rating";
public static final String INSTALLATION_COUNT_BY_ID = "/installationcount/{id}";
public static final String INSTALLATION_COUNT_BY_ID = "/installation-count/{id}";
public static final String PRODUCT_JSON_CONTENT_BY_PRODUCT_ID_AND_VERSION = "/{id}/{version}/json";
public static final String VERSIONS_IN_DESIGNER = "/{id}/designerversions";
public static final String DESIGNER_INSTALLATION_BY_ID = "/installation/{id}/designer";
public static final String CUSTOM_SORT = "custom-sort";
public static final String LATEST_ARTIFACT_DOWNLOAD_URL_BY_ID = "/{id}/artifact";
public static final String EXTERNAL_DOCUMENT = API + "/externaldocument";
public static final String PRODUCT_MARKETPLACE_DATA = API + "/product-marketplace-data";
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import com.axonivy.market.github.service.GHAxonIvyMarketRepoService;
import com.axonivy.market.github.service.GitHubService;
import com.axonivy.market.model.Message;
import com.axonivy.market.model.ProductCustomSortRequest;
import com.axonivy.market.model.ProductModel;
import com.axonivy.market.service.MetadataService;
import com.axonivy.market.service.ProductService;
Expand All @@ -17,7 +16,6 @@
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
Expand All @@ -33,9 +31,7 @@
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
Expand Down Expand Up @@ -177,20 +173,6 @@ public ResponseEntity<Message> syncOneProduct(
return new ResponseEntity<>(message, HttpStatus.OK);
}

@PostMapping(CUSTOM_SORT)
@Operation(hidden = true)
public ResponseEntity<Message> createCustomSortProducts(
@RequestHeader(value = AUTHORIZATION) String authorizationHeader,
@RequestBody @Valid ProductCustomSortRequest productCustomSortRequest) {
String token = AuthorizationUtils.getBearerToken(authorizationHeader);
gitHubService.validateUserInOrganizationAndTeam(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME,
GitHubConstants.AXONIVY_MARKET_TEAM_NAME);
productService.addCustomSortProduct(productCustomSortRequest);
var message = new Message(ErrorCode.SUCCESSFUL.getCode(), ErrorCode.SUCCESSFUL.getHelpText(),
"Custom product sort order added successfully");
return new ResponseEntity<>(message, HttpStatus.OK);
}

@SuppressWarnings("unchecked")
private ResponseEntity<PagedModel<ProductModel>> generateEmptyPagedModel() {
var emptyPagedModel = (PagedModel<ProductModel>) pagedResourcesAssembler.toEmptyModel(Page.empty(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
Expand Down Expand Up @@ -73,18 +72,6 @@ public ResponseEntity<ProductDetailModel> findBestMatchProductDetailsByVersion(
HttpStatus.OK);
}

@PutMapping(INSTALLATION_COUNT_BY_ID)
@Operation(summary = "Update installation count of product",
description = "By default, increase installation count when click download product files by users")
public ResponseEntity<Integer> syncInstallationCount(
@PathVariable(ID) @Parameter(description = "Product id (from meta.json)", example = "approval-decision-utils",
in = ParameterIn.PATH) String productId,
@RequestParam(name = DESIGNER_VERSION, required = false) @Parameter(in = ParameterIn.QUERY,
example = "v10.0.20") String designerVersion) {
int result = productService.updateInstallationCountForProduct(productId, designerVersion);
return new ResponseEntity<>(result, HttpStatus.OK);
}

@GetMapping(BY_ID)
@Operation(summary = "get product detail by ID", description = "Return product detail by product id (from meta.json)")
public ResponseEntity<ProductDetailModel> findProductDetails(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.axonivy.market.controller;

import com.axonivy.market.constants.GitHubConstants;
import com.axonivy.market.enums.ErrorCode;
import com.axonivy.market.github.service.GitHubService;
import com.axonivy.market.model.Message;
import com.axonivy.market.model.ProductCustomSortRequest;
import com.axonivy.market.service.ProductMarketplaceDataService;
import com.axonivy.market.util.AuthorizationUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import static com.axonivy.market.constants.RequestMappingConstants.*;
import static com.axonivy.market.constants.RequestParamConstants.DESIGNER_VERSION;
import static com.axonivy.market.constants.RequestParamConstants.ID;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;

@RestController
@RequestMapping(PRODUCT_MARKETPLACE_DATA)
@Tag(name = "Product Marketplace Data Controller", description = "API collection to get product marketplace data")
@AllArgsConstructor
public class ProductMarketplaceDataController {
private final GitHubService gitHubService;
private final ProductMarketplaceDataService productMarketplaceDataService;

@PostMapping(CUSTOM_SORT)
@Operation(hidden = true)
public ResponseEntity<Message> createCustomSortProducts(
@RequestHeader(value = AUTHORIZATION) String authorizationHeader,
@RequestBody @Valid ProductCustomSortRequest productCustomSortRequest) {
String token = AuthorizationUtils.getBearerToken(authorizationHeader);
gitHubService.validateUserInOrganizationAndTeam(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME,
GitHubConstants.AXONIVY_MARKET_TEAM_NAME);
productMarketplaceDataService.addCustomSortProduct(productCustomSortRequest);
var message = new Message(ErrorCode.SUCCESSFUL.getCode(), ErrorCode.SUCCESSFUL.getHelpText(),
"Custom product sort order added successfully");
return new ResponseEntity<>(message, HttpStatus.OK);
}

@PutMapping(INSTALLATION_COUNT_BY_ID)
@Operation(summary = "Update installation count of product",
description = "By default, increase installation count when click download product files by users")
public ResponseEntity<Integer> syncInstallationCount(
@PathVariable(ID) @Parameter(description = "Product id (from meta.json)", example = "approval-decision-utils",
in = ParameterIn.PATH) String productId,
@RequestParam(name = DESIGNER_VERSION, required = false) @Parameter(in = ParameterIn.QUERY,
example = "v10.0.20") String designerVersion) {
int result = productMarketplaceDataService.updateInstallationCountForProduct(productId, designerVersion);
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,22 @@ public class Product implements Serializable {
private String compatibility;
private Boolean validate;
private Boolean contactUs;
@Transient
private int installationCount;
private Date newestPublishedDate;
private String newestReleaseVersion;
@Transient
private ProductModuleContent productModuleContent;
private List<Artifact> artifacts;
/**
* @deprecated
*/
@Deprecated(forRemoval = true, since = "1.6.0")
private Boolean synchronizedInstallationCount;
/**
* @deprecated
*/
@Deprecated(forRemoval = true, since = "1.6.0")
private Integer customOrder;
private List<String> releasedVersions;
@Transient
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.axonivy.market.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.io.Serial;
import java.io.Serializable;

import static com.axonivy.market.constants.EntityConstants.PRODUCT_MARKETPLACE_DATA;

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Document(PRODUCT_MARKETPLACE_DATA)
public class ProductMarketplaceData implements Serializable {
@Serial
private static final long serialVersionUID = -8770801879877277456L;
@Id
private String id;
private int installationCount;
private Boolean synchronizedInstallationCount;
private Integer customOrder;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
@Getter
@AllArgsConstructor
public enum SortOption {
POPULARITY("popularity", "installationCount", Sort.Direction.DESC),
POPULARITY("popularity", "marketplaceData.installationCount", Sort.Direction.DESC),
ALPHABETICALLY("alphabetically", "names", Sort.Direction.ASC),
RECENT("recent", "newestPublishedDate", Sort.Direction.DESC),
STANDARD("standard", "customOrder", Sort.Direction.DESC),
STANDARD("standard", "marketplaceData.customOrder", Sort.Direction.DESC),
ID("id", "_id", Sort.Direction.ASC);

private final String option;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,6 @@ public static Product mappingByMetaJSONFile(Product product, GHContent ghContent

public static void transferComputedPersistedDataToProduct(Product persisted, Product product) {
product.setMarketDirectory(persisted.getMarketDirectory());
product.setCustomOrder(persisted.getCustomOrder());
product.setInstallationCount(persisted.getInstallationCount());
product.setSynchronizedInstallationCount(persisted.getSynchronizedInstallationCount());
}

private static Map<String, String> mappingMultilingualismValueByMetaJSONFile(List<DisplayValue> list) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.axonivy.market.repository;

public interface CustomProductMarketplaceDataRepository {

int updateInitialCount(String productId, int initialCount);

int increaseInstallationCount(String productId);

void increaseInstallationCountForProductByDesignerVersion(String productId, String designerVersion);

void checkAndInitProductMarketplaceDataIfNotExist(String productId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,10 @@
public interface CustomProductRepository {
Product getProductByIdAndVersion(String id, String version);

Product getProductWithModuleContent(String id);

Product findProductById(String id);

List<String> getReleasedVersionsById(String id);

int updateInitialCount(String productId, int initialCount);

int increaseInstallationCount(String productId);

void increaseInstallationCountForProductByDesignerVersion(String productId, String designerVersion);

List<Product> getAllProductsWithIdAndReleaseTagAndArtifact();

Page<Product> searchByCriteria(ProductSearchCriteria criteria, Pageable pageable);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.axonivy.market.repository;

import com.axonivy.market.entity.ProductMarketplaceData;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductMarketplaceDataRepository extends MongoRepository<ProductMarketplaceData, String>, CustomProductMarketplaceDataRepository {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package com.axonivy.market.repository.impl;

import com.axonivy.market.constants.MongoDBConstants;
import com.axonivy.market.entity.ProductDesignerInstallation;
import com.axonivy.market.entity.ProductMarketplaceData;
import com.axonivy.market.repository.CustomProductMarketplaceDataRepository;
import com.axonivy.market.repository.CustomRepository;
import lombok.AllArgsConstructor;
import lombok.Builder;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

@Builder
@AllArgsConstructor
public class CustomProductMarketplaceDataRepositoryImpl extends CustomRepository implements CustomProductMarketplaceDataRepository {

final MongoTemplate mongoTemplate;

@Override
public int updateInitialCount(String productId, int initialCount) {
Update update = new Update().inc(MongoDBConstants.INSTALLATION_COUNT, initialCount).set(
MongoDBConstants.SYNCHRONIZED_INSTALLATION_COUNT, true);
ProductMarketplaceData updatedProductMarketplaceData = mongoTemplate.findAndModify(createQueryById(productId),
update, FindAndModifyOptions.options().returnNew(true), ProductMarketplaceData.class);
return updatedProductMarketplaceData != null ? updatedProductMarketplaceData.getInstallationCount() : 0;
}

@Override
public int increaseInstallationCount(String productId) {
Update update = new Update().inc(MongoDBConstants.INSTALLATION_COUNT, 1);
ProductMarketplaceData updatedProduct = mongoTemplate.findAndModify(createQueryById(productId), update,
FindAndModifyOptions.options().returnNew(true), ProductMarketplaceData.class);
return updatedProduct != null ? updatedProduct.getInstallationCount() : 0;
}

@Override
public void increaseInstallationCountForProductByDesignerVersion(String productId, String designerVersion) {
Update update = new Update().inc(MongoDBConstants.INSTALLATION_COUNT, 1);
mongoTemplate.upsert(createQueryByProductIdAndDesignerVersion(productId, designerVersion),
update, ProductDesignerInstallation.class);
}

@Override
public void checkAndInitProductMarketplaceDataIfNotExist(String productId) {
Query query = new Query(Criteria.where(MongoDBConstants.ID).is(productId));
if (!mongoTemplate.exists(query, ProductMarketplaceData.class)) {
ProductMarketplaceData productMarketplaceData = new ProductMarketplaceData();
productMarketplaceData.setId(productId);
mongoTemplate.insert(productMarketplaceData);
}
}

private Query createQueryByProductIdAndDesignerVersion(String productId, String designerVersion) {
return new Query(Criteria.where(MongoDBConstants.PRODUCT_ID).is(productId)
.andOperator(Criteria.where(MongoDBConstants.DESIGNER_VERSION).is(designerVersion)));
}
}
Loading

0 comments on commit a0e4988

Please sign in to comment.