diff --git a/CHANGELOG.md b/CHANGELOG.md index c11125b51..7ec80d01f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,14 @@ # Changelog All notable changes to this project will be documented in this file. -## [10.7.1] +## [10.9.0] +### Changed +- Add product-document links in investment + +## [10.8.0] ### Changed - Updated stream-investment to be able to seed extra_data to investment portfolio -- fix content entry seeding issue +- fix content entry seeding issue in investment ## [10.7.0] ### Changed diff --git a/stream-investment/investment-core/pom.xml b/stream-investment/investment-core/pom.xml index 3728e1bff..a9035f09e 100644 --- a/stream-investment/investment-core/pom.xml +++ b/stream-investment/investment-core/pom.xml @@ -15,7 +15,7 @@ true - 1.6.2 + 1.9.0 diff --git a/stream-investment/investment-core/src/main/java/com/backbase/stream/configuration/InvestmentServiceConfiguration.java b/stream-investment/investment-core/src/main/java/com/backbase/stream/configuration/InvestmentServiceConfiguration.java index 954c55a85..b34aca44c 100644 --- a/stream-investment/investment-core/src/main/java/com/backbase/stream/configuration/InvestmentServiceConfiguration.java +++ b/stream-investment/investment-core/src/main/java/com/backbase/stream/configuration/InvestmentServiceConfiguration.java @@ -24,6 +24,7 @@ import com.backbase.stream.investment.service.InvestmentIntradayAssetPriceService; import com.backbase.stream.investment.service.InvestmentModelPortfolioService; import com.backbase.stream.investment.service.InvestmentPortfolioAllocationService; +import com.backbase.stream.investment.service.InvestmentPortfolioProductDocumentService; import com.backbase.stream.investment.service.InvestmentPortfolioProductService; import com.backbase.stream.investment.service.InvestmentPortfolioService; import com.backbase.stream.investment.service.InvestmentRiskAssessmentService; @@ -34,6 +35,7 @@ import com.backbase.stream.investment.service.resttemplate.InvestmentRestNewsContentService; import com.backbase.stream.investment.service.resttemplate.InvestmentRestProductPortfolioService; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -71,14 +73,23 @@ public InvestmentModelPortfolioService investmentModelPortfolioService(Financial portfolioProperties); } + @Bean + @Primary + public InvestmentPortfolioProductDocumentService investmentPortfolioProductDocumentService( + InvestmentProductsApi investmentProductsApi, + @Qualifier("restContentApi") com.backbase.investment.api.service.sync.v1.ContentApi restContentApi) { + return new InvestmentPortfolioProductDocumentService(investmentProductsApi, restContentApi); + } + @Bean @Primary public InvestmentPortfolioProductService investmentPortfolioProductService( InvestmentProductsApi investmentProductsApi, IngestConfigProperties portfolioProperties, InvestmentModelPortfolioService modelPortfolioService, - InvestmentRestProductPortfolioService investmentRestProductPortfolioService) { + InvestmentRestProductPortfolioService investmentRestProductPortfolioService, + InvestmentPortfolioProductDocumentService investmentPortfolioProductDocumentService) { return new InvestmentPortfolioProductService(investmentProductsApi, portfolioProperties, modelPortfolioService, - investmentRestProductPortfolioService); + investmentRestProductPortfolioService, investmentPortfolioProductDocumentService); } @Bean diff --git a/stream-investment/investment-core/src/main/java/com/backbase/stream/investment/ProductPortfolio.java b/stream-investment/investment-core/src/main/java/com/backbase/stream/investment/ProductPortfolio.java index c88a58247..b461d6620 100644 --- a/stream-investment/investment-core/src/main/java/com/backbase/stream/investment/ProductPortfolio.java +++ b/stream-investment/investment-core/src/main/java/com/backbase/stream/investment/ProductPortfolio.java @@ -51,5 +51,6 @@ public class ProductPortfolio { private ProductTypeEnum productType; @JsonProperty(JSON_PROPERTY_EXTRA_DATA) private Map extraData = new HashMap<>(); + private String document; } diff --git a/stream-investment/investment-core/src/main/java/com/backbase/stream/investment/service/InvestmentPortfolioProductDocumentService.java b/stream-investment/investment-core/src/main/java/com/backbase/stream/investment/service/InvestmentPortfolioProductDocumentService.java new file mode 100644 index 000000000..93a6dd80e --- /dev/null +++ b/stream-investment/investment-core/src/main/java/com/backbase/stream/investment/service/InvestmentPortfolioProductDocumentService.java @@ -0,0 +1,173 @@ +package com.backbase.stream.investment.service; + +import com.backbase.investment.api.service.sync.v1.ContentApi; +import com.backbase.investment.api.service.sync.v1.model.OASDocumentResponse; +import com.backbase.investment.api.service.sync.v1.model.PaginatedOASDocumentResponseList; +import com.backbase.investment.api.service.v1.InvestmentProductsApi; +import com.backbase.investment.api.service.v1.model.PortfolioProduct; +import com.backbase.investment.api.service.v1.model.PortfolioProductDocumentLinkRequest; +import com.backbase.investment.api.service.v1.model.PortfolioProductDocumentResponse; +import com.backbase.stream.investment.ProductPortfolio; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.StringUtils; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Links bootstrap content documents to portfolio products via the Investment Products API. + */ +@Slf4j +@RequiredArgsConstructor +public class InvestmentPortfolioProductDocumentService { + + private static final int CONTENT_RETRIEVE_LIMIT = 100; + + private final InvestmentProductsApi productsApi; + private final ContentApi contentApi; + + public static InvestmentPortfolioProductDocumentService noOp() { + return new InvestmentPortfolioProductDocumentService(null, null) { + @Override + public Mono linkProductDocuments(ProductPortfolio template, PortfolioProduct product) { + if (product == null) { + return Mono.empty(); + } + return Mono.just(product); + } + }; + } + + /** + * Links content documents to a portfolio product when missing. Existing links are left unchanged. + * + * @param template bootstrap product template containing newline-separated content document names + * @param product upserted portfolio product + * @return the unchanged product, or empty when linking is skipped + */ + public Mono linkProductDocuments(ProductPortfolio template, PortfolioProduct product) { + if (product == null) { + return Mono.empty(); + } + List documentNames = parseDocumentNames(template != null ? template.getDocument() : null); + if (documentNames.isEmpty()) { + return Mono.just(product); + } + + return Flux.fromIterable(documentNames) + .concatMap(this::findDocumentUuidByName) + .collectList() + .flatMap(documentUuids -> createMissingDocumentLinks(product, documentNames, documentUuids)) + .onErrorResume(error -> { + log.warn( + "Continuing without portfolio product document links: productUuid={}, name={}, reason={}", + product.getUuid(), product.getName(), error.getMessage()); + return Mono.just(product); + }); + } + + private Mono createMissingDocumentLinks(PortfolioProduct product, List documentNames, + List documentUuids) { + List resolvedUuids = documentUuids.stream().filter(Objects::nonNull).toList(); + if (resolvedUuids.isEmpty()) { + return Mono.just(product); + } + + return loadExistingLinkedDocumentUuids(product.getUuid()) + .flatMap(existingLinkedUuids -> { + List missingDocumentUuids = resolvedUuids.stream() + .filter(documentUuid -> !existingLinkedUuids.contains(documentUuid)) + .toList(); + + if (missingDocumentUuids.isEmpty()) { + log.info( + "All content documents already linked to portfolio product: productUuid={}, name={}, " + + "documentUuids={}", + product.getUuid(), product.getName(), resolvedUuids); + return Mono.just(product); + } + + List linkRequests = missingDocumentUuids.stream() + .map(uuid -> new PortfolioProductDocumentLinkRequest().document(uuid)) + .toList(); + + log.info( + "Linking {} missing content document(s) to portfolio product: productUuid={}, name={}, " + + "documentUuids={}", + linkRequests.size(), product.getUuid(), product.getName(), missingDocumentUuids); + + return productsApi.bulkCreatePortfolioProductDocuments(product.getUuid(), linkRequests) + .collectList() + .doOnSuccess(responses -> log.info( + "Linked content documents to portfolio product: productUuid={}, name={}, linkedCount={}", + product.getUuid(), product.getName(), responses.size())) + .doOnError(error -> log.error( + "Failed to link content documents to portfolio product: productUuid={}, name={}, " + + "documentNames={}", + product.getUuid(), product.getName(), documentNames, error)) + .thenReturn(product); + }); + } + + private Mono> loadExistingLinkedDocumentUuids(UUID productUuid) { + return loadExistingLinkedDocumentUuids(productUuid, 0, new HashSet<>()); + } + + private Mono> loadExistingLinkedDocumentUuids(UUID productUuid, int offset, Set accumulated) { + return productsApi.listPortfolioProductDocuments(productUuid, CONTENT_RETRIEVE_LIMIT, offset) + .flatMap(page -> { + if (page.getResults() != null) { + page.getResults().stream() + .filter(Objects::nonNull) + .map(PortfolioProductDocumentResponse::getUuid) + .forEach(accumulated::add); + } + if (page.getNext() == null) { + return Mono.just(accumulated); + } + return loadExistingLinkedDocumentUuids(productUuid, offset + CONTENT_RETRIEVE_LIMIT, accumulated); + }) + .defaultIfEmpty(accumulated); + } + + private List parseDocumentNames(String documentField) { + if (!StringUtils.hasText(documentField)) { + return List.of(); + } + return Arrays.stream(documentField.split("\\R")) + .map(String::trim) + .filter(StringUtils::hasText) + .toList(); + } + + private Mono findDocumentUuidByName(String documentName) { + return Mono.fromCallable(() -> lookupDocumentUuidByName(documentName)) + .flatMap(uuid -> uuid != null ? Mono.just(uuid) : Mono.empty()); + } + + private UUID lookupDocumentUuidByName(String documentName) { + PaginatedOASDocumentResponseList page = contentApi.listContentDocuments( + null, CONTENT_RETRIEVE_LIMIT, documentName, 0, null, null); + if (page == null || page.getResults() == null || page.getResults().isEmpty()) { + log.warn("Content document not found by name: name={}", documentName); + return null; + } + + return page.getResults().stream() + .filter(Objects::nonNull) + .filter(document -> documentName.equals(document.getName())) + .map(OASDocumentResponse::getUuid) + .findFirst() + .orElseGet(() -> { + log.warn("Content document name query returned no exact match: name={}", documentName); + return null; + }); + } + +} diff --git a/stream-investment/investment-core/src/main/java/com/backbase/stream/investment/service/InvestmentPortfolioProductService.java b/stream-investment/investment-core/src/main/java/com/backbase/stream/investment/service/InvestmentPortfolioProductService.java index ef423af57..4f1de596d 100644 --- a/stream-investment/investment-core/src/main/java/com/backbase/stream/investment/service/InvestmentPortfolioProductService.java +++ b/stream-investment/investment-core/src/main/java/com/backbase/stream/investment/service/InvestmentPortfolioProductService.java @@ -58,6 +58,7 @@ public class InvestmentPortfolioProductService { private final IngestConfigProperties config; private final InvestmentModelPortfolioService modelPortfolioService; private final InvestmentRestProductPortfolioService investmentRestProductPortfolioService; + private final InvestmentPortfolioProductDocumentService investmentPortfolioProductDocumentService; private final RestTemplateModelPortfolioMapper modelPortfolioMapper = Mappers.getMapper(RestTemplateModelPortfolioMapper.class); @@ -88,6 +89,7 @@ public Mono> upsertInvestmentProducts(InvestmentData inve .flatMap(p -> listExistingPortfolioProducts(p) .flatMap(existingProduct -> updateExistingPortfolioProduct(existingProduct, p, investmentData)) .switchIfEmpty(Mono.defer(() -> createPortfolioProductWithModel(p, investmentData))) + .flatMap(product -> investmentPortfolioProductDocumentService.linkProductDocuments(p, product)) .doOnSuccess(product -> log.info( "Successfully upserted portfolio product: uuid={}, name={}, engine={}, productType={}, model={}", product.getUuid(), product.getName(), product.getAdviceEngine(), product.getProductType(), diff --git a/stream-investment/investment-core/src/test/java/com/backbase/stream/investment/service/InvestmentPortfolioProductDocumentServiceTest.java b/stream-investment/investment-core/src/test/java/com/backbase/stream/investment/service/InvestmentPortfolioProductDocumentServiceTest.java new file mode 100644 index 000000000..8458164d9 --- /dev/null +++ b/stream-investment/investment-core/src/test/java/com/backbase/stream/investment/service/InvestmentPortfolioProductDocumentServiceTest.java @@ -0,0 +1,261 @@ +package com.backbase.stream.investment.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.backbase.investment.api.service.sync.v1.ContentApi; +import com.backbase.investment.api.service.sync.v1.model.OASDocumentResponse; +import com.backbase.investment.api.service.sync.v1.model.PaginatedOASDocumentResponseList; +import com.backbase.investment.api.service.v1.InvestmentProductsApi; +import com.backbase.investment.api.service.v1.model.PaginatedPortfolioProductDocumentList; +import com.backbase.investment.api.service.v1.model.PortfolioProduct; +import com.backbase.investment.api.service.v1.model.PortfolioProductDocumentLinkRequest; +import com.backbase.investment.api.service.v1.model.PortfolioProductDocumentResponse; +import com.backbase.investment.api.service.v1.model.ProductTypeEnum; +import com.backbase.stream.investment.ProductPortfolio; +import java.util.List; +import java.util.UUID; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +class InvestmentPortfolioProductDocumentServiceTest { + + private static final int CONTENT_RETRIEVE_LIMIT = 100; + + @Mock + private InvestmentProductsApi productsApi; + + @Mock + private ContentApi contentApi; + + private InvestmentPortfolioProductDocumentService service; + + private AutoCloseable mocks; + + @BeforeEach + void setUp() { + mocks = MockitoAnnotations.openMocks(this); + service = new InvestmentPortfolioProductDocumentService(productsApi, contentApi); + } + + @AfterEach + void tearDown() throws Exception { + mocks.close(); + } + + @Nested + @DisplayName("linkProductDocuments") + class LinkProductDocumentsTests { + + @Test + @DisplayName("no document names — skips linking") + void noDocumentNames_skipsLinking() { + ProductPortfolio template = new ProductPortfolio(); + template.setName("Self Trading"); + PortfolioProduct product = buildProduct(UUID.randomUUID(), "Self Trading"); + + StepVerifier.create(service.linkProductDocuments(template, product)) + .expectNext(product) + .verifyComplete(); + + verify(contentApi, never()).listContentDocuments(any(), any(), any(), any(), any(), any()); + verify(productsApi, never()).bulkCreatePortfolioProductDocuments(any(), any()); + } + + @Test + @DisplayName("missing links — bulk creates only unresolved document links") + void missingLinks_bulkCreatesDocuments() { + UUID productUuid = UUID.randomUUID(); + UUID kiidUuid = UUID.randomUUID(); + UUID factsheetUuid = UUID.randomUUID(); + String kiidName = "Key investor information document - Opportunity Horizon Fund"; + String factsheetName = "Factsheet - Opportunity Horizon Fund"; + + ProductPortfolio template = new ProductPortfolio(); + template.setName("Opportunity Horizon Fund"); + template.setDocument(kiidName + "\n" + factsheetName); + + PortfolioProduct product = buildProduct(productUuid, "Opportunity Horizon Fund"); + + stubContentDocumentLookup(kiidName, kiidUuid); + stubContentDocumentLookup(factsheetName, factsheetUuid); + when(productsApi.listPortfolioProductDocuments(eq(productUuid), eq(CONTENT_RETRIEVE_LIMIT), eq(0))) + .thenReturn(Mono.just(new PaginatedPortfolioProductDocumentList().count(0).results(List.of()))); + + when(productsApi.bulkCreatePortfolioProductDocuments(eq(productUuid), any())) + .thenReturn(Flux.just( + new PortfolioProductDocumentResponse(kiidUuid), + new PortfolioProductDocumentResponse(factsheetUuid))); + + StepVerifier.create(service.linkProductDocuments(template, product)) + .expectNext(product) + .verifyComplete(); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(productsApi).bulkCreatePortfolioProductDocuments(eq(productUuid), captor.capture()); + assertThat(captor.getValue()).extracting(PortfolioProductDocumentLinkRequest::getDocument) + .containsExactly(kiidUuid, factsheetUuid); + } + + @Test + @DisplayName("all links already exist — skips bulk create") + void allLinksExist_skipsBulkCreate() { + UUID productUuid = UUID.randomUUID(); + UUID kiidUuid = UUID.randomUUID(); + UUID factsheetUuid = UUID.randomUUID(); + String kiidName = "Key investor information document - Opportunity Horizon Fund"; + String factsheetName = "Factsheet - Opportunity Horizon Fund"; + + ProductPortfolio template = new ProductPortfolio(); + template.setDocument(kiidName + "\n" + factsheetName); + PortfolioProduct product = buildProduct(productUuid, "Opportunity Horizon Fund"); + + stubContentDocumentLookup(kiidName, kiidUuid); + stubContentDocumentLookup(factsheetName, factsheetUuid); + when(productsApi.listPortfolioProductDocuments(eq(productUuid), eq(CONTENT_RETRIEVE_LIMIT), eq(0))) + .thenReturn(Mono.just(new PaginatedPortfolioProductDocumentList() + .count(2) + .results(List.of( + new PortfolioProductDocumentResponse(kiidUuid), + new PortfolioProductDocumentResponse(factsheetUuid))))); + + StepVerifier.create(service.linkProductDocuments(template, product)) + .expectNext(product) + .verifyComplete(); + + verify(productsApi, never()).bulkCreatePortfolioProductDocuments(any(), any()); + } + + @Test + @DisplayName("partial existing links — bulk creates only missing documents") + void partialExistingLinks_bulkCreatesMissingOnly() { + UUID productUuid = UUID.randomUUID(); + UUID kiidUuid = UUID.randomUUID(); + UUID factsheetUuid = UUID.randomUUID(); + String kiidName = "Key investor information document - Opportunity Horizon Fund"; + String factsheetName = "Factsheet - Opportunity Horizon Fund"; + + ProductPortfolio template = new ProductPortfolio(); + template.setDocument(kiidName + "\n" + factsheetName); + PortfolioProduct product = buildProduct(productUuid, "Opportunity Horizon Fund"); + + stubContentDocumentLookup(kiidName, kiidUuid); + stubContentDocumentLookup(factsheetName, factsheetUuid); + when(productsApi.listPortfolioProductDocuments(eq(productUuid), eq(CONTENT_RETRIEVE_LIMIT), eq(0))) + .thenReturn(Mono.just(new PaginatedPortfolioProductDocumentList() + .count(1) + .results(List.of(new PortfolioProductDocumentResponse(kiidUuid))))); + + when(productsApi.bulkCreatePortfolioProductDocuments(eq(productUuid), any())) + .thenReturn(Flux.just(new PortfolioProductDocumentResponse(factsheetUuid))); + + StepVerifier.create(service.linkProductDocuments(template, product)) + .expectNext(product) + .verifyComplete(); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(productsApi).bulkCreatePortfolioProductDocuments(eq(productUuid), captor.capture()); + assertThat(captor.getValue()).extracting(PortfolioProductDocumentLinkRequest::getDocument) + .containsExactly(factsheetUuid); + } + + @Test + @DisplayName("unresolved document name — skips bulk create") + void unresolvedDocumentName_skipsBulkCreate() { + ProductPortfolio template = new ProductPortfolio(); + template.setDocument("UNKNOWN DOC"); + PortfolioProduct product = buildProduct(UUID.randomUUID(), "Opportunity Horizon Fund"); + + when(contentApi.listContentDocuments(isNull(), eq(CONTENT_RETRIEVE_LIMIT), eq("UNKNOWN DOC"), eq(0), + isNull(), isNull())) + .thenReturn(new PaginatedOASDocumentResponseList().count(0).results(List.of())); + + StepVerifier.create(service.linkProductDocuments(template, product)) + .expectNext(product) + .verifyComplete(); + + verify(productsApi, never()).listPortfolioProductDocuments(any(), any(), any()); + verify(productsApi, never()).bulkCreatePortfolioProductDocuments(any(), any()); + } + + @Test + @DisplayName("partial document resolution — bulk creates only resolved documents") + void partialDocumentResolution_bulkCreatesResolvedOnly() { + UUID productUuid = UUID.randomUUID(); + UUID kiidUuid = UUID.randomUUID(); + String kiidName = "Key investor information document - Opportunity Horizon Fund"; + String factsheetName = "Factsheet - Opportunity Horizon Fund"; + + ProductPortfolio template = new ProductPortfolio(); + template.setDocument(kiidName + "\n" + factsheetName); + PortfolioProduct product = buildProduct(productUuid, "Opportunity Horizon Fund"); + + stubContentDocumentLookup(kiidName, kiidUuid); + when(contentApi.listContentDocuments(isNull(), eq(CONTENT_RETRIEVE_LIMIT), eq(factsheetName), eq(0), + isNull(), isNull())) + .thenReturn(new PaginatedOASDocumentResponseList().count(0).results(List.of())); + when(productsApi.listPortfolioProductDocuments(eq(productUuid), eq(CONTENT_RETRIEVE_LIMIT), eq(0))) + .thenReturn(Mono.just(new PaginatedPortfolioProductDocumentList().count(0).results(List.of()))); + when(productsApi.bulkCreatePortfolioProductDocuments(eq(productUuid), any())) + .thenReturn(Flux.just(new PortfolioProductDocumentResponse(kiidUuid))); + + StepVerifier.create(service.linkProductDocuments(template, product)) + .expectNext(product) + .verifyComplete(); + + ArgumentCaptor> captor = ArgumentCaptor.forClass(List.class); + verify(productsApi).bulkCreatePortfolioProductDocuments(eq(productUuid), captor.capture()); + assertThat(captor.getValue()).extracting(PortfolioProductDocumentLinkRequest::getDocument) + .containsExactly(kiidUuid); + } + + @Test + @DisplayName("bulk create failure — product upsert still completes") + void bulkCreateFailure_productStillReturned() { + UUID productUuid = UUID.randomUUID(); + String documentName = "Factsheet - Opportunity Horizon Fund"; + UUID documentUuid = UUID.randomUUID(); + + ProductPortfolio template = new ProductPortfolio(); + template.setDocument(documentName); + PortfolioProduct product = buildProduct(productUuid, "Opportunity Horizon Fund"); + + stubContentDocumentLookup(documentName, documentUuid); + when(productsApi.listPortfolioProductDocuments(eq(productUuid), eq(CONTENT_RETRIEVE_LIMIT), eq(0))) + .thenReturn(Mono.just(new PaginatedPortfolioProductDocumentList().count(0).results(List.of()))); + when(productsApi.bulkCreatePortfolioProductDocuments(eq(productUuid), any())) + .thenReturn(Flux.error(new RuntimeException("link failed"))); + + StepVerifier.create(service.linkProductDocuments(template, product)) + .expectNext(product) + .verifyComplete(); + } + } + + private void stubContentDocumentLookup(String documentName, UUID documentUuid) { + when(contentApi.listContentDocuments(isNull(), eq(CONTENT_RETRIEVE_LIMIT), eq(documentName), eq(0), isNull(), + isNull())) + .thenReturn(new PaginatedOASDocumentResponseList() + .count(1) + .results(List.of(new OASDocumentResponse(documentUuid).name(documentName)))); + } + + private PortfolioProduct buildProduct(UUID uuid, String name) { + return new PortfolioProduct(name, null, null, 1, null, "savings-plan", uuid, null, null, + ProductTypeEnum.SAVINGS_PLAN); + } +} diff --git a/stream-investment/investment-core/src/test/java/com/backbase/stream/investment/service/InvestmentPortfolioProductServiceTest.java b/stream-investment/investment-core/src/test/java/com/backbase/stream/investment/service/InvestmentPortfolioProductServiceTest.java index 11a1a1faa..a27637a65 100644 --- a/stream-investment/investment-core/src/test/java/com/backbase/stream/investment/service/InvestmentPortfolioProductServiceTest.java +++ b/stream-investment/investment-core/src/test/java/com/backbase/stream/investment/service/InvestmentPortfolioProductServiceTest.java @@ -54,6 +54,9 @@ class InvestmentPortfolioProductServiceTest { @Mock private InvestmentRestProductPortfolioService investmentRestProductPortfolioService; + @Mock + private InvestmentPortfolioProductDocumentService investmentPortfolioProductDocumentService; + private final IngestConfigProperties ingestConfigProperties = new IngestConfigProperties(); private InvestmentPortfolioProductService service; @@ -67,7 +70,10 @@ void setUp() { productsApi, ingestConfigProperties, modelPortfolioService, - investmentRestProductPortfolioService); + investmentRestProductPortfolioService, + investmentPortfolioProductDocumentService); + when(investmentPortfolioProductDocumentService.linkProductDocuments(any(), any())) + .thenAnswer(invocation -> Mono.just(invocation.getArgument(1))); } @AfterEach