Skip to content

Commit

Permalink
Merge pull request #203 from axonivy-market/develop
Browse files Browse the repository at this point in the history
MARP-1252 Create release 1.3.0
  • Loading branch information
nqhoan-axonivy authored Oct 18, 2024
2 parents d45f12e + a391b04 commit 2f0d1c1
Show file tree
Hide file tree
Showing 120 changed files with 2,210 additions and 768 deletions.
9 changes: 1 addition & 8 deletions marketplace-build/config/nginx/dev/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ http {
server {
listen 80;
server_name marketplace;
server_tokens off;

root /usr/share/nginx/html;
index index.html;
Expand Down Expand Up @@ -36,14 +37,6 @@ http {
}
# End: Handle for DOCs

# Start: Handle for LIBs
# Workaround for static libs. e.g: https://market.axonivy.com/demos-app/dev/lib/ivy-demos-app.zip
location ~ ^/([^/]+)/([^/]+)/lib/(.*)$ {
alias /usr/share/nginx/html/cache/$1/$2/lib/;
try_files $3 =404;
}
# End: Handle for LIBs

error_page 403 /error-page;
}
}
9 changes: 1 addition & 8 deletions marketplace-build/config/nginx/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ http {
server {
listen 80;
server_name marketplace;
server_tokens off;

root /usr/share/nginx/html;
index index.html;
Expand Down Expand Up @@ -35,14 +36,6 @@ http {
}
# End: Handle for DOCs

# Start: Handle for LIBs
# Workaround for static libs. e.g: https://market.axonivy.com/demos-app/dev/lib/ivy-demos-app.zip
location ~ ^/([^/]+)/([^/]+)/lib/(.*)$ {
alias /usr/share/nginx/html/cache/$1/$2/lib/;
try_files $3 =404;
}
# End: Handle for LIBs

error_page 403 /error-page;
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.axonivy.market.assembler;

import com.axonivy.market.constants.RequestMappingConstants;
import com.axonivy.market.controller.ImageController;
import com.axonivy.market.controller.ProductDetailsController;
import com.axonivy.market.entity.Product;
import com.axonivy.market.model.ProductDetailModel;
Expand Down Expand Up @@ -67,6 +68,7 @@ private ProductDetailModel createModel(Product product, String version, String r

private void createDetailResource(ProductDetailModel model, Product product) {
model.setVendor(product.getVendor());
model.setVendorUrl(product.getVendorUrl());
model.setNewestReleaseVersion(product.getNewestReleaseVersion());
model.setPlatformReview(product.getPlatformReview());
model.setSourceUrl(product.getSourceUrl());
Expand All @@ -78,6 +80,15 @@ private void createDetailResource(ProductDetailModel model, Product product) {
model.setCost(product.getCost());
model.setInstallationCount(product.getInstallationCount());
model.setProductModuleContent(ImageUtils.mappingImageForProductModuleContent(product.getProductModuleContent()));
if (StringUtils.isNotBlank(product.getVendorImage())) {
Link vendorLink = linkTo(methodOn(ImageController.class).findImageById(product.getVendorImage())).withSelfRel();
model.setVendorImage(vendorLink.getHref());
}
if (StringUtils.isNotBlank(product.getVendorImageDarkMode())) {
Link vendorDarkModeLink =
linkTo(methodOn(ImageController.class).findImageById(product.getVendorImageDarkMode())).withSelfRel();
model.setVendorImageDarkMode(vendorDarkModeLink.getHref());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
public class ErrorMessageConstants {
public static final String INVALID_MISSING_HEADER_ERROR_MESSAGE = "Invalid or missing header";
public static final String CURRENT_CLIENT_ID_MISMATCH_MESSAGE = " Client ID mismatch (Request ID: %s, Server ID: %s)";
public static final String INVALID_USER_ERROR = "%s - User must be a member of team %s and organization %s";
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class GitHubConstants {
public static final String AXONIVY_MARKET_ORGANIZATION_NAME = "axonivy-market";
public static final String AXONIVY_MARKET_TEAM_NAME = "team-octopus";
public static final String AXONIVY_MARKETPLACE_REPO_NAME = "market";
public static final String AXONIVY_MARKETPLACE_PATH = "market";
public static final String GITHUB_PROVIDER_NAME = "GitHub";
public static final String GITHUB_GET_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token";
public static final String README_FILE_LOCALE_REGEX = "_(..)";
public static final String STANDARD_TAG_PREFIX = "v";
public static final String COMMON_IMAGES_FOLDER_NAME = "images";
public static final String MS_GRAPH_PRODUCT_DIRECTORY = "msgraph-connector-product";
public static final String MG_GRAPH_IMAGES_FOR_SETUP_FILE = "doc";

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public static class Json {
Expand All @@ -30,6 +33,5 @@ public static class Json {
public static class Url {
private static final String BASE_URL = "https://api.github.com";
public static final String USER = BASE_URL + "/user";
public static final String USER_ORGS = USER + "/orgs";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public class ReadmeConstants {
public static final String README_FILE_NAME = "README";
public static final String DEMO_PART = "## Demo";
public static final String SETUP_PART = "## Setup";
public static final String SETUP_FILE = "setup.md";
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public class RequestMappingConstants {
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_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";
public static final String AUTH = "/auth";
Expand All @@ -28,5 +29,6 @@ public class RequestMappingConstants {
public static final String VERSIONS_IN_DESIGNER = "/{id}/designerversions";
public static final String DESIGNER_INSTALLATION_BY_ID = "/installation/{id}/designer";
public static final String CUSTOM_SORT = "custom-sort";
public static final String LATEST_ARTIFACT_DOWNLOAD_URL_BY_ID = "/{id}/artifact";
public static final String EXTERNAL_DOCUMENT = API + "/externaldocument";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ public class RequestParamConstants {
public static final String SHOW_DEV_VERSION = "isShowDevVersion";
public static final String DESIGNER_VERSION = "designerVersion";
public static final String VERSION = "version";
public static final String ARTIFACT = "artifact";
public static final String MARKET_ITEM_PATH = "marketItemPath";
public static final String OVERRIDE_MARKET_ITEM_PATH = "overrideMarketItemPath";

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.axonivy.market.controller;

import com.axonivy.market.constants.GitHubConstants;
import com.axonivy.market.entity.ExternalDocumentMeta;
import com.axonivy.market.entity.Product;
import com.axonivy.market.enums.ErrorCode;
import com.axonivy.market.github.service.GitHubService;
import com.axonivy.market.model.ExternalDocumentModel;
import com.axonivy.market.model.Message;
import com.axonivy.market.service.ExternalDocumentService;
import com.axonivy.market.util.AuthorizationUtils;
Expand All @@ -13,7 +15,6 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -24,12 +25,12 @@
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

import static com.axonivy.market.constants.RequestMappingConstants.*;
import static com.axonivy.market.constants.RequestParamConstants.*;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;

@RestController
Expand All @@ -41,16 +42,22 @@ public class ExternalDocumentController {
final GitHubService gitHubService;

@GetMapping(BY_ID_AND_VERSION)
public ResponseEntity<URI> findExternalDocumentURI(
public ResponseEntity<ExternalDocumentModel> findExternalDocument(
@PathVariable(ID) @Parameter(description = "Product id (from meta.json)", example = "portal",
in = ParameterIn.PATH) String id,
@PathVariable(VERSION) @Parameter(description = "Release version (from maven metadata.xml)", example = "10.0.20",
in = ParameterIn.PATH) String version) throws URISyntaxException {
String externalDocumentURI = externalDocumentService.findExternalDocumentURI(id, version);
if (StringUtils.isBlank(externalDocumentURI)) {
in = ParameterIn.PATH) String version) {
ExternalDocumentMeta externalDocument = externalDocumentService.findExternalDocument(id, version);
if (externalDocument == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return new ResponseEntity<>(new URI(externalDocumentURI), HttpStatus.OK);

var model = ExternalDocumentModel.builder().productId(externalDocument.getProductId())
.version(externalDocument.getVersion()).relativeLink(externalDocument.getRelativeLink())
.artifactName(externalDocument.getArtifactName()).build();
model.add(linkTo(methodOn(ExternalDocumentController.class).findExternalDocument(id, version)).withSelfRel());

return new ResponseEntity<>(model, HttpStatus.OK);
}

@PutMapping(SYNC)
Expand All @@ -59,7 +66,8 @@ public ResponseEntity<Message> syncDocumentForProduct(
@RequestHeader(value = AUTHORIZATION) String authorizationHeader,
@RequestParam(value = RESET_SYNC, required = false) Boolean resetSync) {
String token = AuthorizationUtils.getBearerToken(authorizationHeader);
gitHubService.validateUserOrganization(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME);
gitHubService.validateUserInOrganizationAndTeam(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME,
GitHubConstants.AXONIVY_MARKET_TEAM_NAME);
var message = new Message();
List<Product> products = externalDocumentService.findAllProductsHaveDocument();
if (ObjectUtils.isEmpty(products)) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.axonivy.market.controller;

import com.axonivy.market.assembler.FeedbackModelAssembler;
import com.axonivy.market.constants.CommonConstants;
import com.axonivy.market.entity.Feedback;
import com.axonivy.market.model.FeedbackModel;
import com.axonivy.market.model.FeedbackModelRequest;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.axonivy.market.constants.GitHubConstants;
import com.axonivy.market.entity.Product;
import com.axonivy.market.enums.ErrorCode;
import com.axonivy.market.github.service.GHAxonIvyMarketRepoService;
import com.axonivy.market.github.service.GitHubService;
import com.axonivy.market.model.Message;
import com.axonivy.market.model.ProductCustomSortRequest;
Expand All @@ -19,6 +20,7 @@
import jakarta.validation.Valid;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Page;
Expand All @@ -28,10 +30,20 @@
import org.springframework.hateoas.PagedModel;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import static com.axonivy.market.constants.RequestMappingConstants.*;
import static com.axonivy.market.constants.RequestParamConstants.*;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;
Expand All @@ -46,6 +58,7 @@ public class ProductController {
private final ProductModelAssembler assembler;
private final PagedResourcesAssembler<Product> pagedResourcesAssembler;
private final MetadataService metadataService;
private final GHAxonIvyMarketRepoService axonIvyMarketRepoService;

@GetMapping()
@Operation(summary = "Retrieve a paginated list of all products, optionally filtered by type, keyword, and language",
Expand Down Expand Up @@ -85,7 +98,8 @@ public ResponseEntity<PagedModel<ProductModel>> findProducts(
public ResponseEntity<Message> syncProducts(@RequestHeader(value = AUTHORIZATION) String authorizationHeader,
@RequestParam(value = RESET_SYNC, required = false) Boolean resetSync) {
String token = AuthorizationUtils.getBearerToken(authorizationHeader);
gitHubService.validateUserOrganization(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME);
gitHubService.validateUserInOrganizationAndTeam(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME,
GitHubConstants.AXONIVY_MARKET_TEAM_NAME);
if (Boolean.TRUE.equals(resetSync)) {
productService.clearAllProducts();
}
Expand All @@ -108,9 +122,14 @@ public ResponseEntity<Message> syncProducts(@RequestHeader(value = AUTHORIZATION

@PutMapping(SYNC_PRODUCT_VERSION)
@Operation(hidden = true)
public ResponseEntity<Message> syncProductVersions(@RequestHeader(value = AUTHORIZATION) String authorizationHeader) {
public ResponseEntity<Message> syncProductVersions(@RequestHeader(value = AUTHORIZATION) String authorizationHeader
,@RequestParam(value = RESET_SYNC, required = false) Boolean resetSync) {
String token = AuthorizationUtils.getBearerToken(authorizationHeader);
gitHubService.validateUserOrganization(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME);
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;
Expand All @@ -125,13 +144,47 @@ public ResponseEntity<Message> syncProductVersions(@RequestHeader(value = AUTHOR
return new ResponseEntity<>(message, statusCode);
}

@PutMapping(SYNC_ONE_PRODUCT_BY_ID)
@Operation(hidden = true)
public ResponseEntity<Message> syncOneProduct(
@RequestHeader(value = AUTHORIZATION) String authorizationHeader,
@PathVariable(ID) @Parameter(description = "Product Id is defined in meta.json file", example = "a-trust",
in = ParameterIn.PATH) String productId,
@RequestParam(value = MARKET_ITEM_PATH) @Parameter(
description = "Item folder path of the market in https://github.com/axonivy-market/market",
example = "market/connector/a-trust") String marketItemPath,
@RequestParam(value = OVERRIDE_MARKET_ITEM_PATH, required = false) Boolean overrideMarketItemPath) {
String token = AuthorizationUtils.getBearerToken(authorizationHeader);
gitHubService.validateUserInOrganizationAndTeam(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME,
GitHubConstants.AXONIVY_MARKET_TEAM_NAME);

var message = new Message();
if (StringUtils.isNotBlank(marketItemPath) && Boolean.TRUE.equals(
overrideMarketItemPath) && CollectionUtils.isEmpty(
axonIvyMarketRepoService.getMarketItemByPath(marketItemPath))) {
message.setHelpCode(ErrorCode.PRODUCT_NOT_FOUND.getCode());
message.setMessageDetails(ErrorCode.PRODUCT_NOT_FOUND.getHelpText());
return new ResponseEntity<>(message, HttpStatus.OK);
}

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

@PostMapping(CUSTOM_SORT)
@Operation(hidden = true)
public ResponseEntity<Message> createCustomSortProducts(
@RequestHeader(value = AUTHORIZATION) String authorizationHeader,
@RequestBody @Valid ProductCustomSortRequest productCustomSortRequest) {
String token = AuthorizationUtils.getBearerToken(authorizationHeader);
gitHubService.validateUserOrganization(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME);
gitHubService.validateUserInOrganizationAndTeam(token, GitHubConstants.AXONIVY_MARKET_ORGANIZATION_NAME,
GitHubConstants.AXONIVY_MARKET_TEAM_NAME);
productService.addCustomSortProduct(productCustomSortRequest);
var message = new Message(ErrorCode.SUCCESSFUL.getCode(), ErrorCode.SUCCESSFUL.getHelpText(),
"Custom product sort order added successfully");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
Expand Down Expand Up @@ -127,4 +129,17 @@ public ResponseEntity<List<VersionAndUrlModel>> findVersionsForDesigner(@PathVar
List<VersionAndUrlModel> versionList = versionService.getVersionsForDesigner(id);
return new ResponseEntity<>(versionList, HttpStatus.OK);
}

@GetMapping(LATEST_ARTIFACT_DOWNLOAD_URL_BY_ID)
@Operation(summary = "Get the download url of latest version from artifact by its id and target version",
description = "Return the download url of artifact from version and id")
public ResponseEntity<String> getLatestArtifactDownloadUrl(
@PathVariable(value = ID) @Parameter(in = ParameterIn.PATH, example = "demos-app") String productId,
@RequestParam(value = VERSION) @Parameter(in = ParameterIn.QUERY, example = "10.0-dev") String version,
@RequestParam(value = ARTIFACT) @Parameter(in = ParameterIn.QUERY,
example = "ivy-demos-app.zip") String artifactId) {
String downloadUrl = versionService.getLatestVersionArtifactDownloadUrl(productId, version, artifactId);
HttpStatusCode statusCode = StringUtils.isBlank(downloadUrl) ? HttpStatus.NOT_FOUND : HttpStatus.OK;
return new ResponseEntity<>(downloadUrl, statusCode);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.axonivy.market.entity;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
Expand All @@ -17,11 +18,14 @@
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Document(EXTERNAL_DOCUMENT_META)
public class ExternalDocumentMeta {
@Id
private String id;
private String productId;
private String artifactId;
private String artifactName;
private String version;
private String storageDirectory;
private String relativeLink;
Expand Down
Loading

0 comments on commit 2f0d1c1

Please sign in to comment.