diff --git a/CHANGELOG.md b/CHANGELOG.md index 99740055b..02ef01c46 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ # Changelog All notable changes to this project will be documented in this file. +## [9.6.0] +### Added +- customer-data-platform (CDP) module for the customer profiles ingestion + ## [9.5.0] ### Added - investment service intraday generation and ingestion function diff --git a/pom.xml b/pom.xml index 723ca3abe..5591a11e8 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 pom Stream :: Services @@ -34,6 +34,7 @@ stream-contacts stream-loans stream-audiences + stream-cdp stream-compositions stream-plan-manager stream-customer-profile diff --git a/stream-access-control/access-control-core/pom.xml b/stream-access-control/access-control-core/pom.xml index 5304fba13..b374b451d 100644 --- a/stream-access-control/access-control-core/pom.xml +++ b/stream-access-control/access-control-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-access-control - 9.5.1 + 9.6.0 access-control-core diff --git a/stream-access-control/pom.xml b/stream-access-control/pom.xml index 6f0a96d20..e4ba6bb4d 100644 --- a/stream-access-control/pom.xml +++ b/stream-access-control/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-access-control diff --git a/stream-approvals/approvals-bootstrap-task/pom.xml b/stream-approvals/approvals-bootstrap-task/pom.xml index 84af24aab..9b18c9930 100644 --- a/stream-approvals/approvals-bootstrap-task/pom.xml +++ b/stream-approvals/approvals-bootstrap-task/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-task-starter-parent - 9.5.1 + 9.6.0 ../../stream-sdk/stream-starter-parents/stream-task-starter-parent diff --git a/stream-approvals/approvals-core/pom.xml b/stream-approvals/approvals-core/pom.xml index c61b60c88..daa89af62 100644 --- a/stream-approvals/approvals-core/pom.xml +++ b/stream-approvals/approvals-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-approvals - 9.5.1 + 9.6.0 approvals-core diff --git a/stream-approvals/pom.xml b/stream-approvals/pom.xml index 2567dc962..bbeedc806 100644 --- a/stream-approvals/pom.xml +++ b/stream-approvals/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-approvals diff --git a/stream-audiences/audiences-core/pom.xml b/stream-audiences/audiences-core/pom.xml index 3779e1808..13507c7ec 100644 --- a/stream-audiences/audiences-core/pom.xml +++ b/stream-audiences/audiences-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-audiences - 9.5.1 + 9.6.0 audiences-core diff --git a/stream-audiences/pom.xml b/stream-audiences/pom.xml index 9e756c222..dfa9ff126 100644 --- a/stream-audiences/pom.xml +++ b/stream-audiences/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-audiences diff --git a/stream-cdp/README.md b/stream-cdp/README.md new file mode 100644 index 000000000..c87c39c9e --- /dev/null +++ b/stream-cdp/README.md @@ -0,0 +1,6 @@ +# Stream Audiences Integration +The goal of this module is to enable ingestion of Customers into Retail Customers and Business Customers segments. + +The ingestion is done through an HTTP call towards `User Segments Collector` service. + +`UserKindSegmentationSaga` (responsible for triggering the ingestion towards the collector) is triggered from `LegalEntitySaga` \ No newline at end of file diff --git a/stream-cdp/cdp-core/pom.xml b/stream-cdp/cdp-core/pom.xml new file mode 100644 index 000000000..ff5681db6 --- /dev/null +++ b/stream-cdp/cdp-core/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + + com.backbase.stream + stream-cdp + 9.6.0 + + + cdp-core + jar + Stream :: CDP Core + + + true + + + + + com.backbase.stream + stream-dbs-clients + ${project.version} + + + com.backbase.stream + stream-worker + ${project.version} + + + io.projectreactor + reactor-test + test + + + com.backbase.buildingblocks + service-sdk-starter-test + test + + + + diff --git a/stream-cdp/cdp-core/src/main/java/com/backbase/stream/cdp/CdpSaga.java b/stream-cdp/cdp-core/src/main/java/com/backbase/stream/cdp/CdpSaga.java new file mode 100644 index 000000000..ed03316b5 --- /dev/null +++ b/stream-cdp/cdp-core/src/main/java/com/backbase/stream/cdp/CdpSaga.java @@ -0,0 +1,69 @@ +package com.backbase.stream.cdp; + +import com.backbase.cdp.ingestion.api.service.v1.CdpApi; +import com.backbase.stream.configuration.CdpProperties; +import com.backbase.stream.worker.StreamTaskExecutor; +import com.backbase.stream.worker.exception.StreamTaskException; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; + +@Slf4j +public class CdpSaga implements StreamTaskExecutor { + + public static final String ENTITY = "CdpProfile"; + public static final String INGEST = "ingest"; + public static final String SUCCESS = "success"; + public static final String ERROR = "error"; + public static final String INGESTED_SUCCESSFULLY = "Customer ingested successfully"; + public static final String FAILED_TO_INGEST = "Failed to ingest Customer"; + + private final CdpApi cdpServiceApi; + private final CdpProperties cdpProperties; + + public CdpSaga( + CdpApi cdpServiceApi, + CdpProperties cdpProperties + ) { + this.cdpServiceApi = cdpServiceApi; + this.cdpProperties = cdpProperties; + } + + @Override + public Mono executeTask(CdpTask streamTask) { + + var request = streamTask.getCdpEvents(); + + return cdpServiceApi.ingestEvents(request) + .then(Mono.fromCallable(() -> { + streamTask.info(ENTITY, INGEST, SUCCESS, null, + request.getCdpEvents().getFirst().getSourceId(), INGESTED_SUCCESSFULLY); + return streamTask; + })) + .onErrorResume(throwable -> { + streamTask.error(ENTITY, INGEST, ERROR, null, + request.getCdpEvents().getFirst().getSourceId(), FAILED_TO_INGEST); + return Mono.error(new StreamTaskException(streamTask, throwable, FAILED_TO_INGEST)); + }); + } + + @Override + public Mono rollBack(CdpTask streamTask) { + return null; + } + + public boolean isEnabled() { + if (cdpProperties == null) { + return false; + } + + return cdpProperties.enabled(); + } + + public String getDefaultCustomerCategory() { + if (!isEnabled()) { + return null; + } + + return cdpProperties.defaultCustomerCategory(); + } +} diff --git a/stream-cdp/cdp-core/src/main/java/com/backbase/stream/cdp/CdpTask.java b/stream-cdp/cdp-core/src/main/java/com/backbase/stream/cdp/CdpTask.java new file mode 100644 index 000000000..dacc8c1a2 --- /dev/null +++ b/stream-cdp/cdp-core/src/main/java/com/backbase/stream/cdp/CdpTask.java @@ -0,0 +1,16 @@ +package com.backbase.stream.cdp; + +import com.backbase.cdp.ingestion.api.service.v1.model.CdpEvents; +import com.backbase.stream.worker.model.StreamTask; +import lombok.Data; + +@Data +public class CdpTask extends StreamTask { + + private CdpEvents cdpEvents; + + @Override + public String getName() { + return "cdpProfilesIngestionTask"; + } +} diff --git a/stream-cdp/cdp-core/src/main/java/com/backbase/stream/configuration/CdpConfiguration.java b/stream-cdp/cdp-core/src/main/java/com/backbase/stream/configuration/CdpConfiguration.java new file mode 100644 index 000000000..a5c95b4dc --- /dev/null +++ b/stream-cdp/cdp-core/src/main/java/com/backbase/stream/configuration/CdpConfiguration.java @@ -0,0 +1,22 @@ +package com.backbase.stream.configuration; + + +import com.backbase.cdp.ingestion.api.service.v1.CdpApi; +import com.backbase.stream.cdp.CdpSaga; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@EnableConfigurationProperties({CdpProperties.class}) +@Configuration +public class CdpConfiguration { + + @Bean + public CdpSaga cdpSaga( + CdpApi cdpServiceApi, + CdpProperties cdpProperties + ) { + return new CdpSaga(cdpServiceApi, cdpProperties); + } + +} diff --git a/stream-cdp/cdp-core/src/main/java/com/backbase/stream/configuration/CdpProperties.java b/stream-cdp/cdp-core/src/main/java/com/backbase/stream/configuration/CdpProperties.java new file mode 100644 index 000000000..bbe340e1f --- /dev/null +++ b/stream-cdp/cdp-core/src/main/java/com/backbase/stream/configuration/CdpProperties.java @@ -0,0 +1,11 @@ +package com.backbase.stream.configuration; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "backbase.stream.cdp") +public record CdpProperties( + boolean enabled, + String defaultCustomerCategory +) { + +} diff --git a/stream-cdp/cdp-core/src/main/java/com/backbase/stream/package-info.java b/stream-cdp/cdp-core/src/main/java/com/backbase/stream/package-info.java new file mode 100644 index 000000000..831c97c86 --- /dev/null +++ b/stream-cdp/cdp-core/src/main/java/com/backbase/stream/package-info.java @@ -0,0 +1 @@ +package com.backbase.stream; \ No newline at end of file diff --git a/stream-cdp/cdp-core/src/test/java/com/backbase/stream/cdp/CdpSagaTest.java b/stream-cdp/cdp-core/src/test/java/com/backbase/stream/cdp/CdpSagaTest.java new file mode 100644 index 000000000..a8a80b59c --- /dev/null +++ b/stream-cdp/cdp-core/src/test/java/com/backbase/stream/cdp/CdpSagaTest.java @@ -0,0 +1,102 @@ +package com.backbase.stream.cdp; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.backbase.cdp.ingestion.api.service.v1.CdpApi; +import com.backbase.cdp.ingestion.api.service.v1.model.CdpEvent; +import com.backbase.cdp.ingestion.api.service.v1.model.CdpEvents; +import com.backbase.stream.configuration.CdpProperties; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; + +@ExtendWith(MockitoExtension.class) +class CdpSagaTest { + + @Mock + private CdpApi cdpServiceApi; + + @Mock + private CdpProperties userKindSegmentationProperties; + + @InjectMocks + private CdpSaga cdpSaga; + + @Test + void testExecuteTask() { + var task = createTask(); + when(cdpServiceApi.ingestEvents(any())).thenReturn(Mono.empty()); + + cdpSaga.executeTask(task).block(); + + verify(cdpServiceApi).ingestEvents(any()); + } + + @Test + void testExecuteTaskFailure() { + var task = createTask(); + when(cdpServiceApi.ingestEvents(any())) + .thenReturn(Mono.error(new RuntimeException("Ingestion failed"))); + + try { + cdpSaga.executeTask(task).block(); + } catch (Exception e) { + assertThat(e.getCause().getMessage()).isEqualTo("Ingestion failed"); + } + + verify(cdpServiceApi).ingestEvents(any()); + } + + @Test + void isDisabledByDefault() { + var saga = new CdpSaga( + cdpServiceApi, + null + ); + + assertThat(saga.isEnabled()).isFalse(); + } + + @Test + void defaultCustomerCategoryIsNullWhenSagaIsDisabled() { + when(userKindSegmentationProperties.enabled()).thenReturn(false); + + assertThat(cdpSaga.getDefaultCustomerCategory()).isNull(); + } + + @Test + void rollbackReturnsNull() { + assertThat(cdpSaga.rollBack(createTask())).isNull(); + } + + @Test + void returnsDefaultCustomerCategoryFromProperties() { + when(userKindSegmentationProperties.enabled()).thenReturn(true); + when(userKindSegmentationProperties.defaultCustomerCategory()).thenReturn("RETAIL"); + + assertThat(cdpSaga.getDefaultCustomerCategory()).isEqualTo("RETAIL"); + } + + private CdpTask createTask() { + var task = new CdpTask(); + task.setCdpEvents( + new CdpEvents() + .addCdpEventsItem(new CdpEvent() + .eventType("ProfileCreatedEvent") + .eventId(UUID.randomUUID().toString()) + .sourceSystem("BACKBASE") + .sourceType("USER_ID") + .sourceId("internal-id") + .data(Map.of())) + ); + return task; + } +} \ No newline at end of file diff --git a/stream-cdp/cdp-core/src/test/java/com/backbase/stream/cdp/CdpTaskTest.java b/stream-cdp/cdp-core/src/test/java/com/backbase/stream/cdp/CdpTaskTest.java new file mode 100644 index 000000000..8beb2bfc2 --- /dev/null +++ b/stream-cdp/cdp-core/src/test/java/com/backbase/stream/cdp/CdpTaskTest.java @@ -0,0 +1,15 @@ +package com.backbase.stream.cdp; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class CdpTaskTest { + + @Test + void testCdpTask() { + CdpTask cdpTask = new CdpTask(); + assertThat(cdpTask.getName()).isEqualTo("cdpProfilesIngestionTask"); + } + +} diff --git a/stream-cdp/cdp-core/src/test/java/com/backbase/stream/configuration/CdpConfigurationTest.java b/stream-cdp/cdp-core/src/test/java/com/backbase/stream/configuration/CdpConfigurationTest.java new file mode 100644 index 000000000..22161b981 --- /dev/null +++ b/stream-cdp/cdp-core/src/test/java/com/backbase/stream/configuration/CdpConfigurationTest.java @@ -0,0 +1,51 @@ +package com.backbase.stream.configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.backbase.cdp.ingestion.api.service.v1.CdpApi; +import com.backbase.stream.cdp.CdpSaga; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; + +class CdpConfigurationTest { + + @Test + void testCdpSagaBeanCreation() { + // Arrange: create mocks + CdpApi cdpApi = Mockito.mock(CdpApi.class); + CdpProperties cdpProperties = new CdpProperties(true, "RETAIL"); + + // Use a test configuration to inject mocks + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.registerBean(CdpApi.class, () -> cdpApi); + context.registerBean(CdpProperties.class, () -> cdpProperties); + context.register(CdpConfiguration.class); + context.refresh(); + + // Act: get the bean + CdpSaga cdpSaga = context.getBean(CdpSaga.class); + + // Assert + assertThat(cdpSaga).isNotNull().isInstanceOf(CdpSaga.class); + context.close(); + } + + @Test + void testCdpSagaBeanCreation_withRecordProperties() { + // Arrange: create mocks + CdpApi cdpApi = Mockito.mock(CdpApi.class); + // Provide required constructor args for record + CdpProperties cdpProperties = new CdpProperties(false, "BUSINESS"); + + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.registerBean(CdpApi.class, () -> cdpApi); + context.registerBean(CdpProperties.class, () -> cdpProperties); + context.register(CdpConfiguration.class); + context.refresh(); + + CdpSaga cdpSaga = context.getBean(CdpSaga.class); + assertThat(cdpSaga).isNotNull().isInstanceOf(CdpSaga.class); + context.close(); + } +} diff --git a/stream-cdp/cdp-core/src/test/java/com/backbase/stream/configuration/CdpPropertiesTest.java b/stream-cdp/cdp-core/src/test/java/com/backbase/stream/configuration/CdpPropertiesTest.java new file mode 100644 index 000000000..fdd253de3 --- /dev/null +++ b/stream-cdp/cdp-core/src/test/java/com/backbase/stream/configuration/CdpPropertiesTest.java @@ -0,0 +1,16 @@ +package com.backbase.stream.configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class CdpPropertiesTest { + + @Test + void testProperties() { + CdpProperties properties = new CdpProperties(true, "testCategory"); + + assertThat(properties.enabled()).isTrue(); + assertThat(properties.defaultCustomerCategory()).isEqualTo("testCategory"); + } +} diff --git a/stream-cdp/pom.xml b/stream-cdp/pom.xml new file mode 100644 index 000000000..e10bfd3e2 --- /dev/null +++ b/stream-cdp/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + + com.backbase.stream + stream-services + 9.6.0 + + + stream-cdp + + pom + Stream :: CDP + + + cdp-core + + + diff --git a/stream-compositions/api/cursors-api/pom.xml b/stream-compositions/api/cursors-api/pom.xml index 1fefc94b8..79b991d05 100644 --- a/stream-compositions/api/cursors-api/pom.xml +++ b/stream-compositions/api/cursors-api/pom.xml @@ -5,7 +5,7 @@ api com.backbase.stream.compositions - 9.5.1 + 9.6.0 cursors-api diff --git a/stream-compositions/api/cursors-api/transaction-cursor-api/pom.xml b/stream-compositions/api/cursors-api/transaction-cursor-api/pom.xml index f587b2097..12ad8a8b4 100644 --- a/stream-compositions/api/cursors-api/transaction-cursor-api/pom.xml +++ b/stream-compositions/api/cursors-api/transaction-cursor-api/pom.xml @@ -5,7 +5,7 @@ cursors-api com.backbase.stream.compositions - 9.5.1 + 9.6.0 4.0.0 diff --git a/stream-compositions/api/integrations-api/legal-entity-integration-api/pom.xml b/stream-compositions/api/integrations-api/legal-entity-integration-api/pom.xml index f01683572..c0063dedb 100644 --- a/stream-compositions/api/integrations-api/legal-entity-integration-api/pom.xml +++ b/stream-compositions/api/integrations-api/legal-entity-integration-api/pom.xml @@ -6,7 +6,7 @@ integrations-api com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.api diff --git a/stream-compositions/api/integrations-api/payment-order-integration-api/pom.xml b/stream-compositions/api/integrations-api/payment-order-integration-api/pom.xml index 444645667..09133d5e0 100644 --- a/stream-compositions/api/integrations-api/payment-order-integration-api/pom.xml +++ b/stream-compositions/api/integrations-api/payment-order-integration-api/pom.xml @@ -6,7 +6,7 @@ integrations-api com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.api diff --git a/stream-compositions/api/integrations-api/pom.xml b/stream-compositions/api/integrations-api/pom.xml index 5a64fc340..ecc13de5d 100644 --- a/stream-compositions/api/integrations-api/pom.xml +++ b/stream-compositions/api/integrations-api/pom.xml @@ -6,7 +6,7 @@ api com.backbase.stream.compositions - 9.5.1 + 9.6.0 integrations-api diff --git a/stream-compositions/api/integrations-api/product-catalog-integration-api/pom.xml b/stream-compositions/api/integrations-api/product-catalog-integration-api/pom.xml index 1e9424baf..07c92b9ed 100644 --- a/stream-compositions/api/integrations-api/product-catalog-integration-api/pom.xml +++ b/stream-compositions/api/integrations-api/product-catalog-integration-api/pom.xml @@ -6,7 +6,7 @@ integrations-api com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.api diff --git a/stream-compositions/api/integrations-api/product-integration-api/pom.xml b/stream-compositions/api/integrations-api/product-integration-api/pom.xml index 40974425d..b6acae63f 100644 --- a/stream-compositions/api/integrations-api/product-integration-api/pom.xml +++ b/stream-compositions/api/integrations-api/product-integration-api/pom.xml @@ -6,7 +6,7 @@ integrations-api com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.api diff --git a/stream-compositions/api/integrations-api/transaction-integration-api/pom.xml b/stream-compositions/api/integrations-api/transaction-integration-api/pom.xml index 48bd65ec7..abde27f50 100644 --- a/stream-compositions/api/integrations-api/transaction-integration-api/pom.xml +++ b/stream-compositions/api/integrations-api/transaction-integration-api/pom.xml @@ -6,7 +6,7 @@ integrations-api com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.api diff --git a/stream-compositions/api/pom.xml b/stream-compositions/api/pom.xml index ff9581fa5..1fbd7ee1d 100644 --- a/stream-compositions/api/pom.xml +++ b/stream-compositions/api/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions diff --git a/stream-compositions/api/service-api/legal-entity-composition-api/pom.xml b/stream-compositions/api/service-api/legal-entity-composition-api/pom.xml index bb34b7cae..aef1da2d1 100644 --- a/stream-compositions/api/service-api/legal-entity-composition-api/pom.xml +++ b/stream-compositions/api/service-api/legal-entity-composition-api/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream.compositions service-api - 9.5.1 + 9.6.0 com.backbase.stream.compositions.api diff --git a/stream-compositions/api/service-api/payment-order-composition-api/pom.xml b/stream-compositions/api/service-api/payment-order-composition-api/pom.xml index 5fa599783..609aefe69 100644 --- a/stream-compositions/api/service-api/payment-order-composition-api/pom.xml +++ b/stream-compositions/api/service-api/payment-order-composition-api/pom.xml @@ -6,7 +6,7 @@ service-api com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.api diff --git a/stream-compositions/api/service-api/pom.xml b/stream-compositions/api/service-api/pom.xml index a4a30c715..1a2d0a83d 100644 --- a/stream-compositions/api/service-api/pom.xml +++ b/stream-compositions/api/service-api/pom.xml @@ -6,7 +6,7 @@ com.backbase.stream.compositions api - 9.5.1 + 9.6.0 service-api diff --git a/stream-compositions/api/service-api/product-catalog-composition-api/pom.xml b/stream-compositions/api/service-api/product-catalog-composition-api/pom.xml index f34792d8b..cb31c7064 100644 --- a/stream-compositions/api/service-api/product-catalog-composition-api/pom.xml +++ b/stream-compositions/api/service-api/product-catalog-composition-api/pom.xml @@ -6,7 +6,7 @@ service-api com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.api diff --git a/stream-compositions/api/service-api/product-composition-api/pom.xml b/stream-compositions/api/service-api/product-composition-api/pom.xml index 432976233..adef570a7 100644 --- a/stream-compositions/api/service-api/product-composition-api/pom.xml +++ b/stream-compositions/api/service-api/product-composition-api/pom.xml @@ -6,7 +6,7 @@ service-api com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.api diff --git a/stream-compositions/api/service-api/transaction-composition-api/pom.xml b/stream-compositions/api/service-api/transaction-composition-api/pom.xml index 70a74a98b..d0777b8c8 100644 --- a/stream-compositions/api/service-api/transaction-composition-api/pom.xml +++ b/stream-compositions/api/service-api/transaction-composition-api/pom.xml @@ -6,7 +6,7 @@ service-api com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.api diff --git a/stream-compositions/cursors/pom.xml b/stream-compositions/cursors/pom.xml index bf133ee51..40b9ecea0 100644 --- a/stream-compositions/cursors/pom.xml +++ b/stream-compositions/cursors/pom.xml @@ -5,7 +5,7 @@ stream-compositions com.backbase.stream - 9.5.1 + 9.6.0 4.0.0 diff --git a/stream-compositions/cursors/transaction-cursor/pom.xml b/stream-compositions/cursors/transaction-cursor/pom.xml index 4804a95e6..62e461471 100644 --- a/stream-compositions/cursors/transaction-cursor/pom.xml +++ b/stream-compositions/cursors/transaction-cursor/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream.compositions cursors - 9.5.1 + 9.6.0 4.0.0 diff --git a/stream-compositions/events/grandcentral/pom.xml b/stream-compositions/events/grandcentral/pom.xml index bae6d955b..981ecc85e 100644 --- a/stream-compositions/events/grandcentral/pom.xml +++ b/stream-compositions/events/grandcentral/pom.xml @@ -5,7 +5,7 @@ events com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.events diff --git a/stream-compositions/events/legal-entity-egress/pom.xml b/stream-compositions/events/legal-entity-egress/pom.xml index b3bc4fc9f..f17efd8f5 100644 --- a/stream-compositions/events/legal-entity-egress/pom.xml +++ b/stream-compositions/events/legal-entity-egress/pom.xml @@ -5,7 +5,7 @@ events com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.events diff --git a/stream-compositions/events/legal-entity-ingress/pom.xml b/stream-compositions/events/legal-entity-ingress/pom.xml index e7242bab6..fee0a0dc4 100644 --- a/stream-compositions/events/legal-entity-ingress/pom.xml +++ b/stream-compositions/events/legal-entity-ingress/pom.xml @@ -5,7 +5,7 @@ events com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.events diff --git a/stream-compositions/events/pom.xml b/stream-compositions/events/pom.xml index 7ac3d1c78..ed67fc334 100644 --- a/stream-compositions/events/pom.xml +++ b/stream-compositions/events/pom.xml @@ -6,7 +6,7 @@ com.backbase.stream stream-compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions diff --git a/stream-compositions/events/product-catalog-egress/pom.xml b/stream-compositions/events/product-catalog-egress/pom.xml index 5b9867019..06a34a197 100644 --- a/stream-compositions/events/product-catalog-egress/pom.xml +++ b/stream-compositions/events/product-catalog-egress/pom.xml @@ -5,7 +5,7 @@ events com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.events diff --git a/stream-compositions/events/product-catalog-ingress/pom.xml b/stream-compositions/events/product-catalog-ingress/pom.xml index 30d6c9fac..7e4b5dc4d 100644 --- a/stream-compositions/events/product-catalog-ingress/pom.xml +++ b/stream-compositions/events/product-catalog-ingress/pom.xml @@ -5,7 +5,7 @@ events com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.events diff --git a/stream-compositions/events/product-egress/pom.xml b/stream-compositions/events/product-egress/pom.xml index b3dc20510..efa61d6f3 100644 --- a/stream-compositions/events/product-egress/pom.xml +++ b/stream-compositions/events/product-egress/pom.xml @@ -5,7 +5,7 @@ events com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.events diff --git a/stream-compositions/events/product-ingress/pom.xml b/stream-compositions/events/product-ingress/pom.xml index 696a2f803..6ffe789af 100644 --- a/stream-compositions/events/product-ingress/pom.xml +++ b/stream-compositions/events/product-ingress/pom.xml @@ -5,7 +5,7 @@ events com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.events diff --git a/stream-compositions/events/transaction-egress/pom.xml b/stream-compositions/events/transaction-egress/pom.xml index 09bcb681f..323f1c2a1 100644 --- a/stream-compositions/events/transaction-egress/pom.xml +++ b/stream-compositions/events/transaction-egress/pom.xml @@ -5,7 +5,7 @@ events com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.events diff --git a/stream-compositions/events/transaction-ingress/pom.xml b/stream-compositions/events/transaction-ingress/pom.xml index 5ad48a060..aea0dcb66 100644 --- a/stream-compositions/events/transaction-ingress/pom.xml +++ b/stream-compositions/events/transaction-ingress/pom.xml @@ -5,7 +5,7 @@ events com.backbase.stream.compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions.events diff --git a/stream-compositions/pom.xml b/stream-compositions/pom.xml index 2daf0d172..969a18657 100644 --- a/stream-compositions/pom.xml +++ b/stream-compositions/pom.xml @@ -7,13 +7,13 @@ com.backbase.stream stream-starter - 9.5.1 + 9.6.0 ../stream-sdk/stream-starter com.backbase.stream stream-compositions - 9.5.1 + 9.6.0 pom Stream :: Compositions diff --git a/stream-compositions/services/legal-entity-composition-service/pom.xml b/stream-compositions/services/legal-entity-composition-service/pom.xml index ad5d530d2..636565e86 100644 --- a/stream-compositions/services/legal-entity-composition-service/pom.xml +++ b/stream-compositions/services/legal-entity-composition-service/pom.xml @@ -7,7 +7,7 @@ com.backbase.stream.compositions services - 9.5.1 + 9.6.0 legal-entity-composition-service diff --git a/stream-compositions/services/payment-order-composition-service/pom.xml b/stream-compositions/services/payment-order-composition-service/pom.xml index 5169ba450..3860580da 100644 --- a/stream-compositions/services/payment-order-composition-service/pom.xml +++ b/stream-compositions/services/payment-order-composition-service/pom.xml @@ -6,7 +6,7 @@ com.backbase.stream.compositions services - 9.5.1 + 9.6.0 payment-order-composition-service diff --git a/stream-compositions/services/pom.xml b/stream-compositions/services/pom.xml index e1525092b..8a8ac9908 100644 --- a/stream-compositions/services/pom.xml +++ b/stream-compositions/services/pom.xml @@ -6,7 +6,7 @@ com.backbase.stream stream-compositions - 9.5.1 + 9.6.0 com.backbase.stream.compositions diff --git a/stream-compositions/services/product-catalog-composition-service/pom.xml b/stream-compositions/services/product-catalog-composition-service/pom.xml index f5f10e710..bef4df81c 100644 --- a/stream-compositions/services/product-catalog-composition-service/pom.xml +++ b/stream-compositions/services/product-catalog-composition-service/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream.compositions services - 9.5.1 + 9.6.0 product-catalog-composition-service diff --git a/stream-compositions/services/product-composition-service/pom.xml b/stream-compositions/services/product-composition-service/pom.xml index 78a8e977a..ae633d3a2 100644 --- a/stream-compositions/services/product-composition-service/pom.xml +++ b/stream-compositions/services/product-composition-service/pom.xml @@ -7,7 +7,7 @@ com.backbase.stream.compositions services - 9.5.1 + 9.6.0 product-composition-service diff --git a/stream-compositions/services/transaction-composition-service/pom.xml b/stream-compositions/services/transaction-composition-service/pom.xml index b0cd1b164..65a310733 100644 --- a/stream-compositions/services/transaction-composition-service/pom.xml +++ b/stream-compositions/services/transaction-composition-service/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream.compositions services - 9.5.1 + 9.6.0 transaction-composition-service diff --git a/stream-compositions/test-utils/pom.xml b/stream-compositions/test-utils/pom.xml index 2e3efa433..c82f08a83 100644 --- a/stream-compositions/test-utils/pom.xml +++ b/stream-compositions/test-utils/pom.xml @@ -6,7 +6,7 @@ stream-compositions com.backbase.stream - 9.5.1 + 9.6.0 com.backbase.stream.compositions diff --git a/stream-contacts/contacts-core/pom.xml b/stream-contacts/contacts-core/pom.xml index b1d365f00..314b35bc0 100644 --- a/stream-contacts/contacts-core/pom.xml +++ b/stream-contacts/contacts-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-contacts - 9.5.1 + 9.6.0 contacts-core diff --git a/stream-contacts/pom.xml b/stream-contacts/pom.xml index 84e4ef306..dc653f25e 100644 --- a/stream-contacts/pom.xml +++ b/stream-contacts/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-contacts diff --git a/stream-cursor/cursor-core/pom.xml b/stream-cursor/cursor-core/pom.xml index 31f8a2685..18cff867f 100644 --- a/stream-cursor/cursor-core/pom.xml +++ b/stream-cursor/cursor-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-cursor - 9.5.1 + 9.6.0 cursor-core diff --git a/stream-cursor/cursor-http/pom.xml b/stream-cursor/cursor-http/pom.xml index 92e17f709..4e74fdd6e 100644 --- a/stream-cursor/cursor-http/pom.xml +++ b/stream-cursor/cursor-http/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-http-starter-parent - 9.5.1 + 9.6.0 ../../stream-sdk/stream-starter-parents/stream-http-starter-parent diff --git a/stream-cursor/cursor-publishers/pom.xml b/stream-cursor/cursor-publishers/pom.xml index 9c5b339db..375ff5a8c 100644 --- a/stream-cursor/cursor-publishers/pom.xml +++ b/stream-cursor/cursor-publishers/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-cursor - 9.5.1 + 9.6.0 cursor-publishers diff --git a/stream-cursor/cursor-store/pom.xml b/stream-cursor/cursor-store/pom.xml index 20acbd710..f7b02efce 100644 --- a/stream-cursor/cursor-store/pom.xml +++ b/stream-cursor/cursor-store/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-cursor - 9.5.1 + 9.6.0 cursor-store diff --git a/stream-cursor/pom.xml b/stream-cursor/pom.xml index 971762600..d7a85cb3e 100644 --- a/stream-cursor/pom.xml +++ b/stream-cursor/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-cursor diff --git a/stream-customer-profile/customer-profile-core/pom.xml b/stream-customer-profile/customer-profile-core/pom.xml index 898a19ee0..586e4a8af 100644 --- a/stream-customer-profile/customer-profile-core/pom.xml +++ b/stream-customer-profile/customer-profile-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-customer-profile - 9.5.1 + 9.6.0 customer-profile-core diff --git a/stream-customer-profile/pom.xml b/stream-customer-profile/pom.xml index c758b1515..7c15da7ea 100644 --- a/stream-customer-profile/pom.xml +++ b/stream-customer-profile/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-customer-profile diff --git a/stream-dbs-clients/pom.xml b/stream-dbs-clients/pom.xml index a604aa11f..b4efc00d7 100644 --- a/stream-dbs-clients/pom.xml +++ b/stream-dbs-clients/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-dbs-clients @@ -745,6 +745,60 @@ + + boat-validation-cdp-spec + + validate + + generate-sources + + ${project.basedir}/src/main/openapi/cdp-ingestion-service-api-v1.0.0-beta.yaml + true + + + + generate-cdp-ingestion-service-api-code + + generate-webclient-embedded + + generate-sources + + REFACTOR_ALLOF_WITH_PROPERTIES_ONLY=true + ${project.basedir}/src/main/openapi/cdp-ingestion-service-api-v1.0.0-beta.yaml + com.backbase.cdp.ingestion.api.service.v1 + com.backbase.cdp.ingestion.api.service.v1.model + + false + + + + + boat-validation-cdp-profiles-spec + + validate + + generate-sources + + ${project.basedir}/src/main/openapi/cdp-profiles-service-api-v1.0.0-beta.yaml + true + + + + generate-cdp-profiles-service-api-code + + generate-webclient-embedded + + generate-sources + + REFACTOR_ALLOF_WITH_PROPERTIES_ONLY=true + ${project.basedir}/src/main/openapi/cdp-profiles-service-api-v1.0.0-beta.yaml + com.backbase.cdp.profiles.api.service.v1 + com.backbase.cdp.profiles.api.service.v1.model + + false + + + generate-plan-manager-service-api-code diff --git a/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/CdpIngestionClientConfig.java b/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/CdpIngestionClientConfig.java new file mode 100644 index 000000000..0b28a9ac4 --- /dev/null +++ b/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/CdpIngestionClientConfig.java @@ -0,0 +1,35 @@ +package com.backbase.stream.clients.config; + +import com.backbase.cdp.ingestion.api.service.ApiClient; +import com.backbase.cdp.ingestion.api.service.v1.CdpApi; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.text.DateFormat; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties("backbase.communication.services.cdp-ingestion") +public class CdpIngestionClientConfig extends CompositeApiClientConfig { + + public static final String CDP_INGESTION_SERVICE_ID = "cdp-ingestion"; + + public CdpIngestionClientConfig() { + super(CDP_INGESTION_SERVICE_ID); + } + + @Bean + @ConditionalOnMissingBean + public ApiClient cdpIngestionApiClient(ObjectMapper objectMapper, DateFormat dateFormat) { + return new ApiClient(getWebClient(), objectMapper, dateFormat) + .setBasePath(createBasePath()); + } + + @Bean + @ConditionalOnMissingBean + public CdpApi cdpIngestionServiceApi(ApiClient apiClient) { + return new CdpApi(apiClient); + } + +} diff --git a/stream-dbs-clients/src/main/openapi/cdp-ingestion-service-api-v1.0.0-beta.yaml b/stream-dbs-clients/src/main/openapi/cdp-ingestion-service-api-v1.0.0-beta.yaml new file mode 100644 index 000000000..d7ceb3fa9 --- /dev/null +++ b/stream-dbs-clients/src/main/openapi/cdp-ingestion-service-api-v1.0.0-beta.yaml @@ -0,0 +1,1955 @@ +openapi: 3.0.3 +info: + title: CDP Ingestion Service API Spec + description: Specs for CDP Ingestion Service API + version: 1.0.0-beta + x-icon: credit_card +servers: +- url: http://localhost:8080 + description: prism mock server +tags: +- name: cdp +paths: + /service-api/v1/ingestEvents: + post: + tags: + - cdp + summary: Ingest multiple CDP events + description: Accepts and processes multiple CDP events for bulk ingestion into + the platform + operationId: ingestEvents + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/CdpEvents" + examples: + cdpEvents: + $ref: "#/components/examples/CdpEventsExample" + value: null + required: true + responses: + "200": + description: Events successfully processed + content: + application/json: + schema: + $ref: "#/components/schemas/BulkIngestionSuccessResponse" + examples: + bulkSuccess: + $ref: "#/components/examples/BulkIngestionSuccessExample" + value: null + "400": + description: | + The server cannot or will not process the request due to something that is perceived to be a client error. + For example, invalid event structure or missing required fields. + content: + application/json: + schema: + $ref: "#/components/schemas/BadRequestError" + examples: + badRequestError: + $ref: "#/components/examples/BadRequestExample" + value: null + "401": + description: Request lacks valid authentication credentials for the target + resource. + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + examples: + unauthorizedError: + $ref: "#/components/examples/UnauthorizedExample" + value: null + "404": + description: Not found. + content: + application/json: + schema: + $ref: "#/components/schemas/error" + example: + $ref: "#/components/examples/not-found-error" + "500": + description: Internal server error occurred while processing the event + content: + application/json: + schema: + $ref: "#/components/schemas/InternalServerError" + examples: + internalServerError: + $ref: "#/components/examples/InternalServerErrorExample" + value: null + x-BbAccessControl: false + /service-api/v1/validation/event-types/{eventType}/rules: + get: + tags: + - validation + summary: Get validation rules + description: Retrieves validation rules for a specific event type + operationId: getValidationRules + parameters: + - name: eventType + in: path + description: Event type to get rules for + required: true + style: simple + explode: false + schema: + maxLength: 100 + type: string + responses: + "200": + description: Successfully retrieved validation rules + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/ValidationRule" + "401": + description: Request lacks valid authentication credentials + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + examples: + unauthorizedError: + $ref: "#/components/examples/UnauthorizedExample" + value: null + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/InternalServerError" + examples: + internalServerError: + $ref: "#/components/examples/InternalServerErrorExample" + value: null + x-BbAccessControl: false + post: + tags: + - validation + summary: Create validation rules + description: Creates one or more validation rules for validating CDP events + operationId: createValidationRules + parameters: + - name: eventType + in: path + description: Event type these rules apply to + required: true + style: simple + explode: false + schema: + maxLength: 100 + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ValidationRule" + examples: + validationRule: + $ref: "#/components/examples/ValidationRuleExample" + value: null + required: true + responses: + "201": + description: Validation rule successfully created + content: + application/json: + schema: + $ref: "#/components/schemas/ValidationRule" + examples: + validationRule: + $ref: "#/components/examples/ValidationRuleExample" + value: null + "400": + description: Bad request - invalid schema format or duplicate schema + content: + application/json: + schema: + $ref: "#/components/schemas/SchemaValidationError" + examples: + schemaValidationError: + $ref: "#/components/examples/SchemaValidationErrorExample" + value: null + "401": + description: Request lacks valid authentication credentials + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + examples: + unauthorizedError: + $ref: "#/components/examples/UnauthorizedExample" + value: null + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/InternalServerError" + examples: + internalServerError: + $ref: "#/components/examples/InternalServerErrorExample" + value: null + x-BbAccessControl: false + /service-api/v1/validation/event-types/{eventType}/rules/{uuid}: + put: + tags: + - validation + summary: Update validation rule + description: Updates an existing validation rule + operationId: updateValidationRule + parameters: + - name: eventType + in: path + description: Event type this rule applies to + required: true + style: simple + explode: false + schema: + maxLength: 100 + type: string + - name: uuid + in: path + description: Validation rule UUID + required: true + style: simple + explode: false + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ValidationRule" + required: true + responses: + "200": + description: Validation rule successfully updated + content: + application/json: + schema: + $ref: "#/components/schemas/ValidationRule" + "400": + description: Bad request - invalid validation rule data + content: + application/json: + schema: + $ref: "#/components/schemas/BadRequestError" + examples: + badRequestError: + $ref: "#/components/examples/BadRequestExample" + value: null + "401": + description: Request lacks valid authentication credentials + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + examples: + unauthorizedError: + $ref: "#/components/examples/UnauthorizedExample" + value: null + "404": + description: Validation rule not found + content: + application/json: + schema: + $ref: "#/components/schemas/NotFoundError" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/InternalServerError" + examples: + internalServerError: + $ref: "#/components/examples/InternalServerErrorExample" + value: null + x-BbAccessControl: false + delete: + tags: + - validation + summary: Delete validation rule + description: Deletes a validation rule by its UUID + operationId: deleteValidationRule + parameters: + - name: eventType + in: path + description: Event type this rule applies to + required: true + style: simple + explode: false + schema: + maxLength: 100 + type: string + - name: uuid + in: path + description: Validation rule UUID + required: true + style: simple + explode: false + schema: + type: string + format: uuid + responses: + "204": + description: Validation rule successfully deleted + "401": + description: Request lacks valid authentication credentials + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + examples: + unauthorizedError: + $ref: "#/components/examples/UnauthorizedExample" + value: null + "404": + description: Validation rule not found + content: + application/json: + schema: + $ref: "#/components/schemas/NotFoundError" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/InternalServerError" + examples: + internalServerError: + $ref: "#/components/examples/InternalServerErrorExample" + value: null + x-BbAccessControl: false + /service-api/v1/validation/event-types: + get: + tags: + - validation + summary: Get all event types + description: Retrieves all available event types + operationId: getAllEventTypes + responses: + "200": + description: Successfully retrieved event types + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/EventType" + "401": + description: Request lacks valid authentication credentials + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + examples: + unauthorizedError: + $ref: "#/components/examples/UnauthorizedExample" + value: null + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/InternalServerError" + examples: + internalServerError: + $ref: "#/components/examples/InternalServerErrorExample" + value: null + x-BbAccessControl: false + post: + tags: + - validation + summary: Create a new event type + description: Creates a new event type for CDP events + operationId: createEventType + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/EventType" + required: true + responses: + "201": + description: Event type successfully created + content: + application/json: + schema: + $ref: "#/components/schemas/EventType" + "400": + description: Bad request - invalid event type data + content: + application/json: + schema: + $ref: "#/components/schemas/BadRequestError" + examples: + badRequestError: + $ref: "#/components/examples/BadRequestExample" + value: null + "401": + description: Request lacks valid authentication credentials + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + examples: + unauthorizedError: + $ref: "#/components/examples/UnauthorizedExample" + value: null + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/InternalServerError" + examples: + internalServerError: + $ref: "#/components/examples/InternalServerErrorExample" + value: null + x-BbAccessControl: false + /service-api/v1/validation/event-types/{eventType}: + delete: + tags: + - validation + summary: Delete the event type + description: Deletes the event type + operationId: deleteEventType + parameters: + - name: eventType + in: path + description: Event type to delete + required: true + style: simple + explode: false + schema: + maxLength: 100 + type: string + responses: + "204": + description: Event type successfully deleted + "401": + description: Request lacks valid authentication credentials + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + examples: + unauthorizedError: + $ref: "#/components/examples/UnauthorizedExample" + value: null + "404": + description: Event type configuration not found + content: + application/json: + schema: + $ref: "#/components/schemas/NotFoundError" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/InternalServerError" + examples: + internalServerError: + $ref: "#/components/examples/InternalServerErrorExample" + value: null + x-BbAccessControl: false + /service-api/v1/validation/event-types/{eventType}/configuration: + get: + tags: + - validation + summary: Get configuration for an event type + description: Retrieves the configuration for a specific event type (1-to-1 relationship) + operationId: getEventTypeConfiguration + parameters: + - name: eventType + in: path + description: Event type to get configuration for + required: true + style: simple + explode: false + schema: + maxLength: 100 + type: string + responses: + "200": + description: Successfully retrieved event type configuration + content: + application/json: + schema: + $ref: "#/components/schemas/EventTypeConfiguration" + examples: + eventTypeConfiguration: + $ref: "#/components/examples/EventTypeConfigurationExample" + value: null + "404": + description: Configuration not found for this event type + content: + application/json: + schema: + $ref: "#/components/schemas/NotFoundError" + "401": + description: Request lacks valid authentication credentials + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + examples: + unauthorizedError: + $ref: "#/components/examples/UnauthorizedExample" + value: null + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/InternalServerError" + examples: + internalServerError: + $ref: "#/components/examples/InternalServerErrorExample" + value: null + x-BbAccessControl: false + put: + tags: + - validation + summary: Create or update configuration for an event type + description: Creates or updates the configuration for a specific event type + (1-to-1 relationship) + operationId: createOrUpdateEventTypeConfiguration + parameters: + - name: eventType + in: path + description: Event type to configure + required: true + style: simple + explode: false + schema: + maxLength: 100 + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/EventTypeConfiguration" + required: true + responses: + "200": + description: Event type configuration successfully updated + content: + application/json: + schema: + $ref: "#/components/schemas/EventTypeConfiguration" + examples: + eventTypeConfiguration: + $ref: "#/components/examples/EventTypeConfigurationExample" + value: null + "201": + description: Event type configuration successfully created + content: + application/json: + schema: + $ref: "#/components/schemas/EventTypeConfiguration" + examples: + eventTypeConfiguration: + $ref: "#/components/examples/EventTypeConfigurationExample" + value: null + "400": + description: Bad request - invalid configuration data + content: + application/json: + schema: + $ref: "#/components/schemas/BadRequestError" + examples: + badRequestError: + $ref: "#/components/examples/BadRequestExample" + value: null + "401": + description: Request lacks valid authentication credentials + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + examples: + unauthorizedError: + $ref: "#/components/examples/UnauthorizedExample" + value: null + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/InternalServerError" + examples: + internalServerError: + $ref: "#/components/examples/InternalServerErrorExample" + value: null + x-BbAccessControl: false + delete: + tags: + - validation + summary: Delete configuration for an event type + description: Deletes the configuration for a specific event type + operationId: deleteEventTypeConfiguration + parameters: + - name: eventType + in: path + description: Event type + required: true + style: simple + explode: false + schema: + maxLength: 100 + type: string + responses: + "204": + description: Event type configuration successfully deleted + "401": + description: Request lacks valid authentication credentials + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + examples: + unauthorizedError: + $ref: "#/components/examples/UnauthorizedExample" + value: null + "404": + description: Event type configuration not found + content: + application/json: + schema: + $ref: "#/components/schemas/NotFoundError" + "500": + description: Internal server error + content: + application/json: + schema: + $ref: "#/components/schemas/InternalServerError" + examples: + internalServerError: + $ref: "#/components/examples/InternalServerErrorExample" + value: null + x-BbAccessControl: false +components: + schemas: + CdpEvent: + required: + - data + - eventId + - eventType + - sourceId + - sourceSystem + - sourceType + type: object + properties: + eventId: + type: string + description: Unique identifier of the event in the source system + example: 12345678-1234-5678-9012-123456789012 + eventType: + type: string + description: Type of the event - used for event validation and processing + definitions + example: login + timestamp: + type: string + description: Time when event was emitted by the source system + format: date-time + example: 2023-09-16T10:30:00Z + sourceSystem: + type: string + description: "System where the event originated (e.g., web_app, mobile_app)" + example: mobile_app + sourceType: + type: string + description: "Identifies the type of the source entity. Examples: USER_ID,\ + \ CUSTOMER_ID, ACCOUNT_ID, DATA_GROUP_ID" + example: USER_ID + sourceId: + type: string + description: The id of the entity in the source system + example: user-123456 + sessionId: + type: string + description: "ID of the user session (for web originated events, mostly)" + example: session-789012 + cdpCustomerId: + type: string + description: Provided if the source system is aware of the CDP customer + profile id + example: cdp_789 + data: + type: object + additionalProperties: true + description: "The main entity Map to provide the information\ + \ for CDP" + context: + type: object + additionalProperties: true + description: Map of String and Object to store any information on the context + metadata: + type: object + properties: + schemaVersion: + type: string + description: Event schema version + source: + type: string + description: The name of the event's service origin + correlationId: + type: string + description: Correlation ID for tracing across services + traceId: + type: string + description: Trace ID for distributed tracing + processedBy: + type: array + description: List of services that processed the event + items: + type: string + sourceIp: + type: string + description: Source IP address (for web/mobile events) + userAgent: + type: string + description: User agent (for web/mobile events) + additionalProperties: false + description: Event metadata from source system + CdpEvents: + required: + - cdpEvents + type: object + properties: + cdpEvents: + maxItems: 100 + minItems: 1 + type: array + description: Array of CDP events to be processed + example: + - eventId: 123e4567-e89b-12d3-a456-426614174000 + eventType: USER_ACTION + timestamp: 2024-06-10T12:34:56Z + sourceSystem: webApp + sourceType: USER_ID + sourceId: user_001 + sessionId: session_abc123 + cdpCustomerId: null + data: + action: login + device: Chrome + location: + country: US + city: New York + context: null + metadata: + schemaVersion: 2.1.0 + source: webApp + correlationId: corr-123e4567-e89b-12d3 + traceId: trace-123e4567-e89b-12d3 + processedBy: [] + sourceIp: 192.168.1.1 + userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) + - eventId: 456e7890-e12b-34d5-b789-567890123456 + eventType: TRANSACTION + timestamp: 2024-06-10T12:35:30Z + sourceSystem: mobileApp + sourceType: USER_ID + sourceId: user_001 + sessionId: session_abc123 + cdpCustomerId: cdp_customer_001 + data: + action: payment + amount: 150.0 + currency: USD + merchant: Amazon + context: + deviceType: mobile + appVersion: 2.1.0 + metadata: + schemaVersion: 2.1.0 + source: mobileApp + correlationId: corr-456e7890-e12b-34d5 + traceId: trace-456e7890-e12b-34d5 + processedBy: [] + sourceIp: 10.0.0.5 + userAgent: MobileApp/2.1.0 (iOS 15.0) + items: + $ref: "#/components/schemas/cdp-event" + IngestionSuccessResponse: + required: + - eventId + - status + type: object + properties: + status: + type: string + description: Status of the ingestion request + example: SUCCESS + enum: + - SUCCESS + eventId: + type: string + description: The ID of the ingested event + format: uuid + example: 12345678-1234-5678-9012-123456789012 + message: + type: string + description: Optional success message + example: Event successfully ingested + timestamp: + type: string + description: When the event was processed (ISO 8601 format) + format: date-time + example: 2023-09-16T10:30:01.5Z + BulkIngestionSuccessResponse: + required: + - processedEvents + - status + - totalEvents + type: object + properties: + status: + type: string + description: Status of the bulk ingestion request + example: SUCCESS + enum: + - SUCCESS + - PARTIAL_SUCCESS + totalEvents: + minimum: 1 + type: integer + description: Total number of events received + example: 3 + processedEvents: + minimum: 0 + type: integer + description: Number of events successfully processed + example: 3 + failedEvents: + minimum: 0 + type: integer + description: Number of events that failed processing + example: 0 + errors: + type: array + description: List of errors for failed events (if any) + example: [] + items: + type: object + properties: + eventId: + type: string + description: ID of the event that failed + error: + type: string + description: Error message + errorCode: + type: string + description: Error code + message: + type: string + description: Optional success message + example: All events successfully ingested + timestamp: + type: string + description: When the bulk processing was completed (ISO 8601 format) + format: date-time + example: 2024-06-10T12:36:05.5Z + ValidationRule: + required: + - errorMessage + - fieldPath + - ruleName + - validationConfig + - validationType + type: object + properties: + id: + type: string + description: Unique identifier for the validation rule + format: uuid + readOnly: true + example: 123e4567-e89b-12d3-a456-426614174000 + ruleName: + maxLength: 200 + minLength: 1 + type: string + description: Name of the validation rule + example: User ID Required + validationType: + $ref: "#/components/schemas/validation-type" + fieldPath: + maxLength: 500 + minLength: 1 + type: string + description: JSONPath to the field to validate + example: $.data.userId + validationConfig: + type: object + additionalProperties: true + description: Configuration object for the validation rule + example: + required: true + errorMessage: + maxLength: 500 + minLength: 1 + type: string + description: Error message to return when validation fails + example: User ID is required + SchemaValidationError: + required: + - message + type: object + properties: + message: + type: string + description: High-level error message + example: Invalid JSON schema format + ruleName: + type: string + description: Name of the validation rule that failed + example: ProfileCreatedEvent Schema v1.0 + details: + type: array + description: Detailed validation errors for specific fields + items: + type: object + properties: + field: + type: string + description: The field path where the error occurred + example: properties.userId.type + error: + type: string + description: Specific error message for this field + example: "Invalid type: should be 'string'" + example: + message: Invalid JSON schema format + ruleName: ProfileCreatedEvent Schema v1.0 + details: + - field: properties.userId.type + error: "Invalid type: should be 'string'" + - field: required + error: Missing required field 'email' + EventType: + required: + - eventType + type: object + properties: + id: + type: string + description: Unique UUID identifier for the event type + format: uuid + readOnly: true + example: 550e8400-e29b-41d4-a716-446655440000 + eventType: + maxLength: 100 + minLength: 1 + type: string + description: The event type identifier + example: login + displayName: + maxLength: 200 + type: string + description: Human-readable display name for the event type + example: User Login Event + description: + type: string + description: Description of the event type + example: Event triggered when a user successfully logs into the system + EventTypeAction: + required: + - actionName + - eventType + type: object + properties: + id: + type: string + description: Unique UUID identifier for the event type action + format: uuid + readOnly: true + example: 660e8400-e29b-41d4-a716-446655440001 + eventType: + maxLength: 100 + minLength: 1 + type: string + description: The event type this action applies to + example: ProfileCreatedEvent + actionName: + maxLength: 200 + minLength: 1 + type: string + description: Name of the action to be performed + example: Identity Resolution + description: + maxLength: 500 + type: string + description: Description of what this action does + example: Enrich event with identity resolution data + actionConfig: + type: object + additionalProperties: true + description: | + Configuration parameters for the event type processing. + Contains two main sections: + - profilesMatchingQueryString: Elasticsearch query object to find target profiles + - actions: Map of action names to enabled/disabled flags + example: + profilesMatchingQueryString: + term: + externalIds.id.keyword: '%s' + actions: + save-event: true + create-profile: true + update-segments: true + send-notification: false + EventTypeConfiguration: + required: + - eventType + type: object + properties: + id: + type: string + description: Unique UUID identifier for the event type configuration + format: uuid + readOnly: true + example: 660e8400-e29b-41d4-a716-446655440001 + eventType: + maxLength: 100 + minLength: 1 + type: string + description: The event type this configuration applies to + example: ProfileCreatedEvent + profilesMatchingQuery: + type: object + additionalProperties: true + description: | + Elasticsearch query object to find target profiles for this event type. + This query determines which user profiles this event should be applied to. + example: + term: + externalIds.id.keyword: '%s' + eventConfig: + type: object + additionalProperties: true + description: | + Configuration for event enrichment actions. + Contains map of action names to enabled/disabled flags. + example: + actions: + save-event: true + create-profile: true + update-segments: true + send-notification: false + example: + $ref: ../examples/event-type-configuration.json + ValidationType: + type: string + description: Type of validation to perform + enum: + - SCHEMA_VALIDATION + x-enum-descriptions: + - Validates the entire data object against a JSON Schema + BadRequestError: + title: BadRequestError + required: + - key + - message + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information + key: + minLength: 1 + type: string + description: Error summary + errors: + type: array + description: Detailed error information + items: + $ref: "#/components/schemas/error-item" + UnauthorizedError: + title: UnauthorizedError + required: + - key + - message + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information + key: + minLength: 1 + type: string + description: Error summary + errors: + type: array + description: Detailed error information + items: + $ref: "#/components/schemas/error-item" + NotFoundError: + title: NotFoundError + required: + - key + - message + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information + key: + minLength: 1 + type: string + description: Error summary + errors: + type: array + description: Detailed error information + items: + $ref: "#/components/schemas/error-item" + InternalServerError: + title: SimpleError + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information + key: + minLength: 1 + type: string + description: Error summary + ErrorItem: + title: ErrorItem + required: + - key + - message + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information. + key: + minLength: 1 + type: string + description: "{capability-name}.api.{api-key-name}. For generated validation\ + \ errors this is the path in the document the error resolves to. e.g.\ + \ object name + '.' + field" + context: + title: Context + type: object + additionalProperties: + type: string + description: Context can be anything used to construct localised messages. + error: + title: Error + required: + - key + - message + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information + key: + minLength: 1 + type: string + description: Error summary + errors: + type: array + description: Detailed error information + items: + $ref: "#/components/schemas/error-item" + cdp-event: + required: + - data + - eventId + - eventType + - sourceId + - sourceSystem + - sourceType + type: object + properties: + eventId: + type: string + description: Unique identifier of the event in the source system + example: 12345678-1234-5678-9012-123456789012 + eventType: + type: string + description: Type of the event - used for event validation and processing + definitions + example: login + timestamp: + type: string + description: Time when event was emitted by the source system + format: date-time + example: 2023-09-16T10:30:00Z + sourceSystem: + type: string + description: "System where the event originated (e.g., web_app, mobile_app)" + example: mobile_app + sourceType: + type: string + description: "Identifies the type of the source entity. Examples: USER_ID,\ + \ CUSTOMER_ID, ACCOUNT_ID, DATA_GROUP_ID" + example: USER_ID + sourceId: + type: string + description: The id of the entity in the source system + example: user-123456 + sessionId: + type: string + description: "ID of the user session (for web originated events, mostly)" + example: session-789012 + cdpCustomerId: + type: string + description: Provided if the source system is aware of the CDP customer + profile id + example: cdp_789 + data: + type: object + additionalProperties: true + description: "The main entity Map to provide the information\ + \ for CDP" + context: + type: object + additionalProperties: true + description: Map of String and Object to store any information on the context + metadata: + type: object + properties: + schemaVersion: + type: string + description: Event schema version + source: + type: string + description: The name of the event's service origin + correlationId: + type: string + description: Correlation ID for tracing across services + traceId: + type: string + description: Trace ID for distributed tracing + processedBy: + type: array + description: List of services that processed the event + items: + type: string + sourceIp: + type: string + description: Source IP address (for web/mobile events) + userAgent: + type: string + description: User agent (for web/mobile events) + additionalProperties: false + description: Event metadata from source system + cdp-events: + required: + - cdpEvents + type: object + properties: + cdpEvents: + maxItems: 100 + minItems: 1 + type: array + description: Array of CDP events to be processed + example: + - eventId: 123e4567-e89b-12d3-a456-426614174000 + eventType: USER_ACTION + timestamp: 2024-06-10T12:34:56Z + sourceSystem: webApp + sourceType: USER_ID + sourceId: user_001 + sessionId: session_abc123 + cdpCustomerId: null + data: + action: login + device: Chrome + location: + country: US + city: New York + context: null + metadata: + schemaVersion: 2.1.0 + source: webApp + correlationId: corr-123e4567-e89b-12d3 + traceId: trace-123e4567-e89b-12d3 + processedBy: [] + sourceIp: 192.168.1.1 + userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) + - eventId: 456e7890-e12b-34d5-b789-567890123456 + eventType: TRANSACTION + timestamp: 2024-06-10T12:35:30Z + sourceSystem: mobileApp + sourceType: USER_ID + sourceId: user_001 + sessionId: session_abc123 + cdpCustomerId: cdp_customer_001 + data: + action: payment + amount: 150.0 + currency: USD + merchant: Amazon + context: + deviceType: mobile + appVersion: 2.1.0 + metadata: + schemaVersion: 2.1.0 + source: mobileApp + correlationId: corr-456e7890-e12b-34d5 + traceId: trace-456e7890-e12b-34d5 + processedBy: [] + sourceIp: 10.0.0.5 + userAgent: MobileApp/2.1.0 (iOS 15.0) + items: + $ref: "#/components/schemas/cdp-event" + ingestion-success-response: + required: + - eventId + - status + type: object + properties: + status: + type: string + description: Status of the ingestion request + example: SUCCESS + enum: + - SUCCESS + eventId: + type: string + description: The ID of the ingested event + format: uuid + example: 12345678-1234-5678-9012-123456789012 + message: + type: string + description: Optional success message + example: Event successfully ingested + timestamp: + type: string + description: When the event was processed (ISO 8601 format) + format: date-time + example: 2023-09-16T10:30:01.5Z + bulk-ingestion-success-response: + required: + - processedEvents + - status + - totalEvents + type: object + properties: + status: + type: string + description: Status of the bulk ingestion request + example: SUCCESS + enum: + - SUCCESS + - PARTIAL_SUCCESS + totalEvents: + minimum: 1 + type: integer + description: Total number of events received + example: 3 + processedEvents: + minimum: 0 + type: integer + description: Number of events successfully processed + example: 3 + failedEvents: + minimum: 0 + type: integer + description: Number of events that failed processing + example: 0 + errors: + type: array + description: List of errors for failed events (if any) + example: [] + items: + type: object + properties: + eventId: + type: string + description: ID of the event that failed + error: + type: string + description: Error message + errorCode: + type: string + description: Error code + message: + type: string + description: Optional success message + example: All events successfully ingested + timestamp: + type: string + description: When the bulk processing was completed (ISO 8601 format) + format: date-time + example: 2024-06-10T12:36:05.5Z + validation-rule: + required: + - errorMessage + - fieldPath + - ruleName + - validationConfig + - validationType + type: object + properties: + id: + type: string + description: Unique identifier for the validation rule + format: uuid + readOnly: true + example: 123e4567-e89b-12d3-a456-426614174000 + ruleName: + maxLength: 200 + minLength: 1 + type: string + description: Name of the validation rule + example: User ID Required + validationType: + $ref: "#/components/schemas/validation-type" + fieldPath: + maxLength: 500 + minLength: 1 + type: string + description: JSONPath to the field to validate + example: $.data.userId + validationConfig: + type: object + additionalProperties: true + description: Configuration object for the validation rule + example: + required: true + errorMessage: + maxLength: 500 + minLength: 1 + type: string + description: Error message to return when validation fails + example: User ID is required + schema-validation-error: + required: + - message + type: object + properties: + message: + type: string + description: High-level error message + example: Invalid JSON schema format + ruleName: + type: string + description: Name of the validation rule that failed + example: ProfileCreatedEvent Schema v1.0 + details: + type: array + description: Detailed validation errors for specific fields + items: + type: object + properties: + field: + type: string + description: The field path where the error occurred + example: properties.userId.type + error: + type: string + description: Specific error message for this field + example: "Invalid type: should be 'string'" + example: + message: Invalid JSON schema format + ruleName: ProfileCreatedEvent Schema v1.0 + details: + - field: properties.userId.type + error: "Invalid type: should be 'string'" + - field: required + error: Missing required field 'email' + event-type: + required: + - eventType + type: object + properties: + id: + type: string + description: Unique UUID identifier for the event type + format: uuid + readOnly: true + example: 550e8400-e29b-41d4-a716-446655440000 + eventType: + maxLength: 100 + minLength: 1 + type: string + description: The event type identifier + example: login + displayName: + maxLength: 200 + type: string + description: Human-readable display name for the event type + example: User Login Event + description: + type: string + description: Description of the event type + example: Event triggered when a user successfully logs into the system + event-type-action: + required: + - actionName + - eventType + type: object + properties: + id: + type: string + description: Unique UUID identifier for the event type action + format: uuid + readOnly: true + example: 660e8400-e29b-41d4-a716-446655440001 + eventType: + maxLength: 100 + minLength: 1 + type: string + description: The event type this action applies to + example: ProfileCreatedEvent + actionName: + maxLength: 200 + minLength: 1 + type: string + description: Name of the action to be performed + example: Identity Resolution + description: + maxLength: 500 + type: string + description: Description of what this action does + example: Enrich event with identity resolution data + actionConfig: + type: object + additionalProperties: true + description: | + Configuration parameters for the event type processing. + Contains two main sections: + - profilesMatchingQueryString: Elasticsearch query object to find target profiles + - actions: Map of action names to enabled/disabled flags + example: + profilesMatchingQueryString: + term: + externalIds.id.keyword: '%s' + actions: + save-event: true + create-profile: true + update-segments: true + send-notification: false + event-type-configuration: + required: + - eventType + type: object + properties: + id: + type: string + description: Unique UUID identifier for the event type configuration + format: uuid + readOnly: true + example: 660e8400-e29b-41d4-a716-446655440001 + eventType: + maxLength: 100 + minLength: 1 + type: string + description: The event type this configuration applies to + example: ProfileCreatedEvent + profilesMatchingQuery: + type: object + additionalProperties: true + description: | + Elasticsearch query object to find target profiles for this event type. + This query determines which user profiles this event should be applied to. + example: + term: + externalIds.id.keyword: '%s' + eventConfig: + type: object + additionalProperties: true + description: | + Configuration for event enrichment actions. + Contains map of action names to enabled/disabled flags. + example: + actions: + save-event: true + create-profile: true + update-segments: true + send-notification: false + example: + $ref: ../examples/event-type-configuration.json + validation-type: + type: string + description: Type of validation to perform + enum: + - SCHEMA_VALIDATION + x-enum-descriptions: + - Validates the entire data object against a JSON Schema + bad-request-error: + title: BadRequestError + required: + - key + - message + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information + key: + minLength: 1 + type: string + description: Error summary + errors: + type: array + description: Detailed error information + items: + $ref: "#/components/schemas/error-item" + unauthorized-error: + title: UnauthorizedError + required: + - key + - message + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information + key: + minLength: 1 + type: string + description: Error summary + errors: + type: array + description: Detailed error information + items: + $ref: "#/components/schemas/error-item" + not-found-error: + title: NotFoundError + required: + - key + - message + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information + key: + minLength: 1 + type: string + description: Error summary + errors: + type: array + description: Detailed error information + items: + $ref: "#/components/schemas/error-item" + simple-error: + title: SimpleError + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information + key: + minLength: 1 + type: string + description: Error summary + error-item: + title: ErrorItem + required: + - key + - message + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information. + key: + minLength: 1 + type: string + description: "{capability-name}.api.{api-key-name}. For generated validation\ + \ errors this is the path in the document the error resolves to. e.g.\ + \ object name + '.' + field" + context: + title: Context + type: object + additionalProperties: + type: string + description: Context can be anything used to construct localised messages. + responses: + "404NotFound": + description: Not found. + content: + application/json: + schema: + $ref: "#/components/schemas/error" + example: + $ref: "#/components/examples/not-found-error" + examples: + ValidationRuleExample: + summary: ValidationRuleExample + value: + id: 550e8400-e29b-41d4-a716-446655440001 + ruleName: ProfileCreatedEvent Schema v1.0 + validationType: SCHEMA_VALIDATION + fieldPath: data + errorMessage: Event data must conform to ProfileCreatedEvent schema + validationConfig: + $schema: http://json-schema.org/draft-07/schema# + type: object + required: + - userId + - email + properties: + userId: + type: string + minLength: 1 + email: + type: string + format: email + ValidationRulesListExample: + summary: ValidationRulesListExample + value: + value: + - id: 550e8400-e29b-41d4-a716-446655440001 + ruleName: ProfileCreatedEvent Schema v1.0 + validationType: SCHEMA_VALIDATION + fieldPath: data + errorMessage: Event data must conform to ProfileCreatedEvent schema + validationConfig: + $schema: http://json-schema.org/draft-07/schema# + type: object + required: + - userId + - email + properties: + userId: + type: string + minLength: 1 + email: + type: string + format: email + SchemaValidationErrorExample: + summary: SchemaValidationErrorExample + value: + message: Invalid JSON schema format + ruleName: ProfileCreatedEvent Schema v1.0 + details: + - field: properties.userId.type + error: "Invalid type: should be 'string'" + - field: required + error: Missing required field 'email' + EventTypeExample: + summary: EventTypeExample + value: + id: 770e8400-e29b-41d4-a716-446655440001 + eventType: ProfileCreatedEvent + description: Event triggered when a new user profile is created in the system + version: 1.0.0 + active: true + EventTypesListExample: + summary: EventTypesListExample + value: + value: + - id: 770e8400-e29b-41d4-a716-446655440001 + eventType: ProfileCreatedEvent + description: Event triggered when a new user profile is created + version: 1.0.0 + active: true + - id: 880e8400-e29b-41d4-a716-446655440002 + eventType: InteractionCapturedEvent + description: Event triggered when a user interaction is captured + version: 1.0.0 + active: true + EventTypeConfigurationExample: + summary: EventTypeConfigurationExample + value: + id: 660e8400-e29b-41d4-a716-446655440001 + eventType: ProfileCreatedEvent + description: Configuration for profile creation event processing + profilesMatchingQuery: + term: + externalIds.id.keyword: '%s' + eventConfig: + actions: + save-event: true + create-profile: true + update-segments: true + send-notification: false + CdpEventExample: + summary: CdpEventExample + value: + eventId: 12345678-1234-5678-9012-123456789012 + eventType: USER_ACTION + timestamp: 2023-09-16T10:30:00.000Z + sourceSystem: mobile_app + sourceType: USER_ID + sourceId: user-123456 + sessionId: session-789012 + cdpCustomerId: null + data: + action: login + page: /dashboard + location: + country: US + city: New York + latitude: 40.7128 + longitude: -74.006 + customProperties: + campaign_id: summer_promo_2023 + ab_test_variant: variant_b + context: null + metadata: + schemaVersion: 2.1.0 + source: mobile_app + correlationId: corr-12345678-1234-5678 + traceId: trace-12345678-1234-5678 + processedBy: [] + sourceIp: 192.168.1.100 + userAgent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) + CdpEventsExample: + summary: CdpEventsExample + value: + cdpEvents: + - eventId: 123e4567-e89b-12d3-a456-426614174000 + eventType: USER_ACTION + timestamp: 2024-06-10T12:34:56Z + sourceSystem: webApp + sourceType: USER_ID + sourceId: user_001 + sessionId: session_abc123 + cdpCustomerId: null + data: + action: login + page: /dashboard + location: + country: US + city: New York + latitude: 40.7128 + longitude: -74.006 + context: null + metadata: + schemaVersion: 2.1.0 + source: webApp + correlationId: corr-123e4567-e89b-12d3 + traceId: trace-123e4567-e89b-12d3 + processedBy: [] + sourceIp: 192.168.1.1 + userAgent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) + - eventId: 456e7890-e12b-34d5-b789-567890123456 + eventType: TRANSACTION + timestamp: 2024-06-10T12:35:30Z + sourceSystem: mobileApp + sourceType: USER_ID + sourceId: user_001 + sessionId: session_abc123 + cdpCustomerId: cdp_customer_001 + data: + action: payment + amount: 150.0 + currency: USD + merchant: Amazon + paymentMethod: credit_card + context: + deviceType: mobile + appVersion: 2.1.0 + metadata: + schemaVersion: 2.1.0 + source: mobileApp + correlationId: corr-456e7890-e12b-34d5 + traceId: trace-456e7890-e12b-34d5 + processedBy: [] + sourceIp: 10.0.0.5 + userAgent: MobileApp/2.1.0 (iOS 15.0) + - eventId: 789e0123-e45f-67g8-c901-234567890123 + eventType: PAGE_VIEW + timestamp: 2024-06-10T12:36:00Z + sourceSystem: webApp + sourceType: USER_ID + sourceId: user_002 + sessionId: session_def456 + cdpCustomerId: null + data: + action: page_view + page: /products/electronics + duration: 45 + referrer: https://google.com + context: null + metadata: + schemaVersion: 2.1.0 + source: webApp + correlationId: corr-789e0123-e45f-67g8 + traceId: trace-789e0123-e45f-67g8 + processedBy: [] + sourceIp: 192.168.1.2 + userAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) + IngestionSuccessExample: + summary: IngestionSuccessExample + value: + status: SUCCESS + eventId: 12345678-1234-5678-9012-123456789012 + message: Event successfully ingested + timestamp: 2023-09-16T10:30:01.500Z + BulkIngestionSuccessExample: + summary: BulkIngestionSuccessExample + value: + status: SUCCESS + totalEvents: 3 + processedEvents: 3 + failedEvents: 0 + errors: [] + message: All 3 events successfully ingested + timestamp: 2024-06-10T12:36:05.500Z + BadRequestExample: + summary: BadRequestExample + value: + message: Bad Request + key: REQUEST_ERROR + errors: + - message: "Value Exceeded. Must be between {min} and {max}." + key: common.api.shoesize + context: + max: "50" + min: "1" + UnauthorizedExample: + summary: UnauthorizedExample + value: + message: Access to requested resource denied. + key: UNAUTHORIZED_ERROR + errors: + - message: Resource access denied due to invalid credentials. + key: common.api.token + context: + accessToken: expired + ErrorItemExample: + summary: ErrorItemExample + value: + message: "Value Exceeded. Must be between {min} and {max}." + key: common.api.shoesize + context: + max: "50" + min: "1" + InternalServerErrorExample: + summary: InternalServerErrorExample + value: + message: Description of error + key: SERVER_ERROR + not-found-error: + summary: not-found-error + value: + message: Resource not found. + key: NOT_FOUND_ERROR + errors: + - message: "Unable to find the requested resource: {resource}." + key: common.api.resource + context: + resource: aResource diff --git a/stream-dbs-clients/src/main/openapi/cdp-profiles-service-api-v1.0.0-beta.yaml b/stream-dbs-clients/src/main/openapi/cdp-profiles-service-api-v1.0.0-beta.yaml new file mode 100644 index 000000000..0a2e95cde --- /dev/null +++ b/stream-dbs-clients/src/main/openapi/cdp-profiles-service-api-v1.0.0-beta.yaml @@ -0,0 +1,1092 @@ +openapi: 3.0.3 +info: + title: CDP Profile Service - Service API + description: | + This internal service API is used to manage customer profiles, including personal information, addresses, and product holdings. + version: 1.0.0-beta + x-api-domain: customer-management + x-api-service: customer-data-platform +servers: +- url: http://localhost:8080 +tags: +- name: Profiles + description: Internal operations related to customer profiles. +paths: + /service-api/v1/profiles: + get: + tags: + - Profiles + summary: Fetch all customer profiles + operationId: getProfiles + parameters: + - name: page + in: query + description: "The page number to retrieve, starting at 0." + required: false + style: form + explode: true + schema: + minimum: 0 + type: integer + default: 0 + - name: size + in: query + description: The number of events to return per page. + required: false + style: form + explode: true + schema: + maximum: 20 + minimum: 1 + type: integer + default: 10 + - name: sortDirection + in: query + description: The direction to sort by. + required: false + style: form + explode: true + schema: + type: string + default: asc + enum: + - asc + - desc + responses: + "200": + description: Customer profiles retrieved successfully. + content: + application/json: + schema: + $ref: "#/components/schemas/paged-customer-profile-response" + "404": + description: Customer profiles can't be retrieved. + post: + tags: + - Profiles + summary: Create a customer profile + operationId: createProfile + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/customer-profile-create-request" + required: true + responses: + "201": + description: Profile created successfully. + content: + application/json: + schema: + $ref: "#/components/schemas/customer-profile" + "400": + description: Invalid input provided. + /service-api/v1/profiles/{id}: + get: + tags: + - Profiles + summary: Get a customer profile by ID + operationId: getProfile + parameters: + - name: id + in: path + description: The unique identifier for the customer. + required: true + style: simple + explode: false + schema: + type: string + example: 4b3488af-a244-40ee-bc74-d7d8699fcc03 + responses: + "200": + description: Customer profile retrieved successfully. + content: + application/json: + schema: + $ref: "#/components/schemas/customer-profile" + "404": + description: Customer profile not found. + delete: + tags: + - Profiles + summary: Delete a customer profile + operationId: deleteProfile + parameters: + - name: id + in: path + description: The unique identifier for the customer to delete. + required: true + style: simple + explode: false + schema: + type: string + responses: + "202": + description: Profile deletion request accepted and will be processed asynchronously. + patch: + tags: + - Profiles + summary: Update a customer profile + operationId: updateProfile + parameters: + - name: id + in: path + description: The unique identifier for the customer to update. + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/customer-profile-update-request" + required: true + responses: + "200": + description: Profile updated successfully. + content: + application/json: + schema: + $ref: "#/components/schemas/customer-profile" + "400": + description: Invalid input provided. + "404": + description: Profile not found. + /service-api/v1/profiles/merge: + post: + tags: + - Profiles + summary: Merge two customer profiles + operationId: mergeProfiles + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/merge-profile-request" + required: true + responses: + "200": + description: Profiles merged successfully. + content: + application/json: + schema: + $ref: "#/components/schemas/customer-profile" + "400": + description: Invalid merge request. + /service-api/v1/profiles/external/{id}: + get: + tags: + - Profiles + summary: Get customers profile by ID + operationId: getProfileByExternalId + parameters: + - name: id + in: path + description: Identifier for the customer. + required: true + style: simple + explode: false + schema: + type: string + example: 4b3488af-a244-40ee-bc74-d7d8699fcc03 + - name: page + in: query + description: "The page number to retrieve, starting at 0." + required: false + style: form + explode: true + schema: + minimum: 0 + type: integer + default: 0 + - name: size + in: query + description: The number of events to return per page. + required: false + style: form + explode: true + schema: + maximum: 20 + minimum: 1 + type: integer + default: 10 + responses: + "200": + description: Customer profiles retrieved successfully. + content: + application/json: + schema: + $ref: "#/components/schemas/paged-customer-profile-response" + "404": + description: Customer profile not found. + delete: + tags: + - Profiles + summary: Delete a customer profile + operationId: deleteProfileByExternalId + parameters: + - name: id + in: path + description: The unique identifier for the customer to delete. + required: true + style: simple + explode: false + schema: + type: string + responses: + "204": + description: Customer profile deleted successfully. + "404": + description: Customer profile not found. + patch: + tags: + - Profiles + summary: Update a customer profile + operationId: updateProfileByExternalId + parameters: + - name: id + in: path + description: The unique identifier for the customer to update. + required: true + style: simple + explode: false + schema: + type: string + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/customer-profile-update" + required: true + responses: + "200": + description: Customer profile updated successfully. + content: + application/json: + schema: + $ref: "#/components/schemas/customer-profile" + "404": + description: Customer profile not found. + /service-api/v1/profiles/{cdpCustomerId}/events: + get: + tags: + - Profiles + summary: Fetch all events for customer + description: | + Fetch all the events. + operationId: getEventsByCdpCustomerId + parameters: + - name: cdpCustomerId + in: path + description: The unique identifier for the event. + required: true + style: simple + explode: false + schema: + type: string + example: 4b3488af-a244-40ee-bc74-d7d8699fcc03 + - name: page + in: query + description: "The page number to retrieve, starting at 0." + required: false + style: form + explode: true + schema: + type: integer + default: 0 + - name: size + in: query + description: The number of events to return per page. + required: false + style: form + explode: true + schema: + type: integer + default: 20 + - name: sortDirection + in: query + description: The direction to sort by. + required: false + style: form + explode: true + schema: + type: string + default: asc + enum: + - asc + - desc + responses: + "200": + description: A paginated list of matching events. + content: + application/json: + schema: + $ref: "#/components/schemas/paged-event-entity-response" + /service-api/v1/events/counts: + get: + tags: + - Events + summary: Count events by types and date range + description: Returns a dictionary of event counts for the specified types within + a given time period. + operationId: countEventsByTypes + parameters: + - name: eventTypes + in: query + description: A list of event types to count (maximum 15 types). + required: true + style: form + explode: true + schema: + maxItems: 15 + minItems: 1 + type: array + items: + type: string + - name: segmentCodes + in: query + description: A list of segmentCodes to filter events (maximum 15 types). + required: false + style: form + explode: true + schema: + maxItems: 15 + minItems: 0 + type: array + items: + type: string + - name: fromDate + in: query + description: Start date-time (ISO 8601) for the search range. + required: true + style: form + explode: true + schema: + type: string + format: date-time + - name: toDate + in: query + description: End date-time (ISO 8601) for the search range. + required: true + style: form + explode: true + schema: + type: string + format: date-time + responses: + "200": + description: Event counts retrieved successfully. + content: + application/json: + schema: + type: object + additionalProperties: + type: integer + format: int64 + description: A map where key is the eventType and value is the count. + example: + LOGIN_ATTEMPT: 150 + PAYMENT_SUCCESS: 45 + PROFILE_UPDATE: 12 + "400": + description: Invalid input parameters. +components: + schemas: + paged-customer-profile-response: + title: Paged Customer Profile Response + type: object + properties: + content: + type: array + description: The list of customer profiles for the current page. + items: + $ref: "#/components/schemas/customer-profile" + page: + type: integer + description: The current page number. + size: + type: integer + description: The number of items per page. + totalElements: + type: integer + description: The total number of profiles available. + description: A paginated response containing a list of customer profiles. + customer-profile-create-request: + title: CustomerProfileCreateRequest + required: + - externalIds + - profileStatus + - profileType + type: object + properties: + externalIds: + type: array + items: + $ref: "#/components/schemas/external-id" + organizationInfo: + $ref: "#/components/schemas/organization-info" + personalInfo: + $ref: "#/components/schemas/personal-info" + deliveryChannels: + type: array + items: + $ref: "#/components/schemas/delivery-channel" + addresses: + type: array + items: + $ref: "#/components/schemas/address" + profileExtendedData: + $ref: "#/components/schemas/profile-extended-data" + cardProductHoldings: + type: array + items: + $ref: "#/components/schemas/card-product-holding" + productHoldings: + type: array + items: + $ref: "#/components/schemas/product-holding" + preferences: + type: object + additionalProperties: + type: object + complianceInfo: + type: object + additionalProperties: + type: object + segments: + type: array + items: + $ref: "#/components/schemas/segment" + profileStatus: + type: string + description: The status of the profile. + example: ACTIVE + profileType: + type: string + enum: + - ORGANIZATION + - PERSON + - EMPLOYEE + description: Request body for creating a new customer profile. + customer-profile: + title: CustomerProfile + required: + - externalIds + - profileStatus + - profileType + type: object + properties: + cdpCustomerId: + type: string + description: Unique identifier for the customer profile in the CDP. + example: 4b3488af-a244-40ee-bc74-d7d8699fcc03 + externalIds: + type: array + items: + $ref: "#/components/schemas/external-id" + organizationInfo: + $ref: "#/components/schemas/organization-info" + personalInfo: + $ref: "#/components/schemas/personal-info" + deliveryChannels: + type: array + items: + $ref: "#/components/schemas/delivery-channel" + addresses: + type: array + items: + $ref: "#/components/schemas/address" + profileExtendedData: + $ref: "#/components/schemas/profile-extended-data" + cardProductHoldings: + type: array + items: + $ref: "#/components/schemas/card-product-holding" + productHoldings: + type: array + items: + $ref: "#/components/schemas/product-holding" + financialSummary: + type: object + additionalProperties: + type: object + description: Aggregated financial data related to the customer profile. + behavioralMetrics: + type: object + additionalProperties: + type: object + description: Aggregated financial data related to the customer profile. + inferredAttributes: + type: object + additionalProperties: + type: object + description: Aggregated financial data related to the customer profile. + preferences: + type: object + additionalProperties: + type: object + description: Aggregated financial data related to the customer profile. + complianceInfo: + type: object + additionalProperties: + type: object + description: Aggregated financial data related to the customer profile. + segments: + type: array + items: + $ref: "#/components/schemas/segment" + segmentCodes: + type: array + description: A list of customer segment codes that this profile belongs + to. + example: + - segment1 + - segment2 + - segment10 + items: + type: string + profileStatus: + type: string + description: The status of the profile. + example: ACTIVE + profileType: + type: string + enum: + - ORGANIZATION + - PERSON + - EMPLOYEE + lastUpdated: + type: string + format: date-time + example: 2025-05-20T18:32:56Z + createdAt: + type: string + format: date-time + example: 2024-05-20T18:32:56Z + version: + type: integer + format: int64 + example: 56 + description: A complete customer profile data model. + customer-profile-update-request: + title: CustomerProfileUpdateRequest + type: object + properties: + organizationInfo: + $ref: "#/components/schemas/organization-info" + personalInfo: + $ref: "#/components/schemas/personal-info" + deliveryChannels: + type: array + items: + $ref: "#/components/schemas/delivery-channel" + addresses: + type: array + items: + $ref: "#/components/schemas/address" + profileExtendedData: + $ref: "#/components/schemas/profile-extended-data" + cardProductHoldings: + type: array + items: + $ref: "#/components/schemas/card-product-holding" + productHoldings: + type: array + items: + $ref: "#/components/schemas/product-holding" + preferences: + type: object + additionalProperties: + type: object + complianceInfo: + type: object + additionalProperties: + type: object + segments: + type: array + items: + $ref: "#/components/schemas/segment" + profileStatus: + type: string + description: The status of the profile. + example: ACTIVE + description: Request body for updating an existing customer profile. + merge-profile-request: + title: Merge Profile Request + type: object + description: Request containing a list of profile IDs to be merged into a single + profile. + customer-profile-update: + title: Customer Profile Update + type: object + description: The request body for updating a customer profile using a PATCH + operation. + paged-event-entity-response: + title: Paged Event Entity Response + type: object + properties: + content: + type: array + description: The list of event entities for the current page. + items: + $ref: "#/components/schemas/event-entity" + page: + type: integer + description: The current page number. + size: + type: integer + description: The number of items per page. + totalElements: + type: integer + description: The total number of events available. + description: A paginated response containing a list of event entities. + external-id: + title: ExternalId + required: + - id + - source + - type + type: object + properties: + source: + type: string + description: "The source system of the identifier (e.g., BACKBASE, CORE)." + example: BACKBASE + id: + type: string + example: 3a3488af-a244-40ee-bc74-d7d8699fcc01 + type: + type: string + description: "The type of identifier (e.g., USER_ID)." + example: USER_ID + verified: + type: boolean + description: Indicates if the identifier has been verified. + example: true + organization-info: + title: OrganizationInfo + type: object + properties: + name: + type: string + description: the name of the organization + legalStructure: + type: string + description: the legal structure of the organization + type: + type: string + description: the type of the organization + sector: + type: string + description: "the business sector of the organisation, according to ISO20022\ + \ standards" + email: + type: string + description: Primary email address + format: email + example: johndoe@gmail.com + phone: + type: string + description: Primary phone number + example: "+380671234567" + address: + $ref: "#/components/schemas/address" + establishmentDate: + type: string + description: the date when the organisation was established + format: date + description: filled in only if this profile stands for the organization + personal-info: + title: PersonalInfo + type: object + properties: + firstName: + type: string + example: John + lastName: + type: string + example: Doe + middleName: + type: string + example: Sebastian + fullName: + type: string + example: John Doe + email: + type: string + description: Primary email address + format: email + example: johndoe@gmail.com + phone: + type: string + description: Primary phone number + example: "+380671234567" + address: + $ref: "#/components/schemas/address" + dateOfBirth: + type: string + format: date + example: 1986-06-14 + gender: + type: string + example: MALE + enum: + - MALE + - FEMALE + - NON-BINARY + preferredLocale: + type: string + example: en_US + nationality: + type: string + example: Ukrainian + maritalStatus: + type: string + example: married + occupation: + type: string + example: software development + employer: + type: string + example: "185726526739063089" + employerName: + type: string + example: Backbase + delivery-channel: + title: DeliveryChannel + required: + - type + - value + type: object + properties: + type: + type: string + example: EMAIL + enum: + - EMAIL + - PHONE + - APP + - PUSH + - SMS + - SOCIAL + - OTHER + value: + type: string + example: johndoe@gmail.com + verified: + type: boolean + example: true + optIn: + type: boolean + example: true + primary: + type: boolean + example: true + address: + title: Address + type: object + properties: + street1: + type: string + example: "Park Lane, ave" + street2: + type: string + city: + type: string + example: New York + state: + type: string + example: New York + postalCode: + type: string + example: "12345" + country: + type: string + example: US + type: + type: string + example: HOME + primary: + type: boolean + example: true + description: Address schema + profile-extended-data: + title: ProfileExtendedData + type: object + additionalProperties: + type: object + description: A map for storing arbitrary key-value pairs of extended profile + data. + example: + loyaltyMemberId: L-12345 + lastLoginPlatform: iOS + isPremiumCustomer: true + customPreferences: + theme: dark + notifications: daily + card-product-holding: + title: CardProductHolding + required: + - accountId + - productKind + - productType + type: object + properties: + externalIds: + type: array + description: External identifiers associated with the card product + items: + $ref: "#/components/schemas/external-id" + cardNumberLast4: + type: string + description: "Type of product (e.g., 'currentAccount', 'termDeposit', 'loan',\ + \ 'creditCard')" + cardType: + type: string + description: "Type of product (e.g., 'currentAccount', 'termDeposit', 'loan',\ + \ 'creditCard')" + cardIssuer: + type: string + description: "Primary identifier for the product (account ID, loan ID, card\ + \ ID)" + validThrough: + type: string + description: Date when the product was opened/created + format: date + metadata: + type: object + additionalProperties: + type: object + description: any additional information for the product holding + description: Details of a credit or debit card product held by the customer. + product-holding: + title: ProductHolding + required: + - externalIds + - productKind + - productType + type: object + properties: + externalIds: + type: array + description: "External identifiers for the product holding (e.g., account\ + \ number, IBAN)" + items: + $ref: "#/components/schemas/external-id" + productKind: + type: string + description: "Type of product (e.g., 'currentAccount', 'termDeposit', 'loan',\ + \ 'creditCard')" + example: loans + productType: + type: string + description: "Type of product (e.g., 'currentAccount', 'termDeposit', 'loan',\ + \ 'creditCard')" + example: Mortgage + accountNumberMasked: + type: string + description: Masked account number for display purposes + example: NL?? ABNA 0417 XXXX + currency: + type: string + description: Currency code (ISO 4217) + example: USD + openingDate: + type: string + description: Date when the product was opened/created + format: date + example: 2024-03-17 + status: + type: string + example: ACTIVE + balance: + type: number + description: "Current balance (for accounts), outstanding balance (for loans),\ + \ principal (for TDs)" + format: decimal + example: 15068.78 + interestRate: + type: number + description: Interest rate (if applicable) + format: decimal + example: 22.5 + linkedAccountId: + type: string + description: For cards/overdrafts linked to an account + creditLimit: + type: number + description: Credit limit for credit cards/overdrafts + format: decimal + example: 1000 + availableCredit: + type: number + description: Available credit (for credit products) + format: decimal + example: 1000 + expiryDate: + type: string + description: Expiry date for cards + format: date + example: 2027-05-01 + maturityDate: + type: string + description: Maturity date for term deposits/loans + format: date + example: 2027-05-20 + productName: + type: string + description: Product name as displayed to customer + productCode: + type: string + description: Internal product code + originatingBranch: + type: string + description: Branch or channel where product was opened + metadata: + type: object + additionalProperties: + type: object + description: any additional information for the product holding + description: "Details of a product holding (account, loan, card, etc.) for a\ + \ customer" + segment: + title: Segment + required: + - assignedAt + - code + - id + - name + type: object + properties: + id: + type: string + code: + type: string + description: "unique programmatic code for the segment (e.g., 'RFM_CHAMPION_CUSTOMER')" + name: + type: string + description: human-readable name of the segment + assignedAt: + type: string + description: timestamp when the segment was assigned to the customer + format: date-time + additional: + type: object + additionalProperties: + type: string + description: A customer segment data model. + event-entity: + title: EventEntity + type: object + properties: + eventId: + type: string + description: The unique identifier for the event. + example: evt_a1b2c3d4e5f6 + eventType: + type: string + description: "The type of the event (e.g., 'user.login', 'transaction.completed')." + example: user.login + timestamp: + type: string + description: The ISO 8601 timestamp of when the event occurred. + format: date-time + example: 2025-10-09T08:32:49Z + sourceSystem: + type: string + description: "The system that originated the event (e.g., 'WebApp', 'MobileApp')." + example: WebApp + sourceType: + type: string + description: "The type of the source entity (e.g., 'user', 'account')." + example: user + sourceId: + type: string + description: The ID of the source entity. + example: usr_f6e5d4c3b2a1 + sessionId: + type: string + description: The session identifier associated with the event. + example: sess_9876543210 + cdpCustomerId: + type: string + description: The Customer Data Platform unique identifier for the customer. + example: cdp_xyz987abc654 + data: + type: object + additionalProperties: + type: object + description: A flexible map containing the primary event payload. + example: + loginMethod: password + successful: true + context: + type: object + additionalProperties: + type: object + description: A flexible map containing contextual information about the + event. + example: + deviceType: mobile + location: "Kraków, PL" + metadata: + $ref: "#/components/schemas/event-metadata" + description: Represents a generic event captured by the system. + event-metadata: + title: EventMetadata + type: object + properties: + schemaVersion: + type: string + description: The version of the event schema. + example: 1.2.0 + source: + type: string + description: The specific source. + traceId: + type: string + description: The distributed tracing identifier. + example: trace_1122334455 + ingestedAt: + type: string + description: The ISO 8601 timestamp of when the event was ingested by the + system. + format: date-time + example: 2025-10-09T08:32:50.123Z + processingStatus: + type: string + description: The current processing status of the event. + example: PROCESSED + processedBy: + type: array + description: A list of services or processors that have handled this event. + example: + - ingestion-service + - profile-builder + items: + type: string + sourceIp: + type: string + description: The IP address from which the event originated. + format: ipv4 + example: 198.51.100.14 + userAgent: + type: string + description: The user agent string of the client that generated the event. + example: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,\ + \ like Gecko) Chrome/125.0.0.0 Safari/537.36" + correlationId: + type: string + description: An ID to correlate events across different systems. + example: corr_aabbccddeeff + additionalProperties: + type: object + additionalProperties: + type: string + description: A map for any other arbitrary metadata properties. + example: + data_center: europe-west3 + description: Metadata associated with the processing and ingestion of the event. + examples: {} diff --git a/stream-investment/investment-core/pom.xml b/stream-investment/investment-core/pom.xml index 24a832ea2..0afc14948 100644 --- a/stream-investment/investment-core/pom.xml +++ b/stream-investment/investment-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-investment - 9.5.1 + 9.6.0 investment-core diff --git a/stream-investment/pom.xml b/stream-investment/pom.xml index 04786c0b4..f5d54ff3a 100644 --- a/stream-investment/pom.xml +++ b/stream-investment/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-investment diff --git a/stream-legal-entity/legal-entity-bootstrap-task/pom.xml b/stream-legal-entity/legal-entity-bootstrap-task/pom.xml index 56ded57ce..b2d709b53 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/pom.xml +++ b/stream-legal-entity/legal-entity-bootstrap-task/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-task-starter-parent - 9.5.1 + 9.6.0 ../../stream-sdk/stream-starter-parents/stream-task-starter-parent diff --git a/stream-legal-entity/legal-entity-core/pom.xml b/stream-legal-entity/legal-entity-core/pom.xml index ac1bf44c1..1154ec059 100644 --- a/stream-legal-entity/legal-entity-core/pom.xml +++ b/stream-legal-entity/legal-entity-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-legal-entity - 9.5.1 + 9.6.0 legal-entity-core @@ -34,6 +34,13 @@ compile + + com.backbase.stream + cdp-core + ${project.version} + compile + + com.backbase.stream contacts-core diff --git a/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntitySagaV2.java b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntitySagaV2.java index b896ee1cf..a9f5c27ae 100644 --- a/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntitySagaV2.java +++ b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntitySagaV2.java @@ -10,6 +10,8 @@ import com.backbase.audiences.collector.api.service.v1.model.CustomerOnboardedRequest; import com.backbase.audiences.collector.api.service.v1.model.CustomerOnboardedRequest.UserKindEnum; +import com.backbase.cdp.ingestion.api.service.v1.model.CdpEvent; +import com.backbase.cdp.ingestion.api.service.v1.model.CdpEvents; import com.backbase.dbs.contact.api.service.v2.model.AccessContextScope; import com.backbase.dbs.contact.api.service.v2.model.ContactsBulkPostRequestBody; import com.backbase.dbs.contact.api.service.v2.model.ExternalAccessContext; @@ -23,6 +25,8 @@ import com.backbase.dbs.user.profile.api.service.v2.model.CreateUserProfile; import com.backbase.stream.audiences.UserKindSegmentationSaga; import com.backbase.stream.audiences.UserKindSegmentationTask; +import com.backbase.stream.cdp.CdpSaga; +import com.backbase.stream.cdp.CdpTask; import com.backbase.stream.configuration.LegalEntitySagaConfigurationProperties; import com.backbase.stream.contact.ContactsSaga; import com.backbase.stream.contact.ContactsTask; @@ -45,6 +49,7 @@ import com.backbase.stream.mapper.LegalEntityV2toV1Mapper; import com.backbase.stream.mapper.ServiceAgreementV2ToV1Mapper; import com.backbase.stream.mapper.UserProfileMapper; +import com.backbase.stream.mapper.UserToCdpEventMapper; import com.backbase.stream.product.utils.StreamUtils; import com.backbase.stream.service.AccessGroupService; import com.backbase.stream.service.CustomerProfileService; @@ -80,6 +85,7 @@ public class LegalEntitySagaV2 extends HelperProcessor implements StreamTaskExec private final UserProfileMapper userProfileMapper = Mappers.getMapper(UserProfileMapper.class); private final LegalEntityV2toV1Mapper leV2Mapper = Mappers.getMapper(LegalEntityV2toV1Mapper.class); private final ServiceAgreementV2ToV1Mapper saV2Mapper = Mappers.getMapper(ServiceAgreementV2ToV1Mapper.class); + private final UserToCdpEventMapper cdpEventMapper = Mappers.getMapper(UserToCdpEventMapper.class); private final LegalEntityService legalEntityService; private final UserService userService; @@ -90,6 +96,7 @@ public class LegalEntitySagaV2 extends HelperProcessor implements StreamTaskExec private final CustomerAccessGroupSaga customerAccessGroupSaga; private final LegalEntitySagaConfigurationProperties legalEntitySagaConfigurationProperties; private final UserKindSegmentationSaga userKindSegmentationSaga; + private final CdpSaga cdpSaga; private final CustomerProfileService customerProfileService; private static final ExternalContactMapper externalContactMapper = ExternalContactMapper.INSTANCE; @@ -104,6 +111,7 @@ public LegalEntitySagaV2( CustomerAccessGroupSaga customerAccessGroupSaga, LegalEntitySagaConfigurationProperties legalEntitySagaConfigurationProperties, UserKindSegmentationSaga userKindSegmentationSaga, + CdpSaga cdpSaga, CustomerProfileService customerProfileService) { this.legalEntityService = legalEntityService; this.userService = userService; @@ -114,6 +122,7 @@ public LegalEntitySagaV2( this.customerAccessGroupSaga = customerAccessGroupSaga; this.legalEntitySagaConfigurationProperties = legalEntitySagaConfigurationProperties; this.userKindSegmentationSaga = userKindSegmentationSaga; + this.cdpSaga = cdpSaga; this.customerProfileService = customerProfileService; } @@ -125,6 +134,7 @@ public Mono executeTask(@SpanTag(value = "streamTask") LegalE .flatMap(this::setupAdministrators) .flatMap(this::setupUsers) .flatMap(this::processAudiencesSegmentation) + .flatMap(this::processCdpProfilesIngestion) .flatMap(this::setupLimits) .flatMap(this::postLegalEntityContacts) .flatMap(this::processSubsidiaries) @@ -185,6 +195,37 @@ private Mono processAudiencesSegmentation(LegalEntityTaskV2 s .then(Mono.just(streamTask)); } + private Mono processCdpProfilesIngestion(LegalEntityTaskV2 streamTask) { + if (!cdpSaga.isEnabled()) { + log.info("Skipping cdp profiles ingestion - feature is disabled."); + return Mono.just(streamTask); + } + + var le = streamTask.getData(); + + if (le.getLegalEntityType() != LegalEntityType.CUSTOMER) { + return Mono.just(streamTask); + } + + if (isNull(le.getInternalId())) { + return Mono.just(streamTask); + } + + log.info("Ingesting customers of LE {} into CDP", le.getExternalId()); + + return Flux.fromStream(StreamUtils.nullableCollectionToStream(le.getUsers())) + .filter(user -> nonNull(user.getInternalId())) + .map(user -> { + var task = new CdpTask(); + CdpEvent cdpEvent = cdpEventMapper.mapUserToCdpEvent( + le.getInternalId(), le.getExternalId(), le.getCustomerCategory(), user); + task.setCdpEvents(new CdpEvents().addCdpEventsItem(cdpEvent)); + return task; + }) + .flatMap(cdpSaga::executeTask) + .then(Mono.just(streamTask)); + } + private UserKindEnum customerCategoryToUserKind(CustomerCategory customerCategory) { return switch (customerCategory) { case RETAIL -> UserKindEnum.RETAILCUSTOMER; diff --git a/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/configuration/LegalEntitySagaConfiguration.java b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/configuration/LegalEntitySagaConfiguration.java index 52fb8ba2c..c102be357 100644 --- a/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/configuration/LegalEntitySagaConfiguration.java +++ b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/configuration/LegalEntitySagaConfiguration.java @@ -7,6 +7,7 @@ import com.backbase.stream.LegalEntityUnitOfWorkExecutor; import com.backbase.stream.ServiceAgreementSagaV2; import com.backbase.stream.audiences.UserKindSegmentationSaga; +import com.backbase.stream.cdp.CdpSaga; import com.backbase.stream.contact.ContactsSaga; import com.backbase.stream.legalentity.repository.LegalEntityUnitOfWorkRepository; import com.backbase.stream.limit.LimitsSaga; @@ -80,8 +81,8 @@ public LegalEntitySagaV2 reactiveLegalEntitySagaV2(LegalEntityService legalEntit CustomerAccessGroupSaga customerAccessGroupSaga, LegalEntitySagaConfigurationProperties sinkConfigurationProperties, UserKindSegmentationSaga userKindSegmentationSaga, - CustomerProfileService customerProfileService - ) { + CdpSaga cdpSaga, + CustomerProfileService customerProfileService) { return new LegalEntitySagaV2( legalEntityService, userService, @@ -92,6 +93,7 @@ public LegalEntitySagaV2 reactiveLegalEntitySagaV2(LegalEntityService legalEntit customerAccessGroupSaga, sinkConfigurationProperties, userKindSegmentationSaga, + cdpSaga, customerProfileService ); } diff --git a/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/mapper/UserToCdpEventMapper.java b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/mapper/UserToCdpEventMapper.java new file mode 100644 index 000000000..fa599b7f6 --- /dev/null +++ b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/mapper/UserToCdpEventMapper.java @@ -0,0 +1,378 @@ +package com.backbase.stream.mapper; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; +import static org.mapstruct.ReportingPolicy.ERROR; + +import com.backbase.cdp.ingestion.api.service.v1.model.CdpEvent; +import com.backbase.cdp.ingestion.api.service.v1.model.CdpEventMetadata; +import com.backbase.cdp.profiles.api.service.v1.model.Address; +import com.backbase.cdp.profiles.api.service.v1.model.CustomerProfile; +import com.backbase.cdp.profiles.api.service.v1.model.DeliveryChannel; +import com.backbase.cdp.profiles.api.service.v1.model.ExternalId; +import com.backbase.cdp.profiles.api.service.v1.model.PersonalInfo; +import com.backbase.stream.legalentity.model.CustomerCategory; +import com.backbase.stream.legalentity.model.EmailAddress; +import com.backbase.stream.legalentity.model.Multivalued; +import com.backbase.stream.legalentity.model.PhoneNumber; +import com.backbase.stream.legalentity.model.User; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +@Mapper(componentModel = "spring", unmappedTargetPolicy = ERROR) +public interface UserToCdpEventMapper { + + String PROFILE_CREATED_EVENT = "ProfileCreatedEvent"; + + String SOURCE_BACKBASE = "BACKBASE"; + String SOURCE_CORE_BANKING_SYSTEM = "CORE_SYSTEM"; + + String TYPE_USER_ID = "USER_ID"; + String TYPE_CUSTOMER_ID = "CUSTOMER_ID"; + + @Mapping(target = "eventId", expression = "java(generateEventId())") + @Mapping(target = "timestamp", expression = "java(generateEventTimestamp())") + @Mapping(target = "sessionId", ignore = true) + @Mapping(target = "cdpCustomerId", ignore = true) + @Mapping(target = "context", ignore = true) + @Mapping(target = "metadata", expression = "java(generateEventMetadata(\"stream-services\"))") + @Mapping(target = "eventType", constant = PROFILE_CREATED_EVENT) + @Mapping(target = "sourceSystem", constant = SOURCE_BACKBASE) + @Mapping(target = "sourceType", constant = TYPE_USER_ID) + @Mapping(target = "sourceId", expression = "java(user.getInternalId())") + @Mapping(target = "data", expression = "java(mapEntityToData(mapGetUserProfileToCdpCustomerProfile(" + + "legalEntityInternalId, legalEntityExternalId, customerCategory, user)))") + CdpEvent mapUserToCdpEvent(String legalEntityInternalId, + String legalEntityExternalId, + CustomerCategory customerCategory, + User user); + + default List
convertPostalAddresses(User user) { + List
addresses = new ArrayList<>(); + if (nonNull(user.getUserProfile()) && nonNull(user.getUserProfile().getAddresses())) { + addresses = user.getUserProfile().getAddresses() + .stream() + .map(this::mapPostalAddress) + .toList(); + } + return addresses; + } + + default List convertElectronicAddressesAndPhoneNumbers(User user) { + List contactDetails = new ArrayList<>(); + if (nonNull(user.getEmailAddress())) { + contactDetails.add(mapEmailAddress(user.getEmailAddress())); + } + if (nonNull(user.getMobileNumber())) { + contactDetails.add(mapPhoneAddress(user.getMobileNumber())); + } + List electronicAddresses = new ArrayList<>(); + List phoneNumbers = new ArrayList<>(); + if (nonNull(user.getUserProfile())) { + if (nonNull(user.getUserProfile().getAdditionalEmails())) { + electronicAddresses.addAll(user.getUserProfile().getAdditionalEmails()); + } + if (nonNull(user.getUserProfile().getAdditionalPhoneNumbers())) { + phoneNumbers.addAll(user.getUserProfile().getAdditionalPhoneNumbers()); + } + } + contactDetails.addAll(electronicAddresses + .stream().map(this::mapElectronicAddress) + .toList()); + contactDetails.addAll(phoneNumbers + .stream().map(this::mapPhoneAddress) + .toList()); + return contactDetails; + } + + @Mapping(target = "street1", source = "streetAddress") + @Mapping(target = "city", source = "locality") + @Mapping(target = "country", source = "country") + @Mapping(target = "postalCode", source = "postalCode") + @Mapping(target = "primary", source = "primary") + @Mapping(target = "type", source = "type") + @Mapping(target = "street2", ignore = true) + @Mapping(target = "state", ignore = true) + Address mapPostalAddress(com.backbase.stream.legalentity.model.Address postalAddress); + + @Mapping(target = "type", + expression = "java(com.backbase.cdp.profiles.api.service.v1.model.DeliveryChannel.TypeEnum.EMAIL)") + @Mapping(target = "value", source = "value") + @Mapping(target = "primary", source = "primary") + @Mapping(target = "verified", constant = "true") + @Mapping(target = "optIn", ignore = true) + DeliveryChannel mapElectronicAddress(Multivalued electronicAddress); + + @Mapping(target = "type", + expression = "java(com.backbase.cdp.profiles.api.service.v1.model.DeliveryChannel.TypeEnum.PHONE)") + @Mapping(target = "value", source = "value") + @Mapping(target = "primary", source = "primary") + @Mapping(target = "verified", constant = "true") + @Mapping(target = "optIn", ignore = true) + DeliveryChannel mapPhoneAddress(Multivalued phoneAddress); + + @Mapping(target = "firstName", expression = "java(mapFirstName(user))") + @Mapping(target = "lastName", expression = "java(mapLastName(user))") + @Mapping(target = "middleName", expression = "java(mapMiddleName(user))") + @Mapping(target = "fullName", ignore = true) + @Mapping(target = "email", expression = "java(getPrimaryEmail(user))") + @Mapping(target = "phone", expression = "java(getPrimaryPhone(user))") + @Mapping(target = "address", expression = "java(getPrimaryAddress(convertPostalAddresses(user)))") + @Mapping(target = "dateOfBirth", expression = "java(mapDateOfBirth(user))") + @Mapping(target = "gender", expression = "java(mapGender(user))") + @Mapping(target = "preferredLocale", expression = "java(mapLocale(user))") + @Mapping(target = "nationality", expression = "java(mapNationality(user))") + @Mapping(target = "maritalStatus", source = "user.userProfile.personalInformation.maritalStatus") + @Mapping(target = "occupation", ignore = true) + @Mapping(target = "employer", source = "user.userProfile.personalInformation.employer") + @Mapping(target = "employerName", ignore = true) + PersonalInfo mapPersonalInfo(User user); + + @Mapping(target = "cdpCustomerId", ignore = true) + @Mapping(target = "externalIds", expression = "java(mapUserToExternalIds(" + + "user, legalEntityInternalId, legalEntityExternalId))") + @Mapping(target = "personalInfo", expression = "java(mapPersonalInfo(user))") + @Mapping(target = "deliveryChannels", expression = "java(convertElectronicAddressesAndPhoneNumbers(user))") + @Mapping(target = "productHoldings", ignore = true) + @Mapping(target = "financialSummary", ignore = true) + @Mapping(target = "behavioralMetrics", ignore = true) + @Mapping(target = "inferredAttributes", ignore = true) + @Mapping(target = "preferences", ignore = true) + @Mapping(target = "complianceInfo", ignore = true) + @Mapping(target = "addresses", expression = "java(convertPostalAddresses(user))") + @Mapping(target = "segments", ignore = true) + @Mapping(target = "segmentCodes", ignore = true) + @Mapping(target = "organizationInfo", ignore = true) + @Mapping(target = "profileExtendedData", ignore = true) + @Mapping(target = "cardProductHoldings", ignore = true) + @Mapping(target = "profileStatus", expression = "java(mapUserStatus(user))") + @Mapping(target = "profileType", expression = "java(mapProfileType(customerCategory))") + @Mapping(target = "lastUpdated", ignore = true) + @Mapping(target = "createdAt", ignore = true) + @Mapping(target = "version", constant = "0L") + CustomerProfile mapGetUserProfileToCdpCustomerProfile(String legalEntityInternalId, + String legalEntityExternalId, + CustomerCategory customerCategory, + User user); + + default CustomerProfile.ProfileTypeEnum mapProfileType(CustomerCategory customerCategory) { + if (isNull(customerCategory)) { + return CustomerProfile.ProfileTypeEnum.PERSON; + } + return customerCategory.equals(CustomerCategory.RETAIL) + ? CustomerProfile.ProfileTypeEnum.PERSON + : CustomerProfile.ProfileTypeEnum.EMPLOYEE; + } + + default String mapUserStatus(User user) { + if (isNull(user) + || isNull(user.getUserProfile()) + || isNull(user.getUserProfile().getActive())) { + return "ACTIVE"; + } + return Boolean.TRUE.equals(user.getUserProfile().getActive()) ? "ACTIVE" : "INACTIVE"; + } + + default String mapNationality(User user) { + if (isNull(user) + || isNull(user.getUserProfile()) + || isNull(user.getUserProfile().getPersonalInformation()) + || isNull(user.getUserProfile().getPersonalInformation().getNationalities()) + || user.getUserProfile().getPersonalInformation().getNationalities().isEmpty()) { + return null; + } + return user.getUserProfile().getPersonalInformation().getNationalities().getFirst(); + } + + default String getPrimaryEmail(User user) { + if (nonNull(user.getEmailAddress())) { + return user.getEmailAddress().getAddress(); + } else if (nonNull(user.getUserProfile()) && nonNull(user.getUserProfile().getAdditionalEmails())) { + return getPrimaryValueFromMultiValued(user.getUserProfile().getAdditionalEmails()); + } + return null; + } + + default String getPrimaryPhone(User user) { + if (nonNull(user.getMobileNumber())) { + return user.getMobileNumber().getNumber(); + } else if (nonNull(user.getUserProfile()) && nonNull(user.getUserProfile().getAdditionalPhoneNumbers())) { + return getPrimaryValueFromMultiValued(user.getUserProfile().getAdditionalPhoneNumbers()); + } + return null; + } + + default String getPrimaryValueFromMultiValued(List addresses) { + if (addresses == null || addresses.isEmpty()) { + return null; + } + return addresses.stream() + .filter(address -> address.getPrimary() != null && address.getPrimary()) + .findFirst() + .orElse(addresses.getFirst()) + .getValue(); + } + + default PersonalInfo.GenderEnum mapGender(User user) { + if (isNull(user) + || isNull(user.getUserProfile()) + || isNull(user.getUserProfile().getPersonalInformation()) + || isNull(user.getUserProfile().getPersonalInformation().getGender()) + || user.getUserProfile().getPersonalInformation().getGender().isEmpty()) { + return null; + } + return mapStringToGender(user.getUserProfile().getPersonalInformation().getGender()); + } + + default String mapLocale(User user) { + if (isNull(user) + || isNull(user.getUserProfile()) + || isNull(user.getUserProfile().getLocale())) { + return null; + } + return user.getUserProfile().getLocale(); + } + + default String mapFirstName(User user) { + if (isNull(user) + || isNull(user.getUserProfile()) + || isNull(user.getUserProfile().getName()) + || isNull(user.getUserProfile().getName().getGivenName())) { + return null; + } + return user.getUserProfile().getName().getGivenName(); + } + + default String mapLastName(User user) { + if (isNull(user) + || isNull(user.getUserProfile()) + || isNull(user.getUserProfile().getName()) + || isNull(user.getUserProfile().getName().getFamilyName())) { + return null; + } + return user.getUserProfile().getName().getFamilyName(); + } + + default String mapMiddleName(User user) { + if (isNull(user) + || isNull(user.getUserProfile()) + || isNull(user.getUserProfile().getName()) + || isNull(user.getUserProfile().getName().getMiddleName())) { + return null; + } + return user.getUserProfile().getName().getMiddleName(); + } + + default LocalDate mapDateOfBirth(User user) { + if (isNull(user) || isNull(user.getUserProfile()) || isNull(user.getUserProfile().getPersonalInformation()) + || isNull(user.getUserProfile().getPersonalInformation().getDateOfBirth())) { + return null; + } + String dateOfBirth = user.getUserProfile().getPersonalInformation().getDateOfBirth(); + if (isNull(dateOfBirth) || dateOfBirth.isEmpty()) { + return null; + } + return LocalDate.parse(dateOfBirth); + } + + @Mapping(target = "type", + expression = "java(com.backbase.cdp.profiles.api.service.v1.model.DeliveryChannel.TypeEnum.PHONE)") + @Mapping(target = "value", source = "number") + @Mapping(target = "primary", source = "primary") + @Mapping(target = "verified", constant = "true") + @Mapping(target = "optIn", ignore = true) + DeliveryChannel mapPhoneAddress(PhoneNumber phoneAddress); + + @Mapping(target = "type", + expression = "java(com.backbase.cdp.profiles.api.service.v1.model.DeliveryChannel.TypeEnum.EMAIL)") + @Mapping(target = "value", source = "address") + @Mapping(target = "primary", source = "primary") + @Mapping(target = "verified", constant = "true") + @Mapping(target = "optIn", ignore = true) + DeliveryChannel mapEmailAddress(EmailAddress emailAddress); + + default Address getPrimaryAddress(List
addresses) { + if (addresses == null || addresses.isEmpty()) { + return null; + } + return addresses.stream() + .filter(address -> address.getPrimary() != null && address.getPrimary()) + .findFirst() + .orElse(addresses.getFirst()); + } + + default PersonalInfo.GenderEnum mapStringToGender(String gender) { + if (gender == null || gender.isEmpty()) { + return null; + } + try { + return PersonalInfo.GenderEnum.fromValue(gender.toUpperCase()); + } catch (Exception e) { + return null; + } + } + + default Map mapEntityToData(Object entity) { + ObjectMapper mapper = new ObjectMapper() + .registerModule(new JavaTimeModule()) + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + return mapper.convertValue(entity, new TypeReference<>() {}); + } + + default String generateEventId() { + return java.util.UUID.randomUUID().toString(); + } + + default OffsetDateTime generateEventTimestamp() { + return OffsetDateTime.now(ZoneOffset.UTC); + } + + default CdpEventMetadata generateEventMetadata(String eventSource) { + return new CdpEventMetadata() + .processedBy(List.of("stream-services")) + .schemaVersion("1") + .source(eventSource); + } + + default List mapUserToExternalIds(User user, String legalEntityInternalId, String legalEntityExternalId) { + List externalIds = new ArrayList<>(); + externalIds.add(new ExternalId() + .source(SOURCE_CORE_BANKING_SYSTEM) + .type(TYPE_USER_ID) + .id(user.getExternalId()) + .verified(true)); + if (nonNull(user.getInternalId())) { + externalIds.add(new ExternalId() + .source(SOURCE_BACKBASE) + .type(TYPE_USER_ID) + .id(user.getInternalId()) + .verified(true)); + } + if (nonNull(legalEntityInternalId)) { + externalIds.add(new ExternalId() + .source(SOURCE_BACKBASE) + .type(TYPE_CUSTOMER_ID) + .id(legalEntityInternalId) + .verified(true)); + } + if (nonNull(legalEntityExternalId)) { + externalIds.add(new ExternalId() + .source(SOURCE_CORE_BANKING_SYSTEM) + .type(TYPE_CUSTOMER_ID) + .id(legalEntityExternalId) + .verified(true)); + } + return externalIds; + } + +} diff --git a/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/LegalEntitySagaV2Test.java b/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/LegalEntitySagaV2Test.java index 8e3030da3..a1db08b70 100644 --- a/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/LegalEntitySagaV2Test.java +++ b/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/LegalEntitySagaV2Test.java @@ -1,17 +1,21 @@ package com.backbase.stream; import static com.backbase.stream.FixtureUtils.reflectiveAlphaFixtureMonkey; +import static com.backbase.stream.mapper.UserToCdpEventMapper.PROFILE_CREATED_EVENT; import static com.backbase.stream.service.UserService.REMOVED_PREFIX; import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.backbase.cdp.ingestion.api.service.v1.model.CdpEvent; import com.backbase.customerprofile.api.integration.v1.model.PartyResponseUpsertDto; import com.backbase.dbs.contact.api.service.v2.model.AccessContextScope; import com.backbase.dbs.contact.api.service.v2.model.ContactsBulkPostRequestBody; @@ -23,20 +27,28 @@ import com.backbase.dbs.user.api.service.v2.model.Realm; import com.backbase.stream.audiences.UserKindSegmentationSaga; import com.backbase.stream.audiences.UserKindSegmentationTask; +import com.backbase.stream.cdp.CdpSaga; +import com.backbase.stream.cdp.CdpTask; import com.backbase.stream.configuration.LegalEntitySagaConfigurationProperties; import com.backbase.stream.contact.ContactsSaga; import com.backbase.stream.contact.ContactsTask; +import com.backbase.stream.legalentity.model.Address; import com.backbase.stream.legalentity.model.CustomerCategory; +import com.backbase.stream.legalentity.model.EmailAddress; import com.backbase.stream.legalentity.model.ExternalAccountInformation; import com.backbase.stream.legalentity.model.ExternalContact; import com.backbase.stream.legalentity.model.LegalEntity; import com.backbase.stream.legalentity.model.LegalEntityType; import com.backbase.stream.legalentity.model.LegalEntityV2; +import com.backbase.stream.legalentity.model.Name; import com.backbase.stream.legalentity.model.Party; +import com.backbase.stream.legalentity.model.PersonalInformation; +import com.backbase.stream.legalentity.model.PhoneNumber; import com.backbase.stream.legalentity.model.SavingsAccount; import com.backbase.stream.legalentity.model.ServiceAgreement; import com.backbase.stream.legalentity.model.ServiceAgreementV2; import com.backbase.stream.legalentity.model.User; +import com.backbase.stream.legalentity.model.UserProfile; import com.backbase.stream.mapper.LegalEntityV2toV1Mapper; import com.backbase.stream.mapper.ServiceAgreementV2ToV1Mapper; import com.backbase.stream.service.AccessGroupService; @@ -49,6 +61,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.UUID; import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -57,6 +70,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.mapstruct.factory.Mappers; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; @@ -92,6 +106,9 @@ class LegalEntitySagaV2Test { @Mock private UserKindSegmentationSaga userKindSegmentationSaga; + @Mock + private CdpSaga cdpSaga; + @Mock private CustomerProfileService customerProfileService; @@ -356,19 +373,46 @@ void updateUserName() { Assertions.assertNotNull(result); Assertions.assertNotNull(result.getData().getUsers()); - Assertions.assertNotNull(result.getData().getUsers().get(0)); + Assertions.assertNotNull(result.getData().getUsers().getFirst()); Assertions.assertEquals(newRegularUser.getFullName(), - result.getData().getUsers().get(0).getFullName()); + result.getData().getUsers().getFirst().getFullName()); } void getMockLegalEntity() { - regularUser = new User().internalId("someRegularUserInId") - .externalId(regularUserExId); + String userInternalId = UUID.randomUUID().toString(); + regularUser = new User().internalId(userInternalId) + .externalId(regularUserExId) + .fullName("User Full Name") + .userProfile(new UserProfile() + .userId(userInternalId) + .externalId(regularUserExId) + .name(new Name() + .familyName("User") + .givenName("Name") + .middleName("Full") + .formatted("Full Name User")) + .active(true) + .addresses(List.of(new Address() + .streetAddress("Some street 1") + .locality("Some city") + .postalCode("02147") + .country("Some country"))) + .personalInformation(new PersonalInformation() + .dateOfBirth("1990-01-01") + .employer("BACKBASE") + .gender("male") + .maritalStatus("single"))) + .legalEntityId(leInternalId) + .emailAddress(new EmailAddress("home", "home", regularUserExId.concat("@domain.com"))) + .mobileNumber(new PhoneNumber("home", "home", "1234567890")); User adminUser = new User().internalId("someAdminInId").externalId(adminExId); ServiceAgreement sa = new ServiceAgreement().creatorLegalEntity(leExternalId); legalEntityV2 = new LegalEntityV2() .internalId(leInternalId) .externalId(leExternalId) + .legalEntityType(LegalEntityType.CUSTOMER) + .name("le-name") + .customerCategory(CustomerCategory.RETAIL) .parties(fixtureMonkey.giveMe(Party.class, PARTY_SIZE)) .addAdministratorsItem(adminUser) .parentExternalId(leParentExternalId) @@ -412,7 +456,7 @@ void test_PostLegalContacts() { legalEntityV2.setContacts(Collections.singletonList(contact)); result = legalEntitySaga.executeTask(task).block(); Assertions.assertNotNull(result); - Assertions.assertEquals(leExternalId, result.getData().getContacts().get(0).getExternalId()); + Assertions.assertEquals(leExternalId, result.getData().getContacts().getFirst().getExternalId()); } @Test @@ -540,6 +584,48 @@ void whenUserKindSegmentationIsEnabledAndNoCustomerCategoryCanBeDeterminedReturn ); } + @Test + void cdpIsDisabled() { + getMockLegalEntity(); + + when(cdpSaga.isEnabled()).thenReturn(false); + + when(customerProfileService.upsertParty(any(Party.class), anyString())).thenReturn( + Mono.just(fixtureMonkey.giveMeOne(PartyResponseUpsertDto.class))); + + legalEntitySaga.executeTask(mockLegalEntityTask(legalEntityV2)).block(); + + verify(cdpSaga, never()).executeTask(Mockito.any()); + } + + @Test + void cdpIsEnabledTestMappings() { + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(CdpTask.class); + getMockLegalEntity(); + + when(cdpSaga.isEnabled()).thenReturn(true); + when(legalEntitySagaConfigurationProperties.isUserProfileEnabled()).thenReturn(false); + + legalEntityV2.setLegalEntityType(LegalEntityType.CUSTOMER); + + when(customerProfileService.upsertParty(any(Party.class), anyString())).thenReturn( + Mono.just(fixtureMonkey.giveMeOne(PartyResponseUpsertDto.class))); + + when(cdpSaga.executeTask(any())).thenReturn( + Mono.just(Mockito.mock(com.backbase.stream.cdp.CdpTask.class))); + + legalEntitySaga.executeTask(mockLegalEntityTask(legalEntityV2)).block(); + + verify(cdpSaga, atLeastOnce()).executeTask(Mockito.any()); + verify(cdpSaga).executeTask(argumentCaptor.capture()); + CdpTask capturedCdpTask = argumentCaptor.getValue(); + assertThat(capturedCdpTask).isNotNull(); + assertThat(capturedCdpTask.getCdpEvents()).isNotNull(); + assertThat(capturedCdpTask.getCdpEvents().getCdpEvents()).isNotNull().hasSize(1); + CdpEvent cdpEvent = capturedCdpTask.getCdpEvents().getCdpEvents().getFirst(); + assertThat(cdpEvent.getEventType()).isEqualTo(PROFILE_CREATED_EVENT); + } + @Test void testSetupParties_IfPartyFound_ThenUpsertParty() { getMockLegalEntity(); @@ -590,7 +676,7 @@ void upster_error(Exception ex, String error) { // Then verify(legalEntityService).getLegalEntityByExternalId(any()); - org.assertj.core.api.Assertions.assertThat(stEx) + assertThat(stEx) .isNotNull() .extracting(e -> e.getTask().getHistory().get(1).getErrorMessage()) .isEqualTo(error); diff --git a/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/mapper/UserToCdpEventMapperTest.java b/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/mapper/UserToCdpEventMapperTest.java new file mode 100644 index 000000000..366bcafb9 --- /dev/null +++ b/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/mapper/UserToCdpEventMapperTest.java @@ -0,0 +1,212 @@ +package com.backbase.stream.mapper; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.backbase.cdp.ingestion.api.service.v1.model.CdpEvent; +import com.backbase.cdp.profiles.api.service.v1.model.CustomerProfile; +import com.backbase.cdp.profiles.api.service.v1.model.ExternalId; +import com.backbase.stream.legalentity.model.Address; +import com.backbase.stream.legalentity.model.CustomerCategory; +import com.backbase.stream.legalentity.model.EmailAddress; +import com.backbase.stream.legalentity.model.Multivalued; +import com.backbase.stream.legalentity.model.PersonalInformation; +import com.backbase.stream.legalentity.model.PhoneNumber; +import com.backbase.stream.legalentity.model.User; +import com.backbase.stream.legalentity.model.UserProfile; +import java.time.LocalDate; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class UserToCdpEventMapperTest { + + private UserToCdpEventMapper mapper; + + @BeforeEach + void setUp() { + mapper = Mappers.getMapper(UserToCdpEventMapper.class); + } + + @Test + void testMapUserToCdpEvent_basicFields() { + User user = new User(); + user.setInternalId("int-1"); + user.setExternalId("ext-1"); + user.setEmailAddress(new EmailAddress().address("test@email.com").primary(true)); + user.setMobileNumber(new PhoneNumber().number("123456789").primary(true)); + user.setUserProfile(new UserProfile()); + CustomerCategory category = CustomerCategory.RETAIL; + CdpEvent event = mapper.mapUserToCdpEvent("leIntId", "leExtId", category, user); + assertThat(event).isNotNull(); + assertThat(event.getEventType()).isEqualTo(UserToCdpEventMapper.PROFILE_CREATED_EVENT); + assertThat(event.getSourceSystem()).isEqualTo(UserToCdpEventMapper.SOURCE_BACKBASE); + assertThat(event.getSourceType()).isEqualTo(UserToCdpEventMapper.TYPE_USER_ID); + assertThat(event.getSourceId()).isEqualTo("int-1"); + assertThat(event.getEventId()).isNotNull(); + assertThat(event.getTimestamp()).isNotNull(); + assertThat(event.getMetadata()).isNotNull(); + assertThat(event.getMetadata().getProcessedBy()).isNotNull().contains("stream-services"); + assertThat(event.getMetadata().getSchemaVersion()).isEqualTo("1"); + assertThat(event.getMetadata().getSource()).isEqualTo("stream-services"); + assertThat(event.getData()).isNotNull(); + } + + @Test + void testConvertPostalAddresses_handlesNulls() { + User user = new User(); + assertThat(mapper.convertPostalAddresses(user)).isNotNull().isEmpty(); + } + + @Test + void testGetPrimaryValueFromMultiValued_prefersPrimary() { + Multivalued m1 = new Multivalued().value("a").primary(false); + Multivalued m2 = new Multivalued().value("b").primary(true); + List list = List.of(m1, m2); + assertThat(mapper.getPrimaryValueFromMultiValued(list)).isEqualTo("b"); + } + + @Test + void shouldProperlyMapProfileTypeTest() { + CustomerProfile.ProfileTypeEnum profileType = mapper.mapProfileType(CustomerCategory.BUSINESS); + assertThat(profileType).isEqualTo(CustomerProfile.ProfileTypeEnum.EMPLOYEE); + } + + @Test + void testGetPrimaryValueFromMultiValued_fallbackToFirst() { + Multivalued m1 = new Multivalued().value("a").primary(false); + Multivalued m2 = new Multivalued().value("b").primary(false); + List list = List.of(m1, m2); + assertThat(mapper.getPrimaryValueFromMultiValued(list)).isEqualTo("a"); + } + + @Test + void testMapUserStatus_nulls() { + assertThat(mapper.mapUserStatus(null)).isEqualTo("ACTIVE"); + User user = new User(); + assertThat(mapper.mapUserStatus(user)).isEqualTo("ACTIVE"); + user.setUserProfile(new UserProfile()); + assertThat(mapper.mapUserStatus(user)).isEqualTo("ACTIVE"); + } + + @Test + void testMapUserStatus_inactive() { + User user = new User(); + UserProfile profile = new UserProfile(); + profile.setActive(false); + user.setUserProfile(profile); + assertThat(mapper.mapUserStatus(user)).isEqualTo("INACTIVE"); + } + + @Test + void testMapDateOfBirth_handlesNullsAndEmpty() { + User user = new User(); + assertThat(mapper.mapDateOfBirth(user)).isNull(); + UserProfile profile = new UserProfile(); + user.setUserProfile(profile); + assertThat(mapper.mapDateOfBirth(user)).isNull(); + profile.setPersonalInformation(new PersonalInformation()); + assertThat(mapper.mapDateOfBirth(user)).isNull(); + profile.getPersonalInformation().setDateOfBirth(""); + assertThat(mapper.mapDateOfBirth(user)).isNull(); + } + + @Test + void testMapDateOfBirth_valid() { + User user = new User(); + UserProfile profile = new UserProfile(); + PersonalInformation pi = new PersonalInformation(); + pi.setDateOfBirth("2000-01-01"); + profile.setPersonalInformation(pi); + user.setUserProfile(profile); + assertThat(mapper.mapDateOfBirth(user)).isEqualTo(LocalDate.of(2000, 1, 1)); + } + + @Test + void testMapUserToExternalIds_allFields() { + User user = new User(); + user.setInternalId("int"); + user.setExternalId("ext"); + List ids = mapper.mapUserToExternalIds(user, "leInt", "leExt"); + assertThat(ids).hasSize(4); + } + + @Test + void testMapUserToExternalIds_handlesNulls() { + User user = new User(); + List ids = mapper.mapUserToExternalIds(user, null, null); + assertThat(ids).isEmpty(); + user.setInternalId("int"); + ids = mapper.mapUserToExternalIds(user, null, null); + assertThat(ids).hasSize(1); + } + + @Test + void testPhoneAddressMultiValuedMapping() { + Multivalued phone = new Multivalued().value("+123456789").primary(false); + var deliveryChannel = mapper.mapPhoneAddress(phone); + assertThat(deliveryChannel).isNotNull(); + assertThat(deliveryChannel.getPrimary()).isFalse(); + assertThat(deliveryChannel.getValue()).isEqualTo("+123456789"); + } + + @Test + void testElectronicAddressMapping() { + Multivalued email = new Multivalued().value("test@address.com").primary(true); + var deliveryChannel = mapper.mapElectronicAddress(email); + assertThat(deliveryChannel).isNotNull(); + assertThat(deliveryChannel.getPrimary()).isTrue(); + assertThat(deliveryChannel.getValue()).isEqualTo("test@address.com"); + } + + @Test + void testPrimaryEmailMapping() { + EmailAddress email = new EmailAddress().address("test@address.com").primary(true); + var deliveryChannel = mapper.mapEmailAddress(email); + assertThat(deliveryChannel).isNotNull(); + assertThat(deliveryChannel.getPrimary()).isTrue(); + assertThat(deliveryChannel.getValue()).isEqualTo("test@address.com"); + } + + @Test + void testPhoneAddressMapping() { + PhoneNumber phone = new PhoneNumber().number("987654321").primary(true); + var deliveryChannel = mapper.mapPhoneAddress(phone); + assertThat(deliveryChannel).isNotNull(); + assertThat(deliveryChannel.getPrimary()).isTrue(); + assertThat(deliveryChannel.getValue()).isEqualTo("987654321"); + } + + @Test + void testPostalAddressMappingWhenNull() { + User user = new User(); + var addresses = mapper.convertPostalAddresses(user); + assertThat(addresses).isNotNull().isEmpty(); + } + + @Test + void testPostalAddressMapping() { + UserProfile userProfile = new UserProfile(); + userProfile.setAddresses(List.of(new Address() + .streetAddress("123 Main St") + .locality("Anytown") + .region("CA") + .postalCode("12345") + .country("USA") + .primary(true) + , new Address() + .streetAddress("124 Main St") + .locality("Sometown") + .region("Alberta") + .postalCode("54321") + .country("Canada") + .primary(true) + )); + User user = new User(); + user.setUserProfile(userProfile); + var addresses = mapper.convertPostalAddresses(user); + assertThat(addresses).isNotNull().hasSize(2); + assertThat(addresses.stream().map(com.backbase.cdp.profiles.api.service.v1.model.Address::getStreet1)) + .containsExactlyInAnyOrder("123 Main St", "124 Main St"); + } +} diff --git a/stream-legal-entity/legal-entity-http/pom.xml b/stream-legal-entity/legal-entity-http/pom.xml index 0f4fc6f3c..fa2f35924 100644 --- a/stream-legal-entity/legal-entity-http/pom.xml +++ b/stream-legal-entity/legal-entity-http/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-http-starter-parent - 9.5.1 + 9.6.0 ../../stream-sdk/stream-starter-parents/stream-http-starter-parent diff --git a/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/controller/ServiceAgreementControllerTest.java b/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/controller/ServiceAgreementControllerTest.java index 81b5b1757..341bdb80e 100644 --- a/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/controller/ServiceAgreementControllerTest.java +++ b/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/controller/ServiceAgreementControllerTest.java @@ -21,6 +21,7 @@ import com.backbase.loan.inbound.api.service.v2.LoansApi; import com.backbase.stream.CustomerAccessGroupSaga; import com.backbase.stream.audiences.UserKindSegmentationSaga; +import com.backbase.stream.cdp.CdpSaga; import com.backbase.stream.clients.config.CustomerProfileClientConfig; import com.backbase.stream.config.LegalEntityHttpConfiguration; import com.backbase.stream.configuration.LegalEntitySagaConfiguration; @@ -126,6 +127,9 @@ class ServiceAgreementControllerTest { @MockitoBean private UserKindSegmentationSaga userKindSegmentationSaga; + @MockitoBean + private CdpSaga cdpSaga; + @MockitoBean private PlansService plansService; diff --git a/stream-legal-entity/pom.xml b/stream-legal-entity/pom.xml index 0dec1aac3..e88a5d003 100644 --- a/stream-legal-entity/pom.xml +++ b/stream-legal-entity/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-legal-entity diff --git a/stream-limits/limits-core/pom.xml b/stream-limits/limits-core/pom.xml index f039f9939..254213240 100644 --- a/stream-limits/limits-core/pom.xml +++ b/stream-limits/limits-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-limits - 9.5.1 + 9.6.0 limits-core diff --git a/stream-limits/pom.xml b/stream-limits/pom.xml index 3050bc565..ed4c9c5c0 100644 --- a/stream-limits/pom.xml +++ b/stream-limits/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-limits diff --git a/stream-loans/loans-core/pom.xml b/stream-loans/loans-core/pom.xml index 716ba27c5..8b5b48cd7 100644 --- a/stream-loans/loans-core/pom.xml +++ b/stream-loans/loans-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-loans - 9.5.1 + 9.6.0 loans-core diff --git a/stream-loans/pom.xml b/stream-loans/pom.xml index fc7de247b..c4a7a7f69 100644 --- a/stream-loans/pom.xml +++ b/stream-loans/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-loans diff --git a/stream-models/approval-model/pom.xml b/stream-models/approval-model/pom.xml index 0678e9ca9..bd24fa04f 100644 --- a/stream-models/approval-model/pom.xml +++ b/stream-models/approval-model/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-models - 9.5.1 + 9.6.0 approval-model diff --git a/stream-models/legal-entity-model/pom.xml b/stream-models/legal-entity-model/pom.xml index 80ad72b2c..feb1202dd 100644 --- a/stream-models/legal-entity-model/pom.xml +++ b/stream-models/legal-entity-model/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-models - 9.5.1 + 9.6.0 legal-entity-model diff --git a/stream-models/pom.xml b/stream-models/pom.xml index 5f3cdefcc..eef047ba2 100644 --- a/stream-models/pom.xml +++ b/stream-models/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-models diff --git a/stream-models/portfolio-model/pom.xml b/stream-models/portfolio-model/pom.xml index 33c5407c2..dd47d72c4 100644 --- a/stream-models/portfolio-model/pom.xml +++ b/stream-models/portfolio-model/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-models - 9.5.1 + 9.6.0 portfolio-model diff --git a/stream-models/product-catalog-model/pom.xml b/stream-models/product-catalog-model/pom.xml index fa9c1db46..36a48ebe1 100644 --- a/stream-models/product-catalog-model/pom.xml +++ b/stream-models/product-catalog-model/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-models - 9.5.1 + 9.6.0 product-catalog-model diff --git a/stream-payment-order/payment-order-core/pom.xml b/stream-payment-order/payment-order-core/pom.xml index c541595b0..cc05b5eb1 100644 --- a/stream-payment-order/payment-order-core/pom.xml +++ b/stream-payment-order/payment-order-core/pom.xml @@ -6,7 +6,7 @@ com.backbase.stream stream-payment-order - 9.5.1 + 9.6.0 payment-order-core diff --git a/stream-payment-order/pom.xml b/stream-payment-order/pom.xml index 3f8031cfe..742bcb56b 100644 --- a/stream-payment-order/pom.xml +++ b/stream-payment-order/pom.xml @@ -6,7 +6,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-payment-order diff --git a/stream-plan-manager/plan-manager-core/pom.xml b/stream-plan-manager/plan-manager-core/pom.xml index e73090e95..a006e4d8d 100644 --- a/stream-plan-manager/plan-manager-core/pom.xml +++ b/stream-plan-manager/plan-manager-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-plan-manager - 9.5.1 + 9.6.0 plan-manager-core diff --git a/stream-plan-manager/pom.xml b/stream-plan-manager/pom.xml index 1dc1b4efe..88b60283a 100644 --- a/stream-plan-manager/pom.xml +++ b/stream-plan-manager/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-plan-manager diff --git a/stream-portfolio/pom.xml b/stream-portfolio/pom.xml index dd63f601f..947516869 100644 --- a/stream-portfolio/pom.xml +++ b/stream-portfolio/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-portfolio diff --git a/stream-portfolio/portfolio-bootstrap-task/pom.xml b/stream-portfolio/portfolio-bootstrap-task/pom.xml index 18f23e3d9..99d89e76c 100644 --- a/stream-portfolio/portfolio-bootstrap-task/pom.xml +++ b/stream-portfolio/portfolio-bootstrap-task/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-task-starter-parent - 9.5.1 + 9.6.0 ../../stream-sdk/stream-starter-parents/stream-task-starter-parent diff --git a/stream-portfolio/portfolio-core/pom.xml b/stream-portfolio/portfolio-core/pom.xml index aa3581f2a..c719be402 100644 --- a/stream-portfolio/portfolio-core/pom.xml +++ b/stream-portfolio/portfolio-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-portfolio - 9.5.1 + 9.6.0 portfolio-core diff --git a/stream-portfolio/portfolio-http/pom.xml b/stream-portfolio/portfolio-http/pom.xml index d15631140..b52d7d1c6 100644 --- a/stream-portfolio/portfolio-http/pom.xml +++ b/stream-portfolio/portfolio-http/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-http-starter-parent - 9.5.1 + 9.6.0 ../../stream-sdk/stream-starter-parents/stream-http-starter-parent diff --git a/stream-product-catalog/pom.xml b/stream-product-catalog/pom.xml index 24ee11a4b..66a0fbe9e 100644 --- a/stream-product-catalog/pom.xml +++ b/stream-product-catalog/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-product-catalog diff --git a/stream-product-catalog/product-catalog-core/pom.xml b/stream-product-catalog/product-catalog-core/pom.xml index ff3c6bb2f..caa9ba64c 100644 --- a/stream-product-catalog/product-catalog-core/pom.xml +++ b/stream-product-catalog/product-catalog-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-product-catalog - 9.5.1 + 9.6.0 product-catalog-core diff --git a/stream-product-catalog/product-catalog-http/pom.xml b/stream-product-catalog/product-catalog-http/pom.xml index 0890af6e3..ded880d1c 100644 --- a/stream-product-catalog/product-catalog-http/pom.xml +++ b/stream-product-catalog/product-catalog-http/pom.xml @@ -6,7 +6,7 @@ com.backbase.stream stream-http-starter-parent - 9.5.1 + 9.6.0 ../../stream-sdk/stream-starter-parents/stream-http-starter-parent diff --git a/stream-product-catalog/product-catalog-task/pom.xml b/stream-product-catalog/product-catalog-task/pom.xml index 6ed319a64..fcb8f8182 100644 --- a/stream-product-catalog/product-catalog-task/pom.xml +++ b/stream-product-catalog/product-catalog-task/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-task-starter-parent - 9.5.1 + 9.6.0 ../../stream-sdk/stream-starter-parents/stream-task-starter-parent diff --git a/stream-product/pom.xml b/stream-product/pom.xml index 5de8ee75d..d5d7d8444 100644 --- a/stream-product/pom.xml +++ b/stream-product/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-product diff --git a/stream-product/product-core/pom.xml b/stream-product/product-core/pom.xml index 55a954474..e86d1bb91 100644 --- a/stream-product/product-core/pom.xml +++ b/stream-product/product-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-product - 9.5.1 + 9.6.0 product-core diff --git a/stream-product/product-ingestion-saga/pom.xml b/stream-product/product-ingestion-saga/pom.xml index 3d51deb14..47a91db12 100644 --- a/stream-product/product-ingestion-saga/pom.xml +++ b/stream-product/product-ingestion-saga/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-product - 9.5.1 + 9.6.0 product-ingestion-saga diff --git a/stream-sdk/pom.xml b/stream-sdk/pom.xml index 39c1f2943..993a85731 100644 --- a/stream-sdk/pom.xml +++ b/stream-sdk/pom.xml @@ -4,7 +4,7 @@ com.backbase.stream stream-sdk - 9.5.1 + 9.6.0 pom Stream :: SDK diff --git a/stream-sdk/stream-parent/pom.xml b/stream-sdk/stream-parent/pom.xml index 5bf1fb5b8..3969b9b6f 100644 --- a/stream-sdk/stream-parent/pom.xml +++ b/stream-sdk/stream-parent/pom.xml @@ -11,7 +11,7 @@ com.backbase.stream stream-parent - 9.5.1 + 9.6.0 pom Stream :: SDK :: Parent Parent for all Stream SDK modules diff --git a/stream-sdk/stream-parent/stream-context-propagation/pom.xml b/stream-sdk/stream-parent/stream-context-propagation/pom.xml index 81fa2a029..b19a009c3 100644 --- a/stream-sdk/stream-parent/stream-context-propagation/pom.xml +++ b/stream-sdk/stream-parent/stream-context-propagation/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-parent - 9.5.1 + 9.6.0 stream-context-propagation diff --git a/stream-sdk/stream-parent/stream-dbs-web-client/pom.xml b/stream-sdk/stream-parent/stream-dbs-web-client/pom.xml index a131b57f9..a94659b8b 100644 --- a/stream-sdk/stream-parent/stream-dbs-web-client/pom.xml +++ b/stream-sdk/stream-parent/stream-dbs-web-client/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-parent - 9.5.1 + 9.6.0 stream-dbs-web-client diff --git a/stream-sdk/stream-parent/stream-openapi-support/pom.xml b/stream-sdk/stream-parent/stream-openapi-support/pom.xml index c05182ecc..78ad2e993 100644 --- a/stream-sdk/stream-parent/stream-openapi-support/pom.xml +++ b/stream-sdk/stream-parent/stream-openapi-support/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-parent - 9.5.1 + 9.6.0 stream-openapi-support diff --git a/stream-sdk/stream-parent/stream-test-support/pom.xml b/stream-sdk/stream-parent/stream-test-support/pom.xml index e3dd067dd..9ded1b622 100644 --- a/stream-sdk/stream-parent/stream-test-support/pom.xml +++ b/stream-sdk/stream-parent/stream-test-support/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-parent - 9.5.1 + 9.6.0 stream-test-support diff --git a/stream-sdk/stream-parent/stream-worker/pom.xml b/stream-sdk/stream-parent/stream-worker/pom.xml index c471a6b21..16021eff7 100644 --- a/stream-sdk/stream-parent/stream-worker/pom.xml +++ b/stream-sdk/stream-parent/stream-worker/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-parent - 9.5.1 + 9.6.0 stream-worker diff --git a/stream-sdk/stream-starter-parents/pom.xml b/stream-sdk/stream-starter-parents/pom.xml index 016bd051f..231d07e7d 100644 --- a/stream-sdk/stream-starter-parents/pom.xml +++ b/stream-sdk/stream-starter-parents/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-parent - 9.5.1 + 9.6.0 ../stream-parent diff --git a/stream-sdk/stream-starter-parents/stream-http-starter-parent/pom.xml b/stream-sdk/stream-starter-parents/stream-http-starter-parent/pom.xml index d98fe53f7..666be655b 100644 --- a/stream-sdk/stream-starter-parents/stream-http-starter-parent/pom.xml +++ b/stream-sdk/stream-starter-parents/stream-http-starter-parent/pom.xml @@ -6,13 +6,13 @@ com.backbase.stream stream-starter - 9.5.1 + 9.6.0 ../../stream-starter com.backbase.stream stream-http-starter-parent - 9.5.1 + 9.6.0 pom Stream :: SDK :: HTTP Services Starter Parent for Stream HTTP Services diff --git a/stream-sdk/stream-starter-parents/stream-task-starter-parent/pom.xml b/stream-sdk/stream-starter-parents/stream-task-starter-parent/pom.xml index d040f27ab..30ad2d577 100644 --- a/stream-sdk/stream-starter-parents/stream-task-starter-parent/pom.xml +++ b/stream-sdk/stream-starter-parents/stream-task-starter-parent/pom.xml @@ -5,13 +5,13 @@ com.backbase.stream stream-starter - 9.5.1 + 9.6.0 ../../stream-starter com.backbase.stream stream-task-starter-parent - 9.5.1 + 9.6.0 pom Stream :: SDK :: Task Starter Parent for Stream Executable Tasks diff --git a/stream-sdk/stream-starter/pom.xml b/stream-sdk/stream-starter/pom.xml index f98040dcd..05c700182 100644 --- a/stream-sdk/stream-starter/pom.xml +++ b/stream-sdk/stream-starter/pom.xml @@ -10,7 +10,7 @@ com.backbase.stream stream-starter Stream :: SDK :: stream-starter - 9.5.1 + 9.6.0 pom diff --git a/stream-transactions/pom.xml b/stream-transactions/pom.xml index ef3d81a92..e64307a40 100644 --- a/stream-transactions/pom.xml +++ b/stream-transactions/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-services - 9.5.1 + 9.6.0 stream-transactions diff --git a/stream-transactions/transactions-core/pom.xml b/stream-transactions/transactions-core/pom.xml index c764efcd8..945be2423 100644 --- a/stream-transactions/transactions-core/pom.xml +++ b/stream-transactions/transactions-core/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-transactions - 9.5.1 + 9.6.0 transactions-core diff --git a/stream-transactions/transactions-item-writer/pom.xml b/stream-transactions/transactions-item-writer/pom.xml index d49aa5e92..6d5d86e82 100644 --- a/stream-transactions/transactions-item-writer/pom.xml +++ b/stream-transactions/transactions-item-writer/pom.xml @@ -5,7 +5,7 @@ com.backbase.stream stream-parent - 9.5.1 + 9.6.0 ../../stream-sdk/stream-parent