diff --git a/src/main/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderService.java b/src/main/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderService.java index ebe5e2af..302e9e1f 100644 --- a/src/main/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderService.java +++ b/src/main/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderService.java @@ -4,6 +4,8 @@ import org.kie.api.KieBase; import reactor.core.publisher.Mono; +import java.util.Set; + /** * This component will retrieve the beneficiaries' rules kieContainer and the PDND token associated to the input initiative id @@ -11,6 +13,7 @@ * */ public interface OnboardingContextHolderService { KieBase getBeneficiaryRulesKieBase(); + Set getBeneficiaryRulesKieInitiativeIds(); void setBeneficiaryRulesKieBase(KieBase kieBase); Mono getInitiativeConfig(String initiativeId); diff --git a/src/main/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderServiceImpl.java b/src/main/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderServiceImpl.java index 26d53dec..e9b8aa60 100644 --- a/src/main/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderServiceImpl.java +++ b/src/main/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderServiceImpl.java @@ -5,6 +5,7 @@ import it.gov.pagopa.admissibility.model.InitiativeConfig; import it.gov.pagopa.admissibility.service.build.KieContainerBuilderService; import lombok.extern.slf4j.Slf4j; +import org.drools.core.definitions.rule.impl.RuleImpl; import org.kie.api.KieBase; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -25,10 +26,13 @@ import reactor.util.retry.Retry; import java.util.Arrays; +import java.util.Collections; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Consumer; import java.util.function.Function; +import java.util.stream.Collectors; @Service @Slf4j @@ -45,6 +49,7 @@ public class OnboardingContextHolderServiceImpl extends ReadinessStateHealthIndi private final boolean preLoadContainer; private KieBase kieBase; + private Set kieInitiatives = Collections.emptySet(); private byte[] kieBaseSerialized; private boolean contextReady=false; @@ -89,8 +94,7 @@ public OnboardingContextHolderReadyEvent(Object source) { //region kieContainer holder @Override public void setBeneficiaryRulesKieBase(KieBase newKieBase) { - preLoadKieBase(newKieBase); - this.kieBase = newKieBase; + acceptNewKieBase(newKieBase); if (isRedisCacheEnabled) { kieBaseSerialized = SerializationUtils.serialize(newKieBase); @@ -102,6 +106,23 @@ public void setBeneficiaryRulesKieBase(KieBase newKieBase) { } } + private void acceptNewKieBase(KieBase newKieBase) { + preLoadKieBase(newKieBase); + this.kieBase = newKieBase; + this.kieInitiatives = readKieInitiatives(newKieBase); + } + + private Set readKieInitiatives(KieBase kieBase) { + if (kieBase == null) { + return Collections.emptySet(); + } else { + return kieBase.getKiePackages().stream() + .flatMap(p -> p.getRules().stream()) + .map(r -> ((RuleImpl) r).getAgendaGroup()) + .collect(Collectors.toSet()); + } + } + private void preLoadKieBase(KieBase kieBase) { if (preLoadContainer) { kieContainerBuilderService.preLoadKieBase(kieBase); @@ -113,6 +134,11 @@ public KieBase getBeneficiaryRulesKieBase() { return kieBase; } + @Override + public Set getBeneficiaryRulesKieInitiativeIds() { + return kieInitiatives; + } + @Scheduled(initialDelayString = "${app.beneficiary-rule.cache.refresh-ms-rate}", fixedRateString = "${app.beneficiary-rule.cache.refresh-ms-rate}") public void refreshKieContainer() { refreshKieContainer(x -> log.trace("Refreshed KieContainer"), Retry.max(3), Mono::error); @@ -129,9 +155,7 @@ public void refreshKieContainer( this.kieBaseSerialized = c; try { KieBase newKieBase = org.apache.commons.lang3.SerializationUtils.deserialize(c); - preLoadKieBase(newKieBase); - - this.kieBase = newKieBase; + acceptNewKieBase(newKieBase); } catch (Exception e) { log.warn("[BENEFICIARY_RULE_BUILDER] Cached KieContainer cannot be executed! refreshing it!"); return null; diff --git a/src/main/java/it/gov/pagopa/admissibility/service/onboarding/evaluate/RuleEngineServiceImpl.java b/src/main/java/it/gov/pagopa/admissibility/service/onboarding/evaluate/RuleEngineServiceImpl.java index 6ad9a15e..499e91d4 100644 --- a/src/main/java/it/gov/pagopa/admissibility/service/onboarding/evaluate/RuleEngineServiceImpl.java +++ b/src/main/java/it/gov/pagopa/admissibility/service/onboarding/evaluate/RuleEngineServiceImpl.java @@ -3,11 +3,13 @@ import it.gov.pagopa.admissibility.dto.onboarding.EvaluationDTO; import it.gov.pagopa.admissibility.dto.onboarding.OnboardingDTO; import it.gov.pagopa.admissibility.dto.onboarding.OnboardingDroolsDTO; +import it.gov.pagopa.admissibility.dto.onboarding.OnboardingRejectionReason; import it.gov.pagopa.admissibility.mapper.Onboarding2EvaluationMapper; import it.gov.pagopa.admissibility.mapper.Onboarding2OnboardingDroolsMapper; import it.gov.pagopa.admissibility.model.InitiativeConfig; import it.gov.pagopa.admissibility.service.CriteriaCodeService; import it.gov.pagopa.admissibility.service.onboarding.OnboardingContextHolderService; +import it.gov.pagopa.admissibility.utils.OnboardingConstants; import it.gov.pagopa.common.reactive.utils.PerformanceLogger; import lombok.extern.slf4j.Slf4j; import org.drools.core.command.runtime.rule.AgendaGroupSetFocusCommand; @@ -15,6 +17,7 @@ import org.kie.api.runtime.StatelessKieSession; import org.kie.internal.command.CommandFactory; import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; import java.util.Arrays; import java.util.List; @@ -52,10 +55,26 @@ public EvaluationDTO applyRules(OnboardingDTO onboardingRequest, InitiativeConfi long before = System.currentTimeMillis(); statelessKieSession.execute(CommandFactory.newBatchExecution(cmds)); + checkIfContainerWasReady(req, initiative); + PerformanceLogger.logTiming("ONBOARDING_RULE_ENGINE", before, "resulted into rejections %s".formatted(req.getOnboardingRejectionReasons())); log.trace("[ONBOARDING_REQUEST] [RULE_ENGINE] Send message prepared: {}", req); return onboarding2EvaluationMapper.apply(req, initiative, req.getOnboardingRejectionReasons()); } + + private void checkIfContainerWasReady(OnboardingDroolsDTO req, InitiativeConfig initiative) { + if (req.getOnboardingRejectionReasons().isEmpty() && // there is not rejection reason + !CollectionUtils.isEmpty(initiative.getAutomatedCriteria()) && // the drools container is supposed to be involved + !onboardingContextHolderService.getBeneficiaryRulesKieInitiativeIds() // the initiative was not inside the container drools + .contains(initiative.getInitiativeId()) + ) { + req.getOnboardingRejectionReasons().add(new OnboardingRejectionReason( + OnboardingRejectionReason.OnboardingRejectionReasonType.TECHNICAL_ERROR, + OnboardingConstants.REJECTION_REASON_RULE_ENGINE_NOT_READY, + null, null, null + )); + } + } } diff --git a/src/main/java/it/gov/pagopa/admissibility/utils/OnboardingConstants.java b/src/main/java/it/gov/pagopa/admissibility/utils/OnboardingConstants.java index 02e0fb88..062d69c4 100644 --- a/src/main/java/it/gov/pagopa/admissibility/utils/OnboardingConstants.java +++ b/src/main/java/it/gov/pagopa/admissibility/utils/OnboardingConstants.java @@ -20,6 +20,7 @@ private OnboardingConstants(){} public static final String REJECTION_REASON_FAMILY_KO = "FAMILY_FAIL"; public static final String REJECTION_REASON_RESIDENCE_KO = "RESIDENCE_FAIL"; public static final String REJECTION_REASON_BIRTHDATE_KO = "BIRTHDATE_FAIL"; + public static final String REJECTION_REASON_RULE_ENGINE_NOT_READY = "RULE_ENGINE_NOT_READY"; public static final String REJECTION_REASON_GENERIC_ERROR = "GENERIC_ERROR"; /** Rejection reason for the second family member due to the family not meeting the requirements of the initiative */ diff --git a/src/test/java/it/gov/pagopa/admissibility/connector/event/consumer/BeneficiaryRuleBuilderConsumerConfigIntegrationTest.java b/src/test/java/it/gov/pagopa/admissibility/connector/event/consumer/BeneficiaryRuleBuilderConsumerConfigIntegrationTest.java index d6f35fd6..30e2daa4 100644 --- a/src/test/java/it/gov/pagopa/admissibility/connector/event/consumer/BeneficiaryRuleBuilderConsumerConfigIntegrationTest.java +++ b/src/test/java/it/gov/pagopa/admissibility/connector/event/consumer/BeneficiaryRuleBuilderConsumerConfigIntegrationTest.java @@ -75,6 +75,7 @@ void testBeneficiaryRuleBuilding() { long timeEnd = System.currentTimeMillis(); Assertions.assertEquals(validRules, countSaved[0]); + Assertions.assertEquals(validRules, onboardingContextHolderServiceSpy.getBeneficiaryRulesKieInitiativeIds().size()); Assertions.assertEquals(expectedRules[0], ruleBuiltSize); checkInitiativeCounters(validRules); diff --git a/src/test/java/it/gov/pagopa/admissibility/service/build/BeneficiaryRule2DroolsRuleImplTest.java b/src/test/java/it/gov/pagopa/admissibility/service/build/BeneficiaryRule2DroolsRuleImplTest.java index c735d979..95f9fc65 100644 --- a/src/test/java/it/gov/pagopa/admissibility/service/build/BeneficiaryRule2DroolsRuleImplTest.java +++ b/src/test/java/it/gov/pagopa/admissibility/service/build/BeneficiaryRule2DroolsRuleImplTest.java @@ -36,6 +36,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Set; /* ****************** @@ -144,6 +145,7 @@ private void checkResult(DroolsRule result, Initiative2BuildDTO dto) { @Test void testExecutions() { testExecution(Collections.emptyList()); + testExecution(List.of("NOTREADY")); testExecution(List.of("ISEE")); testExecution(List.of("BIRTHDATE")); testExecution(List.of("ISEE", "BIRTHDATE")); @@ -153,6 +155,7 @@ void testExecution(List failingCode) { //given boolean expectedIseeFail = failingCode.contains("ISEE"); boolean expectedBirthDateFail = failingCode.contains("BIRTHDATE"); + boolean expectedNotReady = failingCode.equals(List.of("NOTREADY")); Initiative2BuildDTO initiative = buildInitiative(); @@ -175,6 +178,10 @@ void testExecution(List failingCode) { OnboardingContextHolderService onboardingContextHolderService = Mockito.mock(OnboardingContextHolderService.class); Mockito.when(onboardingContextHolderService.getBeneficiaryRulesKieBase()).thenReturn(buildKieBase(rule)); + Mockito.when(onboardingContextHolderService.getBeneficiaryRulesKieInitiativeIds()) + .thenReturn(expectedNotReady + ? Collections.emptySet() + : Set.of(initiative.getInitiativeId())); RuleEngineService ruleEngineService = new RuleEngineServiceImpl(onboardingContextHolderService, new Onboarding2EvaluationMapper(), criteriaCodeServiceMock, new Onboarding2OnboardingDroolsMapper()); @@ -208,7 +215,13 @@ void testExecution(List failingCode) { .authorityLabel("Agenzia per l'Italia Digitale") .build()); } - expectedEvaluationResult.setStatus(expectedEvaluationResult.getOnboardingRejectionReasons().size() == 0 ? OnboardingEvaluationStatus.ONBOARDING_OK : OnboardingEvaluationStatus.ONBOARDING_KO); + if (expectedNotReady) { + expectedEvaluationResult.getOnboardingRejectionReasons().add(OnboardingRejectionReason.builder() + .type(OnboardingRejectionReason.OnboardingRejectionReasonType.TECHNICAL_ERROR) + .code("RULE_ENGINE_NOT_READY") + .build()); + } + expectedEvaluationResult.setStatus(expectedEvaluationResult.getOnboardingRejectionReasons().isEmpty() ? OnboardingEvaluationStatus.ONBOARDING_OK : OnboardingEvaluationStatus.ONBOARDING_KO); Assertions.assertEquals(expectedEvaluationResult, evaluationResult); } diff --git a/src/test/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderServiceImplTest.java b/src/test/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderServiceImplTest.java index 364a249d..a44d7dc5 100644 --- a/src/test/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderServiceImplTest.java +++ b/src/test/java/it/gov/pagopa/admissibility/service/onboarding/OnboardingContextHolderServiceImplTest.java @@ -30,6 +30,7 @@ import java.math.BigDecimal; import java.time.LocalDate; import java.util.ArrayList; +import java.util.Collections; import java.util.List; @ExtendWith(MockitoExtension.class) @@ -78,6 +79,7 @@ void getKieContainer(boolean isRedisCacheEnabled) { //Then Assertions.assertNotNull(result); + Assertions.assertEquals(Collections.emptySet(), onboardingContextHolderService.getBeneficiaryRulesKieInitiativeIds()); if (!isRedisCacheEnabled) { Assertions.assertSame(expectedKieBase, result); } diff --git a/src/test/java/it/gov/pagopa/admissibility/service/onboarding/evaluate/RuleEngineServiceImplTest.java b/src/test/java/it/gov/pagopa/admissibility/service/onboarding/evaluate/RuleEngineServiceImplTest.java index fd5d3230..fd45329c 100644 --- a/src/test/java/it/gov/pagopa/admissibility/service/onboarding/evaluate/RuleEngineServiceImplTest.java +++ b/src/test/java/it/gov/pagopa/admissibility/service/onboarding/evaluate/RuleEngineServiceImplTest.java @@ -1,59 +1,76 @@ package it.gov.pagopa.admissibility.service.onboarding.evaluate; +import it.gov.pagopa.admissibility.connector.repository.DroolsRuleRepository; import it.gov.pagopa.admissibility.drools.transformer.extra_filter.ExtraFilter2DroolsTransformerFacadeImplTest; import it.gov.pagopa.admissibility.dto.onboarding.*; +import it.gov.pagopa.admissibility.dto.rule.AutomatedCriteriaDTO; import it.gov.pagopa.admissibility.enums.OnboardingEvaluationStatus; import it.gov.pagopa.admissibility.mapper.Onboarding2EvaluationMapper; import it.gov.pagopa.admissibility.mapper.Onboarding2OnboardingDroolsMapper; import it.gov.pagopa.admissibility.model.DroolsRule; import it.gov.pagopa.admissibility.model.InitiativeConfig; -import it.gov.pagopa.admissibility.connector.repository.DroolsRuleRepository; import it.gov.pagopa.admissibility.service.CriteriaCodeService; import it.gov.pagopa.admissibility.service.build.KieContainerBuilderServiceImpl; import it.gov.pagopa.admissibility.service.build.KieContainerBuilderServiceImplTest; import it.gov.pagopa.admissibility.service.onboarding.OnboardingContextHolderService; -import it.gov.pagopa.admissibility.service.onboarding.OnboardingContextHolderServiceImpl; +import it.gov.pagopa.admissibility.utils.OnboardingConstants; import lombok.extern.slf4j.Slf4j; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.ExtendWith; import org.kie.api.KieBase; +import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import reactor.core.publisher.Flux; import java.time.LocalDateTime; import java.util.Collections; +import java.util.List; @ExtendWith(MockitoExtension.class) @Slf4j class RuleEngineServiceImplTest { + public static final String INITIATIVEID = "INITIATIVEID"; + @Mock private OnboardingContextHolderService onboardingContextHolderServiceMock; + @Mock private CriteriaCodeService criteriaCodeServiceMock; + + private final Onboarding2EvaluationMapper onboarding2EvaluationMapper = new Onboarding2EvaluationMapper(); + private final Onboarding2OnboardingDroolsMapper onboarding2OnboardingDroolsMapper = new Onboarding2OnboardingDroolsMapper(); + + private RuleEngineService ruleEngineService; + @BeforeAll public static void configDroolsLogLevel() { KieContainerBuilderServiceImplTest.configDroolsLogs(); } + @BeforeEach + void init(){ + ruleEngineService = new RuleEngineServiceImpl(onboardingContextHolderServiceMock, onboarding2EvaluationMapper, criteriaCodeServiceMock, onboarding2OnboardingDroolsMapper); + } + + @AfterEach + void verifyNotMoreInvocations(){ + Mockito.verifyNoMoreInteractions( + onboardingContextHolderServiceMock, + criteriaCodeServiceMock); + } + @Test void applyRules() { // Given - OnboardingContextHolderService onboardingContextHolderServiceMock = Mockito.mock(OnboardingContextHolderServiceImpl.class); - CriteriaCodeService criteriaCodeServiceMock = Mockito.mock(CriteriaCodeService.class); - Onboarding2EvaluationMapper onboarding2EvaluationMapper = new Onboarding2EvaluationMapper(); - Onboarding2OnboardingDroolsMapper onboarding2OnboardingDroolsMapper = new Onboarding2OnboardingDroolsMapper(); - - RuleEngineService ruleEngineService = new RuleEngineServiceImpl(onboardingContextHolderServiceMock, onboarding2EvaluationMapper, criteriaCodeServiceMock, onboarding2OnboardingDroolsMapper); - + String initiativeId = INITIATIVEID; OnboardingDTO onboardingDTO = new OnboardingDTO(); - onboardingDTO.setInitiativeId("INITIATIVEID"); + onboardingDTO.setInitiativeId(initiativeId); InitiativeConfig initiativeConfig = new InitiativeConfig(); - initiativeConfig.setInitiativeId("INITIATIVEID"); + initiativeConfig.setInitiativeId(initiativeId); initiativeConfig.setInitiativeName("INITIATIVENAME"); initiativeConfig.setOrganizationId("ORGANIZATIONID"); - Mockito.when(onboardingContextHolderServiceMock.getBeneficiaryRulesKieBase()).thenReturn(buildContainer(onboardingDTO.getInitiativeId())); + Mockito.when(onboardingContextHolderServiceMock.getBeneficiaryRulesKieBase()) + .thenReturn(buildContainer(onboardingDTO.getInitiativeId())); // When EvaluationDTO result = ruleEngineService.applyRules(onboardingDTO, initiativeConfig); @@ -61,7 +78,7 @@ void applyRules() { // Then Mockito.verify(onboardingContextHolderServiceMock).getBeneficiaryRulesKieBase(); - Assertions.assertTrue(result instanceof EvaluationCompletedDTO); + Assertions.assertInstanceOf(EvaluationCompletedDTO.class, result); Assertions.assertNotNull(result.getAdmissibilityCheckDate()); Assertions.assertFalse(result.getAdmissibilityCheckDate().isAfter(LocalDateTime.now())); Assertions.assertTrue(result.getAdmissibilityCheckDate().isAfter(LocalDateTime.now().minusMinutes(2))); @@ -79,6 +96,58 @@ void applyRules() { Assertions.assertEquals(expected, result); } + @Test + void testNotRules_notKieBasedInitiatives(){ + // Given + String initiativeId = "NOTKIEINITITATIVEID"; + OnboardingDTO onboardingDTO = new OnboardingDTO(); + onboardingDTO.setInitiativeId(initiativeId); + + InitiativeConfig initiativeConfig = new InitiativeConfig(); + initiativeConfig.setInitiativeId(initiativeId); + + Mockito.when(onboardingContextHolderServiceMock.getBeneficiaryRulesKieBase()) + .thenReturn(buildContainer(INITIATIVEID)); + + // When + EvaluationDTO result = ruleEngineService.applyRules(onboardingDTO, initiativeConfig); + + // Then + Assertions.assertInstanceOf(EvaluationCompletedDTO.class, result); + Assertions.assertEquals(OnboardingEvaluationStatus.ONBOARDING_OK, ((EvaluationCompletedDTO)result).getStatus()); + Assertions.assertEquals(Collections.emptyList(), ((EvaluationCompletedDTO)result).getOnboardingRejectionReasons()); + } + + @Test + void testNotRules_kieBasedInitiatives(){ + // Given + String initiativeId = "KIEINITITATIVEID_NOT_IN_CONTAINER"; + OnboardingDTO onboardingDTO = new OnboardingDTO(); + onboardingDTO.setInitiativeId(initiativeId); + + InitiativeConfig initiativeConfig = new InitiativeConfig(); + initiativeConfig.setInitiativeId(initiativeId); + initiativeConfig.setAutomatedCriteria(List.of(AutomatedCriteriaDTO.builder().build())); + + Mockito.when(onboardingContextHolderServiceMock.getBeneficiaryRulesKieBase()) + .thenReturn(buildContainer(INITIATIVEID)); + Mockito.when(onboardingContextHolderServiceMock.getBeneficiaryRulesKieInitiativeIds()) + .thenReturn(Collections.emptySet()); + + // When + EvaluationDTO result = ruleEngineService.applyRules(onboardingDTO, initiativeConfig); + + // Then + Assertions.assertInstanceOf(EvaluationCompletedDTO.class, result); + Assertions.assertEquals(OnboardingEvaluationStatus.ONBOARDING_KO, ((EvaluationCompletedDTO)result).getStatus()); + Assertions.assertEquals(List.of(new OnboardingRejectionReason( + OnboardingRejectionReason.OnboardingRejectionReasonType.TECHNICAL_ERROR, + OnboardingConstants.REJECTION_REASON_RULE_ENGINE_NOT_READY, + null, null, null + )), + ((EvaluationCompletedDTO)result).getOnboardingRejectionReasons()); + } + private KieBase buildContainer(String initiativeId) { DroolsRule ignoredRule = new DroolsRule(); ignoredRule.setId("IGNORED");