Skip to content

Commit

Permalink
Merge branch 'develop' of https://github.com/axonivy-market/marketplace
Browse files Browse the repository at this point in the history
… into bugfix/MARP-959-Axon-Ivy-Portal-Images-and-URLs-are-broken

# Conflicts:
#	marketplace-service/src/main/java/com/axonivy/market/service/impl/ProductServiceImpl.java
  • Loading branch information
nqhoan-axonivy committed Sep 11, 2024
2 parents eb1a5b6 + 2f9a176 commit 93a5be8
Show file tree
Hide file tree
Showing 22 changed files with 423 additions and 165 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ public interface GHAxonIvyProductRepoService {
ProductModuleContent getReadmeAndProductContentsFromTag(Product product, GHRepository ghRepository, String tag);

List<MavenArtifact> convertProductJsonToMavenProductInfo(GHContent content) throws IOException;

void extractReadMeFileFromContents(Product product, List<GHContent> contents, ProductModuleContent productModuleContent);
}
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,19 @@ public ProductModuleContent getReadmeAndProductContentsFromTag(Product product,
productModuleContent.setProductId(product.getId());
productModuleContent.setTag(tag);
updateDependencyContentsFromProductJson(productModuleContent, contents , product);
extractReadMeFileFromContents(product, contents, productModuleContent);
} catch (Exception e) {
log.error("Cannot get product.json content {}", e.getMessage());
return null;
}
return productModuleContent;
}

public void extractReadMeFileFromContents(Product product, List<GHContent> contents, ProductModuleContent productModuleContent) {
try {
List<GHContent> readmeFiles = contents.stream().filter(GHContent::isFile)
.filter(content -> content.getName().startsWith(ReadmeConstants.README_FILE_NAME)).toList();
Map<String,Map<String,String>> moduleContents = new HashMap<>();
Map<String, Map<String, String>> moduleContents = new HashMap<>();
if (!CollectionUtils.isEmpty(readmeFiles)) {
for (GHContent readmeFile : readmeFiles) {
String readmeContents = new String(readmeFile.read().readAllBytes());
Expand All @@ -185,10 +195,8 @@ public ProductModuleContent getReadmeAndProductContentsFromTag(Product product,
productModuleContent.setSetup(replaceEmptyContentsWithEnContent(moduleContents.get(SETUP)));
}
} catch (Exception e) {
log.error("Cannot get product.json and README file's content {}", e.getMessage());
return null;
log.error("Cannot get README file's content {}", e.getMessage());
}
return productModuleContent;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,40 @@
package com.axonivy.market.service.impl;

import static com.axonivy.market.enums.DocumentField.MARKET_DIRECTORY;
import static com.axonivy.market.enums.DocumentField.SHORT_DESCRIPTIONS;

import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.EMPTY;

import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.*;

import com.axonivy.market.comparator.MavenVersionComparator;
import com.axonivy.market.util.VersionUtils;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.kohsuke.github.GHCommit;
import org.kohsuke.github.GHContent;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHTag;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.axonivy.market.constants.CommonConstants;
import com.axonivy.market.constants.GitHubConstants;
import com.axonivy.market.constants.ProductJsonConstants;
Expand All @@ -27,41 +61,10 @@
import com.axonivy.market.repository.ProductModuleContentRepository;
import com.axonivy.market.repository.ProductRepository;
import com.axonivy.market.service.ProductService;
import com.axonivy.market.util.VersionUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.kohsuke.github.GHCommit;
import org.kohsuke.github.GHContent;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHTag;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.SecureRandom;
import java.util.*;

import static com.axonivy.market.enums.DocumentField.MARKET_DIRECTORY;
import static com.axonivy.market.enums.DocumentField.SHORT_DESCRIPTIONS;
import static java.util.Optional.ofNullable;
import static org.apache.commons.lang3.StringUtils.EMPTY;

@Log4j2
@Service
Expand All @@ -88,6 +91,7 @@ public class ProductServiceImpl implements ProductService {
private String marketRepoBranch;

public static final String NON_NUMERIC_CHAR = "[^0-9.]";
private static final String INITIAL_VERSION = "1.0";
private final SecureRandom random = new SecureRandom();

public ProductServiceImpl(ProductRepository productRepository,
Expand Down Expand Up @@ -338,11 +342,23 @@ private void syncProductsFromGitHubRepo() {
if (StringUtils.isNotBlank(product.getRepositoryName())) {
updateProductCompatibility(product);
getProductContents(product);
} else {
updateProductContentForNonStandardProduct(ghContentEntity, product);
}
productRepository.save(product);
});
}

private void updateProductContentForNonStandardProduct(Map.Entry<String, List<GHContent>> ghContentEntity, Product product) {
ProductModuleContent initialContent = new ProductModuleContent();
initialContent.setTag(INITIAL_VERSION);
initialContent.setProductId(product.getId());
product.setReleasedVersions(List.of(INITIAL_VERSION));
product.setNewestReleaseVersion(INITIAL_VERSION);
axonIvyProductRepoService.extractReadMeFileFromContents(product, ghContentEntity.getValue(), initialContent);
productModuleContentRepository.save(initialContent);
}

private void getProductContents(Product product) {
try {
GHRepository productRepo = gitHubService.getRepository(product.getRepositoryName());
Expand Down Expand Up @@ -397,11 +413,9 @@ private void updateProductCompatibility(Product product) {
if (StringUtils.isNotBlank(product.getCompatibility())) {
return;
}
String oldestTag =
getProductReleaseTags(product).stream().map(tag -> tag.getName().replaceAll(NON_NUMERIC_CHAR, Strings.EMPTY))
.distinct().sorted(Comparator.reverseOrder()).reduce((tag1, tag2) -> tag2).orElse(null);
if (oldestTag != null) {
String compatibility = getCompatibilityFromOldestTag(oldestTag);
String oldestVersion = VersionUtils.getOldestVersion(getProductReleaseTags(product));
if (oldestVersion != null) {
String compatibility = getCompatibilityFromOldestTag(oldestVersion);
product.setCompatibility(compatibility);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import com.axonivy.market.enums.NonStandardProduct;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import org.kohsuke.github.GHTag;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
Expand All @@ -17,6 +19,8 @@
import java.util.stream.Stream;

public class VersionUtils {
public static final String NON_NUMERIC_CHAR = "[^0-9.]";

private VersionUtils() {
}
public static List<String> getVersionsToDisplay(List<String> versions, Boolean isShowDevVersion, String designerVersion) {
Expand Down Expand Up @@ -112,6 +116,15 @@ public static String convertVersionToTag(String productId, String version) {
return GitHubConstants.STANDARD_TAG_PREFIX.concat(version);
}

public static String getOldestVersion(List<GHTag> tags) {
String result = StringUtils.EMPTY;
if (!CollectionUtils.isEmpty(tags)) {
List<String> releasedTags = tags.stream().map(tag -> tag.getName().replaceAll(NON_NUMERIC_CHAR, Strings.EMPTY))
.distinct().sorted(new LatestVersionComparator()).toList();
return CollectionUtils.lastElement(releasedTags);
}
return result;
}
public static List<String> getReleaseTagsFromProduct(Product product) {
if (Objects.isNull(product)) {
return new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ class ProductServiceImplTest extends BaseSetup {
private static final String SHA1_SAMPLE = "35baa89091b2452b77705da227f1a964ecabc6c8";
public static final String RELEASE_TAG = "v10.0.2";
private static final String INSTALLATION_FILE_PATH = "src/test/resources/installationCount.json";
private static final String EMPTY_SOURCE_URL_META_JSON_FILE = "/emptySourceUrlMeta.json";

private String keyword;
private String language;
Expand Down Expand Up @@ -121,7 +122,10 @@ class ProductServiceImplTest extends BaseSetup {
ArgumentCaptor<Product> argumentCaptor = ArgumentCaptor.forClass(Product.class);

@Captor
ArgumentCaptor<ArrayList<ProductModuleContent>> argumentCaptorProductModuleContent;
ArgumentCaptor<ArrayList<ProductModuleContent>> argumentCaptorProductModuleContents;

@Captor
ArgumentCaptor<ProductModuleContent> argumentCaptorProductModuleContent;

@Mock
private GHAxonIvyProductRepoService ghAxonIvyProductRepoService;
Expand Down Expand Up @@ -335,13 +339,33 @@ void testSyncProductsFirstTime() throws IOException {

// Executes
productService.syncLatestDataFromMarketRepo();
verify(productModuleContentRepository).saveAll(argumentCaptorProductModuleContent.capture());
verify(productModuleContentRepository).saveAll(argumentCaptorProductModuleContents.capture());
verify(productRepository).save(argumentCaptor.capture());

assertThat(argumentCaptorProductModuleContent.getValue()).usingRecursiveComparison()
assertThat(argumentCaptorProductModuleContents.getValue()).usingRecursiveComparison()
.isEqualTo(List.of(mockReadmeProductContent()));
}

@Test
void testSyncProductsFirstTimeWithOutSourceUrl() throws IOException {
var mockCommit = mockGHCommitHasSHA1(SHA1_SAMPLE);
when(marketRepoService.getLastCommit(anyLong())).thenReturn(mockCommit);
when(repoMetaRepository.findByRepoName(anyString())).thenReturn(null);

var mockContent = mockGHContentAsMetaJSON();
InputStream inputStream = this.getClass().getResourceAsStream(EMPTY_SOURCE_URL_META_JSON_FILE);
when(mockContent.read()).thenReturn(inputStream);

Map<String, List<GHContent>> mockGHContentMap = new HashMap<>();
mockGHContentMap.put(SAMPLE_PRODUCT_ID, List.of(mockContent));
when(marketRepoService.fetchAllMarketItems()).thenReturn(mockGHContentMap);

// Executes
productService.syncLatestDataFromMarketRepo();
verify(productModuleContentRepository).save(argumentCaptorProductModuleContent.capture());
assertEquals("1.0", argumentCaptorProductModuleContent.getValue().getTag());
}

@Test
void testSyncProductsSecondTime() throws IOException {
var gitHubRepoMeta = mock(GitHubRepoMeta.class);
Expand Down Expand Up @@ -375,7 +399,7 @@ void testSyncProductsSecondTime() throws IOException {
// Executes
productService.syncLatestDataFromMarketRepo();

verify(productModuleContentRepository).saveAll(argumentCaptorProductModuleContent.capture());
verify(productModuleContentRepository).saveAll(argumentCaptorProductModuleContents.capture());
verify(productRepository).save(argumentCaptor.capture());
assertThat(argumentCaptor.getValue().getProductModuleContent()).usingRecursiveComparison()
.isEqualTo(mockReadmeProductContent());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.kohsuke.github.GHTag;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;

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

@ExtendWith(MockitoExtension.class)
Expand Down Expand Up @@ -148,4 +151,32 @@ void testConvertTagsToVersions() {
Assertions.assertEquals("10.0.2", results.get(1));
}

@Test
void testGetOldestVersionWithEmptyTags() {
List<GHTag> tags = List.of();

String oldestTag = VersionUtils.getOldestVersion(tags);

Assertions.assertEquals(StringUtils.EMPTY, oldestTag);
}

@Test
void testGetOldestVersionWithNullTags() {
String oldestTag = VersionUtils.getOldestVersion(null);

Assertions.assertEquals(StringUtils.EMPTY, oldestTag);
}

@Test
void testGetOldestVersionWithNonNumericCharacters() {
GHTag tag1 = Mockito.mock(GHTag.class);
GHTag tag2 = Mockito.mock(GHTag.class);
Mockito.when(tag1.getName()).thenReturn("v1.0");
Mockito.when(tag2.getName()).thenReturn("2.1");
List<GHTag> tags = Arrays.asList(tag1, tag2);

String oldestTag = VersionUtils.getOldestVersion(tags);

Assertions.assertEquals("1.0", oldestTag); // Assuming the replacement of non-numeric characters works correctly
}
}
35 changes: 35 additions & 0 deletions marketplace-service/src/test/resources/emptySourceUrlMeta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"$schema": "https://json-schema.axonivy.com/market/10.0.2/meta.json",
"id": "employee-onboarding",
"version": "1.0",
"name": "Employee Onboarding",
"names": [
{
"locale": "en",
"value": "Employee Onboarding"
},
{
"locale": "de",
"value": "Mitarbeiter Onboarding"
}
],
"description": "This solution helps HR managers to accelerate time-to-market for employee onboarding.",
"descriptions": [
{
"locale": "en",
"value": "This solution helps HR managers to accelerate time-to-market for employee onboarding."
},
{
"locale": "de",
"value": "HR-Manager können mit dieser Lösung die Time-to-Market für die Einführung neuer Mitarbeiter effektiv reduzieren."
}
],
"type": "solution",
"cost": "paid",
"language": "EN",
"industry": "Cross-Industry",
"tags": [
"hr"
],
"contactUs": true
}
Loading

0 comments on commit 93a5be8

Please sign in to comment.