diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index a2dde31c7..329aa5e4f 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -29,7 +29,8 @@ jobs: steps: - name: Bring down and remove containers and images - working-directory: ./marketplace-build + continue-on-error: true + working-directory: ./marketplace-build/dev run: | docker compose down --rmi all @@ -46,13 +47,12 @@ jobs: - name: Update environment variables for .env env: - ENV_FILE: './marketplace-build/.env' - MONGODB_HOST: ${{ secrets.MONGODB_HOST }} + ENV_FILE: './marketplace-build/dev/.env' ROOT_USERNAME: ${{ secrets.MONGODB_ROOT_USERNAME }} ROOT_PASSWORD: ${{ secrets.MONGODB_ROOT_PASSWORD }} SERVICE_USERNAME: ${{ secrets.SERVICE_USERNAME }} SERVICE_PASSWORD: ${{ secrets.SERVICE_PASSWORD }} - MONGODB_DATABASE: ${{ secrets.MONGODB_DATABASE }} + GH_MARKET_BRANCH: ${{ secrets.MARKET_GITHUB_MARKET_BRANCH }} GH_TOKEN: ${{ secrets.GH_TOKEN }} MARKET_JWT_SECRET_KEY: ${{ secrets.MARKET_JWT_SECRET_KEY }} MARKET_CORS_ALLOWED_ORIGIN: ${{ secrets.MARKET_CORS_ALLOWED_ORIGIN }} @@ -67,10 +67,9 @@ jobs: sed -i "s/^MONGODB_INITDB_ROOT_USERNAME=.*$/MONGODB_INITDB_ROOT_USERNAME=$ROOT_USERNAME/" $ENV_FILE sed -i "s/^MONGODB_INITDB_ROOT_PASSWORD=.*$/MONGODB_INITDB_ROOT_PASSWORD=$ROOT_PASSWORD/" $ENV_FILE - sed -i "s/^SERVICE_MONGODB_HOST=.*$/SERVICE_MONGODB_HOST=$MONGODB_HOST/" $ENV_FILE - sed -i "s/^SERVICE_MONGODB_DATABASE=.*$/SERVICE_MONGODB_DATABASE=$MONGODB_DATABASE/" $ENV_FILE sed -i "s/^SERVICE_MONGODB_USER=.*$/SERVICE_MONGODB_USER=$SERVICE_USERNAME/" $ENV_FILE sed -i "s/^SERVICE_MONGODB_PASSWORD=.*$/SERVICE_MONGODB_PASSWORD=$SERVICE_PASSWORD/" $ENV_FILE + sed -i "s/^MARKET_GITHUB_MARKET_BRANCH=.*$/MARKET_GITHUB_MARKET_BRANCH=$GH_MARKET_BRANCH/" $ENV_FILE sed -i "s/^MARKET_GITHUB_TOKEN=.*$/MARKET_GITHUB_TOKEN=$GH_TOKEN/" $ENV_FILE sed -i "s/^MARKET_GITHUB_OAUTH_APP_CLIENT_ID=.*$/MARKET_GITHUB_OAUTH_APP_CLIENT_ID=$OAUTH_APP_CLIENT_ID/" $ENV_FILE sed -i "s/^MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET=.*$/MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET=$OAUTH_APP_CLIENT_SECRET/" $ENV_FILE @@ -88,13 +87,13 @@ jobs: sed -i 's/"version": "[^"]*"/"version": "${{ inputs.release_version }}"/' $PACKAGE_FILE - name: Build and bring up containers without cache - working-directory: ./marketplace-build + working-directory: ./marketplace-build/dev run: | if [ -n "${{ inputs.release_version }}" ]; then BUILD_VERSION="${{ inputs.release_version }}" else - BUILD_VERSION=$(xml sel -t -v "//_:project/_:version" ../marketplace-service/pom.xml) + BUILD_VERSION=$(xml sel -t -v "//_:project/_:version" ../../marketplace-service/pom.xml) fi - docker compose build --no-cache --build-arg BUILD_ENV=${{ inputs.build_env }} --build-arg BUILD_VERSION=$BUILD_VERSION + docker compose build --build-arg BUILD_ENV=${{ inputs.build_env }} --build-arg BUILD_VERSION=$BUILD_VERSION docker compose up --force-recreate -d diff --git a/marketplace-build/.env b/marketplace-build/.env index a6dee6c27..75cd6f6b7 100644 --- a/marketplace-build/.env +++ b/marketplace-build/.env @@ -1,12 +1,12 @@ MONGODB_INITDB_ROOT_USERNAME= MONGODB_INITDB_ROOT_PASSWORD= -SERVICE_MONGODB_HOST= +SERVICE_MONGODB_HOST=mongodb SERVICE_MONGODB_USER= SERVICE_MONGODB_PASSWORD= -SERVICE_MONGODB_DATABASE= +SERVICE_MONGODB_DATABASE=marketplace +MARKET_GITHUB_MARKET_BRANCH= MARKET_GITHUB_TOKEN= -MARKETPLACE_INSTALLATION_URL= MARKET_GITHUB_OAUTH_APP_CLIENT_ID= MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET= MARKET_JWT_SECRET_KEY= -MARKET_CORS_ALLOWED_ORIGIN= \ No newline at end of file +MARKET_CORS_ALLOWED_ORIGIN=* \ No newline at end of file diff --git a/marketplace-build/Marketplace Tomcat v10.1 Server.launch b/marketplace-build/Marketplace Tomcat v10.1 Server.launch deleted file mode 100644 index e3efb4803..000000000 --- a/marketplace-build/Marketplace Tomcat v10.1 Server.launch +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/marketplace-build/README.md b/marketplace-build/README.md index 741b59e02..f7e0cb865 100644 --- a/marketplace-build/README.md +++ b/marketplace-build/README.md @@ -1,7 +1,7 @@ # Get starts with Marketplace build ### Set up MongoDB with authentication mode -* Navigate to ``marketplace-build/config/mongodb`` and execute the ``docker-compose up -d`` to start MongoDB with non-auth mode and create a root admin user. +* Navigate to ``marketplace-build/config/mongodb`` and execute the ``docker-compose -f non-authen-docker-compose.yml up -d`` to start MongoDB with non-auth mode and create a root admin user. * [Optional] Execute authentication test for the created user ``` @@ -13,10 +13,18 @@ This command should return the ``OK`` code * Down the non-authen instance to start the main docker compose file by run ``docker-compose down`` ### Docker build for DEV environment +#### Start from scratch: * Navigate to ``marketplace-build`` * Run ``docker-compose up -d --build`` to start a Marketplace DEV at the local +#### If you already have MongoDB on your local machine with public port `27017` +* Navigate to ``marketplace-build/dev`` + +* Run ``docker-compose up -d --build`` to start a Marketplace DEV at the local + +> In case you want to set up the MongoDB as a standalone compose. Please run `docker-compose -f authen-docker-compose.yml up -d` in ``marketplace-build/config/mongodb`` + ### Docker release To release a new version for marketplace images, please trigger the ``Docker Release`` actions. * This GH Actions will trigger the ``Docker build`` on the master branch. diff --git a/marketplace-build/config/mongodb/authen-docker-compose.yml b/marketplace-build/config/mongodb/authen-docker-compose.yml new file mode 100644 index 000000000..cc783c38a --- /dev/null +++ b/marketplace-build/config/mongodb/authen-docker-compose.yml @@ -0,0 +1,19 @@ +name: marketplace-db + +services: + mongodb: + container_name: mongodb + build: + dockerfile: Dockerfile + restart: always + ports: + - "27017:27017" + environment: + MONGODB_INITDB_ROOT_USERNAME: ${MONGODB_INITDB_ROOT_USERNAME} + MONGODB_INITDB_ROOT_PASSWORD: ${MONGODB_INITDB_ROOT_PASSWORD} + volumes: + - mongodata:/data/db + - ./mongod.conf:/etc/mongod.conf + +volumes: + mongodata: \ No newline at end of file diff --git a/marketplace-build/config/mongodb/docker-compose.yml b/marketplace-build/config/mongodb/non-authen-docker-compose.yml similarity index 96% rename from marketplace-build/config/mongodb/docker-compose.yml rename to marketplace-build/config/mongodb/non-authen-docker-compose.yml index 24b64346d..3eb2387fe 100644 --- a/marketplace-build/config/mongodb/docker-compose.yml +++ b/marketplace-build/config/mongodb/non-authen-docker-compose.yml @@ -1,7 +1,7 @@ # This docker-compose to init MongoDB as free access and create a volume as name + mongodata # Then insert the admin user as defined at mogo-init.js # Change the name to other if you want to create more volumes. e.g: marketplace-dev -name: marketplace +name: marketplace-db services: mongodb: diff --git a/marketplace-build/dev/.env b/marketplace-build/dev/.env new file mode 100644 index 000000000..a476ec9f3 --- /dev/null +++ b/marketplace-build/dev/.env @@ -0,0 +1,12 @@ +MONGODB_INITDB_ROOT_USERNAME=octopus +MONGODB_INITDB_ROOT_PASSWORD= +SERVICE_MONGODB_HOST=10.193.8.78 +SERVICE_MONGODB_USER=octopus +SERVICE_MONGODB_PASSWORD= +SERVICE_MONGODB_DATABASE=marketplace-dev +MARKET_GITHUB_MARKET_BRANCH= +MARKET_GITHUB_TOKEN= +MARKET_GITHUB_OAUTH_APP_CLIENT_ID= +MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET= +MARKET_JWT_SECRET_KEY= +MARKET_CORS_ALLOWED_ORIGIN=* \ No newline at end of file diff --git a/marketplace-build/dev/docker-compose.yml b/marketplace-build/dev/docker-compose.yml new file mode 100644 index 000000000..706eeab07 --- /dev/null +++ b/marketplace-build/dev/docker-compose.yml @@ -0,0 +1,43 @@ +name: marketplace + +services: + ui: + container_name: marketplace-ui + build: + context: ../../marketplace-ui + additional_contexts: + assets: ../../marketplace-build/ + dockerfile: Dockerfile + args: + - BUILD_ENV=${BUILD_ENV} + restart: always + volumes: + - ../../marketplace-build/config/nginx/nginx.conf:/etc/nginx/nginx.conf + ports: + - "4200:80" + depends_on: + - service + + service: + container_name: marketplace-service + restart: always + volumes: + - /home/axonivy/marketplace/data/market-installations.json:/home/data/market-installation.json + environment: + - MONGODB_HOST=${SERVICE_MONGODB_HOST} + - MONGODB_DATABASE=${SERVICE_MONGODB_DATABASE} + - MONGODB_USERNAME=${SERVICE_MONGODB_USER} + - MONGODB_PASSWORD=${SERVICE_MONGODB_PASSWORD} + - MARKET_GITHUB_MARKET_BRANCH=${MARKET_GITHUB_MARKET_BRANCH} + - MARKET_GITHUB_TOKEN=${MARKET_GITHUB_TOKEN} + - MARKET_GITHUB_OAUTH_APP_CLIENT_ID=${MARKET_GITHUB_OAUTH_APP_CLIENT_ID} + - MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET=${MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET} + - MARKET_JWT_SECRET_KEY=${MARKET_JWT_SECRET_KEY} + - MARKET_CORS_ALLOWED_ORIGIN=${MARKET_CORS_ALLOWED_ORIGIN} + build: + context: ../../marketplace-service + dockerfile: Dockerfile + args: + - BUILD_VERSION=${BUILD_VERSION} + ports: + - "8080:8080" diff --git a/marketplace-build/docker-compose.yml b/marketplace-build/docker-compose.yml index 4b61cc430..230d269b6 100644 --- a/marketplace-build/docker-compose.yml +++ b/marketplace-build/docker-compose.yml @@ -43,6 +43,7 @@ services: - MONGODB_DATABASE=${SERVICE_MONGODB_DATABASE} - MONGODB_USERNAME=${SERVICE_MONGODB_USER} - MONGODB_PASSWORD=${SERVICE_MONGODB_PASSWORD} + - MARKET_GITHUB_MARKET_BRANCH=${MARKET_GITHUB_MARKET_BRANCH} - MARKET_GITHUB_TOKEN=${MARKET_GITHUB_TOKEN} - MARKET_GITHUB_OAUTH_APP_CLIENT_ID=${MARKET_GITHUB_OAUTH_APP_CLIENT_ID} - MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET=${MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET} diff --git a/marketplace-build/release/.env b/marketplace-build/release/.env index 8737e3380..2b5f160d4 100644 --- a/marketplace-build/release/.env +++ b/marketplace-build/release/.env @@ -1,12 +1,12 @@ RELEASE_VERSION=sprint MONGODB_INITDB_ROOT_USERNAME=octopus MONGODB_INITDB_ROOT_PASSWORD= -SERVICE_MONGODB_HOST=mongodb +SERVICE_MONGODB_HOST=localhost SERVICE_MONGODB_USER=octopus SERVICE_MONGODB_PASSWORD= -SERVICE_MONGODB_DATABASE= +SERVICE_MONGODB_DATABASE=marketplace +MARKET_GITHUB_MARKET_BRANCH=master MARKET_GITHUB_TOKEN= -MARKETPLACE_INSTALLATION_URL= MARKET_GITHUB_OAUTH_APP_CLIENT_ID= MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET= MARKET_JWT_SECRET_KEY= diff --git a/marketplace-build/release/docker-compose.yml b/marketplace-build/release/docker-compose.yml index 30a1d7ba7..a878ae57b 100644 --- a/marketplace-build/release/docker-compose.yml +++ b/marketplace-build/release/docker-compose.yml @@ -6,8 +6,8 @@ services: context: ../config/mongodb dockerfile: Dockerfile restart: always - ports: - - "27017:27017" + expose: + - 27017 environment: MONGODB_INITDB_ROOT_USERNAME: ${MONGODB_INITDB_ROOT_USERNAME} MONGODB_INITDB_ROOT_PASSWORD: ${MONGODB_INITDB_ROOT_PASSWORD} @@ -33,6 +33,7 @@ services: - MONGODB_DATABASE=${SERVICE_MONGODB_DATABASE} - MONGODB_USERNAME=${SERVICE_MONGODB_USER} - MONGODB_PASSWORD=${SERVICE_MONGODB_PASSWORD} + - MARKET_GITHUB_MARKET_BRANCH=${MARKET_GITHUB_MARKET_BRANCH} - MARKET_GITHUB_TOKEN=${MARKET_GITHUB_TOKEN} - MARKET_GITHUB_OAUTH_APP_CLIENT_ID=${MARKET_GITHUB_OAUTH_APP_CLIENT_ID} - MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET=${MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET} diff --git a/marketplace-service/src/main/java/com/axonivy/market/constants/GitHubConstants.java b/marketplace-service/src/main/java/com/axonivy/market/constants/GitHubConstants.java index bd6ab9937..8b4b79b03 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/constants/GitHubConstants.java +++ b/marketplace-service/src/main/java/com/axonivy/market/constants/GitHubConstants.java @@ -8,7 +8,6 @@ public class GitHubConstants { public static final String AXONIVY_MARKET_ORGANIZATION_NAME = "axonivy-market"; public static final String AXONIVY_MARKETPLACE_REPO_NAME = "market"; 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 String GITHUB_PROVIDER_NAME = "GitHub"; public static final String GITHUB_GET_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token"; diff --git a/marketplace-service/src/main/java/com/axonivy/market/github/service/GitHubService.java b/marketplace-service/src/main/java/com/axonivy/market/github/service/GitHubService.java index 23bc9640f..a5996d18b 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/github/service/GitHubService.java +++ b/marketplace-service/src/main/java/com/axonivy/market/github/service/GitHubService.java @@ -7,6 +7,7 @@ import org.kohsuke.github.GHOrganization; import org.kohsuke.github.GHRepository; import org.kohsuke.github.GitHub; +import org.kohsuke.github.GHTag; import com.axonivy.market.entity.User; import com.axonivy.market.exceptions.model.MissingHeaderException; @@ -23,6 +24,8 @@ public interface GitHubService { GHRepository getRepository(String repositoryPath) throws IOException; + List getRepositoryTags(String repositoryPath) throws IOException; + List getDirectoryContent(GHRepository ghRepository, String path, String ref) throws IOException; GHContent getGHContent(GHRepository ghRepository, String path, String ref) throws IOException; diff --git a/marketplace-service/src/main/java/com/axonivy/market/github/service/impl/GHAxonIvyMarketRepoServiceImpl.java b/marketplace-service/src/main/java/com/axonivy/market/github/service/impl/GHAxonIvyMarketRepoServiceImpl.java index c44a047bc..db2053337 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/github/service/impl/GHAxonIvyMarketRepoServiceImpl.java +++ b/marketplace-service/src/main/java/com/axonivy/market/github/service/impl/GHAxonIvyMarketRepoServiceImpl.java @@ -14,6 +14,7 @@ import org.kohsuke.github.GHContent; import org.kohsuke.github.GHOrganization; import org.kohsuke.github.GHRepository; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.IOException; @@ -32,6 +33,8 @@ public class GHAxonIvyMarketRepoServiceImpl implements GHAxonIvyMarketRepoServic private GHRepository repository; private final GitHubService gitHubService; + @Value("${market.github.market.branch}") + private String marketRepoBranch; public GHAxonIvyMarketRepoServiceImpl(GitHubService gitHubService) { this.gitHubService = gitHubService; @@ -42,7 +45,7 @@ public Map> fetchAllMarketItems() { Map> ghContentMap = new HashMap<>(); try { List directoryContent = gitHubService.getDirectoryContent(getRepository(), - GitHubConstants.AXONIVY_MARKETPLACE_PATH, GitHubConstants.DEFAULT_BRANCH); + GitHubConstants.AXONIVY_MARKETPLACE_PATH, marketRepoBranch); for (var content : directoryContent) { extractFileInDirectoryContent(content, ghContentMap); } @@ -82,7 +85,7 @@ public GHCommit getLastCommit(long lastCommitTime) { } private GHCommitQueryBuilder createQueryCommitsBuilder(long lastCommitTime) { - return getRepository().queryCommits().since(lastCommitTime).from(GitHubConstants.DEFAULT_BRANCH); + return getRepository().queryCommits().since(lastCommitTime).from(marketRepoBranch); } @Override diff --git a/marketplace-service/src/main/java/com/axonivy/market/github/service/impl/GitHubServiceImpl.java b/marketplace-service/src/main/java/com/axonivy/market/github/service/impl/GitHubServiceImpl.java index 8b8486fa2..045d745f2 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/github/service/impl/GitHubServiceImpl.java +++ b/marketplace-service/src/main/java/com/axonivy/market/github/service/impl/GitHubServiceImpl.java @@ -13,6 +13,7 @@ import org.kohsuke.github.GHRepository; import org.kohsuke.github.GitHub; import org.kohsuke.github.GitHubBuilder; +import org.kohsuke.github.GHTag; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; @@ -77,6 +78,11 @@ public GHRepository getRepository(String repositoryPath) throws IOException { return getGitHub().getRepository(repositoryPath); } + @Override + public List getRepositoryTags(String repositoryPath) throws IOException { + return getRepository(repositoryPath).listTags().toList(); + } + @Override public GHContent getGHContent(GHRepository ghRepository, String path, String ref) throws IOException { Assert.notNull(ghRepository, "Repository must not be null"); @@ -173,4 +179,4 @@ public List> getUserOrganizations(String accessToken) throws exception.getMessage())); } } -} \ No newline at end of file +} 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 4f8ca3a97..b352a92ce 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 @@ -18,8 +18,10 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Objects; 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; @@ -28,7 +30,6 @@ import org.kohsuke.github.GHTag; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; @@ -89,6 +90,9 @@ public class ProductServiceImpl implements ProductService { @Value("${synchronized.installation.counts.path}") private String installationCountPath; + @Value("${market.github.market.branch}") + private String marketRepoBranch; + public static final String NON_NUMERIC_CHAR = "[^0-9.]"; private final SecureRandom random = new SecureRandom(); @@ -133,6 +137,7 @@ public boolean syncLatestDataFromMarketRepo() { } syncRepoMetaDataStatus(); } + updateLatestReleaseTagContentsFromProductRepo(); return isAlreadyUpToDate; } @@ -202,14 +207,13 @@ private void updateLatestChangeToProductsFromGithubRepo() { GHContent fileContent; try { fileContent = gitHubService.getGHContent(axonIvyMarketRepoService.getRepository(), file.getFileName(), - GitHubConstants.DEFAULT_BRANCH); + marketRepoBranch); } catch (IOException e) { log.error("Get GHContent failed: ", e); continue; } ProductFactory.mappingByGHContent(product, fileContent); - updateProductFromReleaseTags(product); if (FileType.META == file.getType()) { modifyProductByMetaContent(file, product); } else { @@ -302,59 +306,109 @@ private boolean isLastGithubCommitCovered() { return isLastCommitCovered; } - private Page syncProductsFromGitHubRepo() { + private void updateLatestReleaseTagContentsFromProductRepo() { + List products = productRepository.findAll(); + if (ObjectUtils.isEmpty(products)) { + return; + } + + for (Product product : products) { + if (StringUtils.isNotBlank(product.getRepositoryName())) { + getProductContents(product); + productRepository.save(product); + } + } + } + + private void syncProductsFromGitHubRepo() { log.warn("**ProductService: synchronize products from scratch based on the Market repo"); var gitHubContentMap = axonIvyMarketRepoService.fetchAllMarketItems(); - List products = new ArrayList<>(); gitHubContentMap.entrySet().forEach(ghContentEntity -> { Product product = new Product(); for (var content : ghContentEntity.getValue()) { ProductFactory.mappingByGHContent(product, content); - updateProductFromReleaseTags(product); } - products.add(product); + if (StringUtils.isNotBlank(product.getRepositoryName())) { + updateProductCompatibility(product); + getProductContents(product); + } + productRepository.save(product); }); - if (!products.isEmpty()) { - productRepository.saveAll(products); + } + + private void getProductContents(Product product) { + try { + GHRepository productRepo = gitHubService.getRepository(product.getRepositoryName()); + updateProductFromReleaseTags(product, productRepo); + } catch (IOException e) { + log.error("Cannot find product repository {} {}", product.getRepositoryName(), e); } - return new PageImpl<>(products); } - private void updateProductFromReleaseTags(Product product) { - if (StringUtils.isBlank(product.getRepositoryName())) { + private void updateProductFromReleaseTags(Product product, GHRepository productRepo) { + List productModuleContents = new ArrayList<>(); + List tags = getProductReleaseTags(product); + GHTag lastTag = CollectionUtils.firstElement(tags); + + if (lastTag == null || lastTag.getName().equals(product.getNewestReleaseVersion())) { return; } + + getPublishedDateFromLatestTag(product, lastTag); + product.setNewestReleaseVersion(lastTag.getName()); + + if (!ObjectUtils.isEmpty(product.getProductModuleContents())) { + productModuleContents.addAll(product.getProductModuleContents()); + List currentTags = product.getProductModuleContents().stream().filter(Objects::nonNull) + .map(ProductModuleContent::getTag).toList(); + tags = tags.stream().filter(t -> !currentTags.contains(t.getName())).toList(); + } + + for (GHTag ghTag : tags) { + ProductModuleContent productModuleContent = + axonIvyProductRepoService.getReadmeAndProductContentsFromTag(product, productRepo, ghTag.getName()); + productModuleContents.add(productModuleContent); + } + product.setProductModuleContents(productModuleContents); + } + + private void getPublishedDateFromLatestTag(Product product, GHTag lastTag) { try { - GHRepository productRepo = gitHubService.getRepository(product.getRepositoryName()); - List tags = productRepo.listTags().toList(); - GHTag lastTag = CollectionUtils.firstElement(tags); - if (lastTag != null) { - product.setNewestPublishedDate(lastTag.getCommit().getCommitDate()); - product.setNewestReleaseVersion(lastTag.getName()); - } + product.setNewestPublishedDate(lastTag.getCommit().getCommitDate()); + } catch (IOException e) { + log.error("Fail to get commit date ", e); + } + } - String oldestTag = tags.stream().map(tag -> tag.getName().replaceAll(NON_NUMERIC_CHAR, Strings.EMPTY)).distinct() - .sorted(Comparator.reverseOrder()).reduce((tag1, tag2) -> tag2).orElse(null); - if (oldestTag != null && StringUtils.isBlank(product.getCompatibility())) { - String compatibility = getCompatibilityFromOldestTag(oldestTag); - product.setCompatibility(compatibility); - } + 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); + product.setCompatibility(compatibility); + } + } - List productModuleContents = new ArrayList<>(); - for (GHTag ghtag : tags) { - ProductModuleContent productModuleContent = axonIvyProductRepoService.getReadmeAndProductContentsFromTag( - product, productRepo, ghtag.getName()); - productModuleContents.add(productModuleContent); - } - product.setProductModuleContents(productModuleContents); - } catch (Exception e) { - log.error("Cannot find repository by path {} {}", product.getRepositoryName(), e); + private List getProductReleaseTags(Product product) { + List tags = new ArrayList<>(); + try { + tags = gitHubService.getRepositoryTags(product.getRepositoryName()); + } catch (IOException e) { + log.error("Cannot get tag list of product ", e); } + return tags; } // Cover 3 cases after removing non-numeric characters (8, 11.1 and 10.0.2) @Override public String getCompatibilityFromOldestTag(String oldestTag) { + if (StringUtils.isBlank(oldestTag)) { + return Strings.EMPTY; + } if (!oldestTag.contains(CommonConstants.DOT_SEPARATOR)) { return oldestTag + ".0+"; } diff --git a/marketplace-service/src/main/resources/application.properties b/marketplace-service/src/main/resources/application.properties index b5f7f509a..3c279f7ff 100644 --- a/marketplace-service/src/main/resources/application.properties +++ b/marketplace-service/src/main/resources/application.properties @@ -10,6 +10,7 @@ springdoc.swagger-ui.path=/swagger-ui.html market.cors.allowed.origin.maxAge=3600 market.cors.allowed.origin.patterns=${MARKET_CORS_ALLOWED_ORIGIN} synchronized.installation.counts.path=/home/data/market-installation.json +market.github.market.branch=${MARKET_GITHUB_MARKET_BRANCH} market.github.token=${MARKET_GITHUB_TOKEN} market.github.oauth2-clientId=${MARKET_GITHUB_OAUTH_APP_CLIENT_ID} market.github.oauth2-clientSecret=${MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET} diff --git a/marketplace-service/src/test/java/com/axonivy/market/service/ProductServiceImplTest.java b/marketplace-service/src/test/java/com/axonivy/market/service/ProductServiceImplTest.java index 6301aa93d..997e20aec 100644 --- a/marketplace-service/src/test/java/com/axonivy/market/service/ProductServiceImplTest.java +++ b/marketplace-service/src/test/java/com/axonivy/market/service/ProductServiceImplTest.java @@ -25,6 +25,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; @@ -41,7 +42,6 @@ import org.kohsuke.github.GHContent; import org.kohsuke.github.GHRepository; import org.kohsuke.github.GHTag; -import org.kohsuke.github.PagedIterable; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.InjectMocks; @@ -219,7 +219,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(), anyString())).thenReturn(mockGHContent); + when(gitHubService.getGHContent(any(), anyString(), any())).thenReturn(mockGHContent); // Executes var result = productService.syncLatestDataFromMarketRepo(); @@ -250,7 +250,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(), anyString())).thenReturn(mockGHContent); + when(gitHubService.getGHContent(any(), anyString(), any())).thenReturn(mockGHContent); // Executes var result = productService.syncLatestDataFromMarketRepo(); @@ -260,7 +260,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(), anyString())).thenReturn(mockGHContent); + when(gitHubService.getGHContent(any(), anyString(), any())).thenReturn(mockGHContent); when(productRepository.findByLogoUrl(any())).thenReturn(new Product()); // Executes @@ -308,8 +308,6 @@ void testSyncProductsFirstTime() throws IOException { when(ghAxonIvyProductRepoService.getReadmeAndProductContentsFromTag(any(), any(), anyString())).thenReturn( mockReadmeProductContent()); when(gitHubService.getRepository(any())).thenReturn(ghRepository); - PagedIterable pagedIterable = Mockito.mock(String.valueOf(GHTag.class)); - when(ghRepository.listTags()).thenReturn(pagedIterable); GHTag mockTag = mock(GHTag.class); GHCommit mockGHCommit = mock(GHCommit.class); @@ -318,8 +316,7 @@ void testSyncProductsFirstTime() throws IOException { when(mockTag.getCommit()).thenReturn(mockGHCommit); when(mockGHCommit.getCommitDate()).thenReturn(new Date()); - when(pagedIterable.toList()).thenReturn(List.of(mockTag)); - + when(gitHubService.getRepositoryTags(anyString())).thenReturn(List.of(mockTag)); var mockContent = mockGHContentAsMetaJSON(); InputStream inputStream = this.getClass().getResourceAsStream(SLASH.concat(META_FILE)); when(mockContent.read()).thenReturn(inputStream); @@ -331,12 +328,49 @@ void testSyncProductsFirstTime() throws IOException { // Executes productService.syncLatestDataFromMarketRepo(); - verify(productRepository).saveAll(productListArgumentCaptor.capture()); + verify(productRepository).save(argumentCaptor.capture()); - assertThat(productListArgumentCaptor.getValue().get(0).getProductModuleContents()).usingRecursiveComparison() + assertThat(argumentCaptor.getValue().getProductModuleContents()).usingRecursiveComparison() .isEqualTo(List.of(mockReadmeProductContent())); } + @Test + void testSyncProductsSecondTime() throws IOException { + var gitHubRepoMeta = mock(GitHubRepoMeta.class); + when(gitHubRepoMeta.getLastSHA1()).thenReturn(SHA1_SAMPLE); + var mockCommit = mockGHCommitHasSHA1(SHA1_SAMPLE); + when(marketRepoService.getLastCommit(anyLong())).thenReturn(mockCommit); + when(repoMetaRepository.findByRepoName(anyString())).thenReturn(gitHubRepoMeta); + + when(productRepository.findAll()).thenReturn(mockProducts()); + + GHCommit mockGHCommit = mock(GHCommit.class); + + GHTag mockTag = mock(GHTag.class); + when(mockTag.getName()).thenReturn("v10.0.2"); + when(mockTag.getCommit()).thenReturn(mockGHCommit); + + GHTag mockTag2 = mock(GHTag.class); + when(mockTag2.getName()).thenReturn("v10.0.3"); + + when(mockGHCommit.getCommitDate()).thenReturn(new Date()); + when(gitHubService.getRepositoryTags(anyString())).thenReturn(Arrays.asList(mockTag, mockTag2)); + + ProductModuleContent mockReturnProductContent = mockReadmeProductContent(); + mockReturnProductContent.setTag("v10.0.3"); + + when(ghAxonIvyProductRepoService.getReadmeAndProductContentsFromTag(any(), any(), anyString())) + .thenReturn(mockReturnProductContent); + + // Executes + productService.syncLatestDataFromMarketRepo(); + + verify(productRepository).save(argumentCaptor.capture()); + assertEquals(2, argumentCaptor.getValue().getProductModuleContents().size()); + assertThat(argumentCaptor.getValue().getProductModuleContents()).usingRecursiveComparison() + .isEqualTo(List.of(mockReadmeProductContent(), mockReturnProductContent)); + } + @Test void testNothingToSync() { var gitHubRepoMeta = mock(GitHubRepoMeta.class); @@ -489,4 +523,10 @@ private ProductModuleContent mockReadmeProductContent() { productModuleContent.setDescription(description); return productModuleContent; } + + private List mockProducts() { + Product product1 = Product.builder().repositoryName("axonivy-market/amazon-comprehend-connector") + .productModuleContents(List.of(mockReadmeProductContent())).build(); + return List.of(product1); + } } diff --git a/marketplace-service/src/test/java/com/axonivy/market/service/SchedulingTasksTest.java b/marketplace-service/src/test/java/com/axonivy/market/service/SchedulingTasksTest.java index 6e3523138..474c25ed1 100644 --- a/marketplace-service/src/test/java/com/axonivy/market/service/SchedulingTasksTest.java +++ b/marketplace-service/src/test/java/com/axonivy/market/service/SchedulingTasksTest.java @@ -14,7 +14,7 @@ @SpringBootTest(properties = { "MONGODB_USERNAME=user", "MONGODB_PASSWORD=password", "MONGODB_HOST=mongoHost", "MONGODB_DATABASE=product", "MARKET_GITHUB_OAUTH_APP_CLIENT_ID=clientId", "MARKET_GITHUB_OAUTH_APP_CLIENT_SECRET=clientSecret", "MARKET_JWT_SECRET_KEY=jwtSecret", - "MARKET_CORS_ALLOWED_ORIGIN=*" }) + "MARKET_CORS_ALLOWED_ORIGIN=*", "MARKET_GITHUB_MARKET_BRANCH=master" }) class SchedulingTasksTest { @SpyBean diff --git a/marketplace-ui/src/app/modules/product/product-card/product-card.component.html b/marketplace-ui/src/app/modules/product/product-card/product-card.component.html index bd404c3ba..dd414407c 100644 --- a/marketplace-ui/src/app/modules/product/product-card/product-card.component.html +++ b/marketplace-ui/src/app/modules/product/product-card/product-card.component.html @@ -7,7 +7,10 @@ width="70" height="70" [ngSrc]="product | logo" - [alt]="product.names | multilingualism: languageService.selectedLanguage()" /> + [alt]=" + product.names | multilingualism: languageService.selectedLanguage() + " + [lang]="languageService.selectedLanguage()" /> @if (isShowInRESTClientEditor) {
} @else {
{{ 'common.filter.value.' + product.type | translate }} @@ -23,13 +27,15 @@ }
-
- {{ - product.names | multilingualism: languageService.selectedLanguage() - }} +
+ {{ product.names | multilingualism: languageService.selectedLanguage() }}
@if (!isShowInRESTClientEditor) { -

+

{{ product.shortDescriptions | multilingualism: languageService.selectedLanguage() diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-detail-feedback.component.html b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-detail-feedback.component.html index 196c02984..de0d9b4fb 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-detail-feedback.component.html +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-detail-feedback.component.html @@ -1,15 +1,15 @@

- +
@if (isShowBtnMore()) { -
- -
+
+ +
} \ No newline at end of file diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-detail-feedback.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-detail-feedback.component.ts index 7b4e4d318..3ed5bab2a 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-detail-feedback.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-detail-feedback.component.ts @@ -11,6 +11,7 @@ import { ProductFeedbacksPanelComponent } from './product-feedbacks-panel/produc import { AppModalService } from '../../../../shared/services/app-modal.service'; import { ProductFeedbackService } from './product-feedbacks-panel/product-feedback.service'; import { TranslateModule } from '@ngx-translate/core'; +import { LanguageService } from '../../../../core/services/language/language.service'; const MAX_ELEMENTS = 6; @@ -41,6 +42,7 @@ export class ProductDetailFeedbackComponent { productFeedbackService = inject(ProductFeedbackService); appModalService = inject(AppModalService); + languageService = inject(LanguageService); showPopup!: boolean; diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/feedback-filter/feedback-filter.component.html b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/feedback-filter/feedback-filter.component.html index 1741cde6d..9df399dcb 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/feedback-filter/feedback-filter.component.html +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/feedback-filter/feedback-filter.component.html @@ -1,14 +1,12 @@
-

{{'common.sort.label' | translate}}:

- @for (type of feedbackSortTypes; track $index) { - + } -
+
\ No newline at end of file diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/feedback-filter/feedback-filter.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/feedback-filter/feedback-filter.component.ts index e8b316b5a..1a7645579 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/feedback-filter/feedback-filter.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/feedback-filter/feedback-filter.component.ts @@ -3,6 +3,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { FEEDBACK_SORT_TYPES } from '../../../../../../shared/constants/common.constant'; import { FormsModule } from '@angular/forms'; import { ProductFeedbackService } from '../product-feedback.service'; +import { LanguageService } from '../../../../../../core/services/language/language.service'; @Component({ selector: 'app-feedback-filter', @@ -17,6 +18,7 @@ export class FeedbackFilterComponent { @Output() sortChange = new EventEmitter(); productFeedbackService = inject(ProductFeedbackService); + languageService = inject(LanguageService); onSortChange(event: Event): void { const selectElement = event.target as HTMLSelectElement; diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedbacks-panel.component.html b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedbacks-panel.component.html index 37067feed..3e674a7e7 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedbacks-panel.component.html +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedbacks-panel.component.html @@ -1,27 +1,27 @@
@if (isRenderInModalDialog) { -
- @for (feedback of feedbacks(); track $index) { - - } -
+
+ @for (feedback of feedbacks(); track $index) { + + } +
} @else if (isMobileMode()) { -
- @for (feedback of feedbacks(); track $index) { - - } -
+
+ @for (feedback of feedbacks(); track $index) { + + } +
} @else { -
- @for (feedback of feedbacks() | slice:0:6; track $index) { - - } -
+
+ @for (feedback of feedbacks() | slice:0:6; track $index) { + + } +
} -
+
\ No newline at end of file diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedbacks-panel.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedbacks-panel.component.ts index 6f2c46cca..3531a69fc 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedbacks-panel.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-feedbacks-panel/product-feedbacks-panel.component.ts @@ -15,6 +15,7 @@ import { ThemeService } from '../../../../../core/services/theme/theme.service'; import { Feedback } from '../../../../../shared/models/feedback.model'; import { CommonModule } from '@angular/common'; import { ProductDetailService } from '../../product-detail.service'; +import { LanguageService } from '../../../../../core/services/language/language.service'; interface CustomElement extends HTMLElement { scrollTop: number; @@ -43,6 +44,7 @@ export class ProductFeedbacksPanelComponent { themeService = inject(ThemeService); productFeedbackService = inject(ProductFeedbackService); productDetailService = inject(ProductDetailService); + languageService = inject(LanguageService); feedbacks: Signal = this.productFeedbackService.feedbacks; diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/add-feedback-dialog.component.html b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/add-feedback-dialog.component.html index 6ef300d1c..7b6c61bc9 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/add-feedback-dialog.component.html +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/add-feedback-dialog.component.html @@ -1,51 +1,44 @@ + \ No newline at end of file diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/success-dialog/success-dialog.component.html b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/success-dialog/success-dialog.component.html index 6cc10d0a8..0f2d4b724 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/success-dialog/success-dialog.component.html +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/success-dialog/success-dialog.component.html @@ -3,11 +3,11 @@ \ No newline at end of file diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/success-dialog/success-dialog.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/success-dialog/success-dialog.component.ts index 9790866c9..3384b7ce4 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/success-dialog/success-dialog.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/add-feedback-dialog/success-dialog/success-dialog.component.ts @@ -3,6 +3,7 @@ import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateModule } from '@ngx-translate/core'; import { AuthService } from '../../../../../../../auth/auth.service'; import { NgOptimizedImage } from '@angular/common'; +import { LanguageService } from '../../../../../../../core/services/language/language.service'; @Component({ selector: 'app-success-dialog', @@ -16,4 +17,6 @@ export class SuccessDialogComponent { activeModal = inject(NgbActiveModal); authService = inject(AuthService); + + languageService = inject(LanguageService); } \ No newline at end of file diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/product-star-rating-panel.component.html b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/product-star-rating-panel.component.html index eb1c26356..f6ca0d2d9 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/product-star-rating-panel.component.html +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/product-star-rating-panel.component.html @@ -6,7 +6,7 @@
-

+

{{ 'common.feedback.detailedReviews' | translate }}

@@ -19,8 +19,8 @@
} diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/product-star-rating-panel.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/product-star-rating-panel.component.ts index 86888b5e2..351599f1e 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/product-star-rating-panel.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-feedback/product-star-rating-panel/product-star-rating-panel.component.ts @@ -8,6 +8,7 @@ import { } from '../../product-star-rating-number/product-star-rating-number.component'; import { CommonModule } from '@angular/common'; import { StarRatingCounting } from '../../../../../shared/models/star-rating-counting.model'; +import { LanguageService } from '../../../../../core/services/language/language.service'; @Component({ selector: 'app-product-star-rating-panel', @@ -28,6 +29,7 @@ export class ProductStarRatingPanelComponent { @Output() openAddFeedbackDialog = new EventEmitter(); productStarRatingService = inject(ProductStarRatingService); + languageService = inject(LanguageService); starRatings: Signal = this.productStarRatingService.starRatings; } diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.html b/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.html index caf883de5..d61a45eb4 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.html +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.html @@ -1,16 +1,16 @@ -

+

{{ 'common.product.detail.information.label' | translate }}

- + {{ 'common.product.detail.information.value.author' | translate }} {{ productDetail.vendor }}

- + {{ 'common.product.detail.information.value.version' | translate }} @@ -19,7 +19,7 @@


- + {{ 'common.product.detail.information.value.compatibility' | translate }} @@ -28,35 +28,35 @@


- + {{ 'common.product.detail.information.value.cost' | translate }} {{ productDetail.cost }}

- + {{ 'common.product.detail.information.value.language' | translate }} {{ productDetail.language }}

- + {{ 'common.product.detail.type' | translate }} {{ productDetail.type }}

- + {{ 'common.product.detail.information.value.industry' | translate }} {{ productDetail.industry }}

- + {{ 'common.product.detail.information.value.tag' | translate }} @@ -65,7 +65,7 @@


- + {{ 'common.product.detail.information.value.source' | translate }} @@ -76,26 +76,24 @@


- + {{ 'common.product.detail.information.value.status' | translate }}

- + {{ - 'common.product.detail.information.value.moreInformation' | translate + 'common.product.detail.information.value.moreInformation' | translate }} - {{ 'common.product.detail.information.value.contactUs' | translate }}
-
+ \ No newline at end of file diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.ts index 8c8ca8822..5d7bb86dc 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-information-tab/product-detail-information-tab.component.ts @@ -1,7 +1,8 @@ import { CommonModule } from '@angular/common'; -import { Component, Input } from '@angular/core'; +import { Component, inject, Input } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; import { ProductDetail } from '../../../../shared/models/product-detail.model'; +import { LanguageService } from '../../../../core/services/language/language.service'; @Component({ selector: 'app-product-detail-information-tab', @@ -15,4 +16,6 @@ export class ProductDetailInformationTabComponent { productDetail!: ProductDetail; @Input() selectedVersion!: string; + + languageService = inject(LanguageService); } diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-maven-content/product-detail-maven-content.component.html b/marketplace-ui/src/app/modules/product/product-detail/product-detail-maven-content/product-detail-maven-content.component.html index ef973f8cf..15e185a93 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-maven-content/product-detail-maven-content.component.html +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-maven-content/product-detail-maven-content.component.html @@ -1,4 +1,4 @@ -

+

<!-- {{ productModuleContent.name }} -->
@@ -17,4 +17,4 @@   <type>{{ productModuleContent.type }}</type>
</dependency> -
+ \ No newline at end of file diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail-maven-content/product-detail-maven-content.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail-maven-content/product-detail-maven-content.component.ts index 9d46efffb..a645e68e9 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail-maven-content/product-detail-maven-content.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail-maven-content/product-detail-maven-content.component.ts @@ -1,6 +1,7 @@ -import { Component, Input } from '@angular/core'; +import { Component, inject, Input } from '@angular/core'; import { TranslateModule } from '@ngx-translate/core'; import { ProductModuleContent } from '../../../../shared/models/product-module-content.model'; +import { LanguageService } from '../../../../core/services/language/language.service'; @Component({ selector: 'app-product-detail-maven-content', @@ -14,4 +15,6 @@ export class ProductDetailMavenContentComponent { productModuleContent!: ProductModuleContent; @Input() selectedVersion!: string; + + languageService = inject(LanguageService); } 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 28d71be0a..ee74b0136 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 @@ -1,11 +1,12 @@ - @if(!isDesignerEnvironment()) { - } @@ -15,7 +16,7 @@
-
-
- + @if (isDevVersionsDisplayed()) { {{ 'common.product.detail.download.hideDevVersions' | translate }} } @else { @@ -54,7 +57,7 @@ }
-
- +
-

+

{{ productDetail().names | multilingualism: languageService.selectedLanguage() }}

-
+
- +
-

+

{{ 'common.product.detail.type' | translate }}

@@ -52,8 +67,8 @@

height="72" alt="Message Star" />

- {{ 'common.feedback.noFeedbackMessage1' | translate }} + + {{ 'common.feedback.noFeedbackMessage1' | translate }} +
- {{ 'common.feedback.noFeedbackMessage2' | translate }} + + {{ 'common.feedback.noFeedbackMessage2' | translate }} +

diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.scss b/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.scss index 688a50ff7..a17334e1f 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.scss +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.scss @@ -57,6 +57,7 @@ ul, li { font-size: 18px; font-weight: 400; + color: var(--ivy-text-primary-color); } ul, li { diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.ts index 9ba2e1a79..f942f5444 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-detail.component.ts @@ -29,7 +29,7 @@ import { AuthService } from '../../../auth/auth.service'; import { ProductStarRatingNumberComponent } from './product-star-rating-number/product-star-rating-number.component'; import { ProductInstallationCountActionComponent } from './product-installation-count-action/product-installation-count-action.component'; import { ProductTypeIconPipe } from '../../../shared/pipes/icon.pipe'; -import { Observable } from 'rxjs'; +import { interval, Observable } from 'rxjs'; import { ProductStarRatingService } from './product-detail-feedback/product-star-rating-panel/product-star-rating.service'; import { RoutingQueryParamService } from '../../../shared/services/routing.query.param.service'; @@ -103,6 +103,7 @@ export class ProductDetailComponent { this.updateDropdownSelection(); } + constructor() { this.scrollToTop(); this.resizeObserver = new ResizeObserver(() => { diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-installation-count-action/product-installation-count-action.component.html b/marketplace-ui/src/app/modules/product/product-detail/product-installation-count-action/product-installation-count-action.component.html index 60b474ae8..98a1a2082 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-installation-count-action/product-installation-count-action.component.html +++ b/marketplace-ui/src/app/modules/product/product-detail/product-installation-count-action/product-installation-count-action.component.html @@ -1,9 +1,9 @@
-

+

{{ 'common.product.detail.installation' | translate }}

{{this.currentInstallationCount}}

-

+

{{ 'common.product.detail.times' | translate }}

-
+
\ No newline at end of file diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-installation-count-action/product-installation-count-action.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-installation-count-action/product-installation-count-action.component.ts index 6b40a0e5a..dd7be48d5 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-installation-count-action/product-installation-count-action.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-installation-count-action/product-installation-count-action.component.ts @@ -1,5 +1,6 @@ -import {Component, Input} from '@angular/core'; +import {Component, inject, Input} from '@angular/core'; import {TranslateModule} from "@ngx-translate/core"; +import { LanguageService } from '../../../../core/services/language/language.service'; @Component({ selector: 'app-product-installation-count-action', @@ -14,4 +15,6 @@ import {TranslateModule} from "@ngx-translate/core"; export class ProductInstallationCountActionComponent { @Input() currentInstallationCount!: number; + + languageService = inject(LanguageService); } diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-star-rating-number/product-star-rating-number.component.html b/marketplace-ui/src/app/modules/product/product-detail/product-star-rating-number/product-star-rating-number.component.html index a669dd5f7..6a211ed0e 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-star-rating-number/product-star-rating-number.component.html +++ b/marketplace-ui/src/app/modules/product/product-detail/product-star-rating-number/product-star-rating-number.component.html @@ -1,9 +1,8 @@
+ [class]="isShowRateLink ? 'star-rating-min-width' : ''">

@@ -39,7 +38,10 @@

}

@if (isShowRateLink) { -
+ {{ 'common.feedback.rateLinkLabel' | translate }} } diff --git a/marketplace-ui/src/app/modules/product/product-detail/product-star-rating-number/product-star-rating-number.component.ts b/marketplace-ui/src/app/modules/product/product-detail/product-star-rating-number/product-star-rating-number.component.ts index 4516546ff..74413de3f 100644 --- a/marketplace-ui/src/app/modules/product/product-detail/product-star-rating-number/product-star-rating-number.component.ts +++ b/marketplace-ui/src/app/modules/product/product-detail/product-star-rating-number/product-star-rating-number.component.ts @@ -7,6 +7,7 @@ import { import { CommonModule } from '@angular/common'; import { AuthService } from '../../../../auth/auth.service'; import { ProductDetailService } from '../product-detail.service'; +import { LanguageService } from '../../../../core/services/language/language.service'; @Component({ selector: 'app-product-star-rating-number', @@ -19,6 +20,7 @@ export class ProductStarRatingNumberComponent { productStarRatingService = inject(ProductStarRatingService); private readonly productDetailService = inject(ProductDetailService); private readonly authService = inject(AuthService); + languageService = inject(LanguageService); @Input() isShowRateLink = true; @Input() isShowTotalRatingNumber = true; diff --git a/marketplace-ui/src/app/modules/product/product-filter/product-filter.component.html b/marketplace-ui/src/app/modules/product/product-filter/product-filter.component.html index f63101c8b..b1f7f936f 100644 --- a/marketplace-ui/src/app/modules/product/product-filter/product-filter.component.html +++ b/marketplace-ui/src/app/modules/product/product-filter/product-filter.component.html @@ -1,15 +1,12 @@
-

+

{{ translateService.get('common.filter.label') | async }}

@for (type of types; track $index) { -
-

+

{{ type.label | translate }}

}
- @for (type of types; track $index) { - } @@ -39,19 +32,14 @@
-
-

+
+

{{ translateService.get('common.sort.label') | async }}:

- @for (type of sorts; track $index) { - } @@ -62,18 +50,13 @@

- +
- + [ariaLabel]="translateService.get('common.search.placeholder') | async" aria-describedby="search" />
-

+

\ No newline at end of file diff --git a/marketplace-ui/src/app/modules/product/product-filter/product-filter.component.ts b/marketplace-ui/src/app/modules/product/product-filter/product-filter.component.ts index c2d9cfc66..b57b92246 100644 --- a/marketplace-ui/src/app/modules/product/product-filter/product-filter.component.ts +++ b/marketplace-ui/src/app/modules/product/product-filter/product-filter.component.ts @@ -6,6 +6,7 @@ import { ThemeService } from '../../../core/services/theme/theme.service'; import { FILTER_TYPES, SORT_TYPES } from '../../../shared/constants/common.constant'; import { TypeOption } from '../../../shared/enums/type-option.enum'; import { SortOption } from '../../../shared/enums/sort-option.enum'; +import { LanguageService } from '../../../core/services/language/language.service'; @Component({ selector: 'app-product-filter', @@ -28,6 +29,7 @@ export class ProductFilterComponent { themeService = inject(ThemeService); translateService = inject(TranslateService); + languageService = inject(LanguageService); onSelectType(type: TypeOption) { this.selectedType = type; diff --git a/marketplace-ui/src/app/modules/product/product.component.html b/marketplace-ui/src/app/modules/product/product.component.html index 337828546..f91611b02 100644 --- a/marketplace-ui/src/app/modules/product/product.component.html +++ b/marketplace-ui/src/app/modules/product/product.component.html @@ -1,7 +1,7 @@
@if (!isRESTClient()) {
-

+

{{ translateService.get('common.branch') | async }}

@@ -10,10 +10,11 @@

-

+

{{ translateService.get('common.introduction.contribute') | async }}

} @if (products().length > 0) { -
- @for (product of products(); track $index) { -
- -
- } +
+ @for (product of products(); track $index) { +
+
+ } +
} @else { -
-
- Search not found -
-
+
+
+ Search not found +
+
}
-
+
\ No newline at end of file diff --git a/marketplace-ui/src/app/shared/components/footer/footer.component.html b/marketplace-ui/src/app/shared/components/footer/footer.component.html index 6c8b11172..42daf8830 100644 --- a/marketplace-ui/src/app/shared/components/footer/footer.component.html +++ b/marketplace-ui/src/app/shared/components/footer/footer.component.html @@ -1,30 +1,25 @@
-
+
- -
@@ -37,12 +32,12 @@ @for (item of navItems; track $index) { @@ -52,15 +47,14 @@
-
+
-
- + \ No newline at end of file diff --git a/marketplace-ui/src/app/shared/components/footer/footer.component.ts b/marketplace-ui/src/app/shared/components/footer/footer.component.ts index 3ae857a18..0c16ce98f 100644 --- a/marketplace-ui/src/app/shared/components/footer/footer.component.ts +++ b/marketplace-ui/src/app/shared/components/footer/footer.component.ts @@ -4,6 +4,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { ThemeService } from '../../../core/services/theme/theme.service'; import { IVY_FOOTER_LINKS, NAV_ITEMS, SOCIAL_MEDIA_LINK } from '../../constants/common.constant'; import { NavItem } from '../../models/nav-item.model'; +import { LanguageService } from '../../../core/services/language/language.service'; @Component({ selector: 'app-footer', @@ -14,6 +15,7 @@ import { NavItem } from '../../models/nav-item.model'; }) export class FooterComponent { themeService = inject(ThemeService); + languageService = inject(LanguageService); socialMediaLinks = SOCIAL_MEDIA_LINK; navItems: NavItem[] = NAV_ITEMS; ivyFooterLinks = IVY_FOOTER_LINKS; diff --git a/marketplace-ui/src/app/shared/components/header/navigation/navigation.component.html b/marketplace-ui/src/app/shared/components/header/navigation/navigation.component.html index 1b4f513c6..0b2fb3815 100644 --- a/marketplace-ui/src/app/shared/components/header/navigation/navigation.component.html +++ b/marketplace-ui/src/app/shared/components/header/navigation/navigation.component.html @@ -2,14 +2,11 @@ @@ -17,20 +14,14 @@ -

+
\ No newline at end of file diff --git a/marketplace-ui/src/app/shared/components/header/navigation/navigation.component.ts b/marketplace-ui/src/app/shared/components/header/navigation/navigation.component.ts index 858d48ce7..a8db15d56 100644 --- a/marketplace-ui/src/app/shared/components/header/navigation/navigation.component.ts +++ b/marketplace-ui/src/app/shared/components/header/navigation/navigation.component.ts @@ -3,6 +3,7 @@ import { Component, inject, Input } from '@angular/core'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { NAV_ITEMS } from '../../../constants/common.constant'; import { NavItem } from '../../../models/nav-item.model'; +import { LanguageService } from '../../../../core/services/language/language.service'; @Component({ selector: 'app-navigation', @@ -15,4 +16,5 @@ export class NavigationComponent { @Input() navItems: NavItem[] = NAV_ITEMS; translateService = inject(TranslateService); + languageService = inject(LanguageService); } diff --git a/marketplace-ui/src/app/shared/components/header/search-bar/search-bar.component.html b/marketplace-ui/src/app/shared/components/header/search-bar/search-bar.component.html index 38b2df478..fad70609a 100644 --- a/marketplace-ui/src/app/shared/components/header/search-bar/search-bar.component.html +++ b/marketplace-ui/src/app/shared/components/header/search-bar/search-bar.component.html @@ -4,33 +4,27 @@
  • - - - + + +
    - + " /> -
    - - - +
    + + +
  • } @else { -
    +
  • @@ -46,19 +40,16 @@ @if (isSearchBarDisplayed()) { } @else {
    @@ -69,13 +60,13 @@
  • -
  • } - + \ No newline at end of file diff --git a/marketplace-ui/src/app/shared/components/header/search-bar/search-bar.component.ts b/marketplace-ui/src/app/shared/components/header/search-bar/search-bar.component.ts index caf326ec2..fd530437b 100644 --- a/marketplace-ui/src/app/shared/components/header/search-bar/search-bar.component.ts +++ b/marketplace-ui/src/app/shared/components/header/search-bar/search-bar.component.ts @@ -3,6 +3,7 @@ import { Component, ElementRef, HostListener, inject, signal } from '@angular/co import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { LanguageSelectionComponent } from '../language-selection/language-selection.component'; import { ThemeSelectionComponent } from '../theme-selection/theme-selection.component'; +import { LanguageService } from '../../../../core/services/language/language.service'; @Component({ selector: 'app-search-bar', @@ -21,6 +22,7 @@ export class SearchBarComponent { translateService = inject(TranslateService); elementRef = inject(ElementRef); + languageService = inject(LanguageService); @HostListener('document:click', ['$event']) handleClickOutside(event: MouseEvent) { diff --git a/marketplace-ui/src/assets/scss/custom-style.scss b/marketplace-ui/src/assets/scss/custom-style.scss index 78961f4cb..b9a1c1633 100644 --- a/marketplace-ui/src/assets/scss/custom-style.scss +++ b/marketplace-ui/src/assets/scss/custom-style.scss @@ -62,8 +62,8 @@ p { --ivy-active-color: #{$ivyPrimaryColorLight}; --ivy-link-corlor: #{$ivyPrimaryColorLight}; --ivy-text-normal-color: #{$ivyNormalTextColorLight}; - --ivy-text-primary-color: $ivyPrimaryTextColorLight; - --ivy-text-secondary-color: $ivySecondaryTextLight; + --ivy-text-primary-color: #{$ivyPrimaryTextColorLight}; + --ivy-text-secondary-color: #{$ivySecondaryTextLight}; --ivy-border-color: #{$ivySecondaryButtonHoverLight}; --ivy-textarea-background-color: #FAFAFA; --header-border-color: #ebebeb; @@ -256,11 +256,6 @@ p { cursor: pointer; } -*:focus { - box-shadow: none !important; - outline: none !important; -} - .card { padding: 20px; margin: 0 32px 8px 0;