From 81e45308bde44436332fc515b2de42973742c552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tr=E1=BA=A7n=20V=C4=A9nh=20Thi=E1=BB=87n=20Ph=C3=BAc?= <143604440+tvtphuc-axonivy@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:53:12 +0700 Subject: [PATCH 1/3] Feature/marp 760 open marketplace from within designer install item (#117) --- .../ProductDetailModelAssembler.java | 16 +++++++- .../controller/ProductDetailsController.java | 9 +++-- .../com/axonivy/market/entity/Product.java | 4 ++ .../market/model/ProductDetailModel.java | 2 + .../market/model/VersionAndUrlModel.java | 17 +++++++++ .../market/service/VersionService.java | 7 ++-- .../service/impl/VersionServiceImpl.java | 37 ++++++++++++++----- .../ProductDetailModelAssemblerTest.java | 4 ++ .../ProductDetailsControllerTest.java | 29 ++++++++++++--- .../service/impl/VersionServiceImplTest.java | 24 +++++++++--- ...oduct-detail-version-action.component.html | 12 ++---- ...product-detail-version-action.component.ts | 11 +++++- .../product-detail.component.html | 1 + .../product-detail.component.ts | 3 ++ .../modules/product/product.service.spec.ts | 9 ++--- .../app/modules/product/product.service.ts | 3 +- .../common-dropdown.component.html | 5 ++- .../common-dropdown.component.ts | 8 +++- .../app/shared/models/item-dropdown.model.ts | 2 + .../app/shared/models/product-detail.model.ts | 1 + .../src/app/shared/models/version-and-url.ts | 5 +++ 21 files changed, 158 insertions(+), 51 deletions(-) create mode 100644 marketplace-service/src/main/java/com/axonivy/market/model/VersionAndUrlModel.java create mode 100644 marketplace-ui/src/app/shared/models/version-and-url.ts diff --git a/marketplace-service/src/main/java/com/axonivy/market/assembler/ProductDetailModelAssembler.java b/marketplace-service/src/main/java/com/axonivy/market/assembler/ProductDetailModelAssembler.java index 9c3747d9a..ede1c0aba 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/assembler/ProductDetailModelAssembler.java +++ b/marketplace-service/src/main/java/com/axonivy/market/assembler/ProductDetailModelAssembler.java @@ -4,7 +4,10 @@ import com.axonivy.market.controller.ProductDetailsController; import com.axonivy.market.entity.Product; import com.axonivy.market.model.ProductDetailModel; +import com.axonivy.market.util.VersionUtils; +import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; +import org.springframework.hateoas.Link; import org.springframework.hateoas.server.mvc.RepresentationModelAssemblerSupport; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Component; @@ -16,6 +19,7 @@ import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; @Component +@Log4j2 public class ProductDetailModelAssembler extends RepresentationModelAssemblerSupport { private final ProductModelAssembler productModelAssembler; @@ -43,14 +47,22 @@ private ProductDetailModel createModel(Product product, String version, String r ProductDetailModel model = instantiateModel(product); productModelAssembler.createResource(model, product); String productId = Optional.of(product).map(Product::getId).orElse(StringUtils.EMPTY); + + if (requestPath.equals(RequestMappingConstants.BEST_MATCH_BY_ID_AND_VERSION)) { + String bestMatchVersion = VersionUtils.getBestMatchVersion(product.getReleasedVersions(), version); + Link link = linkTo( + methodOn(ProductDetailsController.class).findProductJsonContent(productId, bestMatchVersion)).withSelfRel(); + model.setMetaProductJsonUrl(link.getHref()); + } + selfLinkWithTag = switch (requestPath) { case RequestMappingConstants.BEST_MATCH_BY_ID_AND_VERSION -> methodOn(ProductDetailsController.class).findBestMatchProductDetailsByVersion(productId, version); case RequestMappingConstants.BY_ID_AND_VERSION -> methodOn(ProductDetailsController.class).findProductDetailsByVersion(productId, version); - default -> - methodOn(ProductDetailsController.class).findProductDetails(productId); + default -> methodOn(ProductDetailsController.class).findProductDetails(productId); }; + model.add(linkTo(selfLinkWithTag).withSelfRel()); createDetailResource(model, product); return model; 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 b4f396524..d28e4193e 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 @@ -15,7 +15,8 @@ import static com.axonivy.market.constants.RequestMappingConstants.BEST_MATCH_BY_ID_AND_VERSION; import java.util.List; import java.util.Map; -import com.fasterxml.jackson.core.JsonProcessingException; + +import com.axonivy.market.model.VersionAndUrlModel; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; @@ -99,15 +100,15 @@ public ResponseEntity> findProductVersionsById( @GetMapping(PRODUCT_JSON_CONTENT_BY_PRODUCT_ID_AND_VERSION) @Operation(summary = "Get product json content for designer to install", description = "When we click install in designer, this API will send content of product json for installing in Ivy designer") public ResponseEntity> findProductJsonContent(@PathVariable(PRODUCT_ID) String productId, - @PathVariable(VERSION) String version) throws JsonProcessingException { + @PathVariable(VERSION) String version) { Map productJsonContent = versionService.getProductJsonContentByIdAndVersion(productId, version); return new ResponseEntity<>(productJsonContent, HttpStatus.OK); } @GetMapping(VERSIONS_IN_DESIGNER) @Operation(summary = "Get the list of released version in product", description = "Collect the released versions in product for ivy designer") - public ResponseEntity> findVersionsForDesigner(@PathVariable(ID) String id) { - List versionList = versionService.getVersionsForDesigner(id); + public ResponseEntity> findVersionsForDesigner(@PathVariable(ID) String id) { + List versionList = versionService.getVersionsForDesigner(id); return new ResponseEntity<>(versionList, HttpStatus.OK); } diff --git a/marketplace-service/src/main/java/com/axonivy/market/entity/Product.java b/marketplace-service/src/main/java/com/axonivy/market/entity/Product.java index 6cd687032..ddbec5b50 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/entity/Product.java +++ b/marketplace-service/src/main/java/com/axonivy/market/entity/Product.java @@ -10,6 +10,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Transient; import org.springframework.data.mongodb.core.mapping.Document; import java.io.Serial; @@ -62,6 +63,9 @@ public class Product implements Serializable { private Integer customOrder; private List releasedVersions; + @Transient + private String metaProductJsonUrl; + @Override public int hashCode() { return new HashCodeBuilder().append(id).hashCode(); diff --git a/marketplace-service/src/main/java/com/axonivy/market/model/ProductDetailModel.java b/marketplace-service/src/main/java/com/axonivy/market/model/ProductDetailModel.java index bfe864dbd..061e1fa63 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/model/ProductDetailModel.java +++ b/marketplace-service/src/main/java/com/axonivy/market/model/ProductDetailModel.java @@ -35,6 +35,8 @@ public class ProductDetailModel extends ProductModel { private ProductModuleContent productModuleContent; @Schema(description = "Installation/download count", example = "0") private int installationCount; + @Schema(description = "The api url to get metadata from product.json") + private String metaProductJsonUrl; @Override public int hashCode() { diff --git a/marketplace-service/src/main/java/com/axonivy/market/model/VersionAndUrlModel.java b/marketplace-service/src/main/java/com/axonivy/market/model/VersionAndUrlModel.java new file mode 100644 index 000000000..a43938300 --- /dev/null +++ b/marketplace-service/src/main/java/com/axonivy/market/model/VersionAndUrlModel.java @@ -0,0 +1,17 @@ +package com.axonivy.market.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class VersionAndUrlModel { + String version; + String url; +} diff --git a/marketplace-service/src/main/java/com/axonivy/market/service/VersionService.java b/marketplace-service/src/main/java/com/axonivy/market/service/VersionService.java index 7058fcd1a..e4800c5a9 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/service/VersionService.java +++ b/marketplace-service/src/main/java/com/axonivy/market/service/VersionService.java @@ -1,7 +1,7 @@ package com.axonivy.market.service; import com.axonivy.market.model.MavenArtifactVersionModel; -import com.fasterxml.jackson.core.JsonProcessingException; +import com.axonivy.market.model.VersionAndUrlModel; import java.util.List; import java.util.Map; @@ -14,8 +14,7 @@ public interface VersionService { List getArtifactsAndVersionToDisplay(String productId, Boolean isShowDevVersion, String designerVersion); - Map getProductJsonContentByIdAndVersion(String name , String version) - throws JsonProcessingException; + Map getProductJsonContentByIdAndVersion(String name , String version); - List getVersionsForDesigner(String productId); + List getVersionsForDesigner(String productId); } \ No newline at end of file diff --git a/marketplace-service/src/main/java/com/axonivy/market/service/impl/VersionServiceImpl.java b/marketplace-service/src/main/java/com/axonivy/market/service/impl/VersionServiceImpl.java index d60831163..edbb0b70a 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/service/impl/VersionServiceImpl.java +++ b/marketplace-service/src/main/java/com/axonivy/market/service/impl/VersionServiceImpl.java @@ -5,6 +5,7 @@ import com.axonivy.market.constants.CommonConstants; import com.axonivy.market.constants.GitHubConstants; import com.axonivy.market.constants.MavenConstants; +import com.axonivy.market.controller.ProductDetailsController; import com.axonivy.market.entity.MavenArtifactModel; import com.axonivy.market.entity.MavenArtifactVersion; import com.axonivy.market.entity.Product; @@ -16,6 +17,7 @@ import com.axonivy.market.github.service.GHAxonIvyProductRepoService; import com.axonivy.market.github.util.GitHubUtils; import com.axonivy.market.model.MavenArtifactVersionModel; +import com.axonivy.market.model.VersionAndUrlModel; import com.axonivy.market.repository.MavenArtifactVersionRepository; import com.axonivy.market.repository.ProductJsonContentRepository; import com.axonivy.market.repository.ProductRepository; @@ -29,6 +31,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.kohsuke.github.GHContent; +import org.springframework.hateoas.Link; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; @@ -44,7 +47,8 @@ import java.util.Set; import static com.axonivy.market.constants.ProductJsonConstants.NAME; - +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo; +import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn; @Log4j2 @Service @Getter @@ -109,20 +113,33 @@ public List getArtifactsAndVersionToDisplay(String pr } @Override - public Map getProductJsonContentByIdAndVersion(String productId, String version) - throws JsonProcessingException { - ProductJsonContent productJsonContent = productJsonContentRepository.findByProductIdAndVersion(productId, version); - if (ObjectUtils.isEmpty(productJsonContent)) { - return new HashMap<>(); + public Map getProductJsonContentByIdAndVersion(String productId, String version){ + Map result = new HashMap<>(); + try { + ProductJsonContent productJsonContent = productJsonContentRepository.findByProductIdAndVersion(productId, version); + if (ObjectUtils.isEmpty(productJsonContent)) { + return new HashMap<>(); + } + result = mapper.readValue(productJsonContent.getContent(), Map.class); + result.computeIfAbsent(NAME, k -> productJsonContent.getName()); + + } catch (JsonProcessingException jsonProcessingException){ + log.error(jsonProcessingException.getMessage()); } - Map result = mapper.readValue(productJsonContent.getContent(), Map.class); - result.computeIfAbsent(NAME, k -> productJsonContent.getName()); return result; } @Override - public List getVersionsForDesigner(String productId) { - return productRepository.getReleasedVersionsById(productId); + public List getVersionsForDesigner(String productId) { + List versionAndUrlList = new ArrayList<>(); + List versions = productRepository.getReleasedVersionsById(productId); + for (String version : versions) { + Link link = linkTo( + methodOn(ProductDetailsController.class).findProductJsonContent(productId, version)).withSelfRel(); + VersionAndUrlModel versionAndUrlModel = new VersionAndUrlModel(version, link.getHref()); + versionAndUrlList.add(versionAndUrlModel); + } + return versionAndUrlList; } public boolean handleArtifactForVersionToDisplay(List versionsToDisplay, diff --git a/marketplace-service/src/test/java/com/axonivy/market/assembler/ProductDetailModelAssemblerTest.java b/marketplace-service/src/test/java/com/axonivy/market/assembler/ProductDetailModelAssemblerTest.java index 8e99449fa..366c4962e 100644 --- a/marketplace-service/src/test/java/com/axonivy/market/assembler/ProductDetailModelAssemblerTest.java +++ b/marketplace-service/src/test/java/com/axonivy/market/assembler/ProductDetailModelAssemblerTest.java @@ -10,6 +10,8 @@ import org.mockito.InjectMocks; import org.mockito.junit.jupiter.MockitoExtension; +import java.util.List; + @ExtendWith(MockitoExtension.class) class ProductDetailModelAssemblerTest { private static final String ID = "portal"; @@ -25,6 +27,7 @@ void setup() { productDetailModelAssembler = new ProductDetailModelAssembler(new ProductModelAssembler()); mockProduct = new Product(); mockProduct.setId(ID); + mockProduct.setReleasedVersions(List.of("11.0.1", "10.0.8")); } @Test @@ -51,5 +54,6 @@ void testToModelWithRequestPathAndVersion() { void testToModelWithRequestPathAndBestMatchVersion() { ProductDetailModel model = productDetailModelAssembler.toModel(mockProduct, VERSION, RequestMappingConstants.BEST_MATCH_BY_ID_AND_VERSION); Assertions.assertTrue(model.getLink(SELF_RELATION).get().getHref().endsWith("/api/product-details/portal/10.0.19/bestmatch")); + Assertions.assertTrue(model.getMetaProductJsonUrl().endsWith("/api/product-details/productjsoncontent/portal/10.0.8")); } } \ No newline at end of file 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 6ad422ecd..b487b86e1 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 @@ -13,6 +13,7 @@ import java.util.Map; import com.axonivy.market.constants.RequestMappingConstants; import com.axonivy.market.entity.productjsonfilecontent.ProductJsonContent; +import com.axonivy.market.model.VersionAndUrlModel; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -124,15 +125,31 @@ void testSyncInstallationCount() { @Test void findProductVersionsById() { - when(versionService.getVersionsForDesigner("google-maps-connector")).thenReturn( - List.of("10.0.21", "10.0.22", "10.0.23")); + when(versionService.getVersionsForDesigner("google-maps-connector")).thenReturn(mockVersionAndUrlModels()); var result = productDetailsController.findVersionsForDesigner("google-maps-connector"); - assertEquals(3, Objects.requireNonNull(result.getBody()).size()); - assertEquals("10.0.21", Objects.requireNonNull(result.getBody()).get(0)); - assertEquals("10.0.22", Objects.requireNonNull(result.getBody()).get(1)); - assertEquals("10.0.23", Objects.requireNonNull(result.getBody()).get(2)); + assertEquals(2, Objects.requireNonNull(result.getBody()).size()); + assertEquals("10.0.21", Objects.requireNonNull(result.getBody()).get(0).getVersion()); + assertEquals("/api/product-details/productjsoncontent/portal/10.0.21", + Objects.requireNonNull(result.getBody()).get(0).getUrl()); + assertEquals("10.0.22", Objects.requireNonNull(result.getBody()).get(1).getVersion()); + assertEquals("/api/product-details/productjsoncontent/portal/10.0.22", + Objects.requireNonNull(result.getBody()).get(1).getUrl()); + } + + private List mockVersionAndUrlModels(){ + VersionAndUrlModel versionAndUrlModel = VersionAndUrlModel.builder() + .version("10.0.21") + .url("/api/product-details/productjsoncontent/portal/10.0.21") + .build(); + + VersionAndUrlModel versionAndUrlModel2 = VersionAndUrlModel.builder() + .version("10.0.22") + .url("/api/product-details/productjsoncontent/portal/10.0.22") + .build(); + + return List.of(versionAndUrlModel,versionAndUrlModel2); } @Test diff --git a/marketplace-service/src/test/java/com/axonivy/market/service/impl/VersionServiceImplTest.java b/marketplace-service/src/test/java/com/axonivy/market/service/impl/VersionServiceImplTest.java index f39115f67..0eda7de52 100644 --- a/marketplace-service/src/test/java/com/axonivy/market/service/impl/VersionServiceImplTest.java +++ b/marketplace-service/src/test/java/com/axonivy/market/service/impl/VersionServiceImplTest.java @@ -9,11 +9,11 @@ import com.axonivy.market.github.model.MavenArtifact; import com.axonivy.market.github.service.GHAxonIvyProductRepoService; import com.axonivy.market.model.MavenArtifactVersionModel; +import com.axonivy.market.model.VersionAndUrlModel; import com.axonivy.market.repository.MavenArtifactVersionRepository; import com.axonivy.market.repository.ProductJsonContentRepository; import com.axonivy.market.repository.ProductRepository; import com.axonivy.market.util.XmlReaderUtils; -import com.fasterxml.jackson.core.JsonProcessingException; import org.apache.commons.lang3.StringUtils; import org.assertj.core.api.Fail; import org.junit.jupiter.api.Assertions; @@ -28,7 +28,6 @@ import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; - import java.io.IOException; import java.util.ArrayList; import java.util.Collections; @@ -422,13 +421,19 @@ void testGetVersionsForDesigner() { Mockito.when(productRepository.getReleasedVersionsById(anyString())) .thenReturn(List.of("11.3.0", "11.1.1", "11.1.0", "10.0.2")); - List result = versionService.getVersionsForDesigner("11.3.0"); + List result = versionService.getVersionsForDesigner("11.3.0"); + + Assertions.assertEquals(result.stream().map(VersionAndUrlModel::getVersion).toList(), + List.of("11.3.0", "11.1.1", "11.1.0", "10.0.2")); + Assertions.assertEquals("/api/product-details/productjsoncontent/11.3.0/11.3.0", result.get(0).getUrl()); + Assertions.assertEquals("/api/product-details/productjsoncontent/11.3.0/11.1.1", result.get(1).getUrl()); + Assertions.assertEquals("/api/product-details/productjsoncontent/11.3.0/11.1.0", result.get(2).getUrl()); + Assertions.assertEquals("/api/product-details/productjsoncontent/11.3.0/10.0.2", result.get(3).getUrl()); - Assertions.assertEquals(result, List.of("11.3.0", "11.1.1", "11.1.0", "10.0.2")); } @Test - void testGetProductJsonContentByIdAndVersion() throws JsonProcessingException { + void testGetProductJsonContentByIdAndVersion() { ProductJsonContent mockProductJsonContent = new ProductJsonContent(); String mockContent = """ { @@ -462,4 +467,13 @@ void testGetProductJsonContentByIdAndVersion() throws JsonProcessingException { Assertions.assertEquals("Amazon Comprehend", result.get("name")); } + + @Test + void testGetProductJsonContentByIdAndVersion_noResult() { + Mockito.when(productJsonContentRepository.findByProductIdAndVersion(anyString(), anyString())).thenReturn(null); + + Map result = versionService.getProductJsonContentByIdAndVersion("amazon-comprehend", "11.3.1"); + + Assertions.assertEquals(new HashMap<>(), result); + } } diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.html b/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.html index 0326a68b8..069281404 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.html +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-version-action/product-detail-version-action.component.html @@ -2,28 +2,24 @@