Skip to content

Commit

Permalink
MARP-612 Storing number of downloads version (#128)
Browse files Browse the repository at this point in the history
  • Loading branch information
phhung-axonivy authored Sep 10, 2024
1 parent a1e6514 commit 38f7e68
Show file tree
Hide file tree
Showing 25 changed files with 360 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
public class EntityConstants {
public static final String USER = "User";
public static final String PRODUCT = "Product";
public static final String PRODUCT_DESIGNER_INSTALLATION = "ProductDesignerInstallation";
public static final String MAVEN_ARTIFACT_VERSION = "MavenArtifactVersion";
public static final String GH_REPO_META = "GitHubRepoMeta";
public static final String FEEDBACK = "Feedback";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ private MongoDBConstants() {
public static final String PRODUCT_COLLECTION ="Product";
public static final String INSTALLATION_COUNT = "InstallationCount";
public static final String SYNCHRONIZED_INSTALLATION_COUNT ="SynchronizedInstallationCount";

public static final String PRODUCT_ID = "productId";
public static final String DESIGNER_VERSION = "designerVersion";
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public class RequestMappingConstants {
public static final String SYNC = "sync";
public static final String PRODUCT = API + "/product";
public static final String PRODUCT_DETAILS = API + "/product-details";
public static final String PRODUCT_DESIGNER_INSTALLATION = API + "/product-designer-installation";
public static final String FEEDBACK = API + "/feedback";
public static final String SWAGGER_URL = "/swagger-ui/index.html";
public static final String GIT_HUB_LOGIN = "/github/login";
Expand All @@ -23,5 +24,7 @@ public class RequestMappingConstants {
public static final String INSTALLATION_COUNT_BY_ID = "/installationcount/{id}";
public static final String PRODUCT_JSON_CONTENT_BY_PRODUCT_ID_AND_VERSION = "/productjsoncontent/{productId}/{version}";
public static final String VERSIONS_IN_DESIGNER = "/{id}/designerversions";
public static final String DESIGNER_INSTALLATION_BY_PRODUCT_ID_AND_DESIGNER_VERSION = "/installation/{productId}/designer/{designerVersion}";
public static final String DESIGNER_INSTALLATION_BY_PRODUCT_ID = "/installation/{productId}/designer";
public static final String CUSTOM_SORT = "custom-sort";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.axonivy.market.controller;

import com.axonivy.market.model.DesignerInstallation;
import com.axonivy.market.service.ProductDesignerInstallationService;
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 org.springframework.http.HttpStatus;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

import static com.axonivy.market.constants.RequestMappingConstants.DESIGNER_INSTALLATION_BY_PRODUCT_ID;
import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT_DESIGNER_INSTALLATION;
import static com.axonivy.market.constants.RequestParamConstants.PRODUCT_ID;

@RestController
@RequestMapping(PRODUCT_DESIGNER_INSTALLATION)
@Tag(name = "Product Designer Installation Controllers", description = "API collection to get designer installation count.")
public class ProductDesignerInstallationController {
private final ProductDesignerInstallationService productDesignerInstallationService;

public ProductDesignerInstallationController(ProductDesignerInstallationService productDesignerInstallationService) {
this.productDesignerInstallationService = productDesignerInstallationService;
}

@GetMapping(DESIGNER_INSTALLATION_BY_PRODUCT_ID)
@Operation(summary = "Get designer installation count by product id.", description = "get designer installation count by product id")
public ResponseEntity<List<DesignerInstallation>> getProductDesignerInstallationByProductId(@PathVariable(PRODUCT_ID) @Parameter(description = "Product id (from meta.json)", example = "adobe-acrobat-connector", in = ParameterIn.PATH) String productId) {
List<DesignerInstallation> models = productDesignerInstallationService.findByProductId(productId);
return new ResponseEntity<>(models, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package com.axonivy.market.controller;

import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT_JSON_CONTENT_BY_PRODUCT_ID_AND_VERSION;
import static com.axonivy.market.constants.RequestMappingConstants.VERSIONS_IN_DESIGNER;
import static com.axonivy.market.constants.RequestParamConstants.DESIGNER_VERSION;
import static com.axonivy.market.constants.RequestParamConstants.ID;
import static com.axonivy.market.constants.RequestParamConstants.PRODUCT_ID;
import static com.axonivy.market.constants.RequestParamConstants.SHOW_DEV_VERSION;
import static com.axonivy.market.constants.RequestParamConstants.VERSION;
import static com.axonivy.market.constants.RequestMappingConstants.BEST_MATCH_BY_ID_AND_VERSION;
import static com.axonivy.market.constants.RequestMappingConstants.BY_ID;
import static com.axonivy.market.constants.RequestMappingConstants.BY_ID_AND_VERSION;
import static com.axonivy.market.constants.RequestMappingConstants.INSTALLATION_COUNT_BY_ID;
import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT_DETAILS;
import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT_JSON_CONTENT_BY_PRODUCT_ID_AND_VERSION;
import static com.axonivy.market.constants.RequestMappingConstants.VERSIONS_BY_ID;
import static com.axonivy.market.constants.RequestMappingConstants.BEST_MATCH_BY_ID_AND_VERSION;
import static com.axonivy.market.constants.RequestMappingConstants.VERSIONS_IN_DESIGNER;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -46,7 +46,8 @@ public class ProductDetailsController {
private final ProductService productService;
private final ProductDetailModelAssembler detailModelAssembler;

public ProductDetailsController(VersionService versionService, ProductService productService, ProductDetailModelAssembler detailModelAssembler) {
public ProductDetailsController(VersionService versionService, ProductService productService,
ProductDetailModelAssembler detailModelAssembler) {
this.versionService = versionService;
this.productService = productService;
this.detailModelAssembler = detailModelAssembler;
Expand Down Expand Up @@ -74,8 +75,9 @@ public ResponseEntity<ProductDetailModel> findBestMatchProductDetailsByVersion(
@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) {
int result = productService.updateInstallationCountForProduct(productId);
@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);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.axonivy.market.entity;

import lombok.*;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;

import java.io.Serial;
import java.io.Serializable;
import java.util.Date;

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

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Document(PRODUCT_DESIGNER_INSTALLATION)
public class ProductDesignerInstallation implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Id
private String id;
private String productId;
private String designerVersion;
private int installationCount;

@Override
public int hashCode() {
return new HashCodeBuilder().append(productId).hashCode();
}

@Override
public boolean equals(Object obj) {
if (obj == null || this.getClass() != obj.getClass()) {
return false;
}
return new EqualsBuilder().append(productId, ((ProductDesignerInstallation) obj).getProductId()).isEquals();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.axonivy.market.model;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class DesignerInstallation {
@Schema(description = "Ivy designer version", example = "11.4.0")
private String designerVersion;
private int numberOfDownloads;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ public interface CustomProductRepository {
int updateInitialCount(String productId, int initialCount);

int increaseInstallationCount(String productId);

void increaseInstallationCountForProductByDesignerVersion(String productId, String designerVersion);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.axonivy.market.repository;

import com.axonivy.market.entity.ProductDesignerInstallation;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface ProductDesignerInstallationRepository extends MongoRepository<ProductDesignerInstallation, String> {

List<ProductDesignerInstallation> findByProductId(String productId, Sort sort);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.axonivy.market.constants.MongoDBConstants;
import com.axonivy.market.entity.Product;
import com.axonivy.market.entity.ProductModuleContent;
import com.axonivy.market.entity.ProductDesignerInstallation;
import com.axonivy.market.repository.CustomProductRepository;
import com.axonivy.market.repository.ProductModuleContentRepository;
import lombok.Builder;
Expand Down Expand Up @@ -92,4 +93,16 @@ public int increaseInstallationCount(String productId) {
private Query createQueryById(String id) {
return new Query(Criteria.where(MongoDBConstants.ID).is(id));
}

@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);
}

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)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public ProductSearchRepositoryImpl(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}


@Override
public Page<Product> searchByCriteria(ProductSearchCriteria searchCriteria, Pageable pageable) {
return getResultAsPageable(pageable, buildCriteriaSearch(searchCriteria));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.axonivy.market.service;

import com.axonivy.market.model.DesignerInstallation;

import java.util.List;

public interface ProductDesignerInstallationService {
List<DesignerInstallation> findByProductId(String productId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public interface ProductService {

boolean syncLatestDataFromMarketRepo();

int updateInstallationCountForProduct(String key);
int updateInstallationCountForProduct(String key, String designerVersion);

Product fetchProductDetail(String id);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.axonivy.market.service.impl;

import com.axonivy.market.constants.MongoDBConstants;
import com.axonivy.market.entity.ProductDesignerInstallation;
import com.axonivy.market.model.DesignerInstallation;
import com.axonivy.market.repository.ProductDesignerInstallationRepository;
import com.axonivy.market.service.ProductDesignerInstallationService;
import lombok.extern.log4j.Log4j2;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Log4j2
@Service
public class ProductDesignerInstallationServiceImpl implements ProductDesignerInstallationService {
private final ProductDesignerInstallationRepository productDesignerInstallationRepository;

public ProductDesignerInstallationServiceImpl(ProductDesignerInstallationRepository productDesignerInstallationRepository) {
this.productDesignerInstallationRepository = productDesignerInstallationRepository;
}

@Override
public List<DesignerInstallation> findByProductId(String productId) {
List<DesignerInstallation> designerInstallations = new ArrayList<>();
List<ProductDesignerInstallation> productDesignerInstallations =
productDesignerInstallationRepository.findByProductId(productId, Sort.by(Sort.Direction.DESC, MongoDBConstants.DESIGNER_VERSION));
for (ProductDesignerInstallation productDesignerInstallation : productDesignerInstallations) {
DesignerInstallation designerInstallation = new DesignerInstallation(productDesignerInstallation.getDesignerVersion(), productDesignerInstallation.getInstallationCount());
designerInstallations.add(designerInstallation);
}
return designerInstallations;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,17 @@ public boolean syncLatestDataFromMarketRepo() {
}

@Override
public int updateInstallationCountForProduct(String key) {
public int updateInstallationCountForProduct(String key, String designerVersion) {
Product product= productRepository.getProductById(key);
if (Objects.isNull(product)){
return 0;
}

log.info("Increase installation count for product {} By Designer Version {}", key, designerVersion);
if (StringUtils.isNotBlank(designerVersion)) {
productRepository.increaseInstallationCountForProductByDesignerVersion(key, designerVersion);
}

log.info("updating installation count for product {}", key);
if (BooleanUtils.isTrue(product.getSynchronizedInstallationCount())) {
return productRepository.increaseInstallationCount(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.axonivy.market.entity.ProductDesignerInstallation;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
Expand Down Expand Up @@ -41,4 +43,20 @@ protected Page<Product> createPageProductsMock() {
mockProducts.add(mockProduct);
return new PageImpl<>(mockProducts);
}

protected List<ProductDesignerInstallation> createProductDesignerInstallationsMock() {
var mockProductDesignerInstallations = new ArrayList<ProductDesignerInstallation>();
ProductDesignerInstallation mockProductDesignerInstallation = new ProductDesignerInstallation();
mockProductDesignerInstallation.setProductId(SAMPLE_PRODUCT_ID);
mockProductDesignerInstallation.setDesignerVersion("10.0.22");
mockProductDesignerInstallation.setInstallationCount(50);
mockProductDesignerInstallations.add(mockProductDesignerInstallation);

mockProductDesignerInstallation = new ProductDesignerInstallation();
mockProductDesignerInstallation.setProductId(SAMPLE_PRODUCT_ID);
mockProductDesignerInstallation.setDesignerVersion("11.4.0");
mockProductDesignerInstallation.setInstallationCount(30);
mockProductDesignerInstallations.add(mockProductDesignerInstallation);
return mockProductDesignerInstallations;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.axonivy.market.controller;

import com.axonivy.market.model.DesignerInstallation;
import com.axonivy.market.service.ProductDesignerInstallationService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.util.List;
import java.util.Objects;

@ExtendWith(MockitoExtension.class)
class ProductDesignerInstallationControllerTest {
public static final String DESIGNER_VERSION = "11.4.0";

@Mock
ProductDesignerInstallationService productDesignerInstallationService;

@InjectMocks
private ProductDesignerInstallationController productDesignerInstallationController;

@Test
void testGetProductDesignerInstallationByProductId() {
List<DesignerInstallation> models = List.of(new DesignerInstallation(DESIGNER_VERSION, 5));
Mockito.when(productDesignerInstallationService.findByProductId(Mockito.anyString())).thenReturn(models);
ResponseEntity<List<DesignerInstallation>> result = productDesignerInstallationController.getProductDesignerInstallationByProductId("portal");
Assertions.assertEquals(HttpStatus.OK, result.getStatusCode());
Assertions.assertEquals(1, Objects.requireNonNull(result.getBody()).size());
Assertions.assertEquals(DESIGNER_VERSION, result.getBody().get(0).getDesignerVersion());
Assertions.assertEquals(5, result.getBody().get(0).getNumberOfDownloads());
Assertions.assertEquals(models, result.getBody());
}
}
Loading

0 comments on commit 38f7e68

Please sign in to comment.