Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/marp 315 open rest editor detail pages from within ai designer #75

Merged
Show file tree
Hide file tree
Changes from 50 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
5f7bc47
test
ntqdinh-axonivy Jul 19, 2024
8e844c4
Revert "test"
ntqdinh-axonivy Jul 19, 2024
a218a31
update service
ntqdinh-axonivy Jul 22, 2024
43516fe
update query
ntqdinh-axonivy Jul 22, 2024
9a5a8a7
update service
ntqdinh-axonivy Jul 22, 2024
9e7d407
update service
ntqdinh-axonivy Jul 22, 2024
97514a8
Merge branch 'develop' into feature/MARP-661-Open-Marketplace-from-wi…
ntqdinh-axonivy Jul 22, 2024
59a5c1f
update service
ntqdinh-axonivy Jul 22, 2024
bf1e43f
refactor service
ntqdinh-axonivy Jul 23, 2024
121342a
update service
ntqdinh-axonivy Jul 23, 2024
661b860
Merge branch 'develop' into feature/MARP-661-Open-Marketplace-from-wi…
ntqdinh-axonivy Jul 26, 2024
b4a3639
refactor code
ntqdinh-axonivy Jul 26, 2024
73ad495
update test case
ntqdinh-axonivy Jul 26, 2024
034bbc2
Update service for rest client
nntthuy-axonivy Jul 26, 2024
5d311e5
update test
ntqdinh-axonivy Jul 26, 2024
087ab8f
handle sonnar
ntqdinh-axonivy Jul 26, 2024
f8a5ae7
Merge branch 'develop' into feature/MARP-661-Open-Marketplace-from-wi…
ntqdinh-axonivy Jul 26, 2024
64529e5
update test
ntqdinh-axonivy Jul 26, 2024
02cbe3a
remove unused prop
ntqdinh-axonivy Jul 26, 2024
0125351
Update product-detail.component.spec.ts
ntqdinh-axonivy Jul 26, 2024
8b1e183
handle sonar
ntqdinh-axonivy Jul 26, 2024
da0645a
Update routing.query.param.service.ts
ntqdinh-axonivy Jul 26, 2024
bd69836
Merge branch 'develop' into feature/MARP-661-Open-Marketplace-from-wi…
ntqdinh-axonivy Jul 26, 2024
4dc6a9b
Update ProductDetailsControllerTest.java
ntqdinh-axonivy Jul 26, 2024
c83cd3e
update test
ntqdinh-axonivy Jul 26, 2024
7aa53de
Update product-detail.component.ts
ntqdinh-axonivy Jul 26, 2024
b6cc2b6
update test
ntqdinh-axonivy Jul 26, 2024
c7042ec
update test
ntqdinh-axonivy Jul 28, 2024
6d1d544
update test
ntqdinh-axonivy Jul 28, 2024
36ffc6e
update test
ntqdinh-axonivy Jul 29, 2024
0994528
fix typo and unsued import
ntqdinh-axonivy Jul 29, 2024
232c650
update java warning
ntqdinh-axonivy Jul 29, 2024
44fa6aa
Merge branch 'feature/MARP-661-Open-Marketplace-from-within-AxonIvy' …
nntthuy-axonivy Jul 29, 2024
3b72a95
Customize UI with conditions
nntthuy-axonivy Jul 29, 2024
2be062d
Update docker files
nqhoan-axonivy Jul 30, 2024
f69a1ee
Merge branch 'develop' of https://github.com/axonivy-market/marketpla…
nqhoan-axonivy Jul 30, 2024
86d9aa5
Implemnent search by Criteria
nqhoan-axonivy Jul 30, 2024
98bd7e6
Adapt current service
nntthuy-axonivy Jul 30, 2024
ec2278b
Fix Or collection is empty
nqhoan-axonivy Jul 30, 2024
b504093
Merge branch 'feature/MARP-700-Authenticate-for-SYNC-products-api' in…
nntthuy-axonivy Jul 30, 2024
51bec3e
Unify code
nqhoan-axonivy Jul 30, 2024
731328d
Fix Sonar
nqhoan-axonivy Jul 30, 2024
07bd014
Adapt current service logic to rest-client
nntthuy-axonivy Jul 30, 2024
608322d
Merge branch 'feature/MARP-700-Authenticate-for-SYNC-products-api' in…
nntthuy-axonivy Jul 30, 2024
3c280d9
Merge branch 'develop' into feature/MARP-315-Open-REST-editor-detail-…
nntthuy-axonivy Jul 31, 2024
d0535ed
Update product service
nntthuy-axonivy Aug 1, 2024
50872eb
Merge branch 'develop' into feature/MARP-315-Open-REST-editor-detail-…
nntthuy-axonivy Aug 1, 2024
a9ca0f2
Remove duplicated and format codes
nntthuy-axonivy Aug 1, 2024
9f380b7
Merge branch 'develop' into feature/MARP-315-Open-REST-editor-detail-…
nntthuy-axonivy Aug 1, 2024
8abef23
Add swagger doc for isRestDesigner
nntthuy-axonivy Aug 1, 2024
bed1afb
Handle feedbacks
nntthuy-axonivy Aug 1, 2024
801a096
Handle feedbacks
nntthuy-axonivy Aug 1, 2024
fdc09f2
Handle feedbacks
nntthuy-axonivy Aug 6, 2024
fb3ecb3
Merge branch 'develop' into feature/MARP-315-Open-REST-editor-detail-…
nntthuy-axonivy Aug 6, 2024
1d4cde8
Fix test
nntthuy-axonivy Aug 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +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";
nntthuy-axonivy marked this conversation as resolved.
Show resolved Hide resolved
public static final String USER_ID = "userId";
public static final String AUTHORIZATION = "Authorization";
public static final String X_AUTHORIZATION = "X-Authorization";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
package com.axonivy.market.controller;

import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT;
import static com.axonivy.market.constants.RequestMappingConstants.SYNC;
import static com.axonivy.market.constants.RequestParamConstants.AUTHORIZATION;
import static com.axonivy.market.constants.RequestParamConstants.KEYWORD;
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 io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Schema;
Expand All @@ -24,6 +16,7 @@
import com.axonivy.market.service.ProductService;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;

import org.apache.commons.lang3.time.StopWatch;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Page;
Expand All @@ -43,6 +36,14 @@
import org.springframework.web.bind.annotation.RestController;

import static com.axonivy.market.constants.RequestMappingConstants.CUSTOM_SORT;
import static com.axonivy.market.constants.RequestMappingConstants.PRODUCT;
import static com.axonivy.market.constants.RequestMappingConstants.SYNC;
import static com.axonivy.market.constants.RequestParamConstants.AUTHORIZATION;
import static com.axonivy.market.constants.RequestParamConstants.KEYWORD;
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;

@RestController
@RequestMapping(PRODUCT)
Expand Down Expand Up @@ -72,8 +73,9 @@ public ResponseEntity<PagedModel<ProductModel>> 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,
nntthuy-axonivy marked this conversation as resolved.
Show resolved Hide resolved
@ParameterObject Pageable pageable) {
Page<Product> results = productService.findProducts(type, keyword, language, pageable);
Page<Product> results = productService.findProducts(type, keyword, language, isRestDesigner, pageable);
if (results.isEmpty()) {
return generateEmptyPagedModel();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.springframework.data.domain.Pageable;

public interface ProductService {
Page<Product> findProducts(String type, String keyword, String language, Pageable pageable);
Page<Product> findProducts(String type, String keyword, String language, Boolean isRestDesigner, Pageable pageable);

boolean syncLatestDataFromMarketRepo();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.SecureRandom;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;

Expand Down Expand Up @@ -103,15 +105,20 @@ public ProductServiceImpl(ProductRepository productRepository, GHAxonIvyMarketRe
}

@Override
public Page<Product> findProducts(String type, String keyword, String language, Pageable pageable) {
public Page<Product> findProducts(String type, String keyword, String language, Boolean isRestDesigner,
Pageable pageable) {
final var typeOption = TypeOption.of(type);
final var searchPageable = refinePagination(language, pageable);
var searchCriteria = new ProductSearchCriteria();
searchCriteria.setType(typeOption);
searchCriteria.setListed(true);
searchCriteria.setKeyword(keyword);
searchCriteria.setLanguage(Language.of(language));
searchCriteria.setType(typeOption);
if (BooleanUtils.isTrue(isRestDesigner)) {
searchCriteria.setType(TypeOption.CONNECTORS);
searchCriteria.setLanguage(Language.of(Locale.ENGLISH.toLanguageTag()));
nntthuy-axonivy marked this conversation as resolved.
Show resolved Hide resolved
} else {
searchCriteria.setType(typeOption);
searchCriteria.setLanguage(Language.of(language));
}
return productRepository.searchByCriteria(searchCriteria, searchPageable);
}

Expand Down Expand Up @@ -216,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;
nntthuy-axonivy marked this conversation as resolved.
Show resolved Hide resolved
}
}

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:
nntthuy-axonivy marked this conversation as resolved.
Show resolved Hide resolved
productRepository.save(product);
break;
case REMOVED:
productRepository.deleteById(product.getId());
break;
default:
break;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ void setup() {
void testFindProductsAsEmpty() {
PageRequest pageable = PageRequest.of(0, 20);
Page<Product> mockProducts = new PageImpl<>(List.of(), pageable, 0);
when(service.findProducts(any(), any(), any(), any())).thenReturn(mockProducts);
when(service.findProducts(any(), any(), any(), any() , any())).thenReturn(mockProducts);
when(pagedResourcesAssembler.toEmptyModel(any(), any())).thenReturn(PagedModel.empty());
var result = productController.findProducts(TypeOption.ALL.getOption(), null, "en", pageable);
var result = productController.findProducts(TypeOption.ALL.getOption(), null, "en", false, pageable);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertTrue(result.hasBody());
assertEquals(0, Objects.requireNonNull(result.getBody()).getContent().size());
Expand All @@ -87,12 +87,12 @@ void testFindProducts() {
Product mockProduct = createProductMock();

Page<Product> mockProducts = new PageImpl<>(List.of(mockProduct), pageable, 1);
when(service.findProducts(any(), any(), any(), any())).thenReturn(mockProducts);
when(service.findProducts(any(), any(), any(), any(), any())).thenReturn(mockProducts);
assembler = new ProductModelAssembler();
var mockProductModel = assembler.toModel(mockProduct);
var mockPagedModel = PagedModel.of(List.of(mockProductModel), new PageMetadata(1, 0, 1));
when(pagedResourcesAssembler.toModel(any(), any(ProductModelAssembler.class))).thenReturn(mockPagedModel);
var result = productController.findProducts(TypeOption.ALL.getOption(), "", "en", pageable);
var result = productController.findProducts(TypeOption.ALL.getOption(), "", "en", false, pageable);
assertEquals(HttpStatus.OK, result.getStatusCode());
assertTrue(result.hasBody());
assertEquals(1, Objects.requireNonNull(result.getBody()).getContent().size());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,17 +178,17 @@ void testFindProducts() {
// Start testing by All
when(productRepository.searchByCriteria(any(), any(Pageable.class))).thenReturn(mockResultReturn);
// Executes
var result = productService.findProducts(TypeOption.ALL.getOption(), keyword, language, PAGEABLE);
var result = productService.findProducts(TypeOption.ALL.getOption(), keyword, language, false, PAGEABLE);
assertEquals(mockResultReturn, result);

// Start testing by Connector
// Executes
result = productService.findProducts(TypeOption.CONNECTORS.getOption(), keyword, language, PAGEABLE);
result = productService.findProducts(TypeOption.CONNECTORS.getOption(), keyword, language, true, PAGEABLE);
assertEquals(mockResultReturn, result);

// Start testing by Other
// Executes
result = productService.findProducts(TypeOption.DEMOS.getOption(), keyword, language, PAGEABLE);
result = productService.findProducts(TypeOption.DEMOS.getOption(), keyword, language, false, PAGEABLE);
assertEquals(2, result.getSize());
}

Expand Down Expand Up @@ -260,7 +260,7 @@ void testFindAllProductsWithKeyword() {
language = "en";
when(productRepository.searchByCriteria(any(), any(Pageable.class))).thenReturn(mockResultReturn);
// Executes
var result = productService.findProducts(TypeOption.ALL.getOption(), keyword, language, PAGEABLE);
var result = productService.findProducts(TypeOption.ALL.getOption(), keyword, language, false, PAGEABLE);
assertEquals(mockResultReturn, result);
verify(productRepository).searchByCriteria(any(), any(Pageable.class));

Expand All @@ -270,7 +270,7 @@ void testFindAllProductsWithKeyword() {
.filter(product -> product.getNames().get(Language.EN.getValue()).equals(SAMPLE_PRODUCT_NAME))
.collect(Collectors.toList())));
// Executes
result = productService.findProducts(TypeOption.ALL.getOption(), SAMPLE_PRODUCT_NAME, language, PAGEABLE);
result = productService.findProducts(TypeOption.ALL.getOption(), SAMPLE_PRODUCT_NAME, language, false, PAGEABLE);
assertTrue(result.hasContent());
assertEquals(SAMPLE_PRODUCT_NAME, result.getContent().get(0).getNames().get(Language.EN.getValue()));

Expand All @@ -281,7 +281,8 @@ void testFindAllProductsWithKeyword() {
&& product.getType().equals(TypeOption.CONNECTORS.getCode()))
.collect(Collectors.toList())));
// Executes
result = productService.findProducts(TypeOption.CONNECTORS.getOption(), SAMPLE_PRODUCT_NAME, language, PAGEABLE);
result =
productService.findProducts(TypeOption.CONNECTORS.getOption(), SAMPLE_PRODUCT_NAME, language, false, PAGEABLE);
assertTrue(result.hasContent());
assertEquals(SAMPLE_PRODUCT_NAME, result.getContent().get(0).getNames().get(Language.EN.getValue()));
}
Expand Down Expand Up @@ -345,7 +346,7 @@ void testSearchProducts() {
when(productRepository.searchByCriteria(any(), any(Pageable.class))).thenReturn(
mockResultReturn);

var result = productService.findProducts(type, keyword, language, simplePageable);
var result = productService.findProducts(type, keyword, language, false, simplePageable);
assertEquals(result, mockResultReturn);
verify(productRepository).searchByCriteria(any(), any(Pageable.class));
}
Expand Down
13 changes: 11 additions & 2 deletions marketplace-ui/src/app/modules/home/home.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,27 @@ import {
withInterceptorsFromDi
} from '@angular/common/http';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';

describe('HomeComponent', () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
let activatedRoute: ActivatedRoute;
nntthuy-axonivy marked this conversation as resolved.
Show resolved Hide resolved

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [HomeComponent, TranslateModule.forRoot()],
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
TranslateService
TranslateService,
{
provide: ActivatedRoute,
useValue: {
queryParams: of({})
}
}
]
}).compileComponents();

Expand All @@ -30,4 +39,4 @@ describe('HomeComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
});
});
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
<div class="card product-card w-100">
<div
class="card product-card w-100"
[style.height]="!isProductInRestClient ? '250px' : '164px'">
<div class="d-flex justify-content-between align-items-start">
<img
class="card-img-top rounded"
width="70"
height="70"
[ngSrc]="product | logo"
[alt]="product.names | multilingualism: languageService.selectedLanguage()" />
<div
class="card__tag lh-md px-2 py-1"
[ngClass]="themeService.isDarkMode() ? 'text-dark' : 'text-light'">
{{ 'common.filter.value.' + product.type | translate }}
</div>
[alt]="
product.names | multilingualism: languageService.selectedLanguage()
nntthuy-axonivy marked this conversation as resolved.
Show resolved Hide resolved
" />

@if (!isProductInRestClient) {
<div
class="card__tag lh-md px-2 py-1"
[ngClass]="themeService.isDarkMode() ? 'text-dark' : 'text-light'">
{{ 'common.filter.value.' + product.type | translate }}
</div>
}
@if (isProductInRestClient) {
<div
class="card__tag lh-md px-2 py-1 text-capitalize"
[ngClass]="themeService.isDarkMode() ? 'text-dark' : 'text-light'">
{{ product.tags[0] }}
</div>
}
</div>
<div>
<h5 class="card__title text-primary">
{{
product.names | multilingualism: languageService.selectedLanguage()
}}
{{ product.names | multilingualism: languageService.selectedLanguage() }}
</h5>
<p class="card__description text-secondary">
{{
product.shortDescriptions
| multilingualism: languageService.selectedLanguage()
}}
</p>
@if (!isProductInRestClient) {
<p class="card__description text-secondary">
{{
product.shortDescriptions
| multilingualism: languageService.selectedLanguage()
}}
</p>
}
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
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';
import { ProductComponent } from '../product.component';
import { ProductService } from '../product.service';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import {
provideHttpClient,
withInterceptorsFromDi
} from '@angular/common/http';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';

const products = MOCK_PRODUCTS._embedded.products as Product[];
const noDeNameAndNoLogoUrlProducts =
Expand All @@ -12,11 +24,20 @@ const noDeNameAndNoLogoUrlProducts =
describe('ProductCardComponent', () => {
let component: ProductCardComponent;
let fixture: ComponentFixture<ProductCardComponent>;
let mockActivatedRoute: any;

beforeEach(async () => {
mockActivatedRoute = { queryParams: of({ showPopup: 'true' }) };
await TestBed.configureTestingModule({
imports: [ProductCardComponent, TranslateModule.forRoot()],
providers: [TranslateService]
providers: [
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting(),
TranslateService,
ProductService,
ProductComponent,
{ provide: ActivatedRoute, useValue: mockActivatedRoute }
]
}).compileComponents();

fixture = TestBed.createComponent(ProductCardComponent);
Expand Down
Loading
Loading