diff --git a/.github/workflows/pr_cdc.yml b/.github/workflows/pr_user_cdc.yml
similarity index 93%
rename from .github/workflows/pr_cdc.yml
rename to .github/workflows/pr_user_cdc.yml
index 02da9979..b03d1e0c 100644
--- a/.github/workflows/pr_cdc.yml
+++ b/.github/workflows/pr_user_cdc.yml
@@ -15,7 +15,7 @@ on:
- ready_for_review
paths:
- 'apps/user-cdc/**'
- - '.github/workflows/pr_cdc.yml'
+ - '.github/workflows/pr_user_cdc.yml'
- '.github/workflows/call_code_review.yml'
jobs:
diff --git a/.github/workflows/pr_user_group_cdc.yml b/.github/workflows/pr_user_group_cdc.yml
new file mode 100644
index 00000000..08b65cf4
--- /dev/null
+++ b/.github/workflows/pr_user_group_cdc.yml
@@ -0,0 +1,31 @@
+name: Code Review - user-group-cdc
+
+on:
+ workflow_dispatch:
+
+ pull_request:
+ branches:
+ - main
+ - releases/**
+ types:
+ - opened
+ - edited
+ - synchronize
+ - reopened
+ - ready_for_review
+ paths:
+ - 'apps/user-group-cdc/**'
+ - '.github/workflows/pr_user_cdc.yml'
+ - '.github/workflows/call_code_review.yml'
+
+jobs:
+ code_review:
+ uses: ./.github/workflows/call_code_review.yml
+ name: User cdc Code Review
+ secrets: inherit
+ with:
+ pr_number: ${{ github.event.pull_request.number }}
+ source_branch: ${{ github.head_ref }}
+ target_branch: ${{ github.base_ref }}
+ sonar_key: 'pagopa_selfcare-user'
+ module: 'user-group-cdc'
diff --git a/.github/workflows/release_cdc.yml b/.github/workflows/release_user_cdc.yml
similarity index 100%
rename from .github/workflows/release_cdc.yml
rename to .github/workflows/release_user_cdc.yml
diff --git a/.github/workflows/release_user_group_cdc.yml b/.github/workflows/release_user_group_cdc.yml
new file mode 100644
index 00000000..644fa7c9
--- /dev/null
+++ b/.github/workflows/release_user_group_cdc.yml
@@ -0,0 +1,63 @@
+name: Deploy SELC - user-group-cdc
+
+on:
+ push:
+ branches:
+ - main
+ - releases/*
+ paths:
+ - "apps/user-group-cdc/**"
+ - "infra/container_apps/user-group-cdc/**"
+ - "apps/pom.xml"
+ - "pom.xml"
+
+ workflow_dispatch:
+ inputs:
+ env:
+ type: choice
+ description: Environment
+ options:
+ - dev
+ - uat
+ - prod
+
+jobs:
+
+ release_dev:
+ uses: pagopa/selfcare-commons/.github/workflows/call_release_docker.yml@main
+ name: '[Dev] User cdc Release'
+ if: ${{ (startsWith(github.ref_name, 'releases/') != true && inputs.env == null) || inputs.env == 'dev' }}
+ secrets: inherit
+ with:
+ environment: dev
+ tf_environment: dev
+ dir: 'infra/container_apps/user-group-cdc'
+ dockerfile_path: ./apps/user-group-cdc/Dockerfile
+ docker_image_name: pagopa/selfcare-user-group-cdc
+ upload_openapi_enabled: false
+
+ release_uat:
+ uses: pagopa/selfcare-commons/.github/workflows/call_release_docker.yml@main
+ name: '[UAT] User cdc Release'
+ if: ${{ (startsWith(github.ref_name, 'releases/') == true && inputs.env == null) || inputs.env == 'uat' }}
+ secrets: inherit
+ with:
+ environment: uat
+ tf_environment: uat
+ dir: 'infra/container_apps/user-group-cdc'
+ dockerfile_path: ./apps/user-group-cdc/Dockerfile
+ docker_image_name: pagopa/selfcare-user-group-cdc
+ upload_openapi_enabled: false
+
+ release_prod:
+ uses: pagopa/selfcare-commons/.github/workflows/call_release_docker.yml@main
+ name: '[Prod] User cdc Release'
+ if: ${{ inputs.env == 'prod' }}
+ secrets: inherit
+ with:
+ environment: prod
+ tf_environment: prod
+ dir: 'infra/container_apps/user-group-cdc'
+ dockerfile_path: ./apps/user-group-cdc/Dockerfile
+ docker_image_name: pagopa/selfcare-user-group-cdc
+ upload_openapi_enabled: false
diff --git a/apps/pom.xml b/apps/pom.xml
index e7f3bc5e..e4fc1f38 100644
--- a/apps/pom.xml
+++ b/apps/pom.xml
@@ -69,6 +69,17 @@
user-group-ms
+
+ user-group-cdc
+
+
+ user-group-cdc/pom.xml
+
+
+
+ user-group-cdc
+
+
diff --git a/apps/user-group-cdc/Dockerfile b/apps/user-group-cdc/Dockerfile
new file mode 100644
index 00000000..e328f481
--- /dev/null
+++ b/apps/user-group-cdc/Dockerfile
@@ -0,0 +1,56 @@
+# syntax=docker/dockerfile:1.6@sha256:ac85f380a63b13dfcefa89046420e1781752bab202122f8f50032edf31be0021
+FROM maven:3-eclipse-temurin-17@sha256:0d328fa6843bb26b60cf44d69833f241ffe96218fb29fa19df7a6603863eaae7 AS builder
+
+WORKDIR /src
+COPY --link pom.xml .
+
+WORKDIR /src/libs
+COPY --link ./libs/ .
+
+WORKDIR /src/test-coverage
+COPY --link ./test-coverage/pom.xml .
+
+WORKDIR /src/apps
+COPY --link ./apps/pom.xml .
+
+WORKDIR /src/apps/user-group-cdc
+COPY --link ./apps/user-group-cdc/pom.xml .
+COPY ./apps/user-group-cdc/src/main/ ./src/main/
+
+WORKDIR /src
+
+RUN echo "\n" \
+ "\n" \
+ "\n" \
+ "\${repositoryId}\n" \
+ "\${repoLogin}\n" \
+ "\${repoPwd}\n" \
+ "\n" \
+ "\n" \
+ "\n" > settings.xml
+
+ARG REPO_ONBOARDING
+ARG REPO_USERNAME
+ARG REPO_PASSWORD
+
+RUN mvn --global-settings settings.xml --projects :user-group-cdc -DrepositoryId=${REPO_ONBOARDING} -DrepoLogin=${REPO_USERNAME} -DrepoPwd=${REPO_PASSWORD} --also-make clean package -DskipTests
+
+FROM openjdk:17-jdk@sha256:528707081fdb9562eb819128a9f85ae7fe000e2fbaeaf9f87662e7b3f38cb7d8 AS runtime
+
+ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
+ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+
+WORKDIR /app
+
+COPY --from=builder /src/apps/user-group-cdc/target/quarkus-app/lib/ ./lib/
+COPY --from=builder /src/apps/user-group-cdc/target/quarkus-app/*.jar ./
+COPY --from=builder /src/apps/user-group-cdc/target/quarkus-app/app/ ./app/
+COPY --from=builder /src/apps/user-group-cdc/target/quarkus-app/quarkus/ ./quarkus/
+
+ADD https://github.com/microsoft/ApplicationInsights-Java/releases/download/3.2.11/applicationinsights-agent-3.2.11.jar ./applicationinsights-agent.jar
+RUN chmod 755 ./applicationinsights-agent.jar
+
+EXPOSE 8080
+USER 1001
+
+ENTRYPOINT ["sh", "-c", "java $JAVA_OPTIONS -jar /app/quarkus-run.jar"]
diff --git a/apps/user-group-cdc/README.md b/apps/user-group-cdc/README.md
new file mode 100644
index 00000000..7a50aec8
--- /dev/null
+++ b/apps/user-group-cdc/README.md
@@ -0,0 +1,68 @@
+# Microservice User
+
+Our dedicated microservice is crafted to expertly manage all events related to operations, such as insertion, update, and deletion,
+within the MongoDB collections residing in the user group domain, to send event message on sc-userGroup topic.
+This specialized solution has been meticulously designed to mitigate potential concurrency issues arising from the presence of multiple active instances
+on the main microservices.
+
+## Configuration Properties
+
+Before running you have to set these properties as environment variables.
+
+| **Property** | **Environment Variable** | **Default** | **Required** |
+|-----------------------------------------------|----------------------------------------------|-------------|:------------:|
+| quarkus.mongodb.connection-string | MONGODB-CONNECTION-STRING | | yes |
+| user-group-cdc.app-insights.connection-string | USER-GROUP-CDC-APPINSIGHTS-CONNECTION-STRING | | yes |
+| user-group-cdc.storage.connection-string | STORAGE_CONNECTION_STRING | | yes |
+| quarkus.rest-client.event-hub.url | EVENT_HUB_BASE_PATH | | yes |
+| eventhub.rest-client.keyName | SHARED_ACCESS_KEY_NAME | | yes |
+| eventhub.rest-client.key | EVENTHUB-SC-USERS-GROUP-SELFCARE-WO-KEY-LC | | yes |
+| user-group-cdc.send-events.watch.enabled | USER_GROUP_CDC_SEND_EVENTS_WATCH_ENABLED | false | no |
+
+
+> **_NOTE:_** properties that contains secret must have the same name of its secret as uppercase.
+
+
+## Running the application in dev mode
+
+You can run your application in dev mode that enables live coding using:
+```shell script
+./mvnw compile quarkus:dev
+```
+
+For some endpoints
+
+> **_NOTE:_** Quarkus now ships with a Dev UI, which is available in dev mode only at http://localhost:8083/q/dev/.
+
+## Packaging and running the application
+
+The application can be packaged using:
+```shell script
+./mvnw package
+```
+It produces the `quarkus-run.jar` file in the `target/quarkus-app/` directory.
+Be aware that it’s not an _über-jar_ as the dependencies are copied into the `target/quarkus-app/lib/` directory.
+
+The application is now runnable using `java -jar target/quarkus-app/quarkus-run.jar`.
+
+If you want to build an _über-jar_, execute the following command:
+```shell script
+./mvnw package -Dquarkus.package.type=uber-jar
+```
+
+The application, packaged as an _über-jar_, is now runnable using `java -jar target/*-runner.jar`.
+
+## Related Guides
+
+
+### RESTEasy Reactive
+
+Easily start your Reactive RESTful Web Services
+
+[Related guide section...](https://quarkus.io/guides/getting-started-reactive#reactive-jax-rs-resources)
+
+### OpenAPI Generator
+
+Rest client are generated using a quarkus' extension.
+
+[Related guide section...](hhttps://github.com/quarkiverse/quarkus-openapi-generator)
diff --git a/apps/user-group-cdc/pom.xml b/apps/user-group-cdc/pom.xml
new file mode 100644
index 00000000..25df10c1
--- /dev/null
+++ b/apps/user-group-cdc/pom.xml
@@ -0,0 +1,224 @@
+
+
+ 4.0.0
+
+ it.pagopa.selfcare
+ user-apps
+ 0.0.1
+
+
+ user-group-cdc
+ 1.0.0-SNAPSHOT
+
+ 3.12.1
+ 1.18.28
+ 1.5.5.Final
+ 17
+ UTF-8
+ UTF-8
+ quarkus-bom
+ io.quarkus.platform
+ 3.11.0
+ true
+ 3.1.2
+ 0.1.3
+ 2.4.1
+
+
+
+
+ ${quarkus.platform.group-id}
+ ${quarkus.platform.artifact-id}
+ ${quarkus.platform.version}
+ pom
+ import
+
+
+
+
+
+ io.quarkus
+ quarkus-arc
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+ io.quarkus
+ quarkus-mongodb-client
+
+
+ io.quarkus
+ quarkus-mongodb-panache
+
+
+ com.azure
+ azure-data-tables
+ 12.1.1
+
+
+ io.quarkus
+ quarkus-test-common
+ test
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.quarkus
+ quarkus-test-mongodb
+ test
+
+
+ io.quarkus
+ quarkus-jacoco
+ test
+
+
+ io.quarkus
+ quarkus-test-vertx
+ test
+
+
+
+ org.mockito
+ mockito-inline
+ 2.13.0
+ test
+
+
+ io.quarkus
+ quarkus-panache-mock
+ test
+
+
+ org.mongodb
+ mongodb-driver-core
+
+
+ org.mapstruct
+ mapstruct
+ ${mapstruct.version}
+
+
+ org.apache.commons
+ commons-collections4
+ 4.3
+
+
+ com.microsoft.azure
+ applicationinsights-web
+ 2.6.4
+
+
+ io.quarkus
+ quarkus-smallrye-health
+
+
+
+ io.quarkus
+ quarkus-rest-client-jackson
+
+
+ io.quarkiverse.openapi.generator
+ quarkus-openapi-generator
+ ${quarkus-openapi-generator.version}
+
+
+ it.pagopa.selfcare
+ user-sdk-event
+ 0.0.1
+
+
+ org.glassfish.jaxb
+ txw2
+
+
+
+
+
+ ${quarkus.platform.group-id}
+ quarkus-maven-plugin
+ ${quarkus.platform.version}
+ true
+
+
+
+ build
+ generate-code
+ generate-code-tests
+
+
+
+
+
+ maven-compiler-plugin
+ ${compiler-plugin.version}
+
+
+ -parameters
+
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ org.projectlombok
+ lombok-mapstruct-binding
+ 0.2.0
+
+
+
+
+
+ maven-failsafe-plugin
+ ${surefire-plugin.version}
+
+
+
+ integration-test
+ verify
+
+
+
+ ${project.build.directory}/${project.build.finalName}-runner
+
+ org.jboss.logmanager.LogManager
+ ${maven.home}
+
+
+
+
+
+
+
+
+
+ native
+
+
+ native
+
+
+
+ false
+ true
+ false
+
+
+
+
diff --git a/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/UserGroupCdcLifecycle.java b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/UserGroupCdcLifecycle.java
new file mode 100644
index 00000000..1509e449
--- /dev/null
+++ b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/UserGroupCdcLifecycle.java
@@ -0,0 +1,34 @@
+package it.pagopa.selfcare.user.event;
+
+import com.azure.data.tables.TableServiceClient;
+import com.azure.data.tables.TableServiceClientBuilder;
+import io.quarkus.runtime.StartupEvent;
+import io.quarkus.runtime.configuration.ConfigUtils;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@ApplicationScoped
+public class UserGroupCdcLifecycle {
+ @ConfigProperty(name = "user-group-cdc.storage.connection-string") String storageConnectionString;
+ @ConfigProperty(name = "user-group-cdc.table.name") String tableName;
+
+
+ void onStart(@Observes StartupEvent ev) {
+
+ if(ConfigUtils.getProfiles().contains("test")) {
+ //Not perform any action when testing
+ return;
+ }
+
+ log.info("The application is starting...");
+
+ // Table CdCStartAt will be created
+ TableServiceClient tableServiceClient = new TableServiceClientBuilder()
+ .connectionString(storageConnectionString)
+ .buildClient();
+ tableServiceClient.createTableIfNotExists(tableName);
+ }
+}
diff --git a/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/UserGroupCdcService.java b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/UserGroupCdcService.java
new file mode 100644
index 00000000..3964281d
--- /dev/null
+++ b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/UserGroupCdcService.java
@@ -0,0 +1,162 @@
+package it.pagopa.selfcare.user.event;
+
+import com.azure.data.tables.TableClient;
+import com.azure.data.tables.models.TableEntity;
+import com.azure.data.tables.models.TableServiceException;
+import com.microsoft.applicationinsights.TelemetryClient;
+import com.mongodb.client.model.Aggregates;
+import com.mongodb.client.model.Filters;
+import com.mongodb.client.model.changestream.ChangeStreamDocument;
+import com.mongodb.client.model.changestream.FullDocument;
+import io.quarkus.mongodb.ChangeStreamOptions;
+import io.quarkus.mongodb.reactive.ReactiveMongoClient;
+import io.quarkus.mongodb.reactive.ReactiveMongoCollection;
+import io.quarkus.runtime.Quarkus;
+import io.quarkus.runtime.Startup;
+import io.quarkus.runtime.configuration.ConfigUtils;
+import io.smallrye.mutiny.Multi;
+import io.smallrye.mutiny.Uni;
+import it.pagopa.selfcare.user.client.EventHubRestClient;
+import it.pagopa.selfcare.user.event.entity.UserGroupEntity;
+import it.pagopa.selfcare.user.event.mapper.UserGroupNotificationMapper;
+import it.pagopa.selfcare.user.model.TrackEventInput;
+import it.pagopa.selfcare.user.model.UserGroupNotificationToSend;
+import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Inject;
+import lombok.extern.slf4j.Slf4j;
+import org.bson.BsonDocument;
+import org.bson.conversions.Bson;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+import org.eclipse.microprofile.rest.client.inject.RestClient;
+
+import java.time.Duration;
+import java.util.*;
+
+import static com.mongodb.client.model.Projections.fields;
+import static com.mongodb.client.model.Projections.include;
+import static it.pagopa.selfcare.user.UserUtils.mapPropsForTrackEvent;
+import static it.pagopa.selfcare.user.event.constant.CdcStartAtConstant.*;
+import static it.pagopa.selfcare.user.model.TrackEventInput.toTrackEventInput;
+import static it.pagopa.selfcare.user.model.TrackEventInput.toTrackEventInputForUserGroup;
+import static it.pagopa.selfcare.user.model.constants.EventsMetric.*;
+import static it.pagopa.selfcare.user.model.constants.EventsName.EVENT_USER_CDC_NAME;
+import static it.pagopa.selfcare.user.model.constants.EventsName.EVENT_USER_GROUP_CDC_NAME;
+import static java.util.Arrays.asList;
+
+@Startup
+@Slf4j
+@ApplicationScoped
+public class UserGroupCdcService {
+ private static final String OPERATION_NAME = "USER-GROUP-CDC-sendUserGroupEvent";
+ private static final String COLLECTION_NAME = "userGroups";
+
+ private final TelemetryClient telemetryClient;
+ private final Integer retryMinBackOff;
+ private final Integer retryMaxBackOff;
+ private final Integer maxRetry;
+ private final TableClient tableClient;
+ private final String mongodbDatabase;
+ private final ReactiveMongoClient mongoClient;
+
+ @RestClient
+ @Inject
+ EventHubRestClient eventHubRestClient;
+
+ private final UserGroupNotificationMapper userGroupNotificationMapper;
+
+ public UserGroupCdcService(ReactiveMongoClient mongoClient,
+ @ConfigProperty(name = "quarkus.mongodb.database") String mongodbDatabase,
+ @ConfigProperty(name = "user-group-cdc.retry.min-backoff") Integer retryMinBackOff,
+ @ConfigProperty(name = "user-group-cdc.retry.max-backoff") Integer retryMaxBackOff,
+ @ConfigProperty(name = "user-group-cdc.retry") Integer maxRetry,
+ @ConfigProperty(name = "user-group-cdc.send-events.watch.enabled") Boolean sendEventsEnabled,
+ TelemetryClient telemetryClient,
+ TableClient tableClient,
+
+ UserGroupNotificationMapper userGroupNotificationMapper) {
+ this.maxRetry = maxRetry;
+ this.mongoClient = mongoClient;
+ this.mongodbDatabase = mongodbDatabase;
+ this.retryMaxBackOff = retryMaxBackOff;
+ this.retryMinBackOff = retryMinBackOff;
+ this.telemetryClient = telemetryClient;
+ this.userGroupNotificationMapper = userGroupNotificationMapper;
+ this.tableClient = tableClient;
+ telemetryClient.getContext().getOperation().setName(OPERATION_NAME);
+ initOrderStream(sendEventsEnabled);
+ }
+
+ private void initOrderStream(Boolean sendEventsEnabled) {
+ log.info("Starting initOrderStream ... ");
+
+ String resumeToken = null;
+
+ if (!ConfigUtils.getProfiles().contains("test")) {
+ try {
+ TableEntity cdcStartAtEntity = tableClient.getEntity(CDC_START_AT_PARTITION_KEY, CDC_START_AT_ROW_KEY);
+ if (Objects.nonNull(cdcStartAtEntity))
+ resumeToken = (String) cdcStartAtEntity.getProperty(CDC_START_AT_PROPERTY);
+ } catch (TableServiceException e) {
+ log.warn("Table StarAt not found, it is starting from now ...");
+ }
+ }
+
+ ReactiveMongoCollection dataCollection = getCollection();
+ ChangeStreamOptions options = new ChangeStreamOptions()
+ .fullDocument(FullDocument.UPDATE_LOOKUP);
+ if (Objects.nonNull(resumeToken))
+ options = options.resumeAfter(BsonDocument.parse(resumeToken));
+
+ Bson match = Aggregates.match(Filters.in("operationType", asList("update", "replace", "insert")));
+ Bson project = Aggregates.project(fields(include("_id", "ns", "documentKey", "fullDocument")));
+ List pipeline = Arrays.asList(match, project);
+
+ Multi> publisher = dataCollection.watch(pipeline, UserGroupEntity.class, options);
+
+ if (sendEventsEnabled) {
+ publisher.subscribe().with(
+ this::consumerToSendScUserGroupEvent,
+ failure -> {
+ log.error("Error during subscribe collection, exception: {} , message: {}", failure.toString(), failure.getMessage());
+ telemetryClient.trackEvent(EVENT_USER_GROUP_CDC_NAME, mapPropsForTrackEvent(TrackEventInput.builder().exception(failure.getClass().toString()).build()), Map.of(EVENTS_USER_GROUP_FAILURE, 1D));
+ Quarkus.asyncExit();
+ });
+ }
+
+ log.info("Completed initOrderStream ... ");
+ }
+
+ private ReactiveMongoCollection getCollection() {
+ return mongoClient
+ .getDatabase(mongodbDatabase)
+ .getCollection(COLLECTION_NAME, UserGroupEntity.class);
+ }
+
+ public void consumerToSendScUserGroupEvent(ChangeStreamDocument document) {
+
+ assert document.getFullDocument() != null;
+ assert document.getDocumentKey() != null;
+ UserGroupEntity userGroupChanged = document.getFullDocument();
+
+ log.info("Starting consumerToSendScUserGroupEvent ... ");
+
+ UserGroupNotificationToSend userGroupNotificationToSend = userGroupNotificationMapper.toUserGroupNotificationToSend(userGroupChanged);
+
+ eventHubRestClient.sendUserGroupMessage(userGroupNotificationToSend)
+ .onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry)
+ .onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_GROUP_CDC_NAME,
+ mapPropsForTrackEvent(toTrackEventInputForUserGroup(userGroupNotificationToSend)), Map.of(EVENTS_USER_GROUP_PRODUCT_SUCCESS, 1D)))
+ .onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_GROUP_CDC_NAME,
+ mapPropsForTrackEvent(toTrackEventInputForUserGroup(userGroupNotificationToSend)), Map.of(EVENTS_USER_GROUP_PRODUCT_FAILURE, 1D)))
+ .subscribe().with(
+ result -> {
+ log.info("SendEvents successfully performed from UserInstitution document having id: {}", document.getDocumentKey().toJson());
+ telemetryClient.trackEvent(EVENT_USER_GROUP_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputForUserGroup(userGroupNotificationToSend)), Map.of(EVENTS_USER_GROUP_SUCCESS, 1D));
+ },
+ failure -> {
+ log.error("Error during SendEvents from UserInstitution document having id: {} , message: {}", document.getDocumentKey().toJson(), failure.getMessage());
+ telemetryClient.trackEvent(EVENT_USER_GROUP_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputForUserGroup(userGroupNotificationToSend)), Map.of(EVENTS_USER_GROUP_FAILURE, 1D));
+ });
+ }
+
+}
diff --git a/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/config/UserGroupCdcConfig.java b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/config/UserGroupCdcConfig.java
new file mode 100644
index 00000000..bcd9f0f1
--- /dev/null
+++ b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/config/UserGroupCdcConfig.java
@@ -0,0 +1,29 @@
+package it.pagopa.selfcare.user.event.config;
+
+import com.azure.data.tables.TableClient;
+import com.azure.data.tables.TableClientBuilder;
+import com.microsoft.applicationinsights.TelemetryClient;
+import com.microsoft.applicationinsights.TelemetryConfiguration;
+import jakarta.enterprise.context.ApplicationScoped;
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+@ApplicationScoped
+public class UserGroupCdcConfig {
+
+ @ApplicationScoped
+ public TelemetryClient telemetryClient(@ConfigProperty(name = "user-group-cdc.appinsights.connection-string") String appInsightsConnectionString) {
+ TelemetryConfiguration telemetryConfiguration = TelemetryConfiguration.createDefault();
+ telemetryConfiguration.setConnectionString(appInsightsConnectionString);
+ return new TelemetryClient(telemetryConfiguration);
+ }
+
+ @ApplicationScoped
+ public TableClient tableClient(@ConfigProperty(name = "user-group-cdc.storage.connection-string") String storageConnectionString,
+ @ConfigProperty(name = "user-group-cdc.table.name") String tableName){
+ return new TableClientBuilder()
+ .connectionString(storageConnectionString)
+ .tableName(tableName)
+ .buildClient();
+ }
+
+}
diff --git a/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/constant/CdcStartAtConstant.java b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/constant/CdcStartAtConstant.java
new file mode 100644
index 00000000..1f45eb86
--- /dev/null
+++ b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/constant/CdcStartAtConstant.java
@@ -0,0 +1,10 @@
+package it.pagopa.selfcare.user.event.constant;
+
+public class CdcStartAtConstant {
+
+
+ public static final String CDC_START_AT_PARTITION_KEY = "UserGroup";
+ public static final String CDC_START_AT_ROW_KEY = "0001";
+ public static final String CDC_START_AT_PROPERTY = "startAt";
+
+}
diff --git a/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/entity/UserGroupEntity.java b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/entity/UserGroupEntity.java
new file mode 100644
index 00000000..d1994d87
--- /dev/null
+++ b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/entity/UserGroupEntity.java
@@ -0,0 +1,30 @@
+package it.pagopa.selfcare.user.event.entity;
+
+import io.quarkus.mongodb.panache.common.MongoEntity;
+import io.quarkus.mongodb.panache.reactive.ReactivePanacheMongoEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.FieldNameConstants;
+
+import java.time.Instant;
+import java.util.Set;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@MongoEntity(collection = "userGroups")
+@FieldNameConstants(asEnum = true)
+public class UserGroupEntity extends ReactivePanacheMongoEntity {
+
+ private String id;
+ private String institutionId;
+ private String productId;
+ private String name;
+ private String description;
+ private String status;
+ private Set members;
+ private Instant createdAt;
+ private String createdBy;
+ private Instant modifiedAt;
+ private String modifiedBy;
+
+}
diff --git a/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/UserGroupNotificationMapper.java b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/UserGroupNotificationMapper.java
new file mode 100644
index 00000000..f22d38cf
--- /dev/null
+++ b/apps/user-group-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/UserGroupNotificationMapper.java
@@ -0,0 +1,11 @@
+package it.pagopa.selfcare.user.event.mapper;
+
+import it.pagopa.selfcare.user.event.entity.UserGroupEntity;
+import it.pagopa.selfcare.user.model.UserGroupNotificationToSend;
+import org.mapstruct.Mapper;
+
+@Mapper(componentModel = "cdi")
+public interface UserGroupNotificationMapper {
+
+ UserGroupNotificationToSend toUserGroupNotificationToSend(UserGroupEntity userGroupEntity);
+}
diff --git a/apps/user-group-cdc/src/main/resources/application.properties b/apps/user-group-cdc/src/main/resources/application.properties
new file mode 100644
index 00000000..85dc252f
--- /dev/null
+++ b/apps/user-group-cdc/src/main/resources/application.properties
@@ -0,0 +1,21 @@
+quarkus.http.port=8080
+
+quarkus.log.level=INFO
+quarkus.http.limits.max-form-attribute-size=4096
+
+quarkus.mongodb.connection-string = ${MONGODB-CONNECTION-STRING}
+quarkus.mongodb.database = selcUserGroup
+
+user-group-cdc.send-events.watch.enabled=${USER_GROUP_CDC_SEND_EVENTS_WATCH_ENABLED:false}
+user-group-cdc.appinsights.connection-string=${APPLICATIONINSIGHTS_CONNECTION_STRING:InstrumentationKey=00000000-0000-0000-0000-000000000000}
+user-group-cdc.table.name=${START_AT_TABLE_NAME:CdCStartAt}
+user-group-cdc.storage.connection-string=${STORAGE_CONNECTION_STRING:UseDevelopmentStorage=true;}
+
+
+user-group-cdc.retry.min-backoff=${USER_GROUP_CDC-RETRY-MIN-BACKOFF:10}
+user-group-cdc.retry.max-backoff=${USER_GROUP_CDC-RETRY-MAX-BACKOFF:12}
+user-group-cdc.retry=${USER_GROUP_CDC-RETRY:3}
+
+quarkus.rest-client.event-hub.url=${EVENT_HUB_BASE_PATH:test}
+eventhub.rest-client.keyName=${SHARED_ACCESS_KEY_NAME:test}
+eventhub.rest-client.key=${EVENTHUB-SC-USERS-GROUP-SELFCARE-WO-KEY-LC:test}
\ No newline at end of file
diff --git a/apps/user-group-cdc/src/test/java/it/pagopa/selfcare/user/event/service/UserGroupCdcServiceTest.java b/apps/user-group-cdc/src/test/java/it/pagopa/selfcare/user/event/service/UserGroupCdcServiceTest.java
new file mode 100644
index 00000000..03ba8484
--- /dev/null
+++ b/apps/user-group-cdc/src/test/java/it/pagopa/selfcare/user/event/service/UserGroupCdcServiceTest.java
@@ -0,0 +1,66 @@
+package it.pagopa.selfcare.user.event.service;
+
+import com.mongodb.client.model.changestream.ChangeStreamDocument;
+import io.quarkus.test.InjectMock;
+import io.quarkus.test.common.QuarkusTestResource;
+import io.quarkus.test.junit.QuarkusTest;
+import io.quarkus.test.mongodb.MongoTestResource;
+import it.pagopa.selfcare.user.client.EventHubRestClient;
+import it.pagopa.selfcare.user.event.UserGroupCdcService;
+import it.pagopa.selfcare.user.event.entity.UserGroupEntity;
+import it.pagopa.selfcare.user.event.mapper.UserGroupNotificationMapper;
+import it.pagopa.selfcare.user.model.UserGroupNotificationToSend;
+import jakarta.inject.Inject;
+import org.bson.BsonDocument;
+import org.eclipse.microprofile.rest.client.inject.RestClient;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+
+import java.util.Set;
+
+import static org.mockito.Mockito.*;
+
+@QuarkusTest
+@QuarkusTestResource(MongoTestResource.class)
+public class UserGroupCdcServiceTest {
+
+ @Inject
+ UserGroupCdcService userGroupCdcService;
+
+ @RestClient
+ @InjectMock
+ EventHubRestClient eventHubRestClient;
+
+ @Spy
+ UserGroupNotificationMapper userGroupNotificationMapper;
+
+ @Test
+ void consumerToSendScUserGroupEventTest() {
+ UserGroupEntity userGroupEntity = dummyUserGroup();
+ ChangeStreamDocument document = Mockito.mock(ChangeStreamDocument.class);
+ when(document.getFullDocument()).thenReturn(userGroupEntity);
+ BsonDocument bsonDocument = Mockito.mock(BsonDocument.class);
+ when(document.getDocumentKey()).thenReturn(bsonDocument);
+
+ ArgumentCaptor notification = ArgumentCaptor.forClass(UserGroupNotificationToSend.class);
+ userGroupCdcService.consumerToSendScUserGroupEvent(document);
+ verify(eventHubRestClient, times(1)).
+ sendUserGroupMessage(notification.capture());
+ Assertions.assertEquals(userGroupEntity.getId(), notification.getValue().getId());
+ Assertions.assertEquals(userGroupEntity.getInstitutionId(), notification.getValue().getInstitutionId());
+ Assertions.assertEquals(userGroupEntity.getProductId(), notification.getValue().getProductId());
+ Assertions.assertEquals(2, notification.getValue().getMembers().size());
+ }
+
+ UserGroupEntity dummyUserGroup() {
+ UserGroupEntity userGroupEntity = new UserGroupEntity();
+ userGroupEntity.setId("UserGroupId");
+ userGroupEntity.setInstitutionId("InstitutionId");
+ userGroupEntity.setProductId("ProductId");
+ userGroupEntity.setMembers(Set.of("Member1", "Member2"));
+ return userGroupEntity;
+ }
+}
diff --git a/apps/user-group-cdc/src/test/resources/application.properties b/apps/user-group-cdc/src/test/resources/application.properties
new file mode 100644
index 00000000..8b137891
--- /dev/null
+++ b/apps/user-group-cdc/src/test/resources/application.properties
@@ -0,0 +1 @@
+
diff --git a/infra/container_apps/user-group-cdc/.terraform.lock.hcl b/infra/container_apps/user-group-cdc/.terraform.lock.hcl
new file mode 100644
index 00000000..911037de
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/.terraform.lock.hcl
@@ -0,0 +1,48 @@
+# This file is maintained automatically by "terraform init".
+# Manual edits may be lost in future updates.
+
+provider "registry.terraform.io/azure/azapi" {
+ version = "1.9.0"
+ constraints = "~> 1.9.0"
+ hashes = [
+ "h1:Ow1rr5fYBGSkplH/kcXeWz9y2wA81BnhZ7vTBzJfAAg=",
+ "h1:shpEoqcAbf+p6AvspiYO1YrX//8l1LV/owEcQpujWHw=",
+ "h1:yIJQVdnmGZdvS3yrw0M8ke9KiB/c0tjZ7KUXC46Hjx0=",
+ "h1:zaLH2Owmj61RX2G1Cy6VDy8Ttfzx+lDsSCyiu5cXkm4=",
+ "zh:349569471fbf387feaaf8b88da1690669e201147c342f905e5eb03df42b3cf87",
+ "zh:54346d5fb78cbad3eb7cfd96e1dd7ce4f78666cabaaccfec6ee9437476330018",
+ "zh:64b799da915ea3a9a58ac7a926c6a31c59fd0d911687804d8e815eda88c5580b",
+ "zh:9336ed9e112555e0fda8af6be9ba21478e30117d79ba662233311d9560d2b7c6",
+ "zh:a8aace9897b28ea0b2dbd7a3be3df033e158af40412c9c7670be0956f216ed7e",
+ "zh:ab23df7de700d9e785009a4ca9ceb38ae1ab894a13f5788847f15d018556f415",
+ "zh:b4f13f0b13560a67d427c71c85246f8920f98987120341830071df4535842053",
+ "zh:e58377bf36d8a14d28178a002657865ee17446182dac03525fd43435e41a1b5c",
+ "zh:ea5db4acc6413fd0fe6b35981e58cdc9850f5f3118031cc3d2581de511aee6aa",
+ "zh:f0b32c06c6bd4e4af2c02a62be07b947766aeeb09289a03f21aba16c2fd3c60f",
+ "zh:f1518e766a90c257d7eb36d360dafaf311593a4a9352ff8db0bcfe0ed8cf45ae",
+ "zh:fa89e84cff0776b5b61ff27049b1d8ed52040bd58c81c4628890d644a6fb2989",
+ ]
+}
+
+provider "registry.terraform.io/hashicorp/azurerm" {
+ version = "3.77.0"
+ constraints = "~> 3.77.0"
+ hashes = [
+ "h1:/ImUzZZ+3Ax6HqkhVxSALK6mtyej3W0UlEEYy8A45XM=",
+ "h1:6KahvT54KTIS9rL5D4utjSCnxwDGClf59f/YqsYZbjY=",
+ "h1:7XzdPKIJoKazb3eMhQbjcVWRRu5RSsHeDNYUQW6zSAc=",
+ "h1:Ie29CiuuS6LeQLO0Cdf0k54oMgZd9rkuwOKjVAa4mfw=",
+ "zh:071c82025cda506af90302b3e89f61e086ad9e3b97b8c55382d5aed6f207cf10",
+ "zh:10464b6a85343fdcc5f8d3d60304c3e0bfcfff014b3067f6ead61d0c59fe8371",
+ "zh:5fd48cbeb13ead9158051e563e5354acbc94668fa3d5306c20d476e746e62991",
+ "zh:64b47c2150e1f0a8473c8f79d35be72786777772ab1a7e79d8039de4bc10e8c9",
+ "zh:6958e7d22e6efda97dde6e50d033c4a0f48da3c7e597482bf14774cf8d1f612e",
+ "zh:aad939983c1f28a27c01be636a8079e5f973f9bb640348cf264b35bf6a956bb8",
+ "zh:bb211cbcfb643a7d041afc597e1c8a10749e1d3a0141c16e5c643614b16895c8",
+ "zh:db30a46d335cc1c1e2dd0707c40c6b6b3c8dd72f8905e3177998e1b212d0ace0",
+ "zh:e04f4d086e546cc7ab565bde64e93ed6716c3764579918ecf9077e539b99dd4c",
+ "zh:ea92d15b18a17a31e9f7568efe3bbd07b77906f4585aec98e6d03942788efad8",
+ "zh:f03164fadd13e4991dde22ab31c09731a00aee6ad723c61d0d2835abc79a1a8c",
+ "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c",
+ ]
+}
diff --git a/infra/container_apps/user-group-cdc/env/dev/backend.ini b/infra/container_apps/user-group-cdc/env/dev/backend.ini
new file mode 100644
index 00000000..200a37a1
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/env/dev/backend.ini
@@ -0,0 +1 @@
+subscription=DEV-SelfCare
diff --git a/infra/container_apps/user-group-cdc/env/dev/backend.tfvars b/infra/container_apps/user-group-cdc/env/dev/backend.tfvars
new file mode 100644
index 00000000..8d57c171
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/env/dev/backend.tfvars
@@ -0,0 +1,4 @@
+resource_group_name = "terraform-state-rg"
+storage_account_name = "tfappdevselfcare"
+container_name = "terraform-state"
+key = "selfcare-user-group-cdc.user-group-app.tfstate"
diff --git a/infra/container_apps/user-group-cdc/env/dev/terraform.tfvars b/infra/container_apps/user-group-cdc/env/dev/terraform.tfvars
new file mode 100644
index 00000000..27570046
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/env/dev/terraform.tfvars
@@ -0,0 +1,63 @@
+env_short = "d"
+suffix_increment = "-002"
+cae_name = "cae-002"
+
+tags = {
+ CreatedBy = "Terraform"
+ Environment = "Dev"
+ Owner = "SelfCare"
+ Source = "https://github.com/pagopa/selfcare-user"
+ CostCenter = "TS310 - PAGAMENTI & SERVIZI"
+}
+
+container_app = {
+ min_replicas = 0
+ max_replicas = 1
+ scale_rules = [
+ {
+ custom = {
+ metadata = {
+ "desiredReplicas" = "1"
+ "start" = "0 8 * * MON-FRI"
+ "end" = "0 19 * * MON-FRI"
+ "timezone" = "Europe/Rome"
+ }
+ type = "cron"
+ }
+ name = "cron-scale-rule"
+ }
+ ]
+ cpu = 1
+ memory = "2Gi"
+}
+
+app_settings = [
+ {
+ name = "JAVA_TOOL_OPTIONS"
+ value = "-javaagent:applicationinsights-agent.jar",
+ },
+ {
+ name = "APPLICATIONINSIGHTS_ROLE_NAME"
+ value = "user-group-cdc",
+ },
+ {
+ name = "EVENT_HUB_BASE_PATH"
+ value = "https://selc-d-eventhub-ns.servicebus.windows.net/sc-user-groups"
+ },
+ {
+ name = "SHARED_ACCESS_KEY_NAME"
+ value = "selfcare-wo"
+ },
+ {
+ name = "USER_GROUP_CDC_SEND_EVENTS_WATCH_ENABLED"
+ value = "true"
+ }
+]
+
+secrets_names = {
+ "APPLICATIONINSIGHTS_CONNECTION_STRING" = "appinsights-connection-string"
+ "MONGODB-CONNECTION-STRING" = "mongodb-connection-string"
+ "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string"
+ "EVENTHUB-SC-USER-GROUPS-SELFCARE-WO-KEY-LC" = "eventhub-sc-usergroups-selfcare-wo-key-lc"
+}
+
diff --git a/infra/container_apps/user-group-cdc/env/prod/backend.ini b/infra/container_apps/user-group-cdc/env/prod/backend.ini
new file mode 100644
index 00000000..dc3318a8
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/env/prod/backend.ini
@@ -0,0 +1 @@
+subscription=PROD-SelfCare
diff --git a/infra/container_apps/user-group-cdc/env/prod/backend.tfvars b/infra/container_apps/user-group-cdc/env/prod/backend.tfvars
new file mode 100644
index 00000000..67d7218f
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/env/prod/backend.tfvars
@@ -0,0 +1,4 @@
+resource_group_name = "terraform-state-rg"
+storage_account_name = "tfappprodselfcare"
+container_name = "terraform-state"
+key = "selfcare-user-group-cdc.user-group-app.tfstate"
diff --git a/infra/container_apps/user-group-cdc/env/prod/terraform.tfvars b/infra/container_apps/user-group-cdc/env/prod/terraform.tfvars
new file mode 100644
index 00000000..6a4ea6e5
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/env/prod/terraform.tfvars
@@ -0,0 +1,51 @@
+prefix = "selc"
+env_short = "p"
+suffix_increment = "-002"
+cae_name = "cae-002"
+
+tags = {
+ CreatedBy = "Terraform"
+ Environment = "Prod"
+ Owner = "SelfCare"
+ Source = "https://github.com/pagopa/selfcare-user"
+ CostCenter = "TS310 - PAGAMENTI & SERVIZI"
+}
+
+container_app = {
+ min_replicas = 1
+ max_replicas = 1
+ scale_rules = []
+ cpu = 1
+ memory = "2Gi"
+}
+
+app_settings = [
+ {
+ name = "JAVA_TOOL_OPTIONS"
+ value = "-javaagent:applicationinsights-agent.jar",
+ },
+ {
+ name = "APPLICATIONINSIGHTS_ROLE_NAME"
+ value = "user-group-cdc",
+ },
+ {
+ name = "EVENT_HUB_BASE_PATH"
+ value = "https://selc-p-eventhub-ns.servicebus.windows.net/sc-user-groups"
+ },
+ {
+ name = "SHARED_ACCESS_KEY_NAME"
+ value = "selfcare-wo"
+ },
+ {
+ name = "USER_GROUP_CDC_SEND_EVENTS_WATCH_ENABLED"
+ value = "true"
+ }
+]
+
+secrets_names = {
+ "APPLICATIONINSIGHTS_CONNECTION_STRING" = "appinsights-connection-string"
+ "MONGODB-CONNECTION-STRING" = "mongodb-connection-string"
+ "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string"
+ "EVENTHUB-SC-USER-GROUPS-SELFCARE-WO-KEY-LC" = "eventhub-sc-usergroups-selfcare-wo-key-lc"
+}
+
diff --git a/infra/container_apps/user-group-cdc/env/uat/backend.ini b/infra/container_apps/user-group-cdc/env/uat/backend.ini
new file mode 100644
index 00000000..8be57858
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/env/uat/backend.ini
@@ -0,0 +1 @@
+subscription=UAT-SelfCare
diff --git a/infra/container_apps/user-group-cdc/env/uat/backend.tfvars b/infra/container_apps/user-group-cdc/env/uat/backend.tfvars
new file mode 100644
index 00000000..d6da4704
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/env/uat/backend.tfvars
@@ -0,0 +1,4 @@
+resource_group_name = "terraform-state-rg"
+storage_account_name = "tfappuatselfcare"
+container_name = "terraform-state"
+key = "selfcare-user-group-cdc.user-group-app.tfstate"
diff --git a/infra/container_apps/user-group-cdc/env/uat/terraform.tfvars b/infra/container_apps/user-group-cdc/env/uat/terraform.tfvars
new file mode 100644
index 00000000..ae164931
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/env/uat/terraform.tfvars
@@ -0,0 +1,50 @@
+prefix = "selc"
+env_short = "u"
+suffix_increment = "-002"
+cae_name = "cae-002"
+
+tags = {
+ CreatedBy = "Terraform"
+ Environment = "Uat"
+ Owner = "SelfCare"
+ Source = "https://github.com/pagopa/selfcare-user"
+ CostCenter = "TS310 - PAGAMENTI & SERVIZI"
+}
+
+container_app = {
+ min_replicas = 1
+ max_replicas = 1
+ scale_rules = []
+ cpu = 1
+ memory = "2Gi"
+}
+
+app_settings = [
+ {
+ name = "JAVA_TOOL_OPTIONS"
+ value = "-javaagent:applicationinsights-agent.jar",
+ },
+ {
+ name = "APPLICATIONINSIGHTS_ROLE_NAME"
+ value = "user-group-cdc",
+ },
+ {
+ name = "EVENT_HUB_BASE_PATH"
+ value = "https://selc-u-eventhub-ns.servicebus.windows.net/sc-user-groups"
+ },
+ {
+ name = "SHARED_ACCESS_KEY_NAME"
+ value = "selfcare-wo"
+ },
+ {
+ name = "USER_GROUP_CDC_SEND_EVENTS_WATCH_ENABLED"
+ value = "true"
+ }
+]
+
+secrets_names = {
+ "APPLICATIONINSIGHTS_CONNECTION_STRING" = "appinsights-connection-string"
+ "MONGODB-CONNECTION-STRING" = "mongodb-connection-string"
+ "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string"
+ "EVENTHUB-SC-USER-GROUPS-SELFCARE-WO-KEY-LC" = "eventhub-sc-usergroups-selfcare-wo-key-lc"
+}
diff --git a/infra/container_apps/user-group-cdc/locals.tf b/infra/container_apps/user-group-cdc/locals.tf
new file mode 100644
index 00000000..d93c886c
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/locals.tf
@@ -0,0 +1,6 @@
+locals {
+ project = "selc-${var.env_short}"
+
+ container_app_environment_name = "${local.project}-${var.cae_name}"
+ ca_resource_group_name = "${local.project}-container-app${var.suffix_increment}-rg"
+}
\ No newline at end of file
diff --git a/infra/container_apps/user-group-cdc/main.tf b/infra/container_apps/user-group-cdc/main.tf
new file mode 100644
index 00000000..915afe98
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/main.tf
@@ -0,0 +1,64 @@
+terraform {
+ required_version = ">= 1.6.0"
+
+ backend "azurerm" {}
+}
+
+provider "azurerm" {
+ features {}
+ skip_provider_registration = true
+
+}
+
+module "container_app_user_cdc" {
+ source = "github.com/pagopa/selfcare-commons//infra/terraform-modules/container_app_microservice?ref=main"
+
+ env_short = var.env_short
+ resource_group_name = local.ca_resource_group_name
+ container_app = var.container_app
+ container_app_name = "user-group-cdc"
+ container_app_environment_name = local.container_app_environment_name
+ image_name = "selfcare-user-group-cdc"
+ image_tag = var.image_tag
+ app_settings = var.app_settings
+ secrets_names = var.secrets_names
+ workload_profile_name = var.workload_profile_name
+
+ probes = [
+ {
+ httpGet = {
+ path = "q/health/live"
+ port = 8080
+ scheme = "HTTP"
+ }
+ timeoutSeconds = 5
+ type = "Liveness"
+ failureThreshold = 3
+ initialDelaySeconds = 1
+ },
+ {
+ httpGet = {
+ path = "q/health/ready"
+ port = 8080
+ scheme = "HTTP"
+ }
+ timeoutSeconds = 5
+ type = "Readiness"
+ failureThreshold = 30
+ initialDelaySeconds = 3
+ },
+ {
+ httpGet = {
+ path = "q/health/started"
+ port = 8080
+ scheme = "HTTP"
+ }
+ timeoutSeconds = 5
+ failureThreshold = 5
+ type = "Startup"
+ initialDelaySeconds = 5
+ }
+ ]
+
+ tags = var.tags
+}
diff --git a/infra/container_apps/user-group-cdc/terraform.sh b/infra/container_apps/user-group-cdc/terraform.sh
new file mode 100644
index 00000000..15ca0bfc
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/terraform.sh
@@ -0,0 +1,324 @@
+#!/bin/bash
+############################################################
+# Terraform script for managing infrastructure on Azure
+# Fingerprint: d2hhdHlvdXdhbnQ/Cg==
+############################################################
+# Global variables
+# Version format x.y accepted
+vers="1.11"
+script_name=$(basename "$0")
+git_repo="https://raw.githubusercontent.com/pagopa/eng-common-scripts/main/azure/${script_name}"
+tmp_file="${script_name}.new"
+# Check if the third parameter exists and is a file
+if [ -n "$3" ] && [ -f "$3" ]; then
+ FILE_ACTION=true
+else
+ FILE_ACTION=false
+fi
+
+# Define functions
+function clean_environment() {
+ rm -rf .terraform
+ rm tfplan 2>/dev/null
+ echo "cleaned!"
+}
+
+function download_tool() {
+ #default value
+ cpu_type="intel"
+ os_type=$(uname)
+
+ # only on MacOS
+ if [ "$os_type" == "Darwin" ]; then
+ cpu_brand=$(sysctl -n machdep.cpu.brand_string)
+ if grep -q -i "intel" <<< "$cpu_brand"; then
+ cpu_type="intel"
+ else
+ cpu_type="arm"
+ fi
+ fi
+
+ echo $cpu_type
+ tool=$1
+ git_repo="https://raw.githubusercontent.com/pagopa/eng-common-scripts/main/golang/${tool}_${cpu_type}"
+ if ! command -v $tool &> /dev/null; then
+ if ! curl -sL "$git_repo" -o "$tool"; then
+ echo "Error downloading ${tool}"
+ return 1
+ else
+ chmod +x $tool
+ echo "${tool} downloaded! Please note this tool WON'T be copied in your **/bin folder for safety reasons.
+You need to do it yourself!"
+ read -p "Press enter to continue"
+
+
+ fi
+ fi
+}
+
+function extract_resources() {
+ TF_FILE=$1
+ ENV=$2
+ TARGETS=""
+
+ # Check if the file exists
+ if [ ! -f "$TF_FILE" ]; then
+ echo "File $TF_FILE does not exist."
+ exit 1
+ fi
+
+ # Check if the directory exists
+ if [ ! -d "./env/$ENV" ]; then
+ echo "Directory ./env/$ENV does not exist."
+ exit 1
+ fi
+
+ TMP_FILE=$(mktemp)
+ grep -E '^resource|^module' $TF_FILE > $TMP_FILE
+
+ while read -r line ; do
+ TYPE=$(echo $line | cut -d '"' -f 1 | tr -d ' ')
+ if [ "$TYPE" == "module" ]; then
+ NAME=$(echo $line | cut -d '"' -f 2)
+ TARGETS+=" -target=\"$TYPE.$NAME\""
+ else
+ NAME1=$(echo $line | cut -d '"' -f 2)
+ NAME2=$(echo $line | cut -d '"' -f 4)
+ TARGETS+=" -target=\"$NAME1.$NAME2\""
+ fi
+ done < $TMP_FILE
+
+ rm $TMP_FILE
+
+ echo "./terraform.sh $action $ENV $TARGETS"
+}
+
+function help_usage() {
+ echo "terraform.sh Version ${vers}"
+ echo
+ echo "Usage: ./script.sh [ACTION] [ENV] [OTHER OPTIONS]"
+ echo "es. ACTION: init, apply, plan, etc."
+ echo "es. ENV: dev, uat, prod, etc."
+ echo
+ echo "Available actions:"
+ echo " clean Remove .terraform* folders and tfplan files"
+ echo " help This help"
+ echo " list List every environment available"
+ echo " update Update this script if possible"
+ echo " summ Generate summary of Terraform plan"
+ echo " tflist Generate an improved output of terraform state list"
+ echo " tlock Generate or update the dependency lock file"
+ echo " * any terraform option"
+}
+
+function init_terraform() {
+ if [ -n "$env" ]; then
+ terraform init -reconfigure -backend-config="./env/$env/backend.tfvars"
+ else
+ echo "ERROR: no env configured!"
+ exit 1
+ fi
+}
+
+function list_env() {
+ # Check if env directory exists
+ if [ ! -d "./env" ]; then
+ echo "No environment directory found"
+ exit 1
+ fi
+
+ # List subdirectories under env directory
+ env_list=$(ls -d ./env/*/ 2>/dev/null)
+
+ # Check if there are any subdirectories
+ if [ -z "$env_list" ]; then
+ echo "No environments found"
+ exit 1
+ fi
+
+ # Print the list of environments
+ echo "Available environments:"
+ for env in $env_list; do
+ env_name=$(echo "$env" | sed 's#./env/##;s#/##')
+ echo "- $env_name"
+ done
+}
+
+function other_actions() {
+ if [ -n "$env" ] && [ -n "$action" ]; then
+ terraform "$action" -var-file="./env/$env/terraform.tfvars" -compact-warnings $other
+ else
+ echo "ERROR: no env or action configured!"
+ exit 1
+ fi
+}
+
+function state_output_taint_actions() {
+ if [ "$action" == "tflist" ]; then
+ # If 'tflist' is not installed globally and there is no 'tflist' file in the current directory,
+ # attempt to download the 'tflist' tool
+ if ! command -v tflist &> /dev/null && [ ! -f "tflist" ]; then
+ download_tool "tflist"
+ if [ $? -ne 0 ]; then
+ echo "Error: Failed to download tflist!!"
+ exit 1
+ else
+ echo "tflist downloaded!"
+ fi
+ fi
+ if command -v tflist &> /dev/null; then
+ terraform state list | tflist
+ else
+ terraform state list | ./tflist
+ fi
+ else
+ terraform $action $other
+ fi
+}
+
+
+function parse_tfplan_option() {
+ # Create an array to contain arguments that do not start with '-tfplan='
+ local other_args=()
+
+ # Loop over all arguments
+ for arg in "$@"; do
+ # If the argument starts with '-tfplan=', extract the file name
+ if [[ "$arg" =~ ^-tfplan= ]]; then
+ echo "${arg#*=}"
+ else
+ # If the argument does not start with '-tfplan=', add it to the other_args array
+ other_args+=("$arg")
+ fi
+ done
+
+ # Print all arguments in other_args separated by spaces
+ echo "${other_args[@]}"
+}
+
+function tfsummary() {
+ local plan_file
+ plan_file=$(parse_tfplan_option "$@")
+ if [ -z "$plan_file" ]; then
+ plan_file="tfplan"
+ fi
+ action="plan"
+ other="-out=${plan_file}"
+ other_actions
+ if [ -n "$(command -v tf-summarize)" ]; then
+ tf-summarize -tree "${plan_file}"
+ else
+ echo "tf-summarize is not installed"
+ fi
+ if [ "$plan_file" == "tfplan" ]; then
+ rm $plan_file
+ fi
+}
+
+function update_script() {
+ # Check if the repository was cloned successfully
+ if ! curl -sL "$git_repo" -o "$tmp_file"; then
+ echo "Error cloning the repository"
+ rm "$tmp_file" 2>/dev/null
+ return 1
+ fi
+
+ # Check if a newer version exists
+ remote_vers=$(sed -n '8s/vers="\(.*\)"/\1/p' "$tmp_file")
+ if [ "$(printf '%s\n' "$vers" "$remote_vers" | sort -V | tail -n 1)" == "$vers" ]; then
+ echo "The local script version is equal to or newer than the remote version."
+ rm "$tmp_file" 2>/dev/null
+ return 0
+ fi
+
+ # Check the fingerprint
+ local_fingerprint=$(sed -n '4p' "$0")
+ remote_fingerprint=$(sed -n '4p' "$tmp_file")
+
+ if [ "$local_fingerprint" != "$remote_fingerprint" ]; then
+ echo "The local and remote file fingerprints do not match."
+ rm "$tmp_file" 2>/dev/null
+ return 0
+ fi
+
+ # Show the current and available versions to the user
+ echo "Current script version: $vers"
+ echo "Available script version: $remote_vers"
+
+ # Ask the user if they want to update the script
+ read -rp "Do you want to update the script to version $remote_vers? (y/n): " answer
+
+ if [ "$answer" == "y" ] || [ "$answer" == "Y" ]; then
+ # Replace the local script with the updated version
+ cp "$tmp_file" "$script_name"
+ chmod +x "$script_name"
+ rm "$tmp_file" 2>/dev/null
+
+ echo "Script successfully updated to version $remote_vers"
+ else
+ echo "Update canceled by the user"
+ fi
+
+ rm "$tmp_file" 2>/dev/null
+}
+
+# Check arguments number
+if [ "$#" -lt 1 ]; then
+ help_usage
+ exit 0
+fi
+
+# Parse arguments
+action=$1
+env=$2
+filetf=$3
+shift 2
+other=$@
+
+if [ -n "$env" ]; then
+ # shellcheck source=/dev/null
+ source "./env/$env/backend.ini"
+ if [ -z "$(command -v az)" ]; then
+ echo "az not found, cannot proceed"
+ exit 1
+ fi
+ az account set -s "${subscription}"
+fi
+
+# Call appropriate function based on action
+case $action in
+ clean)
+ clean_environment
+ ;;
+ ?|help|-h)
+ help_usage
+ ;;
+ init)
+ init_terraform "$other"
+ ;;
+ list)
+ list_env
+ ;;
+ output|state|taint|tflist)
+ init_terraform
+ state_output_taint_actions $other
+ ;;
+ summ)
+ init_terraform
+ tfsummary "$other"
+ ;;
+ tlock)
+ terraform providers lock -platform=windows_amd64 -platform=darwin_amd64 -platform=darwin_arm64 -platform=linux_amd64
+ ;;
+ update)
+ update_script
+ ;;
+ *)
+ if [ "$FILE_ACTION" = true ]; then
+ extract_resources "$filetf" "$env"
+ else
+ init_terraform
+ other_actions "$other"
+ fi
+ ;;
+esac
diff --git a/infra/container_apps/user-group-cdc/variables.tf b/infra/container_apps/user-group-cdc/variables.tf
new file mode 100644
index 00000000..0f46be44
--- /dev/null
+++ b/infra/container_apps/user-group-cdc/variables.tf
@@ -0,0 +1,83 @@
+variable "prefix" {
+ description = "Domain prefix"
+ type = string
+ default = "selc"
+ validation {
+ condition = (
+ length(var.prefix) <= 6
+ )
+ error_message = "Max length is 6 chars."
+ }
+}
+
+variable "env_short" {
+ description = "Environment short name"
+ type = string
+ validation {
+ condition = (
+ length(var.env_short) <= 1
+ )
+ error_message = "Max length is 1 chars."
+ }
+}
+
+variable "tags" {
+ type = map(any)
+}
+
+
+variable "container_app" {
+ description = "Container App configuration"
+ type = object({
+ min_replicas = number
+ max_replicas = number
+
+ scale_rules = list(object({
+ name = string
+ custom = object({
+ metadata = map(string)
+ type = string
+ })
+ }))
+
+ cpu = number
+ memory = string
+ })
+}
+
+variable "image_tag" {
+ type = string
+ default = "latest"
+}
+
+variable "app_settings" {
+ type = list(object({
+ name = string
+ value = string
+ }))
+}
+
+variable "secrets_names" {
+ type = map(string)
+ description = "KeyVault secrets to get values from"
+}
+
+variable "workload_profile_name" {
+ type = string
+ description = "Workload Profile name to use"
+ default = null
+}
+
+variable "cae_name" {
+ type = string
+ description = "Container App Environment name"
+ default = "cae-cp"
+}
+
+variable "suffix_increment" {
+ type = string
+ description = "Suffix increment Container App Environment name"
+ default = ""
+}
+
+
diff --git a/libs/user-sdk-event/pom.xml b/libs/user-sdk-event/pom.xml
index 703efd51..5b6f6c79 100644
--- a/libs/user-sdk-event/pom.xml
+++ b/libs/user-sdk-event/pom.xml
@@ -58,6 +58,12 @@
user-sdk-model
0.0.1
+
+ jakarta.validation
+ jakarta.validation-api
+ 3.0.2
+ compile
+
diff --git a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/client/EventHubRestClient.java b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/client/EventHubRestClient.java
index 05d9846d..1b15cc81 100644
--- a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/client/EventHubRestClient.java
+++ b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/client/EventHubRestClient.java
@@ -2,6 +2,7 @@
import io.smallrye.mutiny.Uni;
import it.pagopa.selfcare.user.auth.EventhubSasTokenAuthorization;
+import it.pagopa.selfcare.user.model.UserGroupNotificationToSend;
import it.pagopa.selfcare.user.model.UserNotificationToSend;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.ws.rs.POST;
@@ -19,4 +20,8 @@ public interface EventHubRestClient {
@Path("messages")
Uni sendMessage(UserNotificationToSend notification);
+ @POST
+ @Path("messages")
+ Uni sendUserGroupMessage(UserGroupNotificationToSend notification);
+
}
diff --git a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/TrackEventInput.java b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/TrackEventInput.java
index d45b5c28..368eae09 100644
--- a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/TrackEventInput.java
+++ b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/TrackEventInput.java
@@ -4,6 +4,7 @@
import lombok.Builder;
import lombok.Data;
+import java.util.List;
import java.util.Optional;
@Data
@@ -16,6 +17,7 @@ public class TrackEventInput {
private String institutionId;
private String exception;
private String productRole;
+ private List groupMembers;
public static TrackEventInput toTrackEventInput(UserNotificationToSend userNotificationToSend) {
return TrackEventInput.builder()
@@ -26,4 +28,16 @@ public static TrackEventInput toTrackEventInput(UserNotificationToSend userNotif
.productRole(Optional.ofNullable(userNotificationToSend.getUser()).map(UserToNotify::getProductRole).orElse(null))
.build();
}
+
+ public static TrackEventInput toTrackEventInputForUserGroup(UserGroupNotificationToSend userGroupEntity) {
+ TrackEventInputBuilder trackEventInputBuilder = TrackEventInput.builder()
+ .documentKey(userGroupEntity.getId())
+ .institutionId(userGroupEntity.getInstitutionId())
+ .productId(userGroupEntity.getProductId());
+
+ if(userGroupEntity.getMembers() != null && !userGroupEntity.getMembers().isEmpty()) {
+ trackEventInputBuilder.groupMembers(userGroupEntity.getMembers().stream().toList());
+ }
+ return trackEventInputBuilder.build();
+ }
}
diff --git a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/UserGroupNotificationToSend.java b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/UserGroupNotificationToSend.java
new file mode 100644
index 00000000..a182cfff
--- /dev/null
+++ b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/UserGroupNotificationToSend.java
@@ -0,0 +1,21 @@
+package it.pagopa.selfcare.user.model;
+
+import lombok.Data;
+
+import java.time.Instant;
+import java.util.Set;
+
+@Data
+public class UserGroupNotificationToSend {
+ private String id;
+ private String institutionId;
+ private String productId;
+ private String name;
+ private String description;
+ private String status;
+ private Set members;
+ private Instant createdAt;
+ private String createdBy;
+ private Instant modifiedAt;
+ private String modifiedBy;
+}
diff --git a/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/TrackEventInputTest.java b/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/TrackEventInputTest.java
new file mode 100644
index 00000000..48970c6a
--- /dev/null
+++ b/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/TrackEventInputTest.java
@@ -0,0 +1,51 @@
+package it.pagopa.selfcare.user;
+
+import it.pagopa.selfcare.user.model.TrackEventInput;
+import it.pagopa.selfcare.user.model.UserGroupNotificationToSend;
+import it.pagopa.selfcare.user.model.UserNotificationToSend;
+import it.pagopa.selfcare.user.model.UserToNotify;
+import org.junit.jupiter.api.Test;
+
+import java.time.Instant;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+class TrackEventInputTest {
+
+ @Test
+ void toTrackEventInput_withUserNotification() {
+ UserNotificationToSend userNotification = new UserNotificationToSend();
+ userNotification.setId("docKey");
+ UserToNotify user = new UserToNotify();
+ user.setUserId("userId");
+ user.setProductRole("productRole");
+ userNotification.setUser(user);
+ userNotification.setInstitutionId("institutionId");
+ userNotification.setProductId("productId");
+
+ TrackEventInput result = TrackEventInput.toTrackEventInput(userNotification);
+
+ assertEquals("docKey", result.getDocumentKey());
+ assertEquals("userId", result.getUserId());
+ assertEquals("institutionId", result.getInstitutionId());
+ assertEquals("productId", result.getProductId());
+ assertEquals("productRole", result.getProductRole());
+ }
+
+ @Test
+ void toTrackEventInputForUserGroup_withUserGroupNotification() {
+ UserGroupNotificationToSend userGroupNotification = new UserGroupNotificationToSend();
+ userGroupNotification.setId("docKey");
+ userGroupNotification.setInstitutionId("institutionId");
+ userGroupNotification.setProductId("productId");
+ userGroupNotification.setMembers(Set.of("member1", "member2"));
+
+ TrackEventInput result = TrackEventInput.toTrackEventInputForUserGroup(userGroupNotification);
+
+ assertEquals("docKey", result.getDocumentKey());
+ assertEquals("institutionId", result.getInstitutionId());
+ assertEquals("productId", result.getProductId());
+ }
+}
\ No newline at end of file
diff --git a/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/UserGroupNotificationToSendTest.java b/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/UserGroupNotificationToSendTest.java
new file mode 100644
index 00000000..90834e80
--- /dev/null
+++ b/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/UserGroupNotificationToSendTest.java
@@ -0,0 +1,39 @@
+package it.pagopa.selfcare.user;
+
+import it.pagopa.selfcare.user.model.UserGroupNotificationToSend;
+import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.*;
+import java.time.Instant;
+import java.util.Set;
+
+class UserGroupNotificationToSendTest {
+
+ @Test
+ void userGroupNotificationToSend_withValidData_shouldMapFieldsCorrectly() {
+ UserGroupNotificationToSend notification = new UserGroupNotificationToSend();
+ notification.setId("id");
+ notification.setInstitutionId("institutionId");
+ notification.setProductId("productId");
+ notification.setName("name");
+ notification.setDescription("description");
+ notification.setStatus("status");
+ notification.setMembers(Set.of("member1", "member2"));
+ notification.setCreatedAt(Instant.now());
+ notification.setCreatedBy("createdBy");
+ notification.setModifiedAt(Instant.now());
+ notification.setModifiedBy("modifiedBy");
+
+ assertEquals("id", notification.getId());
+ assertEquals("institutionId", notification.getInstitutionId());
+ assertEquals("productId", notification.getProductId());
+ assertEquals("name", notification.getName());
+ assertEquals("description", notification.getDescription());
+ assertEquals("status", notification.getStatus());
+ assertEquals(Set.of("member1", "member2"), notification.getMembers());
+ assertNotNull(notification.getCreatedAt());
+ assertEquals("createdBy", notification.getCreatedBy());
+ assertNotNull(notification.getModifiedAt());
+ assertEquals("modifiedBy", notification.getModifiedBy());
+ }
+
+}
diff --git a/libs/user-sdk-model/pom.xml b/libs/user-sdk-model/pom.xml
index 8457c610..bbb60e81 100644
--- a/libs/user-sdk-model/pom.xml
+++ b/libs/user-sdk-model/pom.xml
@@ -25,7 +25,7 @@
it.pagopa.selfcare
onboarding-sdk-common
- 0.1.13
+ 0.3.2
compile
diff --git a/libs/user-sdk-model/src/main/java/it.pagopa.selfcare.user.model/constants/EventsMetric.java b/libs/user-sdk-model/src/main/java/it.pagopa.selfcare.user.model/constants/EventsMetric.java
index 5e70a66e..01194e55 100644
--- a/libs/user-sdk-model/src/main/java/it.pagopa.selfcare.user.model/constants/EventsMetric.java
+++ b/libs/user-sdk-model/src/main/java/it.pagopa.selfcare.user.model/constants/EventsMetric.java
@@ -8,6 +8,11 @@ public class EventsMetric {
public static final String EVENTS_USER_INSTITUTION_PRODUCT_FAILURE = "EventsUserInstitutionProduct_failures";
public static final String EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS = "EventsUserInstitutionProduct_success";
+ public static final String EVENTS_USER_GROUP_FAILURE = "EventsUserGroup_failures";
+ public static final String EVENTS_USER_GROUP_SUCCESS = "EventsUserGroup_success";
+ public static final String EVENTS_USER_GROUP_PRODUCT_FAILURE = "EventsUserGroupProduct_failures";
+ public static final String EVENTS_USER_GROUP_PRODUCT_SUCCESS = "EventsUserGroupProduct_success";
+
public static final String USER_INFO_UPDATE_FAILURE = "UserInfoUpdate_failure";
public static final String USER_INFO_UPDATE_SUCCESS = "UserInfoUpdate_success";
diff --git a/libs/user-sdk-model/src/main/java/it.pagopa.selfcare.user.model/constants/EventsName.java b/libs/user-sdk-model/src/main/java/it.pagopa.selfcare.user.model/constants/EventsName.java
index 8a9e7d3e..37a0a53f 100644
--- a/libs/user-sdk-model/src/main/java/it.pagopa.selfcare.user.model/constants/EventsName.java
+++ b/libs/user-sdk-model/src/main/java/it.pagopa.selfcare.user.model/constants/EventsName.java
@@ -4,4 +4,5 @@ public class EventsName {
public static final String EVENT_USER_MS_NAME = "USER_MS";
public static final String EVENT_USER_CDC_NAME = "USER_CDC";
+ public static final String EVENT_USER_GROUP_CDC_NAME = "USER_GROUP_CDC";
}
diff --git a/test-coverage/pom.xml b/test-coverage/pom.xml
index 4a68bcb0..7656c10c 100644
--- a/test-coverage/pom.xml
+++ b/test-coverage/pom.xml
@@ -68,6 +68,20 @@
+
+
+ user-group-cdc
+
+ false
+
+
+
+ it.pagopa.selfcare
+ user-group-cdc
+ 1.0.0-SNAPSHOT
+
+
+
user-group-ms