From e19524fd797b53425b2516936c5e1622012b5fe3 Mon Sep 17 00:00:00 2001 From: freddidierRTE Date: Wed, 12 Mar 2025 18:01:13 +0100 Subject: [PATCH] Refacto : Extract ack an read logic in a new backend service Signed-off-by: freddidierRTE --- .../publication/configuration/Services.java | 11 ++- .../controllers/CardController.java | 13 ++- .../services/CardProcessingService.java | 59 ----------- .../services/CardReadAndAckService.java | 97 +++++++++++++++++++ .../services/CardProcessServiceShould.java | 16 +-- .../services/CardReadAndAckServiceShould.java | 57 +++++++++++ 6 files changed, 177 insertions(+), 76 deletions(-) create mode 100644 services/cards-publication/src/main/java/org/opfab/cards/publication/services/CardReadAndAckService.java create mode 100644 services/cards-publication/src/test/java/org/opfab/cards/publication/services/CardReadAndAckServiceShould.java diff --git a/services/cards-publication/src/main/java/org/opfab/cards/publication/configuration/Services.java b/services/cards-publication/src/main/java/org/opfab/cards/publication/configuration/Services.java index 633d5bdd0b..a304cdb91a 100644 --- a/services/cards-publication/src/main/java/org/opfab/cards/publication/configuration/Services.java +++ b/services/cards-publication/src/main/java/org/opfab/cards/publication/configuration/Services.java @@ -19,6 +19,7 @@ import org.opfab.cards.publication.services.CardDeletionService; import org.opfab.cards.publication.services.CardNotificationService; import org.opfab.cards.publication.services.CardProcessingService; +import org.opfab.cards.publication.services.CardReadAndAckService; import org.opfab.cards.publication.services.CardTranslationService; import org.opfab.cards.publication.services.CardValidationService; import org.opfab.cards.publication.services.ExternalAppService; @@ -38,6 +39,8 @@ public class Services { private final CardTranslationService cardTranslationService; + private final CardReadAndAckService cardReadAndAckService; + private final UserActionLogService userActionLogService; private final CardValidationService cardValidationService; @@ -65,7 +68,8 @@ public class Services { this.cardTranslationService = new CardTranslationService(i18nRepository.get()); } this.userActionLogService = userActionLogService; - CardNotificationService cardNotificationService = new CardNotificationService(eventBus, objectMapper, customScreenDataFields); + CardNotificationService cardNotificationService = new CardNotificationService(eventBus, objectMapper, + customScreenDataFields); cardValidationService = new CardValidationService(cardRepository, new ProcessRepositoryImpl(businessconfigUrl, eventBus)); cardDeletionService = new CardDeletionService(cardNotificationService, cardRepository, externalAppService, @@ -76,6 +80,7 @@ public class Services { checkPerimeterForCardSending, authorizeToSendCardWithInvalidProcessState, cardSendingLimitCardCount, cardSendingLimitPeriod, activateCardSendingLimiter); + cardReadAndAckService = new CardReadAndAckService(cardNotificationService, cardRepository); } @@ -91,6 +96,10 @@ public CardProcessingService getCardProcessingService() { return cardProcessingService; } + public CardReadAndAckService getCardReadAndAckService() { + return cardReadAndAckService; + } + public CardTranslationService getCardTranslationService() { return cardTranslationService; } diff --git a/services/cards-publication/src/main/java/org/opfab/cards/publication/controllers/CardController.java b/services/cards-publication/src/main/java/org/opfab/cards/publication/controllers/CardController.java index b8d16c9395..3a353d35a3 100644 --- a/services/cards-publication/src/main/java/org/opfab/cards/publication/controllers/CardController.java +++ b/services/cards-publication/src/main/java/org/opfab/cards/publication/controllers/CardController.java @@ -19,6 +19,7 @@ import org.opfab.cards.publication.repositories.UserBasedOperationResult; import org.opfab.cards.publication.services.CardDeletionService; import org.opfab.cards.publication.services.CardProcessingService; +import org.opfab.cards.publication.services.CardReadAndAckService; import org.opfab.cards.publication.services.CardTranslationService; import org.opfab.springtools.configuration.oauth.OpFabJwtAuthenticationToken; import org.opfab.users.model.CurrentUserWithPerimeters; @@ -44,6 +45,7 @@ public class CardController { private CardDeletionService cardDeletionService; private CardProcessingService cardProcessingService; private CardTranslationService cardTranslationService; + private CardReadAndAckService cardReadAndAckService; private UserActionLogService userActionLogService; private @Value("${operatorfabric.userActionLogActivated:true}") boolean userActionLogActivated; @@ -54,6 +56,7 @@ public class CardController { cardTranslationService = services.getCardTranslationService(); userActionLogService = services.getUserActionLogService(); cardProcessingService = services.getCardProcessingService(); + cardReadAndAckService = services.getCardReadAndAckService(); } @@ -174,7 +177,7 @@ public Void postUserAcknowledgement(Principal principal, OpFabJwtAuthenticationToken jwtPrincipal = (OpFabJwtAuthenticationToken) principal; CurrentUserWithPerimeters user = (CurrentUserWithPerimeters) jwtPrincipal.getPrincipal(); - UserBasedOperationResult result = cardProcessingService.processUserAcknowledgement(cardUid, user, entitiesAcks); + UserBasedOperationResult result = cardReadAndAckService.processUserAcknowledgement(cardUid, user, entitiesAcks); if (!result.isCardFound()) response.setStatus(404); @@ -202,7 +205,7 @@ public Void postUserCardRead(Principal principal, OpFabJwtAuthenticationToken jwtPrincipal = (OpFabJwtAuthenticationToken) principal; CurrentUserWithPerimeters user = (CurrentUserWithPerimeters) jwtPrincipal.getPrincipal(); - UserBasedOperationResult result = cardProcessingService.processUserRead(cardUid, user.getUserData().getLogin()); + UserBasedOperationResult result = cardReadAndAckService.processUserRead(cardUid, user.getUserData().getLogin()); if (!result.isCardFound()) response.setStatus(404); else { @@ -228,7 +231,7 @@ public Void deleteUserAcknowledgement(Principal principal, @PathVariable("cardUi HttpServletResponse response) { OpFabJwtAuthenticationToken jwtPrincipal = (OpFabJwtAuthenticationToken) principal; CurrentUserWithPerimeters user = (CurrentUserWithPerimeters) jwtPrincipal.getPrincipal(); - UserBasedOperationResult result = cardProcessingService.deleteUserAcknowledgement(cardUid, user, entitiesAcks); + UserBasedOperationResult result = cardReadAndAckService.deleteUserAcknowledgement(cardUid, user, entitiesAcks); if (!result.isCardFound()) response.setStatus(404); else { @@ -253,7 +256,7 @@ public Void deleteUserRead(Principal principal, @PathVariable("cardUid") String HttpServletResponse response) { OpFabJwtAuthenticationToken jwtPrincipal = (OpFabJwtAuthenticationToken) principal; CurrentUserWithPerimeters user = (CurrentUserWithPerimeters) jwtPrincipal.getPrincipal(); - UserBasedOperationResult result = cardProcessingService.deleteUserRead(cardUid, user.getUserData().getLogin()); + UserBasedOperationResult result = cardReadAndAckService.deleteUserRead(cardUid, user.getUserData().getLogin()); if (!result.isCardFound()) response.setStatus(404); else { @@ -303,7 +306,7 @@ public TranslatedField translateCardField(HttpServletRequest request, HttpServle public Void postResetReadAndAcks(Principal principal, @PathVariable("cardUid") String cardUid, HttpServletResponse response) { - UserBasedOperationResult result = cardProcessingService.resetReadAndAcks(cardUid); + UserBasedOperationResult result = cardReadAndAckService.resetReadAndAcks(cardUid); if (!result.isCardFound()) response.setStatus(404); else { diff --git a/services/cards-publication/src/main/java/org/opfab/cards/publication/services/CardProcessingService.java b/services/cards-publication/src/main/java/org/opfab/cards/publication/services/CardProcessingService.java index bd6da03d36..2f831116a3 100644 --- a/services/cards-publication/src/main/java/org/opfab/cards/publication/services/CardProcessingService.java +++ b/services/cards-publication/src/main/java/org/opfab/cards/publication/services/CardProcessingService.java @@ -12,7 +12,6 @@ import org.opfab.cards.publication.model.*; import org.opfab.cards.publication.ratelimiter.CardSendingLimiter; import org.opfab.cards.publication.repositories.CardRepository; -import org.opfab.cards.publication.repositories.UserBasedOperationResult; import org.opfab.springtools.error.model.ApiError; import org.opfab.springtools.error.model.ApiErrorException; import org.opfab.users.model.CurrentUserWithPerimeters; @@ -270,64 +269,6 @@ private String buildPublisherErrorMessage(Card card, String login) { return errorMessagePrefix + ", the card cannot be sent"; } - public UserBasedOperationResult processUserAcknowledgement(String cardUid, CurrentUserWithPerimeters user, - List entitiesAcks) { - if (cardPermissionControlService.isCurrentUserReadOnly(user) && entitiesAcks != null && !entitiesAcks.isEmpty()) - throw new ApiErrorException( - new ApiError(HttpStatus.FORBIDDEN, "Acknowledgement impossible : User has READONLY opfab role")); - - if (!user.getUserData().getEntities().containsAll(entitiesAcks)) - throw new ApiErrorException( - new ApiError(HttpStatus.FORBIDDEN, - "Acknowledgement impossible : User is not member of all the entities given in the request")); - - cardRepository.findByUid(cardUid).ifPresent(selectedCard -> cardNotificationService - .pushAckOfCardInEventBus(cardUid, selectedCard.id, entitiesAcks, CardOperationTypeEnum.ACK)); - - log.info("Set ack on card with uid {} for user {} and entities {}", cardUid, user.getUserData().getLogin(), - entitiesAcks); - return cardRepository.addUserAck(user.getUserData(), cardUid, entitiesAcks); - } - - public UserBasedOperationResult processUserRead(String cardUid, String userName) { - log.info("Set read on card with uid {} for user {} ", cardUid, userName); - return cardRepository.addUserRead(userName, cardUid); - } - - public UserBasedOperationResult deleteUserRead(String cardUid, String userName) { - log.info("Delete read on card with uid {} for user {} ", cardUid, userName); - return cardRepository.deleteUserRead(userName, cardUid); - } - - public UserBasedOperationResult deleteUserAcknowledgement(String cardUid, CurrentUserWithPerimeters user, - List entitiesAcks) { - log.info("Delete ack on card with uid {} for user {} and entities {} ", cardUid, user.getUserData().getLogin(), - entitiesAcks); - - if (!user.getUserData().getEntities().containsAll(entitiesAcks)) - throw new ApiErrorException( - - new ApiError(HttpStatus.FORBIDDEN, - "Cancel acknowledgement impossible : User is not member of all the entities given in the request")); - - cardRepository.findByUid(cardUid).ifPresent(selectedCard -> cardNotificationService - .pushAckOfCardInEventBus(cardUid, selectedCard.id, entitiesAcks, CardOperationTypeEnum.UNACK)); - - return cardRepository.deleteUserAck(user.getUserData().getLogin(), cardUid, entitiesAcks); - - } - - public UserBasedOperationResult resetReadAndAcks(String cardUid) { - log.info("Delete ack and reads on card with uid {} ", cardUid); - UserBasedOperationResult acksResult = cardRepository.deleteAcksAndReads(cardUid); - if (acksResult.isCardFound()) { - cardRepository.findByUid(cardUid) - .ifPresent(card -> cardNotificationService.notifyOneCard(card, CardOperationTypeEnum.UPDATE)); - } - - return acksResult; - } - public void resetRateLimiter() { this.cardSendingLimiter.reset(); } diff --git a/services/cards-publication/src/main/java/org/opfab/cards/publication/services/CardReadAndAckService.java b/services/cards-publication/src/main/java/org/opfab/cards/publication/services/CardReadAndAckService.java new file mode 100644 index 0000000000..af273bc5e9 --- /dev/null +++ b/services/cards-publication/src/main/java/org/opfab/cards/publication/services/CardReadAndAckService.java @@ -0,0 +1,97 @@ + +/* Copyright (c) 2018-2025, RTE (http://www.rte-france.com) + * See AUTHORS.txt + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + * This file is part of the OperatorFabric project. + */ + +package org.opfab.cards.publication.services; + +import org.opfab.cards.publication.model.*; +import org.opfab.cards.publication.repositories.CardRepository; +import org.opfab.cards.publication.repositories.UserBasedOperationResult; +import org.opfab.springtools.error.model.ApiError; +import org.opfab.springtools.error.model.ApiErrorException; +import org.opfab.users.model.CurrentUserWithPerimeters; +import org.springframework.http.HttpStatus; +import java.util.List; + +public class CardReadAndAckService { + + private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CardReadAndAckService.class); + + private CardNotificationService cardNotificationService; + private CardRepository cardRepository; + private CardPermissionControlService cardPermissionControlService; + + public CardReadAndAckService( + CardNotificationService cardNotificationService, + CardRepository cardRepository) { + this.cardNotificationService = cardNotificationService; + this.cardRepository = cardRepository; + this.cardPermissionControlService = new CardPermissionControlService(); + + } + + public UserBasedOperationResult processUserAcknowledgement(String cardUid, CurrentUserWithPerimeters user, + List entitiesAcks) { + if (cardPermissionControlService.isCurrentUserReadOnly(user) && entitiesAcks != null && !entitiesAcks.isEmpty()) + throw new ApiErrorException( + new ApiError(HttpStatus.FORBIDDEN, "Acknowledgement impossible : User has READONLY opfab role")); + + if (!user.getUserData().getEntities().containsAll(entitiesAcks)) + throw new ApiErrorException( + new ApiError(HttpStatus.FORBIDDEN, + "Acknowledgement impossible : User is not member of all the entities given in the request")); + + cardRepository.findByUid(cardUid).ifPresent(selectedCard -> cardNotificationService + .pushAckOfCardInEventBus(cardUid, selectedCard.id, entitiesAcks, CardOperationTypeEnum.ACK)); + + log.info("Set ack on card with uid {} for user {} and entities {}", cardUid, user.getUserData().getLogin(), + entitiesAcks); + return cardRepository.addUserAck(user.getUserData(), cardUid, entitiesAcks); + } + + public UserBasedOperationResult processUserRead(String cardUid, String userName) { + log.info("Set read on card with uid {} for user {} ", cardUid, userName); + return cardRepository.addUserRead(userName, cardUid); + } + + public UserBasedOperationResult deleteUserRead(String cardUid, String userName) { + log.info("Delete read on card with uid {} for user {} ", cardUid, userName); + return cardRepository.deleteUserRead(userName, cardUid); + } + + public UserBasedOperationResult deleteUserAcknowledgement(String cardUid, CurrentUserWithPerimeters user, + List entitiesAcks) { + log.info("Delete ack on card with uid {} for user {} and entities {} ", cardUid, user.getUserData().getLogin(), + entitiesAcks); + + if (!user.getUserData().getEntities().containsAll(entitiesAcks)) + throw new ApiErrorException( + + new ApiError(HttpStatus.FORBIDDEN, + "Cancel acknowledgement impossible : User is not member of all the entities given in the request")); + + cardRepository.findByUid(cardUid).ifPresent(selectedCard -> cardNotificationService + .pushAckOfCardInEventBus(cardUid, selectedCard.id, entitiesAcks, CardOperationTypeEnum.UNACK)); + + return cardRepository.deleteUserAck(user.getUserData().getLogin(), cardUid, entitiesAcks); + + } + + public UserBasedOperationResult resetReadAndAcks(String cardUid) { + log.info("Delete ack and reads on card with uid {} ", cardUid); + UserBasedOperationResult acksResult = cardRepository.deleteAcksAndReads(cardUid); + if (acksResult.isCardFound()) { + cardRepository.findByUid(cardUid) + .ifPresent(card -> cardNotificationService.notifyOneCard(card, CardOperationTypeEnum.UPDATE)); + } + + return acksResult; + } + +} diff --git a/services/cards-publication/src/test/java/org/opfab/cards/publication/services/CardProcessServiceShould.java b/services/cards-publication/src/test/java/org/opfab/cards/publication/services/CardProcessServiceShould.java index 8044bc0e9a..3241daa957 100644 --- a/services/cards-publication/src/test/java/org/opfab/cards/publication/services/CardProcessServiceShould.java +++ b/services/cards-publication/src/test/java/org/opfab/cards/publication/services/CardProcessServiceShould.java @@ -80,6 +80,7 @@ class CardProcessServiceShould { private ExternalAppService externalAppService; private CardProcessingService cardProcessingService; + private CardReadAndAckService cardReadAndAckService; private CardTranslationService cardTranslationService; private EventBusSpy eventBusSpy; private CardNotificationService cardNotificationService; @@ -110,6 +111,7 @@ public void init() { cardRepositoryMock, externalAppService, cardTranslationService, cardValidationService, true, true, false, 1000, 3600, true); + cardReadAndAckService = new CardReadAndAckService(cardNotificationService, cardRepositoryMock); user = TestHelpers.getCurrentUser(); currentUserWithPerimeters = TestHelpers.getCurrentUserWithPerimeter(user); cardRepositoryMock.clear(); @@ -883,24 +885,16 @@ void GIVEN_a_user_with_the_write_right_in_perimeter_for_state1_WHEN_sending_card Assertions.assertThat(TestHelpers.checkCardCount(cardRepositoryMock, 1)).isTrue(); } - @Test - void GIVEN_a_card_WHEN_reset_reads_and_acks_THEN_card_event_UPDATE_is_sent_to_eventBus() { - Card card = TestHelpers.generateOneCard(); - cardProcessingService.processCard(card); - cardProcessingService.resetReadAndAcks(card.uid); - Assertions.assertThat(eventBusSpy.getMessagesSent().get(1)[1]).contains("{\"type\":\"UPDATE\""); - } - @Test void GIVEN_an_existing_card_WHEN_update_card_CONTAINS_KEEP_EXISTING_ACKS_AND_READS_THEN_acks_and_reads_are_kept() { Card card = TestHelpers.generateOneCard("entity2"); cardProcessingService.processUserCard(card, currentUserWithPerimeters, token); - cardProcessingService.processUserRead(card.uid, + cardReadAndAckService.processUserRead(card.uid, currentUserWithPerimeters.getUserData().getLogin()); - cardProcessingService.processUserRead(card.uid, "user2"); + cardReadAndAckService.processUserRead(card.uid, "user2"); List entitiesAcks = List.of("entity2"); - cardProcessingService.processUserAcknowledgement(card.uid, currentUserWithPerimeters, + cardReadAndAckService.processUserAcknowledgement(card.uid, currentUserWithPerimeters, entitiesAcks); Assertions.assertThat(TestHelpers.checkCardCount(cardRepositoryMock, 1)).isTrue(); diff --git a/services/cards-publication/src/test/java/org/opfab/cards/publication/services/CardReadAndAckServiceShould.java b/services/cards-publication/src/test/java/org/opfab/cards/publication/services/CardReadAndAckServiceShould.java new file mode 100644 index 0000000000..e23de59a4b --- /dev/null +++ b/services/cards-publication/src/test/java/org/opfab/cards/publication/services/CardReadAndAckServiceShould.java @@ -0,0 +1,57 @@ + +/* Copyright (c) 2018-2025, RTE (http://www.rte-france.com) + * See AUTHORS.txt + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * SPDX-License-Identifier: MPL-2.0 + * This file is part of the OperatorFabric project. + */ + +package org.opfab.cards.publication.services; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; +import org.opfab.cards.publication.application.UnitTestApplication; +import org.opfab.cards.publication.mocks.CardRepositoryMock; +import org.opfab.cards.publication.model.Card; +import org.opfab.test.EventBusSpy; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import com.fasterxml.jackson.databind.ObjectMapper; + +@ExtendWith(SpringExtension.class) +@SpringBootTest(classes = { UnitTestApplication.class }) +class CardReadAndAckServiceShould { + + @Autowired + private ObjectMapper objectMapper; + + private CardReadAndAckService cardReadAndAckService; + private EventBusSpy eventBusSpy; + private CardNotificationService cardNotificationService; + + @Autowired + private CardRepositoryMock cardRepositoryMock; + + @BeforeEach + void init() { + eventBusSpy = new EventBusSpy(); + cardNotificationService = new CardNotificationService(eventBusSpy, objectMapper, null); + cardReadAndAckService = new CardReadAndAckService( + cardNotificationService, + cardRepositoryMock); + cardRepositoryMock.clear(); + eventBusSpy.clearMessageSent(); + } + + @Test + void GIVEN_a_card_WHEN_reset_reads_and_acks_THEN_card_event_UPDATE_is_sent_to_eventBus() { + Card card = TestHelpers.generateOneCard(); + cardRepositoryMock.saveCard(card); + cardReadAndAckService.resetReadAndAcks(card.uid); + Assertions.assertThat(eventBusSpy.getMessagesSent().get(0)[1]).contains("{\"type\":\"UPDATE\""); + } +}