From 6c1fe534a5809c671d82ca9e93d1cd6ad60d77c0 Mon Sep 17 00:00:00 2001 From: flaminiaScarciofolo Date: Mon, 28 Oct 2024 19:16:17 +0100 Subject: [PATCH 01/10] [SELC-5887] First implementation for send event for FD from user-cdc --- .../user/event/UserInstitutionCdcService.java | 60 +++++++-- .../user/event/mapper/NotificationMapper.java | 17 +++ .../UserInstitutionCdcServiceTest.java | 114 ++++++++++++++++-- .../it/pagopa/selfcare/user/UserUtils.java | 15 +++ .../user/client/EventHubRestClient.java | 5 + .../user/model/FdUserNotificationToSend.java | 21 ++++ .../user/model/NotificationUserType.java | 9 ++ .../selfcare/user/model/TrackEventInput.java | 11 ++ 8 files changed, 237 insertions(+), 15 deletions(-) create mode 100644 libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/FdUserNotificationToSend.java create mode 100644 libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/NotificationUserType.java diff --git a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java index 59e7a565..124efd50 100644 --- a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java +++ b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java @@ -15,11 +15,14 @@ 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.UserUtils; import it.pagopa.selfcare.user.client.EventHubRestClient; import it.pagopa.selfcare.user.event.entity.UserInstitution; import it.pagopa.selfcare.user.event.mapper.NotificationMapper; import it.pagopa.selfcare.user.event.repository.UserInstitutionRepository; +import it.pagopa.selfcare.user.model.NotificationUserType; +import it.pagopa.selfcare.user.model.OnboardedProduct; import it.pagopa.selfcare.user.model.TrackEventInput; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; @@ -52,6 +55,9 @@ public class UserInstitutionCdcService { private static final String COLLECTION_NAME = "userInstitutions"; private static final String OPERATION_NAME = "USER-CDC-UserInfoUpdate"; public static final String USERS_FIELD_LIST_WITHOUT_FISCAL_CODE = "name,familyName,email,workContacts"; + private static final String PROD_FD = "prod-fd"; + private static final String PROD_FD_GARANTITO = "prod-fd-garantito"; + private final TelemetryClient telemetryClient; @@ -103,7 +109,7 @@ private void initOrderStream(Boolean sendEventsEnabled) { //Retrieve last resumeToken for watching collection at specific operation String resumeToken = null; - if(!ConfigUtils.getProfiles().contains("test")) { + if (!ConfigUtils.getProfiles().contains("test")) { try { TableEntity cdcStartAtEntity = tableClient.getEntity(CDC_START_AT_PARTITION_KEY, CDC_START_AT_ROW_KEY); if (Objects.nonNull(cdcStartAtEntity)) @@ -117,7 +123,7 @@ private void initOrderStream(Boolean sendEventsEnabled) { ReactiveMongoCollection dataCollection = getCollection(); ChangeStreamOptions options = new ChangeStreamOptions() .fullDocument(FullDocument.UPDATE_LOOKUP); - if(Objects.nonNull(resumeToken)) + if (Objects.nonNull(resumeToken)) options = options.resumeAfter(BsonDocument.parse(resumeToken)); Bson match = Aggregates.match(Filters.in("operationType", asList("update", "replace", "insert"))); @@ -129,11 +135,11 @@ private void initOrderStream(Boolean sendEventsEnabled) { this::consumerUserInstitutionRepositoryEvent, failure -> { log.error("Error during subscribe collection, exception: {} , message: {}", failure.toString(), failure.getMessage()); - telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(TrackEventInput.builder().exception(failure.getClass().toString()).build()), Map.of(USER_INFO_UPDATE_FAILURE, 1D)); + telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(TrackEventInput.builder().exception(failure.getClass().toString()).build()), Map.of(USER_INFO_UPDATE_FAILURE, 1D)); Quarkus.asyncExit(); }); - if(sendEventsEnabled) { + if (sendEventsEnabled) { publisher.subscribe().with( this::consumerToSendScUserEvent, failure -> { @@ -196,13 +202,13 @@ public void consumerToSendScUserEvent(ChangeStreamDocument docu userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitutionChanged.getUserId()) .onFailure(this::checkIfIsRetryableException) - .retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) + .retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) .onItem().transformToUni(userResource -> Multi.createFrom().iterable(UserUtils.groupingProductAndReturnMinStateProduct(userInstitutionChanged.getProducts())) .map(onboardedProduct -> notificationMapper.toUserNotificationToSend(userInstitutionChanged, onboardedProduct, userResource)) .onItem().transformToUniAndMerge(userNotificationToSend -> eventHubRestClient.sendMessage(userNotificationToSend) - .onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) - .onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(userNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS, 1D))) - .onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(userNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_FAILURE, 1D)))) + .onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) + .onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(userNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS, 1D))) + .onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(userNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_FAILURE, 1D)))) .toUni() ) .subscribe().with( @@ -216,6 +222,44 @@ public void consumerToSendScUserEvent(ChangeStreamDocument docu }); } + public void consumerToSendUserEventForFD(ChangeStreamDocument document) { + + assert document.getFullDocument() != null; + assert document.getDocumentKey() != null; + UserInstitution userInstitutionChanged = document.getFullDocument(); + + log.info("Starting consumerToSendUserEventForFd ... "); + + userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitutionChanged.getUserId()) + .onFailure(this::checkIfIsRetryableException) + .retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) + .onItem().transformToUni(userResource -> Uni.createFrom().item(UserUtils.retrieveFdProductIfItChanged(userInstitutionChanged.getProducts(), List.of(PROD_FD, PROD_FD_GARANTITO))) + .map(onboardedProduct -> notificationMapper.toFdUserNotificationToSend(userInstitutionChanged, onboardedProduct, userResource, evaluateType(onboardedProduct))) + .onItem().ifNotNull().transformToUni(fdUserNotificationToSend -> eventHubRestClient.sendMessage(fdUserNotificationToSend) + .onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) + .onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS, 1D))) + .onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_FAILURE, 1D)))) + ) + .subscribe().with( + result -> { + log.info("SendEvents successfully performed from UserInstitution document having id: {}", document.getDocumentKey().toJson()); + telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_SUCCESS, 1D)); + }, + failure -> { + log.error("Error during SendEvents from UserInstitution document having id: {} , message: {}", document.getDocumentKey().toJson(), failure.getMessage()); + telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D)); + }); + } + + private NotificationUserType evaluateType(OnboardedProduct onboardedProduct) { + return switch (onboardedProduct.getStatus()) { + case ACTIVE -> NotificationUserType.ACTIVE_USER; + case SUSPENDED -> NotificationUserType.SUSPEND_USER; + case DELETED -> NotificationUserType.DELETE_USER; + default -> null; + }; + } + private boolean checkIfIsRetryableException(Throwable throwable) { return throwable instanceof TimeoutException || (throwable instanceof ClientWebApplicationException webApplicationException && webApplicationException.getResponse().getStatus() == 429); diff --git a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java index 3f5d90ac..13fc5835 100644 --- a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java +++ b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java @@ -2,6 +2,8 @@ import it.pagopa.selfcare.user.UserUtils; import it.pagopa.selfcare.user.event.entity.UserInstitution; +import it.pagopa.selfcare.user.model.FdUserNotificationToSend; +import it.pagopa.selfcare.user.model.NotificationUserType; import it.pagopa.selfcare.user.model.OnboardedProduct; import it.pagopa.selfcare.user.model.UserNotificationToSend; import org.mapstruct.Mapper; @@ -33,4 +35,19 @@ public interface NotificationMapper { default String toUniqueIdNotification(UserInstitution userInstitution, OnboardedProduct product) { return UserUtils.uniqueIdNotification(userInstitution.getId().toHexString(), product.getProductId(), product.getProductRole()); } + + @Mapping(target = "id", expression = "java(toUniqueIdNotification(userInstitutionChanged, product))") + @Mapping(target = "onboardingTokenId", source = "product.tokenId") + @Mapping(target = "product", source = "product.productId") + @Mapping(target = "createdAt", source = "product.createdAt") + @Mapping(target = "updatedAt", expression = "java((null == product.getUpdatedAt()) ? product.getCreatedAt() : product.getUpdatedAt())") + @Mapping(target = "user.role", source = "product.role") + @Mapping(target = "user.productRole", source = "product.productRole") + @Mapping(target = "user.relationshipStatus", source = "product.status") + @Mapping(target = "user.userId", source = "userResource.id", ignore = true) + @Mapping(target = "user.name", source = "userResource.name.value") + @Mapping(target = "user.familyName", source = "userResource.familyName.value") + @Mapping(target = "user.email", source = "userResource.email.value") + @Mapping(target = "type", source = "type") + FdUserNotificationToSend toFdUserNotificationToSend(UserInstitution userInstitutionChanged, OnboardedProduct product, UserResource userResource, NotificationUserType type); } diff --git a/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/service/UserInstitutionCdcServiceTest.java b/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/service/UserInstitutionCdcServiceTest.java index f5d29ed6..2e527e95 100644 --- a/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/service/UserInstitutionCdcServiceTest.java +++ b/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/service/UserInstitutionCdcServiceTest.java @@ -9,14 +9,19 @@ import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; import it.pagopa.selfcare.user.client.EventHubRestClient; import it.pagopa.selfcare.user.event.UserInstitutionCdcService; +import it.pagopa.selfcare.user.event.entity.UserInfo; import it.pagopa.selfcare.user.event.entity.UserInstitution; +import it.pagopa.selfcare.user.model.FdUserNotificationToSend; import it.pagopa.selfcare.user.model.OnboardedProduct; +import it.pagopa.selfcare.user.model.UserNotificationToSend; import it.pagopa.selfcare.user.model.constants.OnboardedProductState; import jakarta.inject.Inject; import org.bson.BsonDocument; import org.bson.types.ObjectId; 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.openapi.quarkus.user_registry_json.api.UserApi; import org.openapi.quarkus.user_registry_json.model.UserResource; @@ -28,6 +33,7 @@ import java.util.List; import static it.pagopa.selfcare.user.event.UserInstitutionCdcService.USERS_FIELD_LIST_WITHOUT_FISCAL_CODE; +import static it.pagopa.selfcare.user.model.NotificationUserType.*; import static org.mockito.Mockito.*; @QuarkusTest @@ -45,9 +51,10 @@ public class UserInstitutionCdcServiceTest { @InjectMock EventHubRestClient eventHubRestClient; + @Test void consumerToSendScUserEvent() { - UserInstitution userInstitution = dummyUserInstitution(); + UserInstitution userInstitution = dummyUserInstitution(false, null); ChangeStreamDocument document = Mockito.mock(ChangeStreamDocument.class); when(document.getFullDocument()).thenReturn(userInstitution); when(document.getDocumentKey()).thenReturn(new BsonDocument()); @@ -60,27 +67,120 @@ void consumerToSendScUserEvent() { verify(userRegistryApi, times(1)). findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId()); verify(eventHubRestClient, times(2)). - sendMessage(any()); + sendMessage(any(UserNotificationToSend.class)); } + @Test + void consumerToSendUserEventForFDSendACTIVE_USER() { + UserInstitution userInstitution = dummyUserInstitution(true, OnboardedProductState.ACTIVE); + ChangeStreamDocument document = Mockito.mock(ChangeStreamDocument.class); + when(document.getFullDocument()).thenReturn(userInstitution); + when(document.getDocumentKey()).thenReturn(new BsonDocument()); + + UserResource userResource = dummyUserResource(); + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId())) + .thenReturn(Uni.createFrom().item(userResource)); + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(FdUserNotificationToSend.class); + when(eventHubRestClient.sendMessage(argumentCaptor.capture())) + .thenReturn(Uni.createFrom().nullItem()); + + userInstitutionCdcService.consumerToSendUserEventForFD(document); + verify(userRegistryApi, times(1)). + findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId()); + verify(eventHubRestClient, times(1)). + sendMessage(any(FdUserNotificationToSend.class)); + Assertions.assertEquals(ACTIVE_USER, argumentCaptor.getValue().getType()); + } + + @Test + void consumerToSendUserEventForFDSendSUSPEND_USER() { + UserInstitution userInstitution = dummyUserInstitution(true, OnboardedProductState.SUSPENDED); + ChangeStreamDocument document = Mockito.mock(ChangeStreamDocument.class); + when(document.getFullDocument()).thenReturn(userInstitution); + when(document.getDocumentKey()).thenReturn(new BsonDocument()); + + UserResource userResource = dummyUserResource(); + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId())) + .thenReturn(Uni.createFrom().item(userResource)); + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(FdUserNotificationToSend.class); + + when(eventHubRestClient.sendMessage(argumentCaptor.capture())) + .thenReturn(Uni.createFrom().nullItem()); + + userInstitutionCdcService.consumerToSendUserEventForFD(document); + verify(userRegistryApi, times(1)). + findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId()); + verify(eventHubRestClient, times(1)). + sendMessage(any(FdUserNotificationToSend.class)); + Assertions.assertEquals(SUSPEND_USER, argumentCaptor.getValue().getType()); + } + + @Test + void consumerToSendUserEventForFDSendDELETE_USER() { + UserInstitution userInstitution = dummyUserInstitution(true, OnboardedProductState.DELETED); + ChangeStreamDocument document = Mockito.mock(ChangeStreamDocument.class); + when(document.getFullDocument()).thenReturn(userInstitution); + when(document.getDocumentKey()).thenReturn(new BsonDocument()); + + UserResource userResource = dummyUserResource(); + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId())) + .thenReturn(Uni.createFrom().item(userResource)); + ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(FdUserNotificationToSend.class); + when(eventHubRestClient.sendMessage(argumentCaptor.capture())) + .thenReturn(Uni.createFrom().nullItem()); + + userInstitutionCdcService.consumerToSendUserEventForFD(document); + verify(userRegistryApi, times(1)). + findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId()); + verify(eventHubRestClient, times(1)). + sendMessage(any(FdUserNotificationToSend.class)); + Assertions.assertEquals(DELETE_USER, argumentCaptor.getValue().getType()); + } + + + + @Test + void consumerToSendUserEventForFDNotSend() { + UserInstitution userInstitution = dummyUserInstitution(false, null); + ChangeStreamDocument document = Mockito.mock(ChangeStreamDocument.class); + when(document.getFullDocument()).thenReturn(userInstitution); + when(document.getDocumentKey()).thenReturn(new BsonDocument()); + + UserResource userResource = dummyUserResource(); + when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId())) + .thenReturn(Uni.createFrom().item(userResource)); + + userInstitutionCdcService.consumerToSendUserEventForFD(document); + verify(userRegistryApi, times(1)). + findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId()); + verify(eventHubRestClient, times(0)). + sendMessage(any(UserNotificationToSend.class)); + } + + UserResource dummyUserResource() { UserResource userResource = new UserResource(); return userResource; } - UserInstitution dummyUserInstitution() { + UserInstitution dummyUserInstitution(boolean sendForFd, OnboardedProductState state){ UserInstitution userInstitution = new UserInstitution(); userInstitution.setId(ObjectId.get()); - userInstitution.setProducts(List.of(dummyOnboardedProduct("example-1", OnboardedProductState.ACTIVE, 1), - dummyOnboardedProduct("example-2", OnboardedProductState.DELETED, 1))); + if(sendForFd) { + userInstitution.setProducts(List.of(dummyOnboardedProduct("example-1", state, 2, "prod-fd"), + dummyOnboardedProduct("example-2", OnboardedProductState.ACTIVE, 1, "prod-io"))); + }else { + userInstitution.setProducts(List.of(dummyOnboardedProduct("example-1", OnboardedProductState.ACTIVE, 2, "prod-io"), + dummyOnboardedProduct("example-2", OnboardedProductState.ACTIVE, 1, "prod-fd"))); + } return userInstitution; } - OnboardedProduct dummyOnboardedProduct(String productRole, OnboardedProductState state, int day) { + OnboardedProduct dummyOnboardedProduct(String productRole, OnboardedProductState state, int day, String productId) { OnboardedProduct onboardedProduct = new OnboardedProduct(); - onboardedProduct.setProductId("productId"); + onboardedProduct.setProductId(productId); onboardedProduct.setProductRole(productRole); onboardedProduct.setCreatedAt(OffsetDateTime.of(2024, 1, day, 0, 0, 0, 0, ZoneOffset.UTC)); onboardedProduct.setUpdatedAt(OffsetDateTime.of(2024, 1, day, 0, 0, 0, 0, ZoneOffset.UTC)); diff --git a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java index df3bd980..a3e16a63 100644 --- a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java +++ b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java @@ -66,4 +66,19 @@ public static Map mapPropsForTrackEvent(TrackEventInput trackEve Optional.ofNullable(trackEventInput.getException()).ifPresent(value -> propertiesMap.put("exec", value)); return propertiesMap; } + + /** + * The retrieveFdProductIfItChanged method is designed to retrieve the most recently updated OnboardedProduct + * from a list of products, provided that the product's ID is included in a specified list of product IDs to check. + */ + public static OnboardedProduct retrieveFdProductIfItChanged(List products, List productIdToCheck) { + if (Objects.nonNull(products) && !products.isEmpty()) { + return products.stream() + .max(Comparator.comparing(OnboardedProduct::getUpdatedAt, nullsLast(naturalOrder())) + .thenComparing(OnboardedProduct::getCreatedAt, nullsLast(naturalOrder()))) + .filter(onboardedProduct -> productIdToCheck.contains(onboardedProduct.getProductId())) + .orElse(null); + } + return null; + } } 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 1b15cc81..35de939f 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.FdUserNotificationToSend; import it.pagopa.selfcare.user.model.UserGroupNotificationToSend; import it.pagopa.selfcare.user.model.UserNotificationToSend; import jakarta.enterprise.context.ApplicationScoped; @@ -24,4 +25,8 @@ public interface EventHubRestClient { @Path("messages") Uni sendUserGroupMessage(UserGroupNotificationToSend notification); + @POST + @Path("messages") + Uni sendMessage(FdUserNotificationToSend notification); + } diff --git a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/FdUserNotificationToSend.java b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/FdUserNotificationToSend.java new file mode 100644 index 00000000..602494c9 --- /dev/null +++ b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/FdUserNotificationToSend.java @@ -0,0 +1,21 @@ +package it.pagopa.selfcare.user.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Data; + +import java.time.OffsetDateTime; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@Data +public class FdUserNotificationToSend { + + private String id; + private String institutionId; + private String product; + private OffsetDateTime createdAt; + private OffsetDateTime updatedAt; + private String onboardingTokenId; + private NotificationUserType type; + private UserToNotify user; + +} \ No newline at end of file diff --git a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/NotificationUserType.java b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/NotificationUserType.java new file mode 100644 index 00000000..dcba2266 --- /dev/null +++ b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/NotificationUserType.java @@ -0,0 +1,9 @@ +package it.pagopa.selfcare.user.model; + +public enum NotificationUserType { + + ACTIVE_USER, + SUSPEND_USER, + DELETE_USER; + +} 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 368eae09..090b8b31 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 @@ -29,6 +29,17 @@ public static TrackEventInput toTrackEventInput(UserNotificationToSend userNotif .build(); } + public static TrackEventInput toTrackEventInput(FdUserNotificationToSend fdUserNotificationToSend) { + return TrackEventInput.builder() + .documentKey(fdUserNotificationToSend.getId()) + .userId(Optional.ofNullable(fdUserNotificationToSend.getUser()).map(UserToNotify::getUserId).orElse(null)) + .institutionId(fdUserNotificationToSend.getInstitutionId()) + .productId(fdUserNotificationToSend.getProduct()) + .productRole(Optional.ofNullable(fdUserNotificationToSend.getUser()).map(UserToNotify::getProductRole).orElse(null)) + .build(); + } + + public static TrackEventInput toTrackEventInputForUserGroup(UserGroupNotificationToSend userGroupEntity) { TrackEventInputBuilder trackEventInputBuilder = TrackEventInput.builder() .documentKey(userGroupEntity.getId()) From c7b5eaad2032d076c35624579a93cddb399e6be9 Mon Sep 17 00:00:00 2001 From: flaminiaScarciofolo Date: Wed, 30 Oct 2024 13:19:18 +0100 Subject: [PATCH 02/10] [SELC-5887] added configuration for send fd event --- .../user/event/UserInstitutionCdcService.java | 40 ++++++--- .../user/event/mapper/NotificationMapper.java | 47 ++++++---- .../src/main/resources/application.properties | 11 ++- .../event/mapper/NotificationMapperTest.java | 85 +++++++++++++++++++ .../UserInstitutionCdcServiceTest.java | 23 ++--- .../user-cdc/env/dev/terraform.tfvars | 29 +++++-- .../user-cdc/env/prod/terraform.tfvars | 29 +++++-- .../user-cdc/env/uat/terraform.tfvars | 29 +++++-- libs/user-sdk-event/pom.xml | 18 ++++ .../it/pagopa/selfcare/user/UserUtils.java | 51 +++++++++++ .../auth/EventhubFdSasTokenAuthorization.java | 32 +++++++ .../auth/EventhubSasTokenAuthorization.java | 57 +------------ .../user/client/EventHubFdRestClient.java | 22 +++++ .../user/client/EventHubRestClient.java | 5 -- 14 files changed, 360 insertions(+), 118 deletions(-) create mode 100644 apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/mapper/NotificationMapperTest.java create mode 100644 libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/auth/EventhubFdSasTokenAuthorization.java create mode 100644 libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/client/EventHubFdRestClient.java diff --git a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java index 124efd50..dd9d1f84 100644 --- a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java +++ b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java @@ -17,6 +17,7 @@ import io.smallrye.mutiny.Multi; import io.smallrye.mutiny.Uni; import it.pagopa.selfcare.user.UserUtils; +import it.pagopa.selfcare.user.client.EventHubFdRestClient; import it.pagopa.selfcare.user.client.EventHubRestClient; import it.pagopa.selfcare.user.event.entity.UserInstitution; import it.pagopa.selfcare.user.event.mapper.NotificationMapper; @@ -78,6 +79,10 @@ public class UserInstitutionCdcService { @Inject EventHubRestClient eventHubRestClient; + @RestClient + @Inject + EventHubFdRestClient eventHubFdRestClient; + private final NotificationMapper notificationMapper; @@ -87,6 +92,7 @@ public UserInstitutionCdcService(ReactiveMongoClient mongoClient, @ConfigProperty(name = "user-cdc.retry.max-backoff") Integer retryMaxBackOff, @ConfigProperty(name = "user-cdc.retry") Integer maxRetry, @ConfigProperty(name = "user-cdc.send-events.watch.enabled") Boolean sendEventsEnabled, + @ConfigProperty(name = "user-cdc.send-events-fd.watch.enabled") Boolean sendFdEventsEnabled, UserInstitutionRepository userInstitutionRepository, TelemetryClient telemetryClient, TableClient tableClient, NotificationMapper notificationMapper) { @@ -100,10 +106,10 @@ public UserInstitutionCdcService(ReactiveMongoClient mongoClient, this.tableClient = tableClient; this.notificationMapper = notificationMapper; telemetryClient.getContext().getOperation().setName(OPERATION_NAME); - initOrderStream(sendEventsEnabled); + initOrderStream(sendEventsEnabled, sendFdEventsEnabled); } - private void initOrderStream(Boolean sendEventsEnabled) { + private void initOrderStream(Boolean sendEventsEnabled, Boolean sendFdEventsEnabled) { log.info("Starting initOrderStream ... "); //Retrieve last resumeToken for watching collection at specific operation @@ -149,6 +155,17 @@ private void initOrderStream(Boolean sendEventsEnabled) { }); } + if (sendFdEventsEnabled) { + publisher.subscribe().with( + this::consumerToSendUserEventForFD, + failure -> { + log.error("Error during subscribe collection, exception: {} , message: {}", failure.toString(), failure.getMessage()); + telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(TrackEventInput.builder().exception(failure.getClass().toString()).build()), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D)); + Quarkus.asyncExit(); + }); + } + + log.info("Completed initOrderStream ... "); } @@ -234,19 +251,22 @@ public void consumerToSendUserEventForFD(ChangeStreamDocument d .onFailure(this::checkIfIsRetryableException) .retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) .onItem().transformToUni(userResource -> Uni.createFrom().item(UserUtils.retrieveFdProductIfItChanged(userInstitutionChanged.getProducts(), List.of(PROD_FD, PROD_FD_GARANTITO))) - .map(onboardedProduct -> notificationMapper.toFdUserNotificationToSend(userInstitutionChanged, onboardedProduct, userResource, evaluateType(onboardedProduct))) - .onItem().ifNotNull().transformToUni(fdUserNotificationToSend -> eventHubRestClient.sendMessage(fdUserNotificationToSend) - .onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) - .onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS, 1D))) - .onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_FAILURE, 1D)))) - ) + .onItem().ifNotNull().transform(onboardedProduct -> notificationMapper.toFdUserNotificationToSend(userInstitutionChanged, onboardedProduct, userResource, evaluateType(onboardedProduct))) + .onItem().ifNotNull().transformToUni(fdUserNotificationToSend -> { + log.info("Sending message to EventHubFdRestClient ... "); + return eventHubFdRestClient.sendMessage(fdUserNotificationToSend) + .onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) + .onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS, 1D))) + .onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_FAILURE, 1D))); + } + )) .subscribe().with( result -> { - log.info("SendEvents successfully performed from UserInstitution document having id: {}", document.getDocumentKey().toJson()); + log.info("SendFdEvents successfully performed from UserInstitution document having id: {}", document.getDocumentKey().toJson()); telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_SUCCESS, 1D)); }, failure -> { - log.error("Error during SendEvents from UserInstitution document having id: {} , message: {}", document.getDocumentKey().toJson(), failure.getMessage()); + log.error("Error during SendFdEvents from UserInstitution document having id: {} , message: {}", document.getDocumentKey().toJson(), failure.getMessage()); telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D)); }); } diff --git a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java index 13fc5835..5d3366fb 100644 --- a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java +++ b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java @@ -2,15 +2,16 @@ import it.pagopa.selfcare.user.UserUtils; import it.pagopa.selfcare.user.event.entity.UserInstitution; -import it.pagopa.selfcare.user.model.FdUserNotificationToSend; -import it.pagopa.selfcare.user.model.NotificationUserType; -import it.pagopa.selfcare.user.model.OnboardedProduct; -import it.pagopa.selfcare.user.model.UserNotificationToSend; +import it.pagopa.selfcare.user.model.*; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Named; +import org.openapi.quarkus.user_registry_json.model.CertifiableFieldResourceOfstring; import org.openapi.quarkus.user_registry_json.model.UserResource; +import javax.swing.text.html.Option; +import java.util.Objects; +import java.util.Optional; import java.util.UUID; @Mapper(componentModel = "cdi", imports = UUID.class) @@ -20,13 +21,7 @@ public interface NotificationMapper { @Mapping(target = "productId", source = "product.productId") @Mapping(target = "createdAt", source = "product.createdAt") @Mapping(target = "updatedAt", expression = "java((null == product.getUpdatedAt()) ? product.getCreatedAt() : product.getUpdatedAt())") - @Mapping(target = "user.role", source = "product.role") - @Mapping(target = "user.productRole", source = "product.productRole") - @Mapping(target = "user.relationshipStatus", source = "product.status") - @Mapping(target = "user.userId", source = "userResource.id", ignore = true) - @Mapping(target = "user.name", source = "userResource.name.value") - @Mapping(target = "user.familyName", source = "userResource.familyName.value") - @Mapping(target = "user.email", source = "userResource.email.value") + @Mapping(target = "user", expression = "java(mapUser(userResource, userInstitution.getUserMailUuid(), product))") @Mapping(target = "id", expression = "java(toUniqueIdNotification(userInstitution, product))") @Mapping(target = "eventType", expression = "java(it.pagopa.selfcare.user.model.constants.QueueEvent.UPDATE)") UserNotificationToSend toUserNotificationToSend(UserInstitution userInstitution, OnboardedProduct product, UserResource userResource); @@ -41,13 +36,29 @@ default String toUniqueIdNotification(UserInstitution userInstitution, Onboarded @Mapping(target = "product", source = "product.productId") @Mapping(target = "createdAt", source = "product.createdAt") @Mapping(target = "updatedAt", expression = "java((null == product.getUpdatedAt()) ? product.getCreatedAt() : product.getUpdatedAt())") - @Mapping(target = "user.role", source = "product.role") - @Mapping(target = "user.productRole", source = "product.productRole") - @Mapping(target = "user.relationshipStatus", source = "product.status") - @Mapping(target = "user.userId", source = "userResource.id", ignore = true) - @Mapping(target = "user.name", source = "userResource.name.value") - @Mapping(target = "user.familyName", source = "userResource.familyName.value") - @Mapping(target = "user.email", source = "userResource.email.value") + @Mapping(target = "user", expression = "java(mapUser(userResource, userInstitutionChanged.getUserMailUuid(), product))") @Mapping(target = "type", source = "type") FdUserNotificationToSend toFdUserNotificationToSend(UserInstitution userInstitutionChanged, OnboardedProduct product, UserResource userResource, NotificationUserType type); + + + @Named("mapUser") + default UserToNotify mapUser(UserResource userResource, String userMailUuid, OnboardedProduct onboardedProduct) { + UserToNotify userToNotify = new UserToNotify(); + userToNotify.setUserId(Optional.ofNullable(userResource.getId()).map(UUID::toString).orElse(null)); + userToNotify.setName(Optional.ofNullable(userResource.getName()).map(CertifiableFieldResourceOfstring::getValue).orElse(null)); + userToNotify.setFamilyName(Optional.ofNullable(userResource.getFamilyName()).map(CertifiableFieldResourceOfstring::getValue).orElse(null)); + userToNotify.setEmail(Optional.ofNullable(userMailUuid).map(mailUuid -> retrieveMailFromWorkContacts(userResource, mailUuid)).orElse(null)); + userToNotify.setProductRole(onboardedProduct.getProductRole()); + userToNotify.setRole(Optional.ofNullable(onboardedProduct.getRole()).map(Enum::name).orElse(null)); + userToNotify.setRelationshipStatus(onboardedProduct.getStatus()); + return userToNotify; + } + + default String retrieveMailFromWorkContacts(UserResource userResource, String userMailUuid) { + return Optional.ofNullable(userResource.getWorkContacts()) + .flatMap(stringWorkContactResourceMap -> Optional.ofNullable(stringWorkContactResourceMap.get(userMailUuid)) + .flatMap(workContactResource -> Optional.ofNullable(workContactResource.getEmail()) + .map(CertifiableFieldResourceOfstring::getValue))) + .orElse(null); + } } diff --git a/apps/user-cdc/src/main/resources/application.properties b/apps/user-cdc/src/main/resources/application.properties index 7fa5255f..afaec3d3 100644 --- a/apps/user-cdc/src/main/resources/application.properties +++ b/apps/user-cdc/src/main/resources/application.properties @@ -7,7 +7,8 @@ quarkus.mongodb.connection-string = ${MONGODB-CONNECTION-STRING} quarkus.mongodb.database = selcUser #False for pnpg use case because we must not send events -user-cdc.send-events.watch.enabled=${USER_CDC_SEND_EVENTS_WATCH_ENABLED:false} +user-cdc.send-events.watch.enabled=${USER_CDC_SEND_EVENTS_WATCH_ENABLED:true} +user-cdc.send-events-fd.watch.enabled=${USER_CDC_SEND_EVENTS_FD_WATCH_ENABLED:true} user-cdc.appinsights.connection-string=${APPLICATIONINSIGHTS_CONNECTION_STRING:InstrumentationKey=00000000-0000-0000-0000-000000000000} user-cdc.table.name=${START_AT_TABLE_NAME:CdCStartAt} user-cdc.storage.connection-string=${STORAGE_CONNECTION_STRING:UseDevelopmentStorage=true;} @@ -23,6 +24,10 @@ quarkus.openapi-generator.codegen.spec.user_registry_json.additional-model-type- quarkus.openapi-generator.user_registry_json.auth.api_key.api-key = ${USER-REGISTRY-API-KEY:example-api-key} quarkus.rest-client."org.openapi.quarkus.user_registry_json.api.UserApi".url=${USER_REGISTRY_URL:http://localhost:8080} -quarkus.rest-client.event-hub.url=${EVENT_HUB_BASE_PATH:test} +quarkus.rest-client.event-hub.url=${EVENT_HUB_BASE_PATH:test}${EVENT_HUB_SC_USERS_TOPIC:sc-users} eventhub.rest-client.keyName=${SHARED_ACCESS_KEY_NAME:test} -eventhub.rest-client.key=${EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC:test} \ No newline at end of file +eventhub.rest-client.key=${EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC:test} + +quarkus.rest-client.event-hub-fd.url=${EVENT_HUB_BASE_PATH:test}${EVENT_HUB_SELFCARE_FD_TOPIC:selfcare-fd} +eventhubfd.rest-client.keyName=${FD_SHARED_ACCESS_KEY_NAME:test} +eventhubfd.rest-client.key=${EVENTHUB-SELFCARE-FD-FD-KEY-LC:test} \ No newline at end of file diff --git a/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/mapper/NotificationMapperTest.java b/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/mapper/NotificationMapperTest.java new file mode 100644 index 00000000..fc99117e --- /dev/null +++ b/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/mapper/NotificationMapperTest.java @@ -0,0 +1,85 @@ +package it.pagopa.selfcare.user.event.mapper; + +import it.pagopa.selfcare.onboarding.common.PartyRole; +import it.pagopa.selfcare.user.model.OnboardedProduct; +import it.pagopa.selfcare.user.model.UserToNotify; +import it.pagopa.selfcare.user.model.constants.OnboardedProductState; +import org.junit.jupiter.api.Test; +import org.openapi.quarkus.user_registry_json.model.CertifiableFieldResourceOfstring; +import org.openapi.quarkus.user_registry_json.model.UserResource; +import org.openapi.quarkus.user_registry_json.model.WorkContactResource; + +import static org.junit.jupiter.api.Assertions.*; +import java.util.Map; +import java.util.UUID; + +class NotificationMapperTest { + + @Test + void mapUser_withValidData_shouldMapFieldsCorrectly() { + UserResource userResource = new UserResource(); + userResource.setId(UUID.randomUUID()); + userResource.setName(new CertifiableFieldResourceOfstring().value("John")); + userResource.setFamilyName(new CertifiableFieldResourceOfstring().value("Doe")); + userResource.setWorkContacts(Map.of("mailUuid", new WorkContactResource().email(new CertifiableFieldResourceOfstring().value("john.doe@example.com")))); + OnboardedProduct onboardedProduct = new OnboardedProduct(); + onboardedProduct.setProductRole("Admin"); + onboardedProduct.setRole(PartyRole.MANAGER); + onboardedProduct.setStatus(OnboardedProductState.ACTIVE); + + UserToNotify result = new NotificationMapperImpl().mapUser(userResource, "mailUuid", onboardedProduct); + + assertEquals(userResource.getId().toString(), result.getUserId()); + assertEquals("John", result.getName()); + assertEquals("Doe", result.getFamilyName()); + assertEquals("john.doe@example.com", result.getEmail()); + assertEquals("Admin", result.getProductRole()); + assertEquals("MANAGER", result.getRole()); + assertEquals(OnboardedProductState.ACTIVE, result.getRelationshipStatus()); + } + + @Test + void mapUser_withNullFields_shouldHandleNullValues() { + UserResource userResource = new UserResource(); + OnboardedProduct onboardedProduct = new OnboardedProduct(); + + UserToNotify result = new NotificationMapperImpl().mapUser(userResource, null, onboardedProduct); + + assertNull(result.getUserId()); + assertNull(result.getName()); + assertNull(result.getFamilyName()); + assertNull(result.getEmail()); + assertNull(result.getProductRole()); + assertNull(result.getRole()); + assertNull(result.getRelationshipStatus()); + } + + @Test + void retrieveMailFromWorkContacts_withValidMailUuid_shouldReturnEmail() { + UserResource userResource = new UserResource(); + userResource.setWorkContacts(Map.of("mailUuid", new WorkContactResource().email(new CertifiableFieldResourceOfstring().value("john.doe@example.com")))); + + String result = new NotificationMapperImpl().retrieveMailFromWorkContacts(userResource, "mailUuid"); + + assertEquals("john.doe@example.com", result); + } + + @Test + void retrieveMailFromWorkContacts_withInvalidMailUuid_shouldReturnNull() { + UserResource userResource = new UserResource(); + userResource.setWorkContacts(Map.of("mailUuid", new WorkContactResource().email(new CertifiableFieldResourceOfstring().value("john.doe@example.com")))); + + String result = new NotificationMapperImpl().retrieveMailFromWorkContacts(userResource, "invalidUuid"); + + assertNull(result); + } + + @Test + void retrieveMailFromWorkContacts_withNullWorkContacts_shouldReturnNull() { + UserResource userResource = new UserResource(); + + String result = new NotificationMapperImpl().retrieveMailFromWorkContacts(userResource, "mailUuid"); + + assertNull(result); + } +} diff --git a/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/service/UserInstitutionCdcServiceTest.java b/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/service/UserInstitutionCdcServiceTest.java index 2e527e95..1a97a627 100644 --- a/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/service/UserInstitutionCdcServiceTest.java +++ b/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/service/UserInstitutionCdcServiceTest.java @@ -7,6 +7,7 @@ import io.quarkus.test.mongodb.MongoTestResource; import io.smallrye.mutiny.Uni; import io.smallrye.mutiny.helpers.test.UniAssertSubscriber; +import it.pagopa.selfcare.user.client.EventHubFdRestClient; import it.pagopa.selfcare.user.client.EventHubRestClient; import it.pagopa.selfcare.user.event.UserInstitutionCdcService; import it.pagopa.selfcare.user.event.entity.UserInfo; @@ -51,6 +52,10 @@ public class UserInstitutionCdcServiceTest { @InjectMock EventHubRestClient eventHubRestClient; + @RestClient + @InjectMock + EventHubFdRestClient eventHubFdRestClient; + @Test void consumerToSendScUserEvent() { @@ -81,13 +86,13 @@ void consumerToSendUserEventForFDSendACTIVE_USER() { when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId())) .thenReturn(Uni.createFrom().item(userResource)); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(FdUserNotificationToSend.class); - when(eventHubRestClient.sendMessage(argumentCaptor.capture())) + when(eventHubFdRestClient.sendMessage(argumentCaptor.capture())) .thenReturn(Uni.createFrom().nullItem()); userInstitutionCdcService.consumerToSendUserEventForFD(document); verify(userRegistryApi, times(1)). findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId()); - verify(eventHubRestClient, times(1)). + verify(eventHubFdRestClient, times(1)). sendMessage(any(FdUserNotificationToSend.class)); Assertions.assertEquals(ACTIVE_USER, argumentCaptor.getValue().getType()); } @@ -104,13 +109,13 @@ void consumerToSendUserEventForFDSendSUSPEND_USER() { .thenReturn(Uni.createFrom().item(userResource)); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(FdUserNotificationToSend.class); - when(eventHubRestClient.sendMessage(argumentCaptor.capture())) + when(eventHubFdRestClient.sendMessage(argumentCaptor.capture())) .thenReturn(Uni.createFrom().nullItem()); userInstitutionCdcService.consumerToSendUserEventForFD(document); verify(userRegistryApi, times(1)). findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId()); - verify(eventHubRestClient, times(1)). + verify(eventHubFdRestClient, times(1)). sendMessage(any(FdUserNotificationToSend.class)); Assertions.assertEquals(SUSPEND_USER, argumentCaptor.getValue().getType()); } @@ -126,19 +131,17 @@ void consumerToSendUserEventForFDSendDELETE_USER() { when(userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId())) .thenReturn(Uni.createFrom().item(userResource)); ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(FdUserNotificationToSend.class); - when(eventHubRestClient.sendMessage(argumentCaptor.capture())) + when(eventHubFdRestClient.sendMessage(argumentCaptor.capture())) .thenReturn(Uni.createFrom().nullItem()); userInstitutionCdcService.consumerToSendUserEventForFD(document); verify(userRegistryApi, times(1)). findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId()); - verify(eventHubRestClient, times(1)). + verify(eventHubFdRestClient, times(1)). sendMessage(any(FdUserNotificationToSend.class)); Assertions.assertEquals(DELETE_USER, argumentCaptor.getValue().getType()); } - - @Test void consumerToSendUserEventForFDNotSend() { UserInstitution userInstitution = dummyUserInstitution(false, null); @@ -153,8 +156,8 @@ void consumerToSendUserEventForFDNotSend() { userInstitutionCdcService.consumerToSendUserEventForFD(document); verify(userRegistryApi, times(1)). findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitution.getUserId()); - verify(eventHubRestClient, times(0)). - sendMessage(any(UserNotificationToSend.class)); + verify(eventHubFdRestClient, times(0)). + sendMessage(any(FdUserNotificationToSend.class)); } diff --git a/infra/container_apps/user-cdc/env/dev/terraform.tfvars b/infra/container_apps/user-cdc/env/dev/terraform.tfvars index 84fe364c..61f06383 100644 --- a/infra/container_apps/user-cdc/env/dev/terraform.tfvars +++ b/infra/container_apps/user-cdc/env/dev/terraform.tfvars @@ -44,14 +44,30 @@ app_settings = [ name = "USER_CDC_SEND_EVENTS_WATCH_ENABLED" value = "true" }, + { + name = "USER_CDC_SEND_EVENTS_FD_WATCH_ENABLED" + value = "true" + }, { name = "EVENT_HUB_BASE_PATH" - value = "https://selc-d-eventhub-ns.servicebus.windows.net/sc-users" + value = "https://selc-d-eventhub-ns.servicebus.windows.net/" + }, + { + name = "EVENT_HUB_SC_USERS_TOPIC" + value = "sc-users" + }, + { + name = "EVENT_HUB_SELFCARE_FD_TOPIC" + value = "selfcare-fd" }, { name = "SHARED_ACCESS_KEY_NAME" value = "selfcare-wo" }, + { + name = "FD_SHARED_ACCESS_KEY_NAME" + value = "external-interceptor-wo" + }, { name = "USER_REGISTRY_URL" value = "https://api.uat.pdv.pagopa.it/user-registry/v1" @@ -60,10 +76,11 @@ app_settings = [ secrets_names = { - "APPLICATIONINSIGHTS_CONNECTION_STRING" = "appinsights-connection-string" - "MONGODB-CONNECTION-STRING" = "mongodb-connection-string" - "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string" - "EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC" = "eventhub-sc-users-selfcare-wo-key-lc" - "USER-REGISTRY-API-KEY" = "user-registry-api-key" + "APPLICATIONINSIGHTS_CONNECTION_STRING" = "appinsights-connection-string" + "MONGODB-CONNECTION-STRING" = "mongodb-connection-string" + "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string" + "EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC" = "eventhub-sc-users-selfcare-wo-key-lc" + "USER-REGISTRY-API-KEY" = "user-registry-api-key" + "EVENTHUB-SELFCARE-FD-FD-KEY-LC" = "eventhub-selfcare-fd-fd-key-lc" } diff --git a/infra/container_apps/user-cdc/env/prod/terraform.tfvars b/infra/container_apps/user-cdc/env/prod/terraform.tfvars index 71fe484e..fa9244be 100644 --- a/infra/container_apps/user-cdc/env/prod/terraform.tfvars +++ b/infra/container_apps/user-cdc/env/prod/terraform.tfvars @@ -32,14 +32,30 @@ app_settings = [ name = "USER_CDC_SEND_EVENTS_WATCH_ENABLED" value = "true" }, + { + name = "USER_CDC_SEND_EVENTS_FD_WATCH_ENABLED" + value = "true" + }, { name = "EVENT_HUB_BASE_PATH" - value = "https://selc-p-eventhub-ns.servicebus.windows.net/sc-users" + value = "https://selc-p-eventhub-ns.servicebus.windows.net/" + }, + { + name = "EVENT_HUB_SC_USERS_TOPIC" + value = "sc-users" + }, + { + name = "EVENT_HUB_SELFCARE_FD_TOPIC" + value = "selfcare-fd" }, { name = "SHARED_ACCESS_KEY_NAME" value = "selfcare-wo" }, + { + name = "FD_SHARED_ACCESS_KEY_NAME" + value = "external-interceptor-wo" + }, { name = "USER_REGISTRY_URL" value = "https://api.pdv.pagopa.it/user-registry/v1" @@ -47,10 +63,11 @@ app_settings = [ ] secrets_names = { - "APPLICATIONINSIGHTS_CONNECTION_STRING" = "appinsights-connection-string" - "MONGODB-CONNECTION-STRING" = "mongodb-connection-string" - "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string" - "EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC" = "eventhub-sc-users-selfcare-wo-key-lc" - "USER-REGISTRY-API-KEY" = "user-registry-api-key" + "APPLICATIONINSIGHTS_CONNECTION_STRING" = "appinsights-connection-string" + "MONGODB-CONNECTION-STRING" = "mongodb-connection-string" + "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string" + "EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC" = "eventhub-sc-users-selfcare-wo-key-lc" + "USER-REGISTRY-API-KEY" = "user-registry-api-key" + "EVENTHUB-SELFCARE-FD-FD-KEY-LC" = "eventhub-selfcare-fd-fd-key-lc" } diff --git a/infra/container_apps/user-cdc/env/uat/terraform.tfvars b/infra/container_apps/user-cdc/env/uat/terraform.tfvars index 742c9734..46f9dfa0 100644 --- a/infra/container_apps/user-cdc/env/uat/terraform.tfvars +++ b/infra/container_apps/user-cdc/env/uat/terraform.tfvars @@ -32,14 +32,30 @@ app_settings = [ name = "USER_CDC_SEND_EVENTS_WATCH_ENABLED" value = "false" }, + { + name = "USER_CDC_SEND_EVENTS_FD_WATCH_ENABLED" + value = "true" + }, { name = "EVENT_HUB_BASE_PATH" - value = "https://selc-d-eventhub-ns.servicebus.windows.net/sc-users" + value = "https://selc-d-eventhub-ns.servicebus.windows.net/" + }, + { + name = "EVENT_HUB_SC_USERS_TOPIC" + value = "sc-users" + }, + { + name = "EVENT_HUB_SELFCARE_FD_TOPIC" + value = "selfcare-fd" }, { name = "SHARED_ACCESS_KEY_NAME" value = "selfcare-wo" }, + { + name = "FD_SHARED_ACCESS_KEY_NAME" + value = "external-interceptor-wo" + }, { name = "USER_REGISTRY_URL" value = "https://api.uat.pdv.pagopa.it/user-registry/v1" @@ -47,9 +63,10 @@ app_settings = [ ] secrets_names = { - "APPLICATIONINSIGHTS_CONNECTION_STRING" = "appinsights-connection-string" - "MONGODB-CONNECTION-STRING" = "mongodb-connection-string" - "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string" - "EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC" = "eventhub-sc-users-selfcare-wo-key-lc" - "USER-REGISTRY-API-KEY" = "user-registry-api-key" + "APPLICATIONINSIGHTS_CONNECTION_STRING" = "appinsights-connection-string" + "MONGODB-CONNECTION-STRING" = "mongodb-connection-string" + "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string" + "EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC" = "eventhub-sc-users-selfcare-wo-key-lc" + "USER-REGISTRY-API-KEY" = "user-registry-api-key" + "EVENTHUB-SELFCARE-FD-FD-KEY-LC" = "eventhub-selfcare-fd-fd-key-lc" } diff --git a/libs/user-sdk-event/pom.xml b/libs/user-sdk-event/pom.xml index 5b6f6c79..5da0fb32 100644 --- a/libs/user-sdk-event/pom.xml +++ b/libs/user-sdk-event/pom.xml @@ -64,6 +64,24 @@ 3.0.2 compile + + com.fasterxml.jackson.core + jackson-annotations + 2.17.1 + compile + + + com.fasterxml.jackson.core + jackson-databind + 2.17.1 + compile + + + io.quarkus + quarkus-bootstrap-gradle-resolver + 3.11.3 + compile + diff --git a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java index a3e16a63..98ae4e76 100644 --- a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java +++ b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java @@ -4,6 +4,12 @@ import it.pagopa.selfcare.user.model.TrackEventInput; import it.pagopa.selfcare.user.model.constants.OnboardedProductState; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.stream.Collectors; @@ -81,4 +87,49 @@ public static OnboardedProduct retrieveFdProductIfItChanged(List sendMessage(FdUserNotificationToSend notification); + +} 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 35de939f..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,7 +2,6 @@ import io.smallrye.mutiny.Uni; import it.pagopa.selfcare.user.auth.EventhubSasTokenAuthorization; -import it.pagopa.selfcare.user.model.FdUserNotificationToSend; import it.pagopa.selfcare.user.model.UserGroupNotificationToSend; import it.pagopa.selfcare.user.model.UserNotificationToSend; import jakarta.enterprise.context.ApplicationScoped; @@ -25,8 +24,4 @@ public interface EventHubRestClient { @Path("messages") Uni sendUserGroupMessage(UserGroupNotificationToSend notification); - @POST - @Path("messages") - Uni sendMessage(FdUserNotificationToSend notification); - } From f1ca3b0c6dfd2dcc28e2eced42b4807db6ac0b82 Mon Sep 17 00:00:00 2001 From: flaminiaScarciofolo Date: Wed, 30 Oct 2024 14:02:37 +0100 Subject: [PATCH 03/10] [SELC-5887] added test for user-sdk-event --- .../user/event/UserInstitutionCdcService.java | 67 ++++++++++--------- .../it/pagopa/selfcare/user/UserUtils.java | 39 +++++------ .../user/model/FdUserNotificationToSend.java | 1 + .../user/model/NotificationUserType.java | 1 + .../selfcare/user/TrackEventInputTest.java | 25 +++++-- .../pagopa/selfcare/user/UserUtilsTest.java | 36 ++++++++++ 6 files changed, 108 insertions(+), 61 deletions(-) diff --git a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java index dd9d1f84..12b05ee2 100644 --- a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java +++ b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java @@ -58,6 +58,7 @@ public class UserInstitutionCdcService { public static final String USERS_FIELD_LIST_WITHOUT_FISCAL_CODE = "name,familyName,email,workContacts"; private static final String PROD_FD = "prod-fd"; private static final String PROD_FD_GARANTITO = "prod-fd-garantito"; + public static final String ERROR_DURING_SUBSCRIBE_COLLECTION_EXCEPTION_MESSAGE = "Error during subscribe collection, exception: {} , message: {}"; private final TelemetryClient telemetryClient; @@ -140,26 +141,26 @@ private void initOrderStream(Boolean sendEventsEnabled, Boolean sendFdEventsEnab publisher.subscribe().with( this::consumerUserInstitutionRepositoryEvent, failure -> { - log.error("Error during subscribe collection, exception: {} , message: {}", failure.toString(), failure.getMessage()); + log.error(ERROR_DURING_SUBSCRIBE_COLLECTION_EXCEPTION_MESSAGE, failure.toString(), failure.getMessage()); telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(TrackEventInput.builder().exception(failure.getClass().toString()).build()), Map.of(USER_INFO_UPDATE_FAILURE, 1D)); Quarkus.asyncExit(); }); - if (sendEventsEnabled) { + if (Boolean.TRUE.equals(sendEventsEnabled)) { publisher.subscribe().with( this::consumerToSendScUserEvent, failure -> { - log.error("Error during subscribe collection, exception: {} , message: {}", failure.toString(), failure.getMessage()); + log.error(ERROR_DURING_SUBSCRIBE_COLLECTION_EXCEPTION_MESSAGE, failure.toString(), failure.getMessage()); telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(TrackEventInput.builder().exception(failure.getClass().toString()).build()), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D)); Quarkus.asyncExit(); }); } - if (sendFdEventsEnabled) { + if (Boolean.TRUE.equals(sendFdEventsEnabled)) { publisher.subscribe().with( this::consumerToSendUserEventForFD, failure -> { - log.error("Error during subscribe collection, exception: {} , message: {}", failure.toString(), failure.getMessage()); + log.error(ERROR_DURING_SUBSCRIBE_COLLECTION_EXCEPTION_MESSAGE, failure.toString(), failure.getMessage()); telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(TrackEventInput.builder().exception(failure.getClass().toString()).build()), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D)); Quarkus.asyncExit(); }); @@ -241,34 +242,34 @@ public void consumerToSendScUserEvent(ChangeStreamDocument docu public void consumerToSendUserEventForFD(ChangeStreamDocument document) { - assert document.getFullDocument() != null; - assert document.getDocumentKey() != null; - UserInstitution userInstitutionChanged = document.getFullDocument(); - - log.info("Starting consumerToSendUserEventForFd ... "); - - userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitutionChanged.getUserId()) - .onFailure(this::checkIfIsRetryableException) - .retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) - .onItem().transformToUni(userResource -> Uni.createFrom().item(UserUtils.retrieveFdProductIfItChanged(userInstitutionChanged.getProducts(), List.of(PROD_FD, PROD_FD_GARANTITO))) - .onItem().ifNotNull().transform(onboardedProduct -> notificationMapper.toFdUserNotificationToSend(userInstitutionChanged, onboardedProduct, userResource, evaluateType(onboardedProduct))) - .onItem().ifNotNull().transformToUni(fdUserNotificationToSend -> { - log.info("Sending message to EventHubFdRestClient ... "); - return eventHubFdRestClient.sendMessage(fdUserNotificationToSend) - .onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) - .onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS, 1D))) - .onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_FAILURE, 1D))); - } - )) - .subscribe().with( - result -> { - log.info("SendFdEvents successfully performed from UserInstitution document having id: {}", document.getDocumentKey().toJson()); - telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_SUCCESS, 1D)); - }, - failure -> { - log.error("Error during SendFdEvents from UserInstitution document having id: {} , message: {}", document.getDocumentKey().toJson(), failure.getMessage()); - telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D)); - }); + if (Objects.nonNull(document.getFullDocument()) && Objects.nonNull(document.getDocumentKey()) { + UserInstitution userInstitutionChanged = document.getFullDocument(); + + log.info("Starting consumerToSendUserEventForFd ... "); + + userRegistryApi.findByIdUsingGET(USERS_FIELD_LIST_WITHOUT_FISCAL_CODE, userInstitutionChanged.getUserId()) + .onFailure(this::checkIfIsRetryableException) + .retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) + .onItem().transformToUni(userResource -> Uni.createFrom().item(UserUtils.retrieveFdProductIfItChanged(userInstitutionChanged.getProducts(), List.of(PROD_FD, PROD_FD_GARANTITO))) + .onItem().ifNotNull().transform(onboardedProduct -> notificationMapper.toFdUserNotificationToSend(userInstitutionChanged, onboardedProduct, userResource, evaluateType(onboardedProduct))) + .onItem().ifNotNull().transformToUni(fdUserNotificationToSend -> { + log.info("Sending message to EventHubFdRestClient ... "); + return eventHubFdRestClient.sendMessage(fdUserNotificationToSend) + .onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) + .onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS, 1D))) + .onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_FAILURE, 1D))); + } + )) + .subscribe().with( + result -> { + log.info("SendFdEvents successfully performed from UserInstitution document having id: {}", document.getDocumentKey().toJson()); + telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_SUCCESS, 1D)); + }, + failure -> { + log.error("Error during SendFdEvents from UserInstitution document having id: {} , message: {}", document.getDocumentKey().toJson(), failure.getMessage()); + telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D)); + }); + } } private NotificationUserType evaluateType(OnboardedProduct onboardedProduct) { diff --git a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java index 98ae4e76..628d78df 100644 --- a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java +++ b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/UserUtils.java @@ -3,11 +3,13 @@ import it.pagopa.selfcare.user.model.OnboardedProduct; import it.pagopa.selfcare.user.model.TrackEventInput; import it.pagopa.selfcare.user.model.constants.OnboardedProductState; +import lombok.extern.slf4j.Slf4j; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.util.*; @@ -16,6 +18,7 @@ import static java.util.Comparator.naturalOrder; import static java.util.Comparator.nullsLast; +@Slf4j public class UserUtils { /** @@ -93,40 +96,28 @@ public static String getSASToken(String resourceUri, String keyName, String key) int week = 60 * 60 * 24 * 7; String expiry = Long.toString(epoch + week); - String sasToken = null; - try { - String stringToSign = URLEncoder.encode(resourceUri, "UTF-8") + "\n" + expiry; - String signature = getHMAC256(key, stringToSign); - sasToken = "SharedAccessSignature sr=" + URLEncoder.encode(resourceUri, "UTF-8") + "&sig=" + - URLEncoder.encode(signature, "UTF-8") + "&se=" + expiry + "&skn=" + keyName; - } catch (UnsupportedEncodingException e) { - - e.printStackTrace(); - } - + String sasToken; + String stringToSign = URLEncoder.encode(resourceUri, StandardCharsets.UTF_8) + "\n" + expiry; + String signature = getHMAC256(key, stringToSign); + sasToken = "SharedAccessSignature sr=" + URLEncoder.encode(resourceUri, StandardCharsets.UTF_8) + "&sig=" + + URLEncoder.encode(signature, StandardCharsets.UTF_8) + "&se=" + expiry + "&skn=" + keyName; return sasToken; } public static String getHMAC256(String key, String input) { - Mac sha256_HMAC; + Mac sha256HMAC; String hash = null; try { - sha256_HMAC = Mac.getInstance("HmacSHA256"); - SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256"); - sha256_HMAC.init(secret_key); + sha256HMAC = Mac.getInstance("HmacSHA256"); + SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(), "HmacSHA256"); + sha256HMAC.init(secretKey); Base64.Encoder encoder = Base64.getEncoder(); - hash = new String(encoder.encode(sha256_HMAC.doFinal(input.getBytes("UTF-8")))); + hash = new String(encoder.encode(sha256HMAC.doFinal(input.getBytes(StandardCharsets.UTF_8)))); - } catch (InvalidKeyException e) { - e.printStackTrace(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (IllegalStateException e) { - e.printStackTrace(); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); + } catch (InvalidKeyException | NoSuchAlgorithmException | IllegalStateException e) { + log.error("Exception: {}", e.getMessage(), e); } return hash; diff --git a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/FdUserNotificationToSend.java b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/FdUserNotificationToSend.java index 602494c9..f5e6aa5a 100644 --- a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/FdUserNotificationToSend.java +++ b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/FdUserNotificationToSend.java @@ -7,6 +7,7 @@ @JsonInclude(JsonInclude.Include.NON_NULL) @Data +@SuppressWarnings("java:S1068") public class FdUserNotificationToSend { private String id; diff --git a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/NotificationUserType.java b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/NotificationUserType.java index dcba2266..4e55b536 100644 --- a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/NotificationUserType.java +++ b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/NotificationUserType.java @@ -1,5 +1,6 @@ package it.pagopa.selfcare.user.model; +@SuppressWarnings("java:S1068") public enum NotificationUserType { ACTIVE_USER, 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 index 48970c6a..cd556aad 100644 --- 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 @@ -1,9 +1,6 @@ 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 it.pagopa.selfcare.user.model.*; import org.junit.jupiter.api.Test; import java.time.Instant; @@ -34,6 +31,26 @@ void toTrackEventInput_withUserNotification() { assertEquals("productRole", result.getProductRole()); } + @Test + void toTrackEventInput_withUserFdNotification() { + FdUserNotificationToSend userNotification = new FdUserNotificationToSend(); + userNotification.setId("docKey"); + UserToNotify user = new UserToNotify(); + user.setUserId("userId"); + user.setProductRole("productRole"); + userNotification.setUser(user); + userNotification.setInstitutionId("institutionId"); + userNotification.setProduct("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(); diff --git a/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/UserUtilsTest.java b/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/UserUtilsTest.java index b6647ea9..343d3f62 100644 --- a/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/UserUtilsTest.java +++ b/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/UserUtilsTest.java @@ -88,4 +88,40 @@ OnboardedProduct dummyOnboardedProduct(String productRole, OnboardedProductState onboardedProduct.setStatus(state); return onboardedProduct; } + + @Test + void getSASToken_withValidInputs_shouldReturnValidToken() { + String resourceUri = "https://example.com/resource"; + String keyName = "keyName"; + String key = "secretKey"; + + String sasToken = UserUtils.getSASToken(resourceUri, keyName, key); + + assertNotNull(sasToken); + assertTrue(sasToken.contains("SharedAccessSignature sr=")); + assertTrue(sasToken.contains("&sig=")); + assertTrue(sasToken.contains("&se=")); + assertTrue(sasToken.contains("&skn=")); + } + + @Test + void getSASToken_withInvalidEncoding_shouldHandleException() { + String resourceUri = "https://example.com/resource"; + String keyName = "keyName"; + String key = "secretKey"; + + String sasToken = UserUtils.getSASToken(resourceUri, keyName, key); + + assertNotNull(sasToken); + } + + @Test + void getHMAC256_withValidInputs_shouldReturnValidHash() { + String key = "secretKey"; + String input = "inputString"; + + String hash = UserUtils.getHMAC256(key, input); + + assertNotNull(hash); + } } From b02715ac3d59a935867eb8786c58f3165c38d97e Mon Sep 17 00:00:00 2001 From: flaminiaScarciofolo Date: Wed, 30 Oct 2024 14:20:49 +0100 Subject: [PATCH 04/10] [SELC-5887] added tests in UserUtilsTest --- .../pagopa/selfcare/user/UserUtilsTest.java | 83 ++++++++++++++++++- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/UserUtilsTest.java b/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/UserUtilsTest.java index 343d3f62..f8cfe5f0 100644 --- a/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/UserUtilsTest.java +++ b/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/UserUtilsTest.java @@ -9,10 +9,7 @@ import java.time.LocalTime; import java.time.OffsetDateTime; import java.time.ZoneOffset; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; import static org.junit.jupiter.api.Assertions.*; @@ -124,4 +121,82 @@ void getHMAC256_withValidInputs_shouldReturnValidHash() { assertNotNull(hash); } + + @Test + void retrieveFdProductIfItChanged_withValidProducts_shouldReturnMostRecentlyUpdatedProduct() { + List products = new ArrayList<>(); + OnboardedProduct product1 = new OnboardedProduct(); + product1.setProductId("1"); + product1.setUpdatedAt(OffsetDateTime.now().minusDays(1)); + product1.setCreatedAt(OffsetDateTime.now().minusDays(2)); + products.add(product1); + + OnboardedProduct product2 = new OnboardedProduct(); + product2.setProductId("2"); + product2.setUpdatedAt(OffsetDateTime.now()); + product2.setCreatedAt(OffsetDateTime.now().minusDays(1)); + products.add(product2); + + List productIdToCheck = List.of("1", "2"); + + OnboardedProduct result = UserUtils.retrieveFdProductIfItChanged(products, productIdToCheck); + + assertNotNull(result); + assertEquals("2", result.getProductId()); + } + + @Test + void retrieveFdProductIfItChanged_withEmptyProductList() { + List products = Collections.emptyList(); + List productIdToCheck = List.of("1", "2"); + + OnboardedProduct result = UserUtils.retrieveFdProductIfItChanged(products, productIdToCheck); + + assertNull(result); + } + + @Test + void retrieveFdProductIfItChanged_withNoFdProductIds() { + List products = new ArrayList<>(); + OnboardedProduct product1 = new OnboardedProduct(); + product1.setProductId("1"); + product1.setUpdatedAt(OffsetDateTime.now().minusDays(1)); + product1.setCreatedAt(OffsetDateTime.now().minusDays(2)); + products.add(product1); + + OnboardedProduct product2 = new OnboardedProduct(); + product2.setProductId("2"); + product2.setUpdatedAt(OffsetDateTime.now()); + product2.setCreatedAt(OffsetDateTime.now().minusDays(1)); + products.add(product2); + + List productIdToCheck = List.of("3", "4"); + + OnboardedProduct result = UserUtils.retrieveFdProductIfItChanged(products, productIdToCheck); + + assertNull(result); + } + + @Test + void retrieveFdProductIfItChanged_withProductsWithSameUpdatedAt() { + List products = new ArrayList<>(); + OnboardedProduct product1 = new OnboardedProduct(); + product1.setProductId("1"); + product1.setUpdatedAt(OffsetDateTime.now()); + product1.setCreatedAt(OffsetDateTime.now().minusDays(2)); + products.add(product1); + + OnboardedProduct product2 = new OnboardedProduct(); + product2.setProductId("2"); + product2.setUpdatedAt(OffsetDateTime.now()); + product2.setCreatedAt(OffsetDateTime.now().minusDays(1)); + products.add(product2); + + List productIdToCheck = List.of("1", "2"); + + OnboardedProduct result = UserUtils.retrieveFdProductIfItChanged(products, productIdToCheck); + + assertNotNull(result); + assertEquals("2", result.getProductId()); + } } From d034bc165f4459fc56c679e730ea933120f81173 Mon Sep 17 00:00:00 2001 From: flaminiaScarciofolo Date: Wed, 30 Oct 2024 14:34:35 +0100 Subject: [PATCH 05/10] [SELC-5887] refactor for change requests --- .../user/event/UserInstitutionCdcService.java | 11 +- .../user/FdUserNotificationToSendTest.java | 110 ++++++++++++++++++ .../constants/EventsMetric.java | 5 + .../constants/EventsName.java | 2 + 4 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/FdUserNotificationToSendTest.java diff --git a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java index 12b05ee2..c18cc34e 100644 --- a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java +++ b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/UserInstitutionCdcService.java @@ -46,6 +46,7 @@ import static it.pagopa.selfcare.user.model.TrackEventInput.toTrackEventInput; 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.FD_EVENT_USER_CDC_NAME; import static java.util.Arrays.asList; @Startup @@ -242,7 +243,7 @@ public void consumerToSendScUserEvent(ChangeStreamDocument docu public void consumerToSendUserEventForFD(ChangeStreamDocument document) { - if (Objects.nonNull(document.getFullDocument()) && Objects.nonNull(document.getDocumentKey()) { + if (Objects.nonNull(document.getFullDocument()) && Objects.nonNull(document.getDocumentKey())) { UserInstitution userInstitutionChanged = document.getFullDocument(); log.info("Starting consumerToSendUserEventForFd ... "); @@ -256,18 +257,18 @@ public void consumerToSendUserEventForFD(ChangeStreamDocument d log.info("Sending message to EventHubFdRestClient ... "); return eventHubFdRestClient.sendMessage(fdUserNotificationToSend) .onFailure().retry().withBackOff(Duration.ofSeconds(retryMinBackOff), Duration.ofSeconds(retryMaxBackOff)).atMost(maxRetry) - .onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS, 1D))) - .onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(EVENTS_USER_INSTITUTION_PRODUCT_FAILURE, 1D))); + .onItem().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(FD_EVENTS_USER_INSTITUTION_PRODUCT_SUCCESS, 1D))) + .onFailure().invoke(() -> telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInput(fdUserNotificationToSend)), Map.of(FD_EVENTS_USER_INSTITUTION_PRODUCT_FAILURE, 1D))); } )) .subscribe().with( result -> { log.info("SendFdEvents successfully performed from UserInstitution document having id: {}", document.getDocumentKey().toJson()); - telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_SUCCESS, 1D)); + telemetryClient.trackEvent(FD_EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(FD_EVENTS_USER_INSTITUTION_SUCCESS, 1D)); }, failure -> { log.error("Error during SendFdEvents from UserInstitution document having id: {} , message: {}", document.getDocumentKey().toJson(), failure.getMessage()); - telemetryClient.trackEvent(EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(EVENTS_USER_INSTITUTION_FAILURE, 1D)); + telemetryClient.trackEvent(FD_EVENT_USER_CDC_NAME, mapPropsForTrackEvent(toTrackEventInputByUserInstitution(userInstitutionChanged)), Map.of(FD_EVENTS_USER_INSTITUTION_FAILURE, 1D)); }); } } diff --git a/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/FdUserNotificationToSendTest.java b/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/FdUserNotificationToSendTest.java new file mode 100644 index 00000000..2937e266 --- /dev/null +++ b/libs/user-sdk-event/src/test/java/it/pagopa/selfcare/user/FdUserNotificationToSendTest.java @@ -0,0 +1,110 @@ +package it.pagopa.selfcare.user; + +import it.pagopa.selfcare.user.model.FdUserNotificationToSend; +import it.pagopa.selfcare.user.model.NotificationUserType; +import it.pagopa.selfcare.user.model.UserToNotify; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import java.time.OffsetDateTime; + +class FdUserNotificationToSendTest { + + @Test + void fdUserNotificationToSend_ACTIVE() { + FdUserNotificationToSend notification = new FdUserNotificationToSend(); + notification.setId("123"); + notification.setInstitutionId("inst-456"); + notification.setProduct("product-789"); + notification.setCreatedAt(OffsetDateTime.now().minusDays(1)); + notification.setUpdatedAt(OffsetDateTime.now()); + notification.setOnboardingTokenId("token-101112"); + notification.setType(NotificationUserType.ACTIVE_USER); + UserToNotify user = new UserToNotify(); + notification.setUser(user); + + assertEquals("123", notification.getId()); + assertEquals("inst-456", notification.getInstitutionId()); + assertEquals("product-789", notification.getProduct()); + assertNotNull(notification.getCreatedAt()); + assertNotNull(notification.getUpdatedAt()); + assertEquals("token-101112", notification.getOnboardingTokenId()); + assertEquals(NotificationUserType.ACTIVE_USER, notification.getType()); + assertEquals(user, notification.getUser()); + } + + @Test + void fdUserNotificationToSend_DELETE() { + FdUserNotificationToSend notification = new FdUserNotificationToSend(); + notification.setId("123"); + notification.setInstitutionId("inst-456"); + notification.setProduct("product-789"); + notification.setCreatedAt(OffsetDateTime.now().minusDays(1)); + notification.setUpdatedAt(OffsetDateTime.now()); + notification.setOnboardingTokenId("token-101112"); + notification.setType(NotificationUserType.DELETE_USER); + UserToNotify user = new UserToNotify(); + notification.setUser(user); + + assertEquals("123", notification.getId()); + assertEquals("inst-456", notification.getInstitutionId()); + assertEquals("product-789", notification.getProduct()); + assertNotNull(notification.getCreatedAt()); + assertNotNull(notification.getUpdatedAt()); + assertEquals("token-101112", notification.getOnboardingTokenId()); + assertEquals(NotificationUserType.DELETE_USER, notification.getType()); + assertEquals(user, notification.getUser()); + } + + @Test + void fdUserNotificationToSend_SUSPEND() { + FdUserNotificationToSend notification = new FdUserNotificationToSend(); + notification.setId("123"); + notification.setInstitutionId("inst-456"); + notification.setProduct("product-789"); + notification.setCreatedAt(OffsetDateTime.now().minusDays(1)); + notification.setUpdatedAt(OffsetDateTime.now()); + notification.setOnboardingTokenId("token-101112"); + notification.setType(NotificationUserType.SUSPEND_USER); + UserToNotify user = new UserToNotify(); + notification.setUser(user); + + assertEquals("123", notification.getId()); + assertEquals("inst-456", notification.getInstitutionId()); + assertEquals("product-789", notification.getProduct()); + assertNotNull(notification.getCreatedAt()); + assertNotNull(notification.getUpdatedAt()); + assertEquals("token-101112", notification.getOnboardingTokenId()); + assertEquals(NotificationUserType.SUSPEND_USER, notification.getType()); + assertEquals(user, notification.getUser()); + } + + @Test + void fdUserNotificationToSend_withNullFields_shouldHandleNullValues() { + FdUserNotificationToSend notification = new FdUserNotificationToSend(); + + assertNull(notification.getId()); + assertNull(notification.getInstitutionId()); + assertNull(notification.getProduct()); + assertNull(notification.getCreatedAt()); + assertNull(notification.getUpdatedAt()); + assertNull(notification.getOnboardingTokenId()); + assertNull(notification.getType()); + assertNull(notification.getUser()); + } + + @Test + void fdUserNotificationToSend_withPartialData_shouldSetFieldsCorrectly() { + FdUserNotificationToSend notification = new FdUserNotificationToSend(); + notification.setId("123"); + notification.setProduct("product-789"); + + assertEquals("123", notification.getId()); + assertNull(notification.getInstitutionId()); + assertEquals("product-789", notification.getProduct()); + assertNull(notification.getCreatedAt()); + assertNull(notification.getUpdatedAt()); + assertNull(notification.getOnboardingTokenId()); + assertNull(notification.getType()); + assertNull(notification.getUser()); + } +} 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 01194e55..913ba15a 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 FD_EVENTS_USER_INSTITUTION_FAILURE = "EventsUserInstitution_failures"; + public static final String FD_EVENTS_USER_INSTITUTION_SUCCESS = "EventsUserInstitution_success"; + public static final String FD_EVENTS_USER_INSTITUTION_PRODUCT_FAILURE = "EventsUserInstitutionProduct_failures"; + public static final String FD_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"; 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 37a0a53f..820ae2aa 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,5 +4,7 @@ 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 FD_EVENT_USER_CDC_NAME = "FD_USER_CDC"; public static final String EVENT_USER_GROUP_CDC_NAME = "USER_GROUP_CDC"; } From 4a05115dbc79cad168516e5786975129c7a1f13a Mon Sep 17 00:00:00 2001 From: flaminiaScarciofolo Date: Wed, 30 Oct 2024 16:05:56 +0100 Subject: [PATCH 06/10] [SELC-5887] updated secret name for selfcare fd eventhub key --- infra/container_apps/user-cdc/env/dev/terraform.tfvars | 2 +- infra/container_apps/user-cdc/env/prod/terraform.tfvars | 2 +- infra/container_apps/user-cdc/env/uat/terraform.tfvars | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/infra/container_apps/user-cdc/env/dev/terraform.tfvars b/infra/container_apps/user-cdc/env/dev/terraform.tfvars index 61f06383..909d329e 100644 --- a/infra/container_apps/user-cdc/env/dev/terraform.tfvars +++ b/infra/container_apps/user-cdc/env/dev/terraform.tfvars @@ -81,6 +81,6 @@ secrets_names = { "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string" "EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC" = "eventhub-sc-users-selfcare-wo-key-lc" "USER-REGISTRY-API-KEY" = "user-registry-api-key" - "EVENTHUB-SELFCARE-FD-FD-KEY-LC" = "eventhub-selfcare-fd-fd-key-lc" + "EVENTHUB_SELFCARE_FD_EXTERNAL_KEY_LC" = "eventhub-selfcare-fd-external-interceptor-wo-key-lc" } diff --git a/infra/container_apps/user-cdc/env/prod/terraform.tfvars b/infra/container_apps/user-cdc/env/prod/terraform.tfvars index fa9244be..c04cf280 100644 --- a/infra/container_apps/user-cdc/env/prod/terraform.tfvars +++ b/infra/container_apps/user-cdc/env/prod/terraform.tfvars @@ -68,6 +68,6 @@ secrets_names = { "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string" "EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC" = "eventhub-sc-users-selfcare-wo-key-lc" "USER-REGISTRY-API-KEY" = "user-registry-api-key" - "EVENTHUB-SELFCARE-FD-FD-KEY-LC" = "eventhub-selfcare-fd-fd-key-lc" + "EVENTHUB_SELFCARE_FD_EXTERNAL_KEY_LC" = "eventhub-selfcare-fd-external-interceptor-wo-key-lc" } diff --git a/infra/container_apps/user-cdc/env/uat/terraform.tfvars b/infra/container_apps/user-cdc/env/uat/terraform.tfvars index 46f9dfa0..d4b3c5d5 100644 --- a/infra/container_apps/user-cdc/env/uat/terraform.tfvars +++ b/infra/container_apps/user-cdc/env/uat/terraform.tfvars @@ -68,5 +68,5 @@ secrets_names = { "STORAGE_CONNECTION_STRING" = "blob-storage-product-connection-string" "EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC" = "eventhub-sc-users-selfcare-wo-key-lc" "USER-REGISTRY-API-KEY" = "user-registry-api-key" - "EVENTHUB-SELFCARE-FD-FD-KEY-LC" = "eventhub-selfcare-fd-fd-key-lc" + "EVENTHUB_SELFCARE_FD_EXTERNAL_KEY_LC" = "eventhub-selfcare-fd-external-interceptor-wo-key-lc" } From ddfd59308ae95e5f0f1b458e65f6bd350e863ad5 Mon Sep 17 00:00:00 2001 From: flaminiaScarciofolo Date: Wed, 30 Oct 2024 16:19:51 +0100 Subject: [PATCH 07/10] [SELC-5887] fix properties --- apps/user-cdc/src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/user-cdc/src/main/resources/application.properties b/apps/user-cdc/src/main/resources/application.properties index afaec3d3..28cc9ceb 100644 --- a/apps/user-cdc/src/main/resources/application.properties +++ b/apps/user-cdc/src/main/resources/application.properties @@ -30,4 +30,4 @@ eventhub.rest-client.key=${EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC:test} quarkus.rest-client.event-hub-fd.url=${EVENT_HUB_BASE_PATH:test}${EVENT_HUB_SELFCARE_FD_TOPIC:selfcare-fd} eventhubfd.rest-client.keyName=${FD_SHARED_ACCESS_KEY_NAME:test} -eventhubfd.rest-client.key=${EVENTHUB-SELFCARE-FD-FD-KEY-LC:test} \ No newline at end of file +eventhubfd.rest-client.key=${EVENTHUB_SELFCARE_FD_EXTERNAL_KEY_LC:test} \ No newline at end of file From ff3894f49247b3f1a90fd3865a18ef4becde4b50 Mon Sep 17 00:00:00 2001 From: flaminiaScarciofolo Date: Wed, 30 Oct 2024 17:09:04 +0100 Subject: [PATCH 08/10] [SELC-5887] fix user data for fd events --- .../user/event/mapper/NotificationMapper.java | 15 +++++++++++---- .../event/mapper/NotificationMapperTest.java | 17 +++++++++++++++++ .../selfcare/user/model/UserToNotify.java | 5 +++++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java index 5d3366fb..88632682 100644 --- a/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java +++ b/apps/user-cdc/src/main/java/it/pagopa/selfcare/user/event/mapper/NotificationMapper.java @@ -1,5 +1,6 @@ package it.pagopa.selfcare.user.event.mapper; +import com.microsoft.applicationinsights.web.dependencies.apachecommons.lang3.StringUtils; import it.pagopa.selfcare.user.UserUtils; import it.pagopa.selfcare.user.event.entity.UserInstitution; import it.pagopa.selfcare.user.model.*; @@ -10,9 +11,7 @@ import org.openapi.quarkus.user_registry_json.model.UserResource; import javax.swing.text.html.Option; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; +import java.util.*; @Mapper(componentModel = "cdi", imports = UUID.class) public interface NotificationMapper { @@ -36,10 +35,18 @@ default String toUniqueIdNotification(UserInstitution userInstitution, Onboarded @Mapping(target = "product", source = "product.productId") @Mapping(target = "createdAt", source = "product.createdAt") @Mapping(target = "updatedAt", expression = "java((null == product.getUpdatedAt()) ? product.getCreatedAt() : product.getUpdatedAt())") - @Mapping(target = "user", expression = "java(mapUser(userResource, userInstitutionChanged.getUserMailUuid(), product))") + @Mapping(target = "user", expression = "java(mapUserForFD(userResource, product))") @Mapping(target = "type", source = "type") FdUserNotificationToSend toFdUserNotificationToSend(UserInstitution userInstitutionChanged, OnboardedProduct product, UserResource userResource, NotificationUserType type); + @Named("mapUserForFD") + default UserToNotify mapUserForFD(UserResource userResource,OnboardedProduct onboardedProduct) { + UserToNotify userToNotify = new UserToNotify(); + userToNotify.setUserId(Optional.ofNullable(userResource.getId()).map(UUID::toString).orElse(null)); + userToNotify.setRoles(StringUtils.isNotBlank(onboardedProduct.getProductRole()) ? List.of(onboardedProduct.getProductRole()) : Collections.emptyList()); + userToNotify.setRole(Optional.ofNullable(onboardedProduct.getRole()).map(Enum::name).orElse(null)); + return userToNotify; + } @Named("mapUser") default UserToNotify mapUser(UserResource userResource, String userMailUuid, OnboardedProduct onboardedProduct) { diff --git a/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/mapper/NotificationMapperTest.java b/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/mapper/NotificationMapperTest.java index fc99117e..f6568b88 100644 --- a/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/mapper/NotificationMapperTest.java +++ b/apps/user-cdc/src/test/java/it/pagopa/selfcare/user/event/mapper/NotificationMapperTest.java @@ -10,6 +10,8 @@ import org.openapi.quarkus.user_registry_json.model.WorkContactResource; import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; import java.util.Map; import java.util.UUID; @@ -38,6 +40,21 @@ void mapUser_withValidData_shouldMapFieldsCorrectly() { assertEquals(OnboardedProductState.ACTIVE, result.getRelationshipStatus()); } + @Test + void mapUserForFdTest() { + UserResource userResource = new UserResource(); + userResource.setId(UUID.randomUUID()); + OnboardedProduct onboardedProduct = new OnboardedProduct(); + onboardedProduct.setProductRole("Admin"); + onboardedProduct.setRole(PartyRole.MANAGER); + + UserToNotify result = new NotificationMapperImpl().mapUserForFD(userResource, onboardedProduct); + + assertEquals(userResource.getId().toString(), result.getUserId()); + assertEquals(List.of("Admin"), result.getRoles()); + assertEquals("MANAGER", result.getRole()); + } + @Test void mapUser_withNullFields_shouldHandleNullValues() { UserResource userResource = new UserResource(); diff --git a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/UserToNotify.java b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/UserToNotify.java index 8030707f..6510cf6d 100644 --- a/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/UserToNotify.java +++ b/libs/user-sdk-event/src/main/java/it/pagopa/selfcare/user/model/UserToNotify.java @@ -1,14 +1,18 @@ package it.pagopa.selfcare.user.model; +import com.fasterxml.jackson.annotation.JsonInclude; import it.pagopa.selfcare.user.model.constants.OnboardedProductState; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.eclipse.microprofile.openapi.annotations.media.Schema; +import java.util.List; + @Data @AllArgsConstructor @NoArgsConstructor +@JsonInclude(JsonInclude.Include.NON_NULL) public class UserToNotify { private String userId; @@ -18,6 +22,7 @@ public class UserToNotify { @Schema(description = "Available values: MANAGER, DELEGATE, SUB_DELEGATE, OPERATOR, ADMIN_EA") private String role; private String productRole; + private List roles; private OnboardedProductState relationshipStatus; } From 62dc8d6baf4130aee9ae408adc0313e0e1087b3b Mon Sep 17 00:00:00 2001 From: flaminiaScarciofolo Date: Thu, 31 Oct 2024 11:14:59 +0100 Subject: [PATCH 09/10] [SELC-5887] fix pipeline name --- .github/workflows/create_release_branch.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/create_release_branch.yml b/.github/workflows/create_release_branch.yml index a5e45d99..5bba0729 100644 --- a/.github/workflows/create_release_branch.yml +++ b/.github/workflows/create_release_branch.yml @@ -42,9 +42,9 @@ jobs: gh workflow run release_pnpg_ms.yml \ --ref ${{ steps.create_branch.outputs.new_branch_name }} - - name: Trigger release cdc UAT Release + - name: Trigger release user cdc UAT Release run: | - gh workflow run release_cdc.yml \ + gh workflow run release_user_cdc.yml \ --ref ${{ steps.create_branch.outputs.new_branch_name }} - name: Trigger PNPG release cdc UAT Release @@ -61,4 +61,9 @@ jobs: run: | gh workflow run release_pnpg_user_group_ms.yml \ --ref ${{ steps.create_branch.outputs.new_branch_name }} + + - name: Trigger release user-group-cdc UAT Release + run: | + gh workflow run release_user_group_cdc.yml \ + --ref ${{ steps.create_branch.outputs.new_branch_name }} From 42783a3c87fcf6a39830145aa58ae81ec55dd991 Mon Sep 17 00:00:00 2001 From: flaminiaScarciofolo Date: Thu, 31 Oct 2024 15:08:40 +0100 Subject: [PATCH 10/10] [SELC-5887] added properties for eventHub --- apps/user-ms/src/main/resources/application.properties | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/user-ms/src/main/resources/application.properties b/apps/user-ms/src/main/resources/application.properties index a6faba35..72277c96 100644 --- a/apps/user-ms/src/main/resources/application.properties +++ b/apps/user-ms/src/main/resources/application.properties @@ -17,13 +17,19 @@ quarkus.smallrye-openapi.info-title=User API quarkus.smallrye-openapi.info-version=1.0.0 ## Eventhub ## -quarkus.rest-client.event-hub.url=${EVENT_HUB_BASE_PATH:test} +quarkus.rest-client.event-hub.url=${EVENT_HUB_BASE_PATH:test}${EVENT_HUB_SC_USERS_TOPIC:sc-users} eventhub.rest-client.keyName=${SHARED_ACCESS_KEY_NAME:test} eventhub.rest-client.key=${EVENTHUB-SC-USERS-SELFCARE-WO-KEY-LC:test} + +quarkus.rest-client.event-hub-fd.url=${EVENT_HUB_BASE_PATH:test}${EVENT_HUB_SELFCARE_FD_TOPIC:selfcare-fd} +eventhubfd.rest-client.keyName=${FD_SHARED_ACCESS_KEY_NAME:test} +eventhubfd.rest-client.key=${EVENTHUB_SELFCARE_FD_EXTERNAL_KEY_LC:test} + user-ms.eventhub.users.enabled=${USER_MS_EVENTHUB_USERS_ENABLED:false} user-ms.eventhub.users.concurrency-level=${USER_MS_EVENTHUB_USERS_CONCURRENCY_LEVEL:1} user-ms.eventhub.users.page-size=${USER_MS_EVENTHUB_USERS_PAGE_SIZE:50} + quarkus.log.level=INFO quarkus.http.limits.max-form-attribute-size=4096