diff --git a/marketplace-service/src/main/java/com/axonivy/market/constants/RequestParamConstants.java b/marketplace-service/src/main/java/com/axonivy/market/constants/RequestParamConstants.java index 80d067f44..5a409c28b 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/constants/RequestParamConstants.java +++ b/marketplace-service/src/main/java/com/axonivy/market/constants/RequestParamConstants.java @@ -10,7 +10,7 @@ public class RequestParamConstants { public static final String TYPE = "type"; public static final String KEYWORD = "keyword"; public static final String LANGUAGE = "language"; - public static final String IS_REST_DESIGNER = "isRestDesigner"; + public static final String IS_REST_CLIENT = "isRESTClient"; public static final String USER_ID = "userId"; public static final String AUTHORIZATION = "Authorization"; public static final String X_AUTHORIZATION = "X-Authorization"; diff --git a/marketplace-service/src/main/java/com/axonivy/market/controller/ProductController.java b/marketplace-service/src/main/java/com/axonivy/market/controller/ProductController.java index 351af1b8a..42d213626 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/controller/ProductController.java +++ b/marketplace-service/src/main/java/com/axonivy/market/controller/ProductController.java @@ -43,7 +43,7 @@ import static com.axonivy.market.constants.RequestParamConstants.LANGUAGE; import static com.axonivy.market.constants.RequestParamConstants.RESET_SYNC; import static com.axonivy.market.constants.RequestParamConstants.TYPE; -import static com.axonivy.market.constants.RequestParamConstants.IS_REST_DESIGNER; +import static com.axonivy.market.constants.RequestParamConstants.IS_REST_CLIENT; @RestController @RequestMapping(PRODUCT) @@ -73,9 +73,9 @@ public ResponseEntity> findProducts( @RequestParam(name = TYPE) @Parameter(description = "Type of product.", in = ParameterIn.QUERY, schema = @Schema(type = "string", allowableValues = {"all", "connectors", "utilities", "solutions", "demos"})) String type, @RequestParam(required = false, name = KEYWORD) @Parameter(description = "Keyword that exist in product's name or short description", example = "connector", in = ParameterIn.QUERY) String keyword, @RequestParam(name = LANGUAGE) @Parameter(description = "Language of product short description", in = ParameterIn.QUERY, schema = @Schema(allowableValues = {"en", "de"})) String language, - @RequestParam(name = IS_REST_DESIGNER) @Parameter(description = "Option to get REST client environment", in = ParameterIn.QUERY) Boolean isRestDesigner, + @RequestParam(name = IS_REST_CLIENT) @Parameter(description = "Option to render the website in the REST Client Editor of Designer", in = ParameterIn.QUERY) Boolean isRESTClient, @ParameterObject Pageable pageable) { - Page results = productService.findProducts(type, keyword, language, isRestDesigner, pageable); + Page results = productService.findProducts(type, keyword, language, isRESTClient, pageable); if (results.isEmpty()) { return generateEmptyPagedModel(); } diff --git a/marketplace-service/src/main/java/com/axonivy/market/service/ProductService.java b/marketplace-service/src/main/java/com/axonivy/market/service/ProductService.java index c16352bc5..5a6581a90 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/service/ProductService.java +++ b/marketplace-service/src/main/java/com/axonivy/market/service/ProductService.java @@ -7,7 +7,7 @@ import org.springframework.data.domain.Pageable; public interface ProductService { - Page findProducts(String type, String keyword, String language, Boolean isRestDesigner, Pageable pageable); + Page findProducts(String type, String keyword, String language, Boolean isRESTClient, Pageable pageable); boolean syncLatestDataFromMarketRepo(); 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 4d327805a..5723980d3 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 @@ -1,6 +1,8 @@ package com.axonivy.market.service.impl; import static com.axonivy.market.enums.DocumentField.MARKET_DIRECTORY; +import static com.axonivy.market.enums.DocumentField.SHORT_DESCRIPTIONS; + import static java.util.Optional.ofNullable; import static org.apache.commons.lang3.StringUtils.EMPTY; @@ -14,7 +16,6 @@ import java.util.Comparator; import java.util.HashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Optional; @@ -105,16 +106,15 @@ public ProductServiceImpl(ProductRepository productRepository, GHAxonIvyMarketRe } @Override - public Page findProducts(String type, String keyword, String language, Boolean isRestDesigner, + public Page findProducts(String type, String keyword, String language, Boolean isRESTClient, Pageable pageable) { final var typeOption = TypeOption.of(type); final var searchPageable = refinePagination(language, pageable); var searchCriteria = new ProductSearchCriteria(); searchCriteria.setListed(true); searchCriteria.setKeyword(keyword); - if (BooleanUtils.isTrue(isRestDesigner)) { - searchCriteria.setType(TypeOption.CONNECTORS); - searchCriteria.setLanguage(Language.of(Locale.ENGLISH.toLanguageTag())); + if (BooleanUtils.isTrue(isRESTClient)) { + searchCriteria.setExcludeFields(List.of(SHORT_DESCRIPTIONS)); } else { searchCriteria.setType(typeOption); searchCriteria.setLanguage(Language.of(language)); @@ -223,37 +223,37 @@ private void updateLatestChangeToProductsFromGithubRepo() { private void modifyProductLogo(String parentPath, GitHubFile file, Product product, GHContent fileContent) { Product result; switch (file.getStatus()) { - case MODIFIED, ADDED: - var searchCriteria = new ProductSearchCriteria(); - searchCriteria.setKeyword(parentPath); - searchCriteria.setFields(List.of(MARKET_DIRECTORY)); - result = productRepository.findByCriteria(searchCriteria); - if (result != null) { - result.setLogoUrl(GitHubUtils.getDownloadUrl(fileContent)); - productRepository.save(result); - } - break; - case REMOVED: - result = productRepository.findByLogoUrl(product.getLogoUrl()); - if (result != null) { - productRepository.deleteById(result.getId()); - } - break; - default: - break; + case MODIFIED, ADDED: + var searchCriteria = new ProductSearchCriteria(); + searchCriteria.setKeyword(parentPath); + searchCriteria.setFields(List.of(MARKET_DIRECTORY)); + result = productRepository.findByCriteria(searchCriteria); + if (result != null) { + result.setLogoUrl(GitHubUtils.getDownloadUrl(fileContent)); + productRepository.save(result); + } + break; + case REMOVED: + result = productRepository.findByLogoUrl(product.getLogoUrl()); + if (result != null) { + productRepository.deleteById(result.getId()); + } + break; + default: + break; } } private void modifyProductByMetaContent(GitHubFile file, Product product) { switch (file.getStatus()) { - case MODIFIED, ADDED: - productRepository.save(product); - break; - case REMOVED: - productRepository.deleteById(product.getId()); - break; - default: - break; + case MODIFIED, ADDED: + productRepository.save(product); + break; + case REMOVED: + productRepository.deleteById(product.getId()); + break; + default: + break; } } 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 461a089f4..6301aa93d 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 @@ -3,6 +3,8 @@ import static com.axonivy.market.constants.CommonConstants.LOGO_FILE; import static com.axonivy.market.constants.CommonConstants.SLASH; import static com.axonivy.market.constants.MetaConstants.META_FILE; +import static com.axonivy.market.enums.DocumentField.SHORT_DESCRIPTIONS; + import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -31,6 +33,7 @@ import java.util.UUID; import java.util.stream.Collectors; +import com.axonivy.market.criteria.ProductSearchCriteria; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -120,6 +123,9 @@ class ProductServiceImplTest extends BaseSetup { @Captor ArgumentCaptor> productListArgumentCaptor; + @Captor + ArgumentCaptor productSearchCriteriaArgumentCaptor; + @InjectMocks private ProductServiceImpl productService; @@ -183,7 +189,7 @@ void testFindProducts() { // Start testing by Connector // Executes - result = productService.findProducts(TypeOption.CONNECTORS.getOption(), keyword, language, true, PAGEABLE); + result = productService.findProducts(TypeOption.CONNECTORS.getOption(), keyword, language, false, PAGEABLE); assertEquals(mockResultReturn, result); // Start testing by Other @@ -192,6 +198,13 @@ void testFindProducts() { assertEquals(2, result.getSize()); } + @Test + void testFindProductsInRESTClientOfDesigner() { + productService.findProducts(TypeOption.CONNECTORS.getOption(), keyword, Language.EN.getValue(), true, PAGEABLE); + verify(productRepository).searchByCriteria(productSearchCriteriaArgumentCaptor.capture(), any(Pageable.class)); + assertEquals(List.of(SHORT_DESCRIPTIONS), productSearchCriteriaArgumentCaptor.getValue().getExcludeFields()); + } + @Test void testSyncProductsAsUpdateMetaJSONFromGitHub() throws IOException { // Start testing by adding new meta 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 edb044273..bd404c3ba 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 @@ -1,36 +1,34 @@
+ [style.height]="!isShowInRESTClientEditor ? '250px' : '164px'">
- - @if (!isProductInRestClient) { + [alt]="product.names | multilingualism: languageService.selectedLanguage()" /> + @if (isShowInRESTClientEditor) {
- {{ 'common.filter.value.' + product.type | translate }} + {{ product.tags[0] }}
- } - @if (isProductInRestClient) { + } @else {
- {{ product.tags[0] }} + {{ 'common.filter.value.' + product.type | translate }}
}
- {{ product.names | multilingualism: languageService.selectedLanguage() }} + {{ + product.names | multilingualism: languageService.selectedLanguage() + }}
- @if (!isProductInRestClient) { + @if (!isShowInRESTClientEditor) {

{{ product.shortDescriptions diff --git a/marketplace-ui/src/app/modules/product/product-card/product-card.component.spec.ts b/marketplace-ui/src/app/modules/product/product-card/product-card.component.spec.ts index 29951a610..1163d6362 100644 --- a/marketplace-ui/src/app/modules/product/product-card/product-card.component.spec.ts +++ b/marketplace-ui/src/app/modules/product/product-card/product-card.component.spec.ts @@ -1,9 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; -import { - MOCK_EMPTY_DE_VALUES_AND_NO_LOGO_URL_PRODUCTS, - MOCK_PRODUCTS -} from '../../../shared/mocks/mock-data'; +import { MOCK_EMPTY_DE_VALUES_AND_NO_LOGO_URL_PRODUCTS, MOCK_PRODUCTS } from '../../../shared/mocks/mock-data'; import { ProductCardComponent } from './product-card.component'; import { Product } from '../../../shared/models/product.model'; import { Language } from '../../../shared/enums/language.enum'; @@ -16,6 +13,7 @@ import { } from '@angular/common/http'; import { ActivatedRoute } from '@angular/router'; import { of } from 'rxjs'; +import { By } from '@angular/platform-browser'; const products = MOCK_PRODUCTS._embedded.products as Product[]; const noDeNameAndNoLogoUrlProducts = @@ -69,4 +67,24 @@ describe('ProductCardComponent', () => { 'Amazon Comprehend is a AI service that uses machine learning to uncover information in unstructured data.' ); }); + + it('should display product tag in REST client', () => { + component.isShowInRESTClientEditor = true; + fixture.detectChanges(); + + const tagElement = fixture.debugElement.query(By.css('.card__tag')); + expect(tagElement).toBeTruthy(); + expect(tagElement.nativeElement.textContent).toContain('AI'); + }); + + it('should display product type in marketplace website', () => { + component.isShowInRESTClientEditor = false; + fixture.detectChanges(); + + const tagElement = fixture.debugElement.query(By.css('.card__tag')); + expect(tagElement).toBeTruthy(); + expect(tagElement.nativeElement.textContent).toContain( + 'common.filter.value.connector' + ); + }); }); diff --git a/marketplace-ui/src/app/modules/product/product-card/product-card.component.ts b/marketplace-ui/src/app/modules/product/product-card/product-card.component.ts index fd44ed75f..c302784a0 100644 --- a/marketplace-ui/src/app/modules/product/product-card/product-card.component.ts +++ b/marketplace-ui/src/app/modules/product/product-card/product-card.component.ts @@ -11,21 +11,14 @@ import { ProductComponent } from '../product.component'; @Component({ selector: 'app-product-card', standalone: true, - imports: [ - CommonModule, - ProductLogoPipe, - MultilingualismPipe, - TranslateModule, - NgOptimizedImage - ], + imports: [CommonModule, ProductLogoPipe, MultilingualismPipe, TranslateModule, NgOptimizedImage], templateUrl: './product-card.component.html', styleUrl: './product-card.component.scss' }) export class ProductCardComponent { themeService = inject(ThemeService); languageService = inject(LanguageService); - - isProductInRestClient = inject(ProductComponent).isRestClient(); + isShowInRESTClientEditor = inject(ProductComponent).isRestClient(); @Input() product!: Product; } diff --git a/marketplace-ui/src/app/modules/product/product.component.ts b/marketplace-ui/src/app/modules/product/product.component.ts index 62a2b74a0..c348ee169 100644 --- a/marketplace-ui/src/app/modules/product/product.component.ts +++ b/marketplace-ui/src/app/modules/product/product.component.ts @@ -54,7 +54,7 @@ export class ProductComponent implements AfterViewInit, OnDestroy { criteria: Criteria = { search: '', type: TypeOption.All_TYPES, - isRestDesigner: false, + isRESTClientEditor: false, sort: SortOption.STANDARD, language: Language.EN }; @@ -136,7 +136,9 @@ export class ProductComponent implements AfterViewInit, OnDestroy { loadProductItems(shouldCleanData = false) { this.criteria.language = this.languageService.selectedLanguage(); if (this.isRestClient()) { - this.criteria.isRestDesigner = true; + this.criteria.isRESTClientEditor = true; + this.criteria.language = Language.EN; + this.criteria.type = TypeOption.CONNECTORS; } this.subscriptions.push( diff --git a/marketplace-ui/src/app/modules/product/product.service.spec.ts b/marketplace-ui/src/app/modules/product/product.service.spec.ts index 1850a6020..70c766405 100644 --- a/marketplace-ui/src/app/modules/product/product.service.spec.ts +++ b/marketplace-ui/src/app/modules/product/product.service.spec.ts @@ -1,13 +1,7 @@ import { TestBed } from '@angular/core/testing'; -import { - provideHttpClient, - withInterceptorsFromDi -} from '@angular/common/http'; -import { - HttpTestingController, - provideHttpClientTesting -} from '@angular/common/http/testing'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; +import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing'; import { LoadingService } from '../../core/services/loading/loading.service'; import { Language } from '../../shared/enums/language.enum'; import { SortOption } from '../../shared/enums/sort-option.enum'; @@ -56,7 +50,7 @@ describe('ProductService', () => { sort: SortOption.ALPHABETICALLY, type: TypeOption.CONNECTORS, language: Language.EN, - isRestDesigner: false + isRESTClientEditor: false }; service.findProductsByCriteria(criteria).subscribe(response => { let products = response._embedded.products; @@ -78,7 +72,7 @@ describe('ProductService', () => { sort: null, type: null, language: Language.EN, - isRestDesigner: false + isRESTClientEditor: false }; service.findProductsByCriteria(criteria).subscribe(response => { expect(response._embedded.products.length).toEqual(products.length); @@ -91,7 +85,7 @@ describe('ProductService', () => { sort: SortOption.POPULARITY, type: null, language: Language.EN, - isRestDesigner: false + isRESTClientEditor: false }; service.findProductsByCriteria(criteria).subscribe(response => { let products = response._embedded.products; @@ -115,7 +109,7 @@ describe('ProductService', () => { sort: SortOption.RECENT, type: null, language: Language.EN, - isRestDesigner: false + isRESTClientEditor: false }; service.findProductsByCriteria(criteria).subscribe(response => { expect(response._embedded.products.length).toEqual(products.length); @@ -130,7 +124,7 @@ describe('ProductService', () => { sort: SortOption.RECENT, type: TypeOption.All_TYPES, language: Language.EN, - isRestDesigner: false + isRESTClientEditor: false }; service.findProductsByCriteria(criteria).subscribe(response => { expect(response._embedded.products.length).toEqual(0); @@ -188,17 +182,13 @@ describe('ProductService', () => { }); it('sendRequestToUpdateInstallationCount', () => { - const productId = 'google-maps-connector'; + const productId = "google-maps-connector"; - service - .sendRequestToUpdateInstallationCount(productId) - .subscribe(response => { - expect(response).toBe(3); - }); + service.sendRequestToUpdateInstallationCount(productId).subscribe(response => { + expect(response).toBe(3); + }); - const req = httpMock.expectOne( - `api/product-details/installationcount/${productId}` - ); + const req = httpMock.expectOne(`api/product-details/installationcount/${productId}`); expect(req.request.method).toBe('PUT'); expect(req.request.headers.get('X-Requested-By')).toBe('ivy'); req.flush(3); diff --git a/marketplace-ui/src/app/modules/product/product.service.ts b/marketplace-ui/src/app/modules/product/product.service.ts index 61696c3c0..a78a5599e 100644 --- a/marketplace-ui/src/app/modules/product/product.service.ts +++ b/marketplace-ui/src/app/modules/product/product.service.ts @@ -25,7 +25,7 @@ export class ProductService { .set(RequestParam.SORT, `${criteria.sort}`) .set(RequestParam.KEYWORD, `${criteria.search}`) .set(RequestParam.LANGUAGE, `${criteria.language}`) - .set(RequestParam.IS_REST_DESIGNER, `${criteria.isRestDesigner}`); + .set(RequestParam.IS_REST_CLIENT_EDITOR, `${criteria.isRESTClientEditor}`); } return this.httpClient.get(requestURL, { params: requestParams @@ -66,8 +66,6 @@ export class ProductService { sendRequestToUpdateInstallationCount(productId: string) { const url = 'api/product-details/installationcount/' + productId; - return this.httpClient.put(url, null, { - headers: { 'X-Requested-By': 'ivy' } - }); + return this.httpClient.put(url, null, { headers: { 'X-Requested-By': 'ivy' } }); } } diff --git a/marketplace-ui/src/app/shared/enums/request-param.ts b/marketplace-ui/src/app/shared/enums/request-param.ts index 1b97c9bf3..6e3e2943b 100644 --- a/marketplace-ui/src/app/shared/enums/request-param.ts +++ b/marketplace-ui/src/app/shared/enums/request-param.ts @@ -3,5 +3,5 @@ export enum RequestParam { KEYWORD = 'keyword', SORT = 'sort', LANGUAGE = 'language', - IS_REST_DESIGNER = 'isRestDesigner' + IS_REST_CLIENT_EDITOR = 'isRESTClient' } diff --git a/marketplace-ui/src/app/shared/models/criteria.model.ts b/marketplace-ui/src/app/shared/models/criteria.model.ts index 4504ea6bd..e82a9322e 100644 --- a/marketplace-ui/src/app/shared/models/criteria.model.ts +++ b/marketplace-ui/src/app/shared/models/criteria.model.ts @@ -7,6 +7,6 @@ export interface Criteria { sort: SortOption | null; type: TypeOption | null; language: Language; - isRestDesigner: boolean; + isRESTClientEditor: boolean; nextPageHref?: string; }