Skip to content

Commit

Permalink
Merge branch 'develop' into feature/MARP-1294-Create-a-central-monito…
Browse files Browse the repository at this point in the history
…ring-reporting-for-security-issues-of-axonivy-marketplace
  • Loading branch information
ndkhanh-axonivy committed Dec 15, 2024
2 parents 259ce0a + 4439b5d commit 2e834b1
Show file tree
Hide file tree
Showing 44 changed files with 770 additions and 329 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class RequestMappingConstants {
public static final String FEEDBACK = API + "/feedback";
public static final String IMAGE = API + "/image";
public static final String SYNC = "sync";
public static final String SYNC_PRODUCT_VERSION = SYNC + "/product-version";
public static final String SYNC_FIRST_PUBLISHED_DATE_ALL_PRODUCTS = SYNC + "/first-published-date";
public static final String SYNC_ONE_PRODUCT_BY_ID = "sync/{id}";
public static final String SWAGGER_URL = "/swagger-ui/index.html";
public static final String GIT_HUB_LOGIN = "/github/login";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,34 +112,6 @@ public ResponseEntity<Message> syncProducts(@RequestHeader(value = AUTHORIZATION
return new ResponseEntity<>(message, HttpStatus.OK);
}

/**
* @deprecated
*/
@Deprecated(forRemoval = true , since = "1.6.0")
@PutMapping(SYNC_PRODUCT_VERSION)
@Operation(hidden = true)
public ResponseEntity<Message> syncProductVersions(@RequestHeader(value = AUTHORIZATION) String authorizationHeader
,@RequestParam(value = RESET_SYNC, required = false) Boolean resetSync) {
String token = AuthorizationUtils.getBearerToken(authorizationHeader);
gitHubService.validateUserInOrganizationAndTeam(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME,
GitHubConstants.AXONIVY_MARKET_TEAM_NAME);
if (Boolean.TRUE.equals(resetSync)) {
productService.clearAllProductVersion();
}
int nonSyncResult = metadataService.syncAllProductsMetadata();
var message = new Message();
HttpStatus statusCode = HttpStatus.OK;
if(nonSyncResult == 1) {
message.setHelpCode(ErrorCode.SUCCESSFUL.getCode());
message.setHelpText(ErrorCode.SUCCESSFUL.getHelpText());
} else {
statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
message.setHelpCode(ErrorCode.MAVEN_VERSION_SYNC_FAILED.getCode());
message.setMessageDetails(ErrorCode.MAVEN_VERSION_SYNC_FAILED.getHelpText());
}
return new ResponseEntity<>(message, statusCode);
}

@PutMapping(SYNC_ONE_PRODUCT_BY_ID)
@Operation(hidden = true)
public ResponseEntity<Message> syncOneProduct(
Expand Down Expand Up @@ -173,6 +145,25 @@ public ResponseEntity<Message> syncOneProduct(
return new ResponseEntity<>(message, HttpStatus.OK);
}

@PutMapping(SYNC_FIRST_PUBLISHED_DATE_ALL_PRODUCTS)
@Operation(hidden = true)
public ResponseEntity<Message> syncFirstPublishedDateOfAllProducts(
@RequestHeader(value = AUTHORIZATION) String authorizationHeader) {
String token = AuthorizationUtils.getBearerToken(authorizationHeader);
gitHubService.validateUserInOrganizationAndTeam(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME,
GitHubConstants.AXONIVY_MARKET_TEAM_NAME);

var message = new Message();
var isSuccess = productService.syncFirstPublishedDateOfAllProducts();
if (isSuccess) {
message.setHelpCode(ErrorCode.SUCCESSFUL.getCode());
message.setMessageDetails("Sync successfully!");
} else {
message.setMessageDetails("Sync unsuccessfully!");
}
return new ResponseEntity<>(message, HttpStatus.OK);
}

@SuppressWarnings("unchecked")
private ResponseEntity<PagedModel<ProductModel>> generateEmptyPagedModel() {
var emptyPagedModel = (PagedModel<ProductModel>) pagedResourcesAssembler.toEmptyModel(Page.empty(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,20 +64,12 @@ public class Product implements Serializable {
@Transient
private int installationCount;
private Date newestPublishedDate;
private Date firstPublishedDate;
private String newestReleaseVersion;
@Transient
private ProductModuleContent productModuleContent;
private List<Artifact> artifacts;
/**
* @deprecated
*/
@Deprecated(forRemoval = true, since = "1.6.0")
private Boolean synchronizedInstallationCount;
/**
* @deprecated
*/
@Deprecated(forRemoval = true, since = "1.6.0")
private Integer customOrder;
private List<String> releasedVersions;
@Transient
private String metaProductJsonUrl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ public class ProductJsonContent {
@JsonIgnore
private String id;
private String version;
/**
* @deprecated
*/
@Deprecated(forRemoval = true, since = "1.5.0")
private String relatedTag;
private String productId;
private String name;
private String content;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
import java.io.Serializable;
import java.util.Date;
import java.util.Map;
import java.util.Set;

import static com.axonivy.market.constants.EntityConstants.PRODUCT_MODULE_CONTENT;

Expand All @@ -31,18 +30,6 @@ public class ProductModuleContent implements Serializable {
private static final long serialVersionUID = 1L;
@Schema(description = "product Id (from meta.json)", example = "portal")
private String productId;
/**
* @deprecated
*/
@Deprecated(forRemoval = true, since = "1.5.0")
@Schema(description = "Target release tag", example = "v10.0.25")
private String tag;
/**
* @deprecated
*/
@Deprecated(forRemoval = true, since = "1.5.0")
@Schema(description = "Versions in maven", example = "10.0.25-SNAPSHOT")
private Set<String> mavenVersions;
@Schema(description = "Maven version", example = "10.0.25")
private String version;
@Schema(description = "Product detail description content ",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
public enum SortOption {
POPULARITY("popularity", "marketplaceData.installationCount", Sort.Direction.DESC),
ALPHABETICALLY("alphabetically", "names", Sort.Direction.ASC),
RECENT("recent", "newestPublishedDate", Sort.Direction.DESC),
RECENT("recent", "firstPublishedDate", Sort.Direction.DESC),
STANDARD("standard", "marketplaceData.customOrder", Sort.Direction.DESC),
ID("id", "_id", Sort.Direction.ASC);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,5 @@ public interface ProductService {

boolean syncOneProduct(String productId, String marketItemPath, Boolean overrideMarketItemPath);

void clearAllProductVersion();
boolean syncFirstPublishedDateOfAllProducts();
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
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;
Expand All @@ -57,15 +58,7 @@
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.*;

import static com.axonivy.market.constants.CommonConstants.SLASH;
import static com.axonivy.market.constants.MavenConstants.*;
Expand Down Expand Up @@ -370,8 +363,8 @@ private List<String> syncProductsFromGitHubRepo(Boolean resetSync) {
} else if (productRepo.findById(product.getId()).isPresent()) {
continue;
}

updateProductContentForNonStandardProduct(ghContentEntity.getValue(), product);
updateFirstPublishedDate(product);
updateProductFromReleasedVersions(product);
transferComputedDataFromDB(product);
productMarketplaceDataRepo.checkAndInitProductMarketplaceDataIfNotExist(product.getId());
Expand Down Expand Up @@ -412,6 +405,53 @@ private String mapVendorImage(String productId, GHContent ghContent, String imag
return EMPTY;
}

private void updateFirstPublishedDate(Product product) {
try {
if (StringUtils.isNotBlank(product.getRepositoryName())) {
List<GHTag> gitHubTags = gitHubService.getRepositoryTags(product.getRepositoryName());
Date firstTagPublishedDate = getFirstTagPublishedDate(gitHubTags);
product.setFirstPublishedDate(firstTagPublishedDate);
}
} catch (IOException e) {
log.error("Get GH Tags failed: ", e);
}
}

private Date getFirstTagPublishedDate(List<GHTag> gitHubTags) {
Date firstTagPublishedDate = null;
try {
if (!CollectionUtils.isEmpty(gitHubTags)) {
List<GHTag> sortedTags = sortByTagCommitDate(gitHubTags);
GHCommit commit = sortedTags.get(0).getCommit();
if (commit != null) {
firstTagPublishedDate = commit.getCommitDate();
}
}
} catch (IOException e) {
log.error("Get first tag published date failed: ", e);
}

return firstTagPublishedDate;
}

private List<GHTag> sortByTagCommitDate(List<GHTag> gitHubTags) {
List<GHTag> sortedTags = new ArrayList<>(gitHubTags);
sortedTags.sort(Comparator.comparing(this::sortByCommitDate, Comparator.nullsLast(Comparator.naturalOrder())));
return sortedTags;
}

private Date sortByCommitDate(GHTag gitHubTag) {
Date commitDate = null;
try {
if (gitHubTag.getCommit() != null) {
commitDate = gitHubTag.getCommit().getCommitDate();
}
} catch (IOException e) {
log.error("Get commit date of tag commit failed: ", e);
}
return commitDate;
}

private void updateProductFromReleasedVersions(Product product) {
if (ObjectUtils.isEmpty(product.getArtifacts())) {
return;
Expand Down Expand Up @@ -536,7 +576,6 @@ private String createProductArtifactId(Artifact mavenArtifact) {
: mavenArtifact.getArtifactId().concat(PRODUCT_ARTIFACT_POSTFIX);
}


// Cover 3 cases after removing non-numeric characters (8, 11.1 and 10.0.2)
@Override
public String getCompatibilityFromOldestVersion(String oldestVersion) {
Expand Down Expand Up @@ -628,6 +667,7 @@ public boolean syncOneProduct(String productId, String marketItemPath, Boolean o
log.info("Update data of product {} from meta.json and logo files", productId);
mappingMetaDataAndLogoFromGHContent(gitHubContents, product);
updateProductContentForNonStandardProduct(gitHubContents, product);
updateFirstPublishedDate(product);
updateProductFromReleasedVersions(product);
productMarketplaceDataRepo.checkAndInitProductMarketplaceDataIfNotExist(productId);
productRepo.save(product);
Expand All @@ -640,13 +680,6 @@ public boolean syncOneProduct(String productId, String marketItemPath, Boolean o
return false;
}

@Override
public void clearAllProductVersion() {
metadataRepo.deleteAll();
metadataSyncRepo.deleteAll();
mavenArtifactVersionRepo.deleteAll();
}

private Product renewProductById(String productId, String marketItemPath, Boolean overrideMarketItemPath) {
Product product = new Product();
productRepo.findById(productId).ifPresent(foundProduct -> {
Expand Down Expand Up @@ -692,4 +725,28 @@ private void updateProductContentForNonStandardProduct(List<GHContent> ghContent
productModuleContentRepo.save(initialContent);
}
}

@Override
public boolean syncFirstPublishedDateOfAllProducts() {
try {
List<Product> products = productRepo.findAll();
if (!CollectionUtils.isEmpty(products)) {
for (Product product : products) {
if (product.getFirstPublishedDate() == null) {
log.info("sync FirstPublishedDate of product {} is starting ...", product.getId());
updateFirstPublishedDate(product);
productRepo.save(product);
log.info("Sync FirstPublishedDate of product {} is finished!", product.getId());
} else {
log.info("FirstPublishedDate of product {} is existing!", product.getId());
}
}
}
log.info("sync FirstPublishedDate of all products is finished!");
return true;
} catch (Exception e) {
log.error(e.getStackTrace());
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public class ProductContentUtils {
public static final String DESCRIPTION = "description";
public static final String DEMO = "demo";
public static final String SETUP = "setup";
public static final String README_IMAGE_FORMAT = "\\(([^)]*?%s[^)]*?)\\)";
public static final String README_IMAGE_FORMAT = "\\(([^)]*?/)?%s\\)";
public static final String IMAGE_DOWNLOAD_URL_FORMAT = "(%s)";

private ProductContentUtils() {
Expand Down Expand Up @@ -59,7 +59,7 @@ public static String getReadmeFileLocale(String readmeFile) {

// Cover some cases including when demo and setup parts switch positions or
// missing one of them
public static ReadmeContentsModel getExtractedPartsOfReadme( String readmeContents) {
public static ReadmeContentsModel getExtractedPartsOfReadme(String readmeContents) {
String[] parts = readmeContents.split(DEMO_SETUP_TITLE);
int demoIndex = readmeContents.indexOf(ReadmeConstants.DEMO_PART);
int setupIndex = readmeContents.indexOf(ReadmeConstants.SETUP_PART);
Expand All @@ -71,18 +71,20 @@ public static ReadmeContentsModel getExtractedPartsOfReadme( String readmeConten
description = removeFirstLine(parts[0]);
}

if (demoIndex != -1 && setupIndex != -1) {
if (parts.length == 2) {
if (demoIndex != -1) {
demo = parts[1];
} else {
setup = parts[1];
}
} else if (demoIndex != -1 && setupIndex != -1 && parts.length > 2) {
if (demoIndex < setupIndex) {
demo = parts[1];
setup = parts[2];
} else {
setup = parts[1];
demo = parts[2];
}
} else if (demoIndex != -1) {
demo = parts[1];
} else if (setupIndex != -1) {
setup = parts[1];
}

ReadmeContentsModel readmeContentsModel = new ReadmeContentsModel();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class BaseSetup {
protected static final String SAMPLE_PRODUCT_ID = "amazon-comprehend";
protected static final String SAMPLE_PRODUCT_PATH = "/market/connector/amazon-comprehend";
protected static final String SAMPLE_PRODUCT_NAME = "prody Comprehend";
protected static final String SAMPLE_PRODUCT_REPOSITORY_NAME = "axonivy-market/amazon-comprehend";
protected static final Pageable PAGEABLE = PageRequest.of(0, 20,
Sort.by(SortOption.ALPHABETICALLY.getOption()).descending());
protected static final String MOCK_PRODUCT_ID = "bpmn-statistic";
Expand All @@ -51,6 +52,8 @@ public class BaseSetup {
protected static final String MOCK_GROUP_ID = "com.axonivy.util";
protected static final String MOCK_PRODUCT_NAME = "bpmn statistic";
protected static final String MOCK_PRODUCT_REPOSITORY_NAME = "axonivy-market/bpmn-statistic";
protected static final String MOCK_IMAGE_ID_FORMAT_1 = "imageId-66e2b14868f2f95b2f95549a";
protected static final String MOCK_IMAGE_ID_FORMAT_2 = "imageId-66e2b14868f2f95b2f95550a";
protected static final String MOCK_PRODUCT_JSON_FILE_PATH = "src/test/resources/product.json";
protected static final String MOCK_PRODUCT_JSON_FILE_PATH_NO_URL = "src/test/resources/productMissingURL.json";
protected static final String MOCK_PRODUCT_JSON_WITH_DROPINS_FILE_PATH = "src/test/resources/product-dropins.json";
Expand All @@ -59,6 +62,10 @@ public class BaseSetup {
protected static final String MOCK_METADATA_FILE_PATH = "src/test/resources/metadata.xml";
protected static final String MOCK_SNAPSHOT_METADATA_FILE_PATH = "src/test/resources/snapshotMetadata.xml";
protected static final String MOCK_README_FILE = "src/test/resources/README.md";
protected static final String MOCK_README_DE_FILE = "src/test/resources/README_DE.md";
protected static final String MOCK_README_FILE_NO_DEMO_PART = "src/test/resources/README_NO_DEMO_PART.md";
protected static final String MOCK_README_FILE_NO_SETUP_PART = "src/test/resources/README_NO_SETUP_PART.md";
protected static final String MOCK_README_FILE_SWAP_DEMO_SETUP_PARTS = "src/test/resources/README_SWAP_DEMO_SETUP.md";
protected static final String INVALID_FILE_PATH = "test/file/path";
protected static final String MOCK_MAVEN_URL = "https://maven.axonivy.com/com/axonivy/util/bpmn-statistic/maven" +
"-metadata.xml";
Expand Down Expand Up @@ -134,6 +141,14 @@ protected static String getMockReadmeContent() {
return getContentFromTestResourcePath(MOCK_README_FILE);
}

protected static String getMockReadmeContent(String filePath) {
if (StringUtils.isBlank(filePath)) {
return getMockReadmeContent();
}

return getContentFromTestResourcePath(filePath);
}

protected Artifact getMockArtifact() {
Artifact mockArtifact = new Artifact();
mockArtifact.setIsDependency(true);
Expand Down
Loading

0 comments on commit 2e834b1

Please sign in to comment.