diff --git a/marketplace-service/src/main/java/com/axonivy/market/constants/EntityConstants.java b/marketplace-service/src/main/java/com/axonivy/market/constants/EntityConstants.java
index fc813d23a..157aa49d4 100644
--- a/marketplace-service/src/main/java/com/axonivy/market/constants/EntityConstants.java
+++ b/marketplace-service/src/main/java/com/axonivy/market/constants/EntityConstants.java
@@ -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";
diff --git a/marketplace-service/src/main/java/com/axonivy/market/constants/MongoDBConstants.java b/marketplace-service/src/main/java/com/axonivy/market/constants/MongoDBConstants.java
index df9c3c1e0..6a41ad547 100644
--- a/marketplace-service/src/main/java/com/axonivy/market/constants/MongoDBConstants.java
+++ b/marketplace-service/src/main/java/com/axonivy/market/constants/MongoDBConstants.java
@@ -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";
}
diff --git a/marketplace-service/src/main/java/com/axonivy/market/constants/RequestMappingConstants.java b/marketplace-service/src/main/java/com/axonivy/market/constants/RequestMappingConstants.java
index 6890a6424..abfc30ba3 100644
--- a/marketplace-service/src/main/java/com/axonivy/market/constants/RequestMappingConstants.java
+++ b/marketplace-service/src/main/java/com/axonivy/market/constants/RequestMappingConstants.java
@@ -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";
@@ -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";
}
\ No newline at end of file
diff --git a/marketplace-service/src/main/java/com/axonivy/market/controller/ProductDesignerInstallationController.java b/marketplace-service/src/main/java/com/axonivy/market/controller/ProductDesignerInstallationController.java
new file mode 100644
index 000000000..645717df2
--- /dev/null
+++ b/marketplace-service/src/main/java/com/axonivy/market/controller/ProductDesignerInstallationController.java
@@ -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> getProductDesignerInstallationByProductId(@PathVariable(PRODUCT_ID) @Parameter(description = "Product id (from meta.json)", example = "adobe-acrobat-connector", in = ParameterIn.PATH) String productId) {
+ List models = productDesignerInstallationService.findByProductId(productId);
+ return new ResponseEntity<>(models, HttpStatus.OK);
+ }
+}
diff --git a/marketplace-service/src/main/java/com/axonivy/market/controller/ProductDetailsController.java b/marketplace-service/src/main/java/com/axonivy/market/controller/ProductDetailsController.java
index 7c4efc906..bdb4f4e84 100644
--- a/marketplace-service/src/main/java/com/axonivy/market/controller/ProductDetailsController.java
+++ b/marketplace-service/src/main/java/com/axonivy/market/controller/ProductDetailsController.java
@@ -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;
@@ -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;
@@ -74,8 +75,9 @@ public ResponseEntity 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 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);
}
diff --git a/marketplace-service/src/main/java/com/axonivy/market/entity/ProductDesignerInstallation.java b/marketplace-service/src/main/java/com/axonivy/market/entity/ProductDesignerInstallation.java
new file mode 100644
index 000000000..7c913c67d
--- /dev/null
+++ b/marketplace-service/src/main/java/com/axonivy/market/entity/ProductDesignerInstallation.java
@@ -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();
+ }
+}
diff --git a/marketplace-service/src/main/java/com/axonivy/market/model/DesignerInstallation.java b/marketplace-service/src/main/java/com/axonivy/market/model/DesignerInstallation.java
new file mode 100644
index 000000000..398ad26af
--- /dev/null
+++ b/marketplace-service/src/main/java/com/axonivy/market/model/DesignerInstallation.java
@@ -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;
+}
diff --git a/marketplace-service/src/main/java/com/axonivy/market/repository/CustomProductRepository.java b/marketplace-service/src/main/java/com/axonivy/market/repository/CustomProductRepository.java
index 3cd980656..3fc59e926 100644
--- a/marketplace-service/src/main/java/com/axonivy/market/repository/CustomProductRepository.java
+++ b/marketplace-service/src/main/java/com/axonivy/market/repository/CustomProductRepository.java
@@ -14,4 +14,6 @@ public interface CustomProductRepository {
int updateInitialCount(String productId, int initialCount);
int increaseInstallationCount(String productId);
+
+ void increaseInstallationCountForProductByDesignerVersion(String productId, String designerVersion);
}
diff --git a/marketplace-service/src/main/java/com/axonivy/market/repository/ProductDesignerInstallationRepository.java b/marketplace-service/src/main/java/com/axonivy/market/repository/ProductDesignerInstallationRepository.java
new file mode 100644
index 000000000..2b5789521
--- /dev/null
+++ b/marketplace-service/src/main/java/com/axonivy/market/repository/ProductDesignerInstallationRepository.java
@@ -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 {
+
+ List findByProductId(String productId, Sort sort);
+}
diff --git a/marketplace-service/src/main/java/com/axonivy/market/repository/impl/CustomProductRepositoryImpl.java b/marketplace-service/src/main/java/com/axonivy/market/repository/impl/CustomProductRepositoryImpl.java
index 34a538b15..efb1ef4be 100644
--- a/marketplace-service/src/main/java/com/axonivy/market/repository/impl/CustomProductRepositoryImpl.java
+++ b/marketplace-service/src/main/java/com/axonivy/market/repository/impl/CustomProductRepositoryImpl.java
@@ -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;
@@ -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)));
+ }
}
diff --git a/marketplace-service/src/main/java/com/axonivy/market/repository/impl/ProductSearchRepositoryImpl.java b/marketplace-service/src/main/java/com/axonivy/market/repository/impl/ProductSearchRepositoryImpl.java
index 538f8f447..68a86856f 100644
--- a/marketplace-service/src/main/java/com/axonivy/market/repository/impl/ProductSearchRepositoryImpl.java
+++ b/marketplace-service/src/main/java/com/axonivy/market/repository/impl/ProductSearchRepositoryImpl.java
@@ -34,7 +34,6 @@ public ProductSearchRepositoryImpl(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
}
-
@Override
public Page searchByCriteria(ProductSearchCriteria searchCriteria, Pageable pageable) {
return getResultAsPageable(pageable, buildCriteriaSearch(searchCriteria));
diff --git a/marketplace-service/src/main/java/com/axonivy/market/service/ProductDesignerInstallationService.java b/marketplace-service/src/main/java/com/axonivy/market/service/ProductDesignerInstallationService.java
new file mode 100644
index 000000000..046a7be7f
--- /dev/null
+++ b/marketplace-service/src/main/java/com/axonivy/market/service/ProductDesignerInstallationService.java
@@ -0,0 +1,9 @@
+package com.axonivy.market.service;
+
+import com.axonivy.market.model.DesignerInstallation;
+
+import java.util.List;
+
+public interface ProductDesignerInstallationService {
+ List findByProductId(String productId);
+}
diff --git a/marketplace-service/src/main/java/com/axonivy/market/service/ProductService.java b/marketplace-service/src/main/java/com/axonivy/market/service/ProductService.java
index f63495616..44c27771f 100644
--- a/marketplace-service/src/main/java/com/axonivy/market/service/ProductService.java
+++ b/marketplace-service/src/main/java/com/axonivy/market/service/ProductService.java
@@ -11,7 +11,7 @@ public interface ProductService {
boolean syncLatestDataFromMarketRepo();
- int updateInstallationCountForProduct(String key);
+ int updateInstallationCountForProduct(String key, String designerVersion);
Product fetchProductDetail(String id);
diff --git a/marketplace-service/src/main/java/com/axonivy/market/service/impl/ProductDesignerInstallationServiceImpl.java b/marketplace-service/src/main/java/com/axonivy/market/service/impl/ProductDesignerInstallationServiceImpl.java
new file mode 100644
index 000000000..c95241e08
--- /dev/null
+++ b/marketplace-service/src/main/java/com/axonivy/market/service/impl/ProductDesignerInstallationServiceImpl.java
@@ -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 findByProductId(String productId) {
+ List designerInstallations = new ArrayList<>();
+ List 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;
+ }
+}
diff --git a/marketplace-service/src/main/java/com/axonivy/market/service/impl/ProductServiceImpl.java b/marketplace-service/src/main/java/com/axonivy/market/service/impl/ProductServiceImpl.java
index dbeea37d2..a5c2f3e6b 100644
--- a/marketplace-service/src/main/java/com/axonivy/market/service/impl/ProductServiceImpl.java
+++ b/marketplace-service/src/main/java/com/axonivy/market/service/impl/ProductServiceImpl.java
@@ -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);
diff --git a/marketplace-service/src/test/java/com/axonivy/market/BaseSetup.java b/marketplace-service/src/test/java/com/axonivy/market/BaseSetup.java
index d53fddf43..35ca031e0 100644
--- a/marketplace-service/src/test/java/com/axonivy/market/BaseSetup.java
+++ b/marketplace-service/src/test/java/com/axonivy/market/BaseSetup.java
@@ -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;
@@ -41,4 +43,20 @@ protected Page createPageProductsMock() {
mockProducts.add(mockProduct);
return new PageImpl<>(mockProducts);
}
+
+ protected List createProductDesignerInstallationsMock() {
+ var mockProductDesignerInstallations = new ArrayList();
+ 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;
+ }
}
diff --git a/marketplace-service/src/test/java/com/axonivy/market/controller/ProductDesignerInstallationControllerTest.java b/marketplace-service/src/test/java/com/axonivy/market/controller/ProductDesignerInstallationControllerTest.java
new file mode 100644
index 000000000..b8fd3f553
--- /dev/null
+++ b/marketplace-service/src/test/java/com/axonivy/market/controller/ProductDesignerInstallationControllerTest.java
@@ -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 models = List.of(new DesignerInstallation(DESIGNER_VERSION, 5));
+ Mockito.when(productDesignerInstallationService.findByProductId(Mockito.anyString())).thenReturn(models);
+ ResponseEntity> 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());
+ }
+}
diff --git a/marketplace-service/src/test/java/com/axonivy/market/controller/ProductDetailsControllerTest.java b/marketplace-service/src/test/java/com/axonivy/market/controller/ProductDetailsControllerTest.java
index 766a1611f..05f37b299 100644
--- a/marketplace-service/src/test/java/com/axonivy/market/controller/ProductDetailsControllerTest.java
+++ b/marketplace-service/src/test/java/com/axonivy/market/controller/ProductDetailsControllerTest.java
@@ -14,6 +14,7 @@
import com.axonivy.market.constants.RequestMappingConstants;
import com.axonivy.market.entity.ProductJsonContent;
import com.axonivy.market.model.VersionAndUrlModel;
+import com.axonivy.market.service.ProductDesignerInstallationService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -41,6 +42,9 @@ class ProductDetailsControllerTest {
@Mock
VersionService versionService;
+ @Mock
+ ProductDesignerInstallationService productDesignerInstallationService;
+
@Mock
private ProductDetailModelAssembler detailModelAssembler;
@@ -116,9 +120,9 @@ void testFindProductVersionsById() {
@Test
void testSyncInstallationCount() {
- when(productService.updateInstallationCountForProduct("google-maps-connector")).thenReturn(1);
+ when(productService.updateInstallationCountForProduct("google-maps-connector", "10.0.20")).thenReturn(1);
- var result = productDetailsController.syncInstallationCount("google-maps-connector");
+ var result = productDetailsController.syncInstallationCount("google-maps-connector", "10.0.20");
assertEquals(1, result.getBody());
}
diff --git a/marketplace-service/src/test/java/com/axonivy/market/repository/impl/CustomProductRepositoryImplTest.java b/marketplace-service/src/test/java/com/axonivy/market/repository/impl/CustomProductRepositoryImplTest.java
index 4d9fb26db..3938e5d54 100644
--- a/marketplace-service/src/test/java/com/axonivy/market/repository/impl/CustomProductRepositoryImplTest.java
+++ b/marketplace-service/src/test/java/com/axonivy/market/repository/impl/CustomProductRepositoryImplTest.java
@@ -3,6 +3,7 @@
import com.axonivy.market.BaseSetup;
import com.axonivy.market.constants.MongoDBConstants;
import com.axonivy.market.entity.Product;
+import com.axonivy.market.entity.ProductDesignerInstallation;
import com.axonivy.market.repository.ProductModuleContentRepository;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -132,4 +133,10 @@ void testUpdateInitialCount() {
repo.updateInitialCount(ID, initialCount);
verify(mongoTemplate).updateFirst(any(Query.class), eq(new Update().inc("InstallationCount", initialCount).set("SynchronizedInstallationCount", true)), eq(Product.class));
}
+
+ @Test
+ void testIncreaseInstallationCountForProductByDesignerVersion() {
+ repo.increaseInstallationCountForProductByDesignerVersion("portal", "10.0.20");
+ verify(mongoTemplate).upsert(any(Query.class), any(Update.class), eq(ProductDesignerInstallation.class));
+ }
}
diff --git a/marketplace-service/src/test/java/com/axonivy/market/service/impl/ProductDesignerInstallationServiceImplTest.java b/marketplace-service/src/test/java/com/axonivy/market/service/impl/ProductDesignerInstallationServiceImplTest.java
new file mode 100644
index 000000000..6e8998fef
--- /dev/null
+++ b/marketplace-service/src/test/java/com/axonivy/market/service/impl/ProductDesignerInstallationServiceImplTest.java
@@ -0,0 +1,44 @@
+package com.axonivy.market.service.impl;
+
+import com.axonivy.market.BaseSetup;
+import com.axonivy.market.entity.ProductDesignerInstallation;
+import com.axonivy.market.model.DesignerInstallation;
+import com.axonivy.market.repository.ProductDesignerInstallationRepository;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+class ProductDesignerInstallationServiceImplTest extends BaseSetup {
+ private List mockResultReturn;
+ @Mock
+ private ProductDesignerInstallationRepository productDesignerInstallationRepository;
+
+ @InjectMocks
+ private ProductDesignerInstallationServiceImpl productDesignerInstallationServiceImpl;
+
+ @BeforeEach
+ public void setup() {
+ mockResultReturn = createProductDesignerInstallationsMock();
+ }
+
+ @Test
+ void testFindByProductId() {
+ when(productDesignerInstallationRepository.findByProductId(any(), any())).thenReturn(this.mockResultReturn);
+ List results = productDesignerInstallationServiceImpl.findByProductId(BaseSetup.SAMPLE_PRODUCT_ID);
+ assertEquals(2,results.size());
+ assertEquals("10.0.22", results.get(0).getDesignerVersion());
+ assertEquals(50, results.get(0).getNumberOfDownloads());
+ assertEquals("11.4.0", results.get(1).getDesignerVersion());
+ assertEquals(30, results.get(1).getNumberOfDownloads());
+ }
+}
diff --git a/marketplace-service/src/test/java/com/axonivy/market/service/impl/ProductServiceImplTest.java b/marketplace-service/src/test/java/com/axonivy/market/service/impl/ProductServiceImplTest.java
index 127eaa577..5e473b619 100644
--- a/marketplace-service/src/test/java/com/axonivy/market/service/impl/ProductServiceImplTest.java
+++ b/marketplace-service/src/test/java/com/axonivy/market/service/impl/ProductServiceImplTest.java
@@ -142,13 +142,17 @@ public void setup() {
@Test
void testUpdateInstallationCountForProduct() {
- int result = productService.updateInstallationCountForProduct(null);
+ String designerVersion = "10.0.20";
+ int result = productService.updateInstallationCountForProduct(null, designerVersion);
assertEquals(0, result);
Product product = mockProduct();
when(productRepository.getProductById(product.getId())).thenReturn(product);
when(productRepository.increaseInstallationCount(product.getId())).thenReturn(31);
- result = productService.updateInstallationCountForProduct(product.getId());
+ result = productService.updateInstallationCountForProduct(product.getId(), designerVersion);
+ assertEquals(31, result);
+
+ result = productService.updateInstallationCountForProduct(product.getId(), "");
assertEquals(31, result);
}
diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.spec.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.spec.ts
index c96440d10..825a955c0 100644
--- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.spec.ts
+++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.spec.ts
@@ -16,7 +16,6 @@ describe('ProductVersionActionComponent', () => {
let component: ProductDetailVersionActionComponent;
let fixture: ComponentFixture;
let productServiceMock: any;
- let elementRef: MockElementRef;
beforeEach(() => {
productServiceMock = jasmine.createSpyObj('ProductService', [
diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.ts
index 2304f195b..4236a2867 100644
--- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.ts
+++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.ts
@@ -1,10 +1,14 @@
import {
AfterViewInit,
- Component, computed,
- ElementRef, EventEmitter,
+ Component,
+ computed,
+ ElementRef,
+ EventEmitter,
inject,
Input,
- model, Output, Signal,
+ model,
+ Output,
+ Signal,
signal,
WritableSignal
} from '@angular/core';
@@ -27,7 +31,12 @@ const delayTimeBeforeHideMessage = 2000;
@Component({
selector: 'app-product-version-action',
standalone: true,
- imports: [CommonModule, TranslateModule, FormsModule, CommonDropdownComponent],
+ imports: [
+ CommonModule,
+ TranslateModule,
+ FormsModule,
+ CommonDropdownComponent
+ ],
templateUrl: './product-detail-version-action.component.html',
styleUrl: './product-detail-version-action.component.scss'
})
@@ -39,10 +48,10 @@ export class ProductDetailVersionActionComponent implements AfterViewInit {
@Input() product!: ProductDetail;
selectedVersion = model('');
versions: WritableSignal = signal([]);
- versionDropdown : Signal = computed(() => {
+ versionDropdown: Signal = computed(() => {
return this.versions().map(version => ({
value: version,
- label: version,
+ label: version
}));
});
metaDataJsonUrl = model('');
@@ -55,7 +64,7 @@ export class ProductDetailVersionActionComponent implements AfterViewInit {
isInvalidInstallationEnvironment = signal(false);
designerVersion = '';
selectedArtifact: string | undefined = '';
- selectedArtifactName:string | undefined = '';
+ selectedArtifactName: string | undefined = '';
versionMap: Map = new Map();
routingQueryParamService = inject(RoutingQueryParamService);
@@ -75,7 +84,6 @@ export class ProductDetailVersionActionComponent implements AfterViewInit {
this.isDesignerEnvironment.set(
this.routingQueryParamService.isDesignerEnv()
);
-
}
getInstallationTooltipText() {
@@ -86,11 +94,11 @@ export class ProductDetailVersionActionComponent implements AfterViewInit {
(minimum version 9.2.0)
`;
}
- onSelectVersion(version : string) {
+ onSelectVersion(version: string) {
this.selectedVersion.set(version);
this.artifacts.set(this.versionMap.get(this.selectedVersion()) ?? []);
this.artifacts().forEach(artifact => {
- if(artifact.name) {
+ if (artifact.name) {
artifact.label = artifact.name;
}
});
@@ -148,19 +156,27 @@ export class ProductDetailVersionActionComponent implements AfterViewInit {
});
}
-
getVersionInDesigner(): void {
if (this.versions().length === 0) {
- this.productService.sendRequestToGetProductVersionsForDesigner(this.productId
- ).subscribe(data => {
- const versionMap = data.map(dataVersionAndUrl => dataVersionAndUrl.version).map(version => VERSION.displayPrefix.concat(version));
- data.forEach(dataVersionAndUrl => {
- const currentVersion = VERSION.displayPrefix.concat(dataVersionAndUrl.version);
- const versionAndUrl: ItemDropdown = { value: currentVersion, label: currentVersion, metaDataJsonUrl: dataVersionAndUrl.url };
- this.versionDropdownInDesigner.push(versionAndUrl);
+ this.productService
+ .sendRequestToGetProductVersionsForDesigner(this.productId)
+ .subscribe(data => {
+ const versionMap = data
+ .map(dataVersionAndUrl => dataVersionAndUrl.version)
+ .map(version => VERSION.displayPrefix.concat(version));
+ data.forEach(dataVersionAndUrl => {
+ const currentVersion = VERSION.displayPrefix.concat(
+ dataVersionAndUrl.version
+ );
+ const versionAndUrl: ItemDropdown = {
+ value: currentVersion,
+ label: currentVersion,
+ metaDataJsonUrl: dataVersionAndUrl.url
+ };
+ this.versionDropdownInDesigner.push(versionAndUrl);
+ });
+ this.versions.set(versionMap);
});
- this.versions.set(versionMap);
- });
}
}
@@ -182,7 +198,10 @@ export class ProductDetailVersionActionComponent implements AfterViewInit {
onUpdateInstallationCount() {
this.productService
- .sendRequestToUpdateInstallationCount(this.productId)
+ .sendRequestToUpdateInstallationCount(
+ this.productId,
+ this.routingQueryParamService.getDesignerVersionFromCookie()
+ )
.subscribe((data: number) => this.installationCount.emit(data));
}
@@ -191,5 +210,4 @@ export class ProductDetailVersionActionComponent implements AfterViewInit {
this.onUpdateInstallationCount();
}
}
-
}
diff --git a/marketplace-ui/src/app/modules/product/product.service.spec.ts b/marketplace-ui/src/app/modules/product/product.service.spec.ts
index f9d3ff539..212ca073a 100644
--- a/marketplace-ui/src/app/modules/product/product.service.spec.ts
+++ b/marketplace-ui/src/app/modules/product/product.service.spec.ts
@@ -215,12 +215,13 @@ describe('ProductService', () => {
it('sendRequestToUpdateInstallationCount', () => {
const productId = "google-maps-connector";
+ const designerVersion = "10.0.0";
- service.sendRequestToUpdateInstallationCount(productId).subscribe(response => {
+ service.sendRequestToUpdateInstallationCount(productId, designerVersion).subscribe(response => {
expect(response).toBe(3);
});
- const req = httpMock.expectOne(`api/product-details/installationcount/${productId}`);
+ const req = httpMock.expectOne(`api/product-details/installationcount/${productId}?designerVersion=${designerVersion}`);
expect(req.request.method).toBe('PUT');
expect(req.request.headers.get('X-Requested-By')).toBe('ivy');
req.flush(3);
@@ -241,4 +242,5 @@ describe('ProductService', () => {
expect(req.request.headers.get('X-Requested-By')).toBe('ivy');
req.flush([{ version: '10.0.2' }, {version: '10.0.1'}, {version: '10.0.0'}]);
});
+
});
diff --git a/marketplace-ui/src/app/modules/product/product.service.ts b/marketplace-ui/src/app/modules/product/product.service.ts
index f28ec3343..9ecbebff4 100644
--- a/marketplace-ui/src/app/modules/product/product.service.ts
+++ b/marketplace-ui/src/app/modules/product/product.service.ts
@@ -76,14 +76,15 @@ export class ProductService {
);
}
- sendRequestToUpdateInstallationCount(productId: string) {
+ sendRequestToUpdateInstallationCount(productId: string, designerVersion: string) {
const url = 'api/product-details/installationcount/' + productId;
- return this.httpClient.put(url, null, { headers: { 'X-Requested-By': 'ivy' } });
+ const headers = { 'X-Requested-By': 'ivy' };
+ const params = new HttpParams().append('designerVersion', designerVersion);
+ return this.httpClient.put(url, null, { headers, params });
}
sendRequestToGetProductVersionsForDesigner(productId: string) {
const url = `api/product-details/${productId}/designerversions`;
return this.httpClient.get(url, { headers: { 'X-Requested-By': 'ivy' } });
}
-
}