Skip to content

Commit

Permalink
Product description multiple languages
Browse files Browse the repository at this point in the history
  • Loading branch information
tutn-axonivy committed Jul 8, 2024
1 parent c4f45c9 commit 8b1188e
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 85 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.axonivy.market.constants;

import java.util.List;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

Expand All @@ -10,4 +12,6 @@ public class GitHubConstants {
public static final String AXONIVY_MARKETPLACE_PATH = "market";
public static final String DEFAULT_BRANCH = "feature/MARP-463-Multilingualism-for-Website";
public static final String PRODUCT_JSON_FILE_PATH_FORMAT = "%s/product.json";
public static final List<String> PRODUCT_README_FILES = List.of("README.md", "README_DE.md");
public static final String README_FILE_LOCALE_REGEX = "_(..)";
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
public class ReadmeConstants {
public static final String IMAGES = "images";
public static final String README_FILE = "README.md";
public static final String README_FILE_DE = "README_DE.md";
public static final String DEMO_PART = "## Demo";
public static final String SETUP_PART = "## Setup";
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@

import java.io.Serializable;

import com.axonivy.market.model.MultilingualismValue;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class ProductModuleContent implements Serializable {
private static final long serialVersionUID = 1L;
private String tag;
private String description;
private MultilingualismValue description;
private String setup;
private String demo;
private Boolean isDependency;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,42 @@
package com.axonivy.market.github.service.impl;

import java.io.IOException;
import java.util.*;

import com.axonivy.market.constants.*;
import com.axonivy.market.entity.ProductModuleContent;
import com.axonivy.market.github.util.GitHubUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import com.axonivy.market.github.service.GHAxonIvyProductRepoService;
import lombok.extern.log4j.Log4j2;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
import com.axonivy.market.github.model.MavenArtifact;
import org.kohsuke.github.GHContent;
import org.kohsuke.github.GHOrganization;
import org.kohsuke.github.GHRepository;
import org.kohsuke.github.GHTag;
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.MavenConstants;
import com.axonivy.market.constants.ProductJsonConstants;
import com.axonivy.market.constants.ReadmeConstants;
import com.axonivy.market.entity.ProductModuleContent;
import com.axonivy.market.enums.Language;
import com.axonivy.market.github.model.MavenArtifact;
import com.axonivy.market.github.service.GHAxonIvyProductRepoService;
import com.axonivy.market.github.service.GitHubService;
import com.axonivy.market.github.util.GitHubUtils;
import com.axonivy.market.model.MultilingualismValue;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import lombok.extern.log4j.Log4j2;

@Log4j2
@Service
Expand Down Expand Up @@ -147,15 +154,18 @@ public ProductModuleContent getReadmeAndProductContentsFromTag(GHRepository ghRe
List<GHContent> contents = getProductFolderContents(ghRepository, tag);
productModuleContent.setTag(tag);
getProductJsonContent(productModuleContent, contents);
GHContent readmeFile = contents.stream().filter(GHContent::isFile)
.filter(content -> ReadmeConstants.README_FILE.equals(content.getName())).findFirst()
.orElse(null);
if (Objects.nonNull(readmeFile)) {
String readmeContents = new String(readmeFile.read().readAllBytes());
if (hasImageDirectives(readmeContents)) {
readmeContents = updateImagesWithDownloadUrl(contents, readmeContents);
List<GHContent> readmeFiles = contents.stream().filter(GHContent::isFile)
.filter(content -> GitHubConstants.PRODUCT_README_FILES.contains(content.getName()))
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(readmeFiles)) {
for (GHContent readmeFile : readmeFiles) {
String readmeContents = new String(readmeFile.read().readAllBytes());
if (hasImageDirectives(readmeContents)) {
readmeContents = updateImagesWithDownloadUrl(contents, readmeContents);
}
String locale = getReadmeFileLocale(readmeFile.getName());
getExtractedPartsOfReadme(productModuleContent, readmeContents, locale);
}
getExtractedPartsOfReadme(productModuleContent, readmeContents);
}
} catch (Exception e) {
log.error("Cannot get product.json and README file's content {}", e);
Expand All @@ -164,6 +174,16 @@ public ProductModuleContent getReadmeAndProductContentsFromTag(GHRepository ghRe
return productModuleContent;
}

private String getReadmeFileLocale(String readmeFile) {
String result = StringUtils.EMPTY;
Pattern pattern = Pattern.compile(GitHubConstants.README_FILE_LOCALE_REGEX);
Matcher matcher = pattern.matcher(readmeFile);
if (matcher.find()) {
result = matcher.group(1);
}
return result;
}

private void getProductJsonContent(ProductModuleContent productModuleContent, List<GHContent> contents)
throws IOException {
String productJsonContents;
Expand Down Expand Up @@ -217,7 +237,7 @@ public String updateImagesWithDownloadUrl(List<GHContent> contents, String readm

// Cover some cases including when demo and setup parts switch positions or
// missing one of them
public void getExtractedPartsOfReadme(ProductModuleContent productModuleContent, String readmeContents) {
public void getExtractedPartsOfReadme(ProductModuleContent productModuleContent, String readmeContents, String locale) {
String[] parts = readmeContents.split(DEMO_SETUP_TITLE);
boolean hasDemoPart = readmeContents.contains(ReadmeConstants.DEMO_PART);
boolean hasSetupPart = readmeContents.contains(ReadmeConstants.SETUP_PART);
Expand All @@ -243,9 +263,21 @@ public void getExtractedPartsOfReadme(ProductModuleContent productModuleContent,
setup = parts[1];
}

productModuleContent.setDescription(description.trim());
productModuleContent.setDemo(demo.trim());
productModuleContent.setSetup(setup.trim());
setDescriptionWithLocale(productModuleContent, description, locale);
}

private void setDescriptionWithLocale(ProductModuleContent productModuleContent, String description,
String locale) {
if (productModuleContent.getDescription() == null) {
productModuleContent.setDescription(new MultilingualismValue());
}
if (Language.DE.getValue().equalsIgnoreCase(locale)) {
productModuleContent.getDescription().setDe(description);
} else {
productModuleContent.getDescription().setEn(description);
}
}

private static boolean isDemoPlaceBeforeSetupPart(String readmeContents) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static org.mockito.Mockito.verify;

import com.axonivy.market.model.MavenArtifactVersionModel;
import com.axonivy.market.model.MultilingualismValue;
import com.axonivy.market.service.VersionService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -71,13 +72,14 @@ void testFindProductVersionsById() {
}

private Product mockProduct() {
return Product.builder().id("docker-connector").name("Docker").language("English").build();
MultilingualismValue productName = new MultilingualismValue() {{setEn("Docker");}};
return Product.builder().id("docker-connector").names(productName).language("English").build();
}

private ProductDetailModel createProductMockWithDetails() {
ProductDetailModel mockProduct = new ProductDetailModel();
mockProduct.setId("docker-connector");
mockProduct.setName("Docker");
mockProduct.getNames().setEn("Docker");
mockProduct.setType("connector");
mockProduct.setCompatibility("10.0+");
mockProduct.setSourceUrl("https://github.com/axonivy-market/docker-connector");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void testMappingByGHContent() throws IOException {
when(mockContent.read()).thenReturn(inputStream);
result = ProductFactory.mappingByGHContent(product, mockContent);
assertNotEquals(null, result);
assertEquals("Amazon Comprehend", result.getName());
assertEquals("Amazon Comprehend", result.getNames().getEn());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class ProductServiceImplTest {
private static final String SHA1_SAMPLE = "35baa89091b2452b77705da227f1a964ecabc6c8";
public static final String RELEASE_TAG = "v10.0.2";
private String keyword;
private String langague;
private Page<Product> mockResultReturn;

@Mock
Expand Down Expand Up @@ -95,24 +96,24 @@ public void setup() {
}

@Test
void testFindProducts() {
// Start testing by All
when(productRepository.findAll(any(Pageable.class))).thenReturn(mockResultReturn);
// Executes
var result = productService.findProducts(TypeOption.ALL.getOption(), keyword, PAGEABLE);
assertEquals(mockResultReturn, result);

// Start testing by Connector
when(productRepository.findByType(any(), any(Pageable.class))).thenReturn(mockResultReturn);
// Executes
result = productService.findProducts(TypeOption.CONNECTORS.getOption(), keyword, PAGEABLE);
assertEquals(mockResultReturn, result);

// Start testing by Other
// Executes
result = productService.findProducts(TypeOption.DEMOS.getOption(), keyword, PAGEABLE);
assertEquals(0, result.getSize());
}
void testFindProducts() {
// Start testing by All
when(productRepository.findAll(any(Pageable.class))).thenReturn(mockResultReturn);
// Executes
var result = productService.findProducts(TypeOption.ALL.getOption(), keyword, langague, PAGEABLE);
assertEquals(mockResultReturn, result);

// Start testing by Connector
when(productRepository.findByType(any(), any(Pageable.class))).thenReturn(mockResultReturn);
// Executes
result = productService.findProducts(TypeOption.CONNECTORS.getOption(), keyword, langague, PAGEABLE);
assertEquals(mockResultReturn, result);

// Start testing by Other
// Executes
result = productService.findProducts(TypeOption.DEMOS.getOption(), keyword, langague, PAGEABLE);
assertEquals(0, result.getSize());
}

@Test
void testSyncProductsAsUpdateMetaJSONFromGitHub() throws IOException {
Expand All @@ -128,7 +129,7 @@ void testSyncProductsAsUpdateMetaJSONFromGitHub() throws IOException {
mockGithubFile.setStatus(FileStatus.ADDED);
when(marketRepoService.fetchMarketItemsBySHA1Range(any(), any())).thenReturn(List.of(mockGithubFile));
var mockGHContent = mockGHContentAsMetaJSON();
when(gitHubService.getGHContent(any(), anyString())).thenReturn(mockGHContent);
when(gitHubService.getGHContent(any(), anyString(), anyString())).thenReturn(mockGHContent);

// Executes
var result = productService.syncLatestDataFromMarketRepo();
Expand Down Expand Up @@ -159,7 +160,7 @@ void testSyncProductsAsUpdateLogoFromGitHub() throws IOException {
mockGitHubFile.setStatus(FileStatus.ADDED);
when(marketRepoService.fetchMarketItemsBySHA1Range(any(), any())).thenReturn(List.of(mockGitHubFile));
var mockGHContent = mockGHContentAsMetaJSON();
when(gitHubService.getGHContent(any(), anyString())).thenReturn(mockGHContent);
when(gitHubService.getGHContent(any(), anyString(), anyString())).thenReturn(mockGHContent);

// Executes
var result = productService.syncLatestDataFromMarketRepo();
Expand All @@ -169,7 +170,7 @@ void testSyncProductsAsUpdateLogoFromGitHub() throws IOException {
when(mockCommit.getSHA1()).thenReturn(UUID.randomUUID().toString());
mockGitHubFile.setStatus(FileStatus.REMOVED);
when(marketRepoService.fetchMarketItemsBySHA1Range(any(), any())).thenReturn(List.of(mockGitHubFile));
when(gitHubService.getGHContent(any(), anyString())).thenReturn(mockGHContent);
when(gitHubService.getGHContent(any(), anyString(), anyString())).thenReturn(mockGHContent);
when(productRepository.findByLogoUrl(any())).thenReturn(new Product());

// Executes
Expand All @@ -178,32 +179,38 @@ void testSyncProductsAsUpdateLogoFromGitHub() throws IOException {
}

@Test
void testFindAllProductsWithKeyword() throws IOException {
when(productRepository.findAll(any(Pageable.class))).thenReturn(mockResultReturn);
// Executes
var result = productService.findProducts(TypeOption.ALL.getOption(), keyword, PAGEABLE);
assertEquals(mockResultReturn, result);
verify(productRepository).findAll(any(Pageable.class));

// Test has keyword
when(productRepository.searchByNameOrShortDescriptionRegex(any(), any(Pageable.class)))
.thenReturn(new PageImpl<>(mockResultReturn.stream()
.filter(product -> product.getName().equals(SAMPLE_PRODUCT_NAME)).collect(Collectors.toList())));
// Executes
result = productService.findProducts(TypeOption.ALL.getOption(), SAMPLE_PRODUCT_NAME, PAGEABLE);
verify(productRepository).findAll(any(Pageable.class));
assertTrue(result.hasContent());
assertEquals(SAMPLE_PRODUCT_NAME, result.getContent().get(0).getName());

// Test has keyword and type is connector
when(productRepository.searchByKeywordAndType(any(), any(), any(Pageable.class))).thenReturn(
new PageImpl<>(mockResultReturn.stream().filter(product -> product.getName().equals(SAMPLE_PRODUCT_NAME)
&& product.getType().equals(TypeOption.CONNECTORS.getCode())).collect(Collectors.toList())));
// Executes
result = productService.findProducts(TypeOption.CONNECTORS.getOption(), SAMPLE_PRODUCT_NAME, PAGEABLE);
assertTrue(result.hasContent());
assertEquals(SAMPLE_PRODUCT_NAME, result.getContent().get(0).getName());
}
void testFindAllProductsWithKeyword() throws IOException {
langague = "en";
// Start testing by All
when(productRepository.findAll(any(Pageable.class))).thenReturn(mockResultReturn);
// Executes
var result = productService.findProducts(TypeOption.ALL.getOption(), keyword, langague, PAGEABLE);
assertEquals(mockResultReturn, result);
verify(productRepository).findAll(any(Pageable.class));

// Test has keyword
when(productRepository.searchByNameOrShortDescriptionRegex(anyString(), anyString(), any(Pageable.class)))
.thenReturn(new PageImpl<>(mockResultReturn.stream()
.filter(product -> product.getNames().getEn().equals(SAMPLE_PRODUCT_NAME))
.collect(Collectors.toList())));
// Executes
result = productService.findProducts(TypeOption.ALL.getOption(), SAMPLE_PRODUCT_NAME, langague, PAGEABLE);
verify(productRepository).findAll(any(Pageable.class));
assertTrue(result.hasContent());
assertEquals(SAMPLE_PRODUCT_NAME, result.getContent().get(0).getNames().getEn());

// Test has keyword and type is connector
when(productRepository.searchByKeywordAndType(any(), any(), anyString(), any(Pageable.class)))
.thenReturn(new PageImpl<>(mockResultReturn.stream()
.filter(product -> product.getNames().getEn().equals(SAMPLE_PRODUCT_NAME)
&& product.getType().equals(TypeOption.CONNECTORS.getCode()))
.collect(Collectors.toList())));
// Executes
result = productService.findProducts(TypeOption.CONNECTORS.getOption(), SAMPLE_PRODUCT_NAME, langague,
PAGEABLE);
assertTrue(result.hasContent());
assertEquals(SAMPLE_PRODUCT_NAME, result.getContent().get(0).getNames().getEn());
}

@Test
void testSyncProductsFirstTime() throws IOException {
Expand Down Expand Up @@ -261,12 +268,12 @@ void testSearchProducts() {
var simplePageable = PageRequest.of(0, 20);
String type = TypeOption.ALL.getOption();
keyword = "on";
when(productRepository.searchByNameOrShortDescriptionRegex(keyword, simplePageable))
when(productRepository.searchByNameOrShortDescriptionRegex(keyword, langague, simplePageable))
.thenReturn(mockResultReturn);

var result = productService.findProducts(type, keyword, simplePageable);
var result = productService.findProducts(type, keyword, langague, simplePageable);
assertEquals(result, mockResultReturn);
verify(productRepository).searchByNameOrShortDescriptionRegex(keyword, simplePageable);
verify(productRepository).searchByNameOrShortDescriptionRegex(keyword, langague, simplePageable);
}

@Test
Expand Down Expand Up @@ -299,13 +306,13 @@ private Page<Product> createPageProductsMock() {
var mockProducts = new ArrayList<Product>();
Product mockProduct = new Product();
mockProduct.setId(SAMPLE_PRODUCT_ID);
mockProduct.setName(SAMPLE_PRODUCT_NAME);
mockProduct.getNames().setEn(SAMPLE_PRODUCT_NAME);
mockProduct.setType("connector");
mockProducts.add(mockProduct);

mockProduct = new Product();
mockProduct.setId("tel-search-ch-connector");
mockProduct.setName("Swiss phone directory");
mockProduct.getNames().setEn("Swiss phone directory");
mockProduct.setType("util");
mockProducts.add(mockProduct);
return new PageImpl<>(mockProducts);
Expand Down Expand Up @@ -336,7 +343,7 @@ private ProductModuleContent mockReadmeProductContent() {
ProductModuleContent productModuleContent = new ProductModuleContent();
productModuleContent.setTag("v10.0.2");
productModuleContent.setName("Amazon Comprehend");
productModuleContent.setDescription("testDescription");
productModuleContent.getDescription().setEn("testDescription");
return productModuleContent;
}
}

0 comments on commit 8b1188e

Please sign in to comment.