FINERACT-1694-Validating-event-configuration

This commit is contained in:
Ruchi Dhamankar
2022-10-28 17:48:59 +02:00
committed by Arnold Gálovics
parent 7be193dd0b
commit 1c944dd830
4 changed files with 322 additions and 0 deletions
@@ -20,6 +20,10 @@ package org.apache.fineract.infrastructure.event.external.exception;
public class ExternalEventConfigurationNotFoundException extends RuntimeException {
public ExternalEventConfigurationNotFoundException() {
super("All external events are not configured");
}
public ExternalEventConfigurationNotFoundException(final String externalEventType) {
super("Configuration not found for external event " + externalEventType);
}
@@ -0,0 +1,98 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.event.external.service;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.ClassInfoList;
import io.github.classgraph.ScanResult;
import java.util.List;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.event.business.domain.BulkBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.BusinessEvent;
import org.apache.fineract.infrastructure.event.external.exception.ExternalEventConfigurationNotFoundException;
import org.apache.fineract.infrastructure.security.service.TenantDetailsService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Slf4j
@RequiredArgsConstructor
@Service
public class ExternalEventConfigurationValidationService implements InitializingBean {
private static final String EXTERNAL_EVENT_CLASSES_BASE_PACKAGE = "org.apache.fineract.infrastructure.event.business.domain";
private static final String EXTERNAL_EVENT_BUSINESS_INTERFACE = BusinessEvent.class.getName();
private static final String BULK_BUSINESS_EVENT = BulkBusinessEvent.class.getName();
private final TenantDetailsService tenantDetailsService;
private final JdbcTemplateFactory jdbcTemplateFactory;
@Override
public void afterPropertiesSet() throws Exception {
validateEventConfigurationForAllTenants();
}
private void validateEventConfigurationForAllTenants() throws ExternalEventConfigurationNotFoundException {
List<String> eventClasses = getAllEventClasses();
List<FineractPlatformTenant> tenants = tenantDetailsService.findAllTenants();
if (isNotEmpty(tenants)) {
for (FineractPlatformTenant tenant : tenants) {
validateEventConfigurationForIndividualTenant(tenant, eventClasses);
}
}
}
private void validateEventConfigurationForIndividualTenant(FineractPlatformTenant tenant, List<String> eventClasses)
throws ExternalEventConfigurationNotFoundException {
log.info("Validating external event configuration for {}", tenant.getTenantIdentifier());
List<String> eventConfigurations = getExternalEventConfigurationsForTenant(tenant);
if (eventClasses.size() != eventConfigurations.size()) {
throw new ExternalEventConfigurationNotFoundException();
}
for (String eventTypeClass : eventClasses) {
if (!eventConfigurations.contains(eventTypeClass)) {
throw new ExternalEventConfigurationNotFoundException(eventTypeClass);
}
}
}
private List<String> getExternalEventConfigurationsForTenant(FineractPlatformTenant tenant) {
final JdbcTemplate jdbcTemplate = jdbcTemplateFactory.create(tenant);
final StringBuilder eventConfigurations = new StringBuilder();
eventConfigurations.append("select me.type as type from m_external_event_configuration me");
List<String> configuredEventTypes = jdbcTemplate.queryForList(eventConfigurations.toString(), String.class);
return configuredEventTypes;
}
private List<String> getAllEventClasses() {
try (ScanResult scanResult = new ClassGraph().enableAllInfo().acceptPackages(EXTERNAL_EVENT_CLASSES_BASE_PACKAGE).scan()) {
ClassInfoList businessEventClasses = scanResult.getClassesImplementing(EXTERNAL_EVENT_BUSINESS_INTERFACE)
.filter(classInfo -> (!classInfo.isInterface() && !classInfo.isAbstract()
&& !classInfo.getName().equalsIgnoreCase(BULK_BUSINESS_EVENT)));
return businessEventClasses.stream().map(classInfo -> classInfo.getSimpleName()).collect(Collectors.toList());
}
}
}
@@ -0,0 +1,38 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.event.external.service;
import javax.sql.DataSource;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.migration.TenantDataSourceFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@RequiredArgsConstructor
@Component
public class JdbcTemplateFactory {
private final TenantDataSourceFactory tenantDataSourceFactory;
public JdbcTemplate create(FineractPlatformTenant tenant) {
DataSource tenantDataSource = tenantDataSourceFactory.create(tenant);
return new JdbcTemplate(tenantDataSource);
}
}
@@ -0,0 +1,182 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.infrastructure.event.external.service;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.event.external.exception.ExternalEventConfigurationNotFoundException;
import org.apache.fineract.infrastructure.security.service.TenantDetailsService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.jdbc.core.JdbcTemplate;
@ExtendWith(MockitoExtension.class)
public class ExternalEventConfigurationValidationServiceTest {
@Mock
private JdbcTemplateFactory jdbcTemplateFactory;
@Mock
private TenantDetailsService tenantDetailsService;
private ExternalEventConfigurationValidationService underTest;
@BeforeEach
public void setUp() {
underTest = new ExternalEventConfigurationValidationService(tenantDetailsService, jdbcTemplateFactory);
}
@Test
public void givenAllConfigurationWhenValidatedThenValidationSuccessful() throws Exception {
// given
List<String> configurations = Arrays.asList("CentersCreateBusinessEvent", "ClientActivateBusinessEvent",
"ClientCreateBusinessEvent", "ClientRejectBusinessEvent", "FixedDepositAccountCreateBusinessEvent",
"GroupsCreateBusinessEvent", "LoanAcceptTransferBusinessEvent", "LoanAddChargeBusinessEvent",
"LoanAdjustTransactionBusinessEvent", "LoanApplyOverdueChargeBusinessEvent", "LoanApprovedBusinessEvent",
"LoanBalanceChangedBusinessEvent", "LoanChargebackTransactionBusinessEvent", "LoanChargePaymentPostBusinessEvent",
"LoanChargePaymentPreBusinessEvent", "LoanChargeRefundBusinessEvent", "LoanCloseAsRescheduleBusinessEvent",
"LoanCloseBusinessEvent", "LoanCreatedBusinessEvent", "LoanCreditBalanceRefundPostBusinessEvent",
"LoanCreditBalanceRefundPreBusinessEvent", "LoanDeleteChargeBusinessEvent", "LoanDisbursalBusinessEvent",
"LoanDisbursalTransactionBusinessEvent", "LoanForeClosurePostBusinessEvent", "LoanForeClosurePreBusinessEvent",
"LoanInitiateTransferBusinessEvent", "LoanInterestRecalculationBusinessEvent", "LoanProductCreateBusinessEvent",
"LoanReassignOfficerBusinessEvent", "LoanRefundPostBusinessEvent", "LoanRefundPreBusinessEvent",
"LoanRejectedBusinessEvent", "LoanRejectTransferBusinessEvent", "LoanRemoveOfficerBusinessEvent",
"LoanRescheduledDueCalendarChangeBusinessEvent", "LoanRescheduledDueHolidayBusinessEvent",
"LoanScheduleVariationsAddedBusinessEvent", "LoanScheduleVariationsDeletedBusinessEvent", "LoanStatusChangedBusinessEvent",
"LoanTransactionGoodwillCreditPostBusinessEvent", "LoanTransactionGoodwillCreditPreBusinessEvent",
"LoanTransactionMakeRepaymentPostBusinessEvent", "LoanTransactionMakeRepaymentPreBusinessEvent",
"LoanTransactionMerchantIssuedRefundPostBusinessEvent", "LoanTransactionMerchantIssuedRefundPreBusinessEvent",
"LoanTransactionPayoutRefundPostBusinessEvent", "LoanTransactionPayoutRefundPreBusinessEvent",
"LoanTransactionRecoveryPaymentPostBusinessEvent", "LoanTransactionRecoveryPaymentPreBusinessEvent",
"LoanUndoApprovalBusinessEvent", "LoanUndoDisbursalBusinessEvent", "LoanUndoLastDisbursalBusinessEvent",
"LoanUndoWrittenOffBusinessEvent", "LoanUpdateChargeBusinessEvent", "LoanUpdateDisbursementDataBusinessEvent",
"LoanWaiveChargeBusinessEvent", "LoanWaiveChargeUndoBusinessEvent", "LoanWaiveInterestBusinessEvent",
"LoanWithdrawTransferBusinessEvent", "LoanWrittenOffPostBusinessEvent", "LoanWrittenOffPreBusinessEvent",
"RecurringDepositAccountCreateBusinessEvent", "SavingsActivateBusinessEvent", "SavingsApproveBusinessEvent",
"SavingsCloseBusinessEvent", "SavingsCreateBusinessEvent", "SavingsDepositBusinessEvent",
"SavingsPostInterestBusinessEvent", "SavingsRejectBusinessEvent", "SavingsWithdrawalBusinessEvent",
"ShareAccountApproveBusinessEvent", "ShareAccountCreateBusinessEvent", "ShareProductDividentsCreateBusinessEvent");
List<FineractPlatformTenant> tenants = Arrays
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
JdbcTemplate jdbcTemplate = Mockito.mock(JdbcTemplate.class);
when(tenantDetailsService.findAllTenants()).thenReturn(tenants);
when(jdbcTemplateFactory.create(any())).thenReturn(jdbcTemplate);
when(jdbcTemplate.queryForList(anyString(), eq(String.class))).thenReturn(configurations);
// when
underTest.afterPropertiesSet();
// then
verify(tenantDetailsService).findAllTenants();
verify(jdbcTemplateFactory, times(1)).create(any());
}
@Test
public void givenNoEventConfigurationWhenValidatedThenThrowException() throws Exception {
// given
List<FineractPlatformTenant> tenants = Arrays
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
JdbcTemplate jdbcTemplate = Mockito.mock(JdbcTemplate.class);
when(tenantDetailsService.findAllTenants()).thenReturn(tenants);
when(jdbcTemplateFactory.create(any())).thenReturn(jdbcTemplate);
when(jdbcTemplate.queryForList(anyString(), eq(String.class))).thenReturn(new ArrayList<>());
// when
ExternalEventConfigurationNotFoundException exceptionThrown = assertThrows(ExternalEventConfigurationNotFoundException.class,
() -> underTest.afterPropertiesSet());
// then
String expectedMessage = "All external events are not configured";
String actualMessage = exceptionThrown.getMessage();
assertTrue(actualMessage.contains(expectedMessage));
}
@Test
public void givenMissingEventConfigurationWhenValidatedThenThrowException() throws Exception {
// given
List<String> configurationWithMissingCentersCreateBusinessEvent = Arrays.asList("MockBusinessEvent", "ClientActivateBusinessEvent",
"ClientCreateBusinessEvent", "ClientRejectBusinessEvent", "FixedDepositAccountCreateBusinessEvent",
"GroupsCreateBusinessEvent", "LoanAcceptTransferBusinessEvent", "LoanAddChargeBusinessEvent",
"LoanAdjustTransactionBusinessEvent", "LoanApplyOverdueChargeBusinessEvent", "LoanApprovedBusinessEvent",
"LoanBalanceChangedBusinessEvent", "LoanChargebackTransactionBusinessEvent", "LoanChargePaymentPostBusinessEvent",
"LoanChargePaymentPreBusinessEvent", "LoanChargeRefundBusinessEvent", "LoanCloseAsRescheduleBusinessEvent",
"LoanCloseBusinessEvent", "LoanCreatedBusinessEvent", "LoanCreditBalanceRefundPostBusinessEvent",
"LoanCreditBalanceRefundPreBusinessEvent", "LoanDeleteChargeBusinessEvent", "LoanDisbursalBusinessEvent",
"LoanDisbursalTransactionBusinessEvent", "LoanForeClosurePostBusinessEvent", "LoanForeClosurePreBusinessEvent",
"LoanInitiateTransferBusinessEvent", "LoanInterestRecalculationBusinessEvent", "LoanProductCreateBusinessEvent",
"LoanReassignOfficerBusinessEvent", "LoanRefundPostBusinessEvent", "LoanRefundPreBusinessEvent",
"LoanRejectedBusinessEvent", "LoanRejectTransferBusinessEvent", "LoanRemoveOfficerBusinessEvent",
"LoanRescheduledDueCalendarChangeBusinessEvent", "LoanRescheduledDueHolidayBusinessEvent",
"LoanScheduleVariationsAddedBusinessEvent", "LoanScheduleVariationsDeletedBusinessEvent", "LoanStatusChangedBusinessEvent",
"LoanTransactionGoodwillCreditPostBusinessEvent", "LoanTransactionGoodwillCreditPreBusinessEvent",
"LoanTransactionMakeRepaymentPostBusinessEvent", "LoanTransactionMakeRepaymentPreBusinessEvent",
"LoanTransactionMerchantIssuedRefundPostBusinessEvent", "LoanTransactionMerchantIssuedRefundPreBusinessEvent",
"LoanTransactionPayoutRefundPostBusinessEvent", "LoanTransactionPayoutRefundPreBusinessEvent",
"LoanTransactionRecoveryPaymentPostBusinessEvent", "LoanTransactionRecoveryPaymentPreBusinessEvent",
"LoanUndoApprovalBusinessEvent", "LoanUndoDisbursalBusinessEvent", "LoanUndoLastDisbursalBusinessEvent",
"LoanUndoWrittenOffBusinessEvent", "LoanUpdateChargeBusinessEvent", "LoanUpdateDisbursementDataBusinessEvent",
"LoanWaiveChargeBusinessEvent", "LoanWaiveChargeUndoBusinessEvent", "LoanWaiveInterestBusinessEvent",
"LoanWithdrawTransferBusinessEvent", "LoanWrittenOffPostBusinessEvent", "LoanWrittenOffPreBusinessEvent",
"RecurringDepositAccountCreateBusinessEvent", "SavingsActivateBusinessEvent", "SavingsApproveBusinessEvent",
"SavingsCloseBusinessEvent", "SavingsCreateBusinessEvent", "SavingsDepositBusinessEvent",
"SavingsPostInterestBusinessEvent", "SavingsRejectBusinessEvent", "SavingsWithdrawalBusinessEvent",
"ShareAccountApproveBusinessEvent", "ShareAccountCreateBusinessEvent", "ShareProductDividentsCreateBusinessEvent");
List<FineractPlatformTenant> tenants = Arrays
.asList(new FineractPlatformTenant(1L, "default", "Default Tenant", "Europe/Budapest", null));
JdbcTemplate jdbcTemplate = Mockito.mock(JdbcTemplate.class);
when(tenantDetailsService.findAllTenants()).thenReturn(tenants);
when(jdbcTemplateFactory.create(any())).thenReturn(jdbcTemplate);
when(jdbcTemplate.queryForList(anyString(), eq(String.class))).thenReturn(configurationWithMissingCentersCreateBusinessEvent);
// when
ExternalEventConfigurationNotFoundException exceptionThrown = assertThrows(ExternalEventConfigurationNotFoundException.class,
() -> underTest.afterPropertiesSet());
// then
String expectedMessage = "Configuration not found for external event CentersCreateBusinessEvent";
String actualMessage = exceptionThrown.getMessage();
assertTrue(actualMessage.contains(expectedMessage));
}
}