From 034bbc2035c3adaaa9a87266f19f7814a9ea14ae Mon Sep 17 00:00:00 2001 From: nntthuy-axonivy Date: Fri, 26 Jul 2024 15:38:20 +0700 Subject: [PATCH] Update service for rest client --- .../market/controller/ProductController.java | 12 +++ .../market/repository/ProductRepository.java | 5 +- .../market/service/ProductService.java | 2 + .../service/impl/ProductServiceImpl.java | 13 +++ .../src/app/cookie.management.service.ts | 64 +++++++++++---- .../modules/product/product.component.html | 48 +++++------ .../app/modules/product/product.component.ts | 80 +++++++++++++++---- .../app/modules/product/product.service.ts | 23 +++++- .../app/shared/constants/common.constant.ts | 6 +- .../src/app/shared/enums/request-param.ts | 5 +- 10 files changed, 199 insertions(+), 59 deletions(-) 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 7f8b7d5cd..9ee5ade39 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 @@ -53,6 +53,18 @@ public ResponseEntity> findProducts(@RequestParam(name return new ResponseEntity<>(pageResources, HttpStatus.OK); } + @GetMapping("/designer") + public ResponseEntity> findProductsInDesigner(@RequestParam(required = false, name = "search") String search, + Pageable pageable) { + Page results = productService.findProductsInDesigner(search, pageable); + if (results.isEmpty()) { + return generateEmptyPagedModel(); + } + var responseContent = new PageImpl<>(results.getContent(), pageable, results.getTotalElements()); + var pageResources = pagedResourcesAssembler.toModel(responseContent, assembler); + return new ResponseEntity<>(pageResources, HttpStatus.OK); + } + @PutMapping(SYNC) public ResponseEntity syncProducts(@RequestHeader(value = "Authorization") String authorizationHeader, @RequestParam(value = "resetSync", required = false) Boolean resetSync) { diff --git a/marketplace-service/src/main/java/com/axonivy/market/repository/ProductRepository.java b/marketplace-service/src/main/java/com/axonivy/market/repository/ProductRepository.java index b638e30ea..74130b845 100644 --- a/marketplace-service/src/main/java/com/axonivy/market/repository/ProductRepository.java +++ b/marketplace-service/src/main/java/com/axonivy/market/repository/ProductRepository.java @@ -16,8 +16,6 @@ public interface ProductRepository extends MongoRepository { Product findByLogoUrl(String logoUrl); - Product findByIdAndType(String id, String type); - Optional findById(String productId); @Query("{'marketDirectory': {$regex : ?0, $options: 'i'}}") @@ -28,4 +26,7 @@ public interface ProductRepository extends MongoRepository { @Query("{ $or: [ { 'names.?1': { $regex: ?0, $options: 'i' } }, { 'shortDescriptions.?1': { $regex: ?0, $options: 'i' } } ] }") Page searchByNameOrShortDescriptionRegex(String keyword, String language, Pageable unifiedPageabe); + + @Query("{ $and: [ { $or: [ { 'names.?': { $regex: ?0, $options: 'i' } } ] }") + Page searchByNameAndType(String search, String type, String language, Pageable unifiedPageable); } 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 87a57f2d3..a7e2a3ab5 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,6 +7,8 @@ public interface ProductService { Page findProducts(String type, String keyword, String language, Pageable pageable); + Page findProductsInDesigner(String search, Pageable pageable); + boolean syncLatestDataFromMarketRepo(); int updateInstallationCountForProduct(String key); 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 cf1c86090..d40f0a0f8 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 @@ -6,6 +6,7 @@ import com.axonivy.market.entity.Product; import com.axonivy.market.entity.ProductModuleContent; import com.axonivy.market.enums.FileType; +import com.axonivy.market.enums.Language; import com.axonivy.market.enums.SortOption; import com.axonivy.market.enums.TypeOption; import com.axonivy.market.factory.ProductFactory; @@ -107,6 +108,18 @@ public Page findProducts(String type, String keyword, String language, return result; } + @Override + public Page findProductsInDesigner(String search, Pageable pageable) { + final var searchPageable = refinePagination(Language.EN.getValue(), pageable); + Page result; + if (StringUtils.isBlank(search)) { + result = productRepository.findByType(TypeOption.CONNECTORS.getCode(), searchPageable); + } else { + result = productRepository.searchByNameAndType(search, TypeOption.CONNECTORS.getCode(), Language.EN.getValue(), searchPageable); + } + return result; + } + @Override public boolean syncLatestDataFromMarketRepo() { var isAlreadyUpToDate = isLastGithubCommitCovered(); diff --git a/marketplace-ui/src/app/cookie.management.service.ts b/marketplace-ui/src/app/cookie.management.service.ts index ea65c8e34..d6c4b0b44 100644 --- a/marketplace-ui/src/app/cookie.management.service.ts +++ b/marketplace-ui/src/app/cookie.management.service.ts @@ -1,12 +1,11 @@ - -import { computed, Injectable, Signal, signal } from '@angular/core'; +import { computed, Injectable, signal } from '@angular/core'; import { CookieService } from 'ngx-cookie-service'; import { DESIGNER_COOKIE_VARIABLE } from './shared/constants/common.constant'; import { Router, - NavigationEnd, Params, - NavigationStart + NavigationStart, + ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { filter } from 'rxjs/operators'; @@ -14,22 +13,49 @@ import { filter } from 'rxjs/operators'; providedIn: 'root' }) export class CookieManagementService { - private isDesigner = signal(false); + isDesigner = signal(false); isDesignerEnv = computed(() => this.isDesigner()); designerVersion = signal(''); + resultsOnly = WritableS + isResultsOnly = computed(() => this.resultsOnly()); - constructor(private cookieService: CookieService, private router: Router) { - this.getNavigationStartEvent().subscribe(()=> { + constructor( + private cookieService: CookieService, + private router: Router, + private route: ActivatedRoute + ) { + this.getNavigationStartEvent().subscribe(() => { if (!this.isDesigner()) { - this.isDesigner.set(this.cookieService.get(DESIGNER_COOKIE_VARIABLE.ivyViewerParamName) == DESIGNER_COOKIE_VARIABLE.defaultDesignerViewer); + this.isDesigner.set( + this.cookieService.get(DESIGNER_COOKIE_VARIABLE.ivyViewerParamName) == + DESIGNER_COOKIE_VARIABLE.defaultDesignerViewer + ); } - }) + }); + } + + ngOnInit(): void { + // Accessing query parameters + this.resultsOnly = this.route.snapshot.queryParamMap.has('resultsOnly'); + console.log('resultsOnly:', this.resultsOnly); } + // ngOnInit(): void { + // this.route.queryParams.subscribe(params => { + // if (params['resultsOnly']) { + // this.resultsOnly.set(true); + // console.log('resultsOnly:', this.resultsOnly); + // } + // }); + // } + checkCookieForDesignerVersion(params: Params) { const versionParam = params[DESIGNER_COOKIE_VARIABLE.ivyVersionParamName]; if (versionParam != undefined) { - this.cookieService.set(DESIGNER_COOKIE_VARIABLE.ivyVersionParamName, versionParam); + this.cookieService.set( + DESIGNER_COOKIE_VARIABLE.ivyVersionParamName, + versionParam + ); this.designerVersion.set(versionParam); } } @@ -37,28 +63,36 @@ export class CookieManagementService { checkCookieForDesignerEnv(params: Params) { const ivyViewerParam = params[DESIGNER_COOKIE_VARIABLE.ivyViewerParamName]; if (ivyViewerParam == DESIGNER_COOKIE_VARIABLE.defaultDesignerViewer) { - this.cookieService.set(DESIGNER_COOKIE_VARIABLE.ivyViewerParamName, ivyViewerParam); + this.cookieService.set( + DESIGNER_COOKIE_VARIABLE.ivyViewerParamName, + ivyViewerParam + ); this.isDesigner.set(true); } } getDesignerVersionFromCookie() { if (this.designerVersion() == '') { - this.designerVersion.set(this.cookieService.get(DESIGNER_COOKIE_VARIABLE.ivyVersionParamName)) + this.designerVersion.set( + this.cookieService.get(DESIGNER_COOKIE_VARIABLE.ivyVersionParamName) + ); } return this.designerVersion(); } isDesignerViewer() { if (!this.isDesigner()) { - this.isDesigner.set(this.cookieService.get(DESIGNER_COOKIE_VARIABLE.ivyViewerParamName) == DESIGNER_COOKIE_VARIABLE.defaultDesignerViewer); + this.isDesigner.set( + this.cookieService.get(DESIGNER_COOKIE_VARIABLE.ivyViewerParamName) == + DESIGNER_COOKIE_VARIABLE.defaultDesignerViewer + ); } return this.isDesigner(); } - getNavigationStartEvent():Observable{ + getNavigationStartEvent(): Observable { return this.router.events.pipe( filter(event => event instanceof NavigationStart) ) as Observable; } -} \ No newline at end of file +} diff --git a/marketplace-ui/src/app/modules/product/product.component.html b/marketplace-ui/src/app/modules/product/product.component.html index 2cc8ff608..6f2978d1e 100644 --- a/marketplace-ui/src/app/modules/product/product.component.html +++ b/marketplace-ui/src/app/modules/product/product.component.html @@ -1,29 +1,31 @@
-
-

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

-
-

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

-
-
-

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

-

+ @if (!isRestClient()) { +
+

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

+
+

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

+
+
+

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

+

+
-
- + + } @if (products().length > 0) {
diff --git a/marketplace-ui/src/app/modules/product/product.component.ts b/marketplace-ui/src/app/modules/product/product.component.ts index 3117e6d7b..83a416523 100644 --- a/marketplace-ui/src/app/modules/product/product.component.ts +++ b/marketplace-ui/src/app/modules/product/product.component.ts @@ -10,7 +10,7 @@ import { WritableSignal } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { NavigationStart, Router } from '@angular/router'; +import { ActivatedRoute, NavigationStart, Router } from '@angular/router'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { debounceTime, Subject, Subscription } from 'rxjs'; import { ThemeService } from '../../core/services/theme/theme.service'; @@ -27,6 +27,7 @@ import { Page } from '../../shared/models/apis/page.model'; import { Language } from '../../shared/enums/language.enum'; import { ProductDetail } from '../../shared/models/product-detail.model'; import { LanguageService } from '../../core/services/language/language.service'; +import { CookieManagementService } from '../../cookie.management.service'; const SEARCH_DEBOUNCE_TIME = 500; @@ -55,30 +56,58 @@ export class ProductComponent implements AfterViewInit, OnDestroy { sort: SortOption.POPULARITY, language: Language.EN }; + + designerCriteria: Criteria = { + search: '', + type: TypeOption.CONNECTORS, + sort: SortOption.POPULARITY, + language: Language.EN + }; + responseLink!: Link; responsePage!: Page; + isRestClient = signal(false); productService = inject(ProductService); themeService = inject(ThemeService); translateService = inject(TranslateService); languageService = inject(LanguageService); + cookieService = inject(CookieManagementService); router = inject(Router); @ViewChild('observer', { static: true }) observerElement!: ElementRef; constructor() { - this.loadProductItems(); - this.subscriptions.push( - this.searchTextChanged - .pipe(debounceTime(SEARCH_DEBOUNCE_TIME)) - .subscribe(value => { - this.criteria = { - ...this.criteria, - search: value - }; - this.loadProductItems(true); - }) - ); + this.isRestClient.set(this.cookieService.isResultsOnly()); + console.log(this.isRestClient()); + if (this.isRestClient()) { + this.loadProductInDesigner(); + this.subscriptions.push( + this.searchTextChanged + .pipe(debounceTime(SEARCH_DEBOUNCE_TIME)) + .subscribe(value => { + this.designerCriteria = { + ...this.designerCriteria, + search: value + }; + this.loadProductInDesigner(true); + }) + ); + } else { + this.loadProductItems(); + this.subscriptions.push( + this.searchTextChanged + .pipe(debounceTime(SEARCH_DEBOUNCE_TIME)) + .subscribe(value => { + this.criteria = { + ...this.criteria, + search: value + }; + this.loadProductItems(true); + }) + ); + } + this.router.events?.subscribe(event => { if (!(event instanceof NavigationStart)) { return; @@ -137,13 +166,36 @@ export class ProductComponent implements AfterViewInit, OnDestroy { ); } + loadProductInDesigner(shouldCleanData = false) { + this.subscriptions.push( + this.productService + .findProductsInDesignerByCriteria(this.designerCriteria) + .subscribe((response: ProductApiResponse) => { + const newProducts = response._embedded.products; + if (shouldCleanData) { + this.products.set(newProducts); + } else { + this.products.update(existingProducts => + existingProducts.concat(newProducts) + ); + } + this.responseLink = response._links; + this.responsePage = response.page; + }) + ); + } + setupIntersectionObserver() { const options = { root: null, rootMargin: '0px', threshold: 0.1 }; const observer = new IntersectionObserver(entries => { entries.forEach(entry => { if (entry.isIntersecting && this.hasMore()) { this.criteria.nextPageHref = this.responseLink?.next?.href; - this.loadProductItems(); + if (this.isRestClient()) { + this.loadProductInDesigner(); + } else { + this.loadProductItems(); + } } }); }, options); diff --git a/marketplace-ui/src/app/modules/product/product.service.ts b/marketplace-ui/src/app/modules/product/product.service.ts index 3d93052a5..8ef51ec6e 100644 --- a/marketplace-ui/src/app/modules/product/product.service.ts +++ b/marketplace-ui/src/app/modules/product/product.service.ts @@ -9,6 +9,7 @@ import { ProductDetail } from '../../shared/models/product-detail.model'; import { VersionData } from '../../shared/models/vesion-artifact.model'; const PRODUCT_API_URL = 'api/product'; +const PRODUCT_IN_DESIGNER_URL = 'api/product/designer'; @Injectable() export class ProductService { httpClient = inject(HttpClient); @@ -31,6 +32,24 @@ export class ProductService { }); } + findProductsInDesignerByCriteria( + criteria: Criteria + ): Observable { + let requestParams = new HttpParams(); + let requestURL = PRODUCT_IN_DESIGNER_URL; + if (criteria.nextPageHref) { + requestURL = criteria.nextPageHref; + } else { + requestParams = requestParams.set( + RequestParam.SEARCH, + `${criteria.search}` + ); + } + return this.httpClient.get(requestURL, { + params: requestParams + }); + } + getProductDetailsWithVersion( productId: string, tag: string @@ -65,6 +84,8 @@ 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/constants/common.constant.ts b/marketplace-ui/src/app/shared/constants/common.constant.ts index 1bd976def..a63a0fd01 100644 --- a/marketplace-ui/src/app/shared/constants/common.constant.ts +++ b/marketplace-ui/src/app/shared/constants/common.constant.ts @@ -164,5 +164,7 @@ export const FEEDBACK_SORT_TYPES = [ export const DESIGNER_COOKIE_VARIABLE = { ivyViewerParamName: 'ivy-viewer', ivyVersionParamName: 'ivy-version', - defaultDesignerViewer: 'designer-market' -}; \ No newline at end of file + defaultDesignerViewer: 'designer-market', + restClientParamName: 'resultsOnly', + typeParamName: 'type' +}; diff --git a/marketplace-ui/src/app/shared/enums/request-param.ts b/marketplace-ui/src/app/shared/enums/request-param.ts index 207ea34f9..c07de64d1 100644 --- a/marketplace-ui/src/app/shared/enums/request-param.ts +++ b/marketplace-ui/src/app/shared/enums/request-param.ts @@ -2,5 +2,6 @@ export enum RequestParam { TYPE = 'type', KEYWORD = 'keyword', SORT = 'sort', - LANGUAGE = 'language' -} \ No newline at end of file + LANGUAGE = 'language', + SEARCH = 'search' +}