[FINERACT-1678] Configuration for enabling/disabling Loan COB

This commit is contained in:
taskain7
2023-08-07 14:32:39 +02:00
parent a04c8da659
commit 7559e90ce2
27 changed files with 535 additions and 18 deletions
@@ -380,6 +380,7 @@ public class FineractProperties {
public static class FineractJobProperties { public static class FineractJobProperties {
private int stuckRetryThreshold; private int stuckRetryThreshold;
private boolean loanCobEnabled;
} }
@Getter @Getter
@@ -177,6 +177,17 @@ public class ApiGlobalErrorResponse {
return globalErrorResponse; return globalErrorResponse;
} }
public static ApiGlobalErrorResponse jobIsDisabled(final String globalisationMessageCode, final String defaultUserMessage) {
final ApiGlobalErrorResponse globalErrorResponse = new ApiGlobalErrorResponse();
globalErrorResponse.setHttpStatusCode(Status.FORBIDDEN.toString());
globalErrorResponse.setDeveloperMessage(defaultUserMessage);
globalErrorResponse.setUserMessageGlobalisationCode(globalisationMessageCode);
globalErrorResponse.setDefaultUserMessage(defaultUserMessage);
return globalErrorResponse;
}
public static ApiGlobalErrorResponse badClientRequest(final String globalisationMessageCode, final String defaultUserMessage) { public static ApiGlobalErrorResponse badClientRequest(final String globalisationMessageCode, final String defaultUserMessage) {
return badClientRequest(globalisationMessageCode, defaultUserMessage, Collections.emptyList()); return badClientRequest(globalisationMessageCode, defaultUserMessage, Collections.emptyList());
} }
@@ -0,0 +1,30 @@
/**
* 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.core.exception;
public class JobIsNotFoundOrNotEnabledException extends AbstractPlatformException {
public JobIsNotFoundOrNotEnabledException(Exception e, String jobName) {
super("error.msg.job.disabled", "Job " + jobName + " is not found or it is disabled", e);
}
public JobIsNotFoundOrNotEnabledException(String jobName) {
super("error.msg.job.disabled", "Job " + jobName + " is not found or it is disabled");
}
}
@@ -0,0 +1,42 @@
/**
* 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.core.exceptionmapper;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.data.ApiGlobalErrorResponse;
import org.apache.fineract.infrastructure.core.exception.JobIsNotFoundOrNotEnabledException;
import org.springframework.stereotype.Component;
@Provider
@Component
@Slf4j
public class JobIsNotFoundOrNotEnabledExceptionMapper implements ExceptionMapper<JobIsNotFoundOrNotEnabledException> {
@Override
public Response toResponse(JobIsNotFoundOrNotEnabledException exception) {
log.warn("Exception: {}, Message: {}", exception.getClass().getName(), exception.getMessage());
return Response.status(Response.Status.FORBIDDEN)
.entity(ApiGlobalErrorResponse.jobIsDisabled(exception.getGlobalisationMessageCode(), exception.getDefaultUserMessage()))
.type(MediaType.APPLICATION_JSON).build();
}
}
@@ -254,3 +254,55 @@ PUT /fineract-provider/api/v1/jobs/{jobName}/steps?tenantIdentifier={tenantId}
---- ----
The business step configuration for jobs are tracked within the database in the `m_batch_business_steps` table. The business step configuration for jobs are tracked within the database in the `m_batch_business_steps` table.
== Inline Jobs
Some jobs that work with business entities have a corresponding job that can trigger the job with a list of specified entities.
When the Inline job gets triggered then then the corresponding existing job will run in real time with the given entities as a dataset.
=== List of Inline jobs
[cols="1,1"]
|===
|*Inline Job name*
|*Corresponding Job*
|LOAN_COB
|LOAN_CLOSE_OF_BUSINESS
|===
Triggering the Inline Loan COB Job:
[source]
----
POST /fineract-provider/api/v1/jobs/LOAN_COB/inline?tenantIdentifier={tenantId}
{
"loanIds": [
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14
]
}
----
In this case the Loan COB job will work only with the given loan IDs.
== Global Configuration for enabling/disabling jobs
Some jobs can be enabled/disabled with global configuration.
If the job is disabled with the global configuration then it cannot be scheduled and cannot be triggered via API.
=== List of jobs with global configuration
[cols="1,1,1,1"]
|===
|*Job name*
|*Application property*
|*Environment variable*
|*Default value*
|LOAN_CLOSE_OF_BUSINESS
|fineract.job.loan-cob-enabled
|FINERACT_JOB_LOAN_COB_ENABLED
|true
|===
@@ -31,22 +31,27 @@ import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces; import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response; import jakarta.ws.rs.core.Response;
import lombok.RequiredArgsConstructor; import java.util.Objects;
import org.apache.fineract.cob.data.IsCatchUpRunningDTO; import org.apache.fineract.cob.data.IsCatchUpRunningDTO;
import org.apache.fineract.cob.data.OldestCOBProcessedLoanDTO; import org.apache.fineract.cob.data.OldestCOBProcessedLoanDTO;
import org.apache.fineract.cob.service.LoanCOBCatchUpService; import org.apache.fineract.cob.service.LoanCOBCatchUpService;
import org.apache.fineract.infrastructure.core.exception.JobIsNotFoundOrNotEnabledException;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer; import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.infrastructure.jobs.service.JobName;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Path("/v1/loans") @Path("/v1/loans")
@Component @Component
@Tag(name = "Loan COB Catch Up", description = "") @Tag(name = "Loan COB Catch Up", description = "")
@RequiredArgsConstructor
public class LoanCOBCatchUpApiResource { public class LoanCOBCatchUpApiResource {
private final DefaultToApiJsonSerializer<OldestCOBProcessedLoanDTO> oldestCOBProcessedLoanSerializeService; @Autowired
private final DefaultToApiJsonSerializer<IsCatchUpRunningDTO> isCatchUpRunningSerializer; private DefaultToApiJsonSerializer<OldestCOBProcessedLoanDTO> oldestCOBProcessedLoanSerializeService;
private final LoanCOBCatchUpService loanCOBCatchUpService; @Autowired
private DefaultToApiJsonSerializer<IsCatchUpRunningDTO> isCatchUpRunningSerializer;
@Autowired(required = false)
private LoanCOBCatchUpService loanCOBCatchUpService;
@GET @GET
@Path("oldest-cob-closed") @Path("oldest-cob-closed")
@@ -56,6 +61,9 @@ public class LoanCOBCatchUpApiResource {
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanCOBCatchUpApiResourceSwagger.GetOldestCOBProcessedLoanResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanCOBCatchUpApiResourceSwagger.GetOldestCOBProcessedLoanResponse.class))) })
public String getOldestCOBProcessedLoan() { public String getOldestCOBProcessedLoan() {
if (Objects.isNull(loanCOBCatchUpService)) {
throw new JobIsNotFoundOrNotEnabledException(JobName.LOAN_COB.name());
}
OldestCOBProcessedLoanDTO response = loanCOBCatchUpService.getOldestCOBProcessedLoan(); OldestCOBProcessedLoanDTO response = loanCOBCatchUpService.getOldestCOBProcessedLoan();
return oldestCOBProcessedLoanSerializeService.serialize(response); return oldestCOBProcessedLoanSerializeService.serialize(response);
} }
@@ -69,6 +77,9 @@ public class LoanCOBCatchUpApiResource {
@ApiResponse(responseCode = "202", description = "Catch Up has been started"), @ApiResponse(responseCode = "202", description = "Catch Up has been started"),
@ApiResponse(responseCode = "400", description = "Catch Up is already running") }) @ApiResponse(responseCode = "400", description = "Catch Up is already running") })
public Response executeLoanCOBCatchUp() { public Response executeLoanCOBCatchUp() {
if (Objects.isNull(loanCOBCatchUpService)) {
throw new JobIsNotFoundOrNotEnabledException(JobName.LOAN_COB.name());
}
if (loanCOBCatchUpService.isCatchUpRunning().isCatchUpRunning()) { if (loanCOBCatchUpService.isCatchUpRunning().isCatchUpRunning()) {
return Response.status(Response.Status.BAD_REQUEST).build(); return Response.status(Response.Status.BAD_REQUEST).build();
} }
@@ -90,6 +101,9 @@ public class LoanCOBCatchUpApiResource {
@ApiResponses({ @ApiResponses({
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanCOBCatchUpApiResourceSwagger.IsCatchUpRunningResponse.class))) }) @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanCOBCatchUpApiResourceSwagger.IsCatchUpRunningResponse.class))) })
public String isCatchUpRunning() { public String isCatchUpRunning() {
if (Objects.isNull(loanCOBCatchUpService)) {
throw new JobIsNotFoundOrNotEnabledException(JobName.LOAN_COB.name());
}
IsCatchUpRunningDTO response = loanCOBCatchUpService.isCatchUpRunning(); IsCatchUpRunningDTO response = loanCOBCatchUpService.isCatchUpRunning();
return isCatchUpRunningSerializer.serialize(response); return isCatchUpRunningSerializer.serialize(response);
@@ -0,0 +1,30 @@
/**
* 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.cob.conditions;
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
public class LoanCOBEnabledCondition extends PropertiesCondition {
@Override
protected boolean matches(FineractProperties properties) {
return properties.getJob().isLoanCobEnabled();
}
}
@@ -0,0 +1,32 @@
/**
* 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.cob.conditions;
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
public class LoanCOBManagerCondition extends PropertiesCondition {
@Override
protected boolean matches(FineractProperties properties) {
boolean loanCobEnabled = properties.getJob().isLoanCobEnabled();
boolean batchManagerEnabled = properties.getMode().isBatchManagerEnabled();
return loanCobEnabled && batchManagerEnabled;
}
}
@@ -0,0 +1,32 @@
/**
* 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.cob.conditions;
import org.apache.fineract.infrastructure.core.condition.PropertiesCondition;
import org.apache.fineract.infrastructure.core.config.FineractProperties;
public class LoanCOBWorkerCondition extends PropertiesCondition {
@Override
protected boolean matches(FineractProperties properties) {
boolean loanCobEnabled = properties.getJob().isLoanCobEnabled();
boolean batchWorkerEnabled = properties.getMode().isBatchWorkerEnabled();
return loanCobEnabled && batchWorkerEnabled;
}
}
@@ -20,6 +20,7 @@ package org.apache.fineract.cob.loan;
import org.apache.fineract.cob.COBBusinessStepService; import org.apache.fineract.cob.COBBusinessStepService;
import org.apache.fineract.cob.common.CustomJobParameterResolver; import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.conditions.LoanCOBManagerCondition;
import org.apache.fineract.cob.listener.COBExecutionListenerRunner; import org.apache.fineract.cob.listener.COBExecutionListenerRunner;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.infrastructure.jobs.service.JobName;
@@ -37,16 +38,16 @@ import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.integration.config.annotation.EnableBatchIntegration; import org.springframework.batch.integration.config.annotation.EnableBatchIntegration;
import org.springframework.batch.integration.partition.RemotePartitioningManagerStepBuilderFactory; import org.springframework.batch.integration.partition.RemotePartitioningManagerStepBuilderFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.DirectChannel;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
@Configuration @Configuration
@EnableBatchIntegration @EnableBatchIntegration
@ConditionalOnProperty(value = "fineract.mode.batch-manager-enabled", havingValue = "true") @Conditional(LoanCOBManagerCondition.class)
public class LoanCOBManagerConfiguration { public class LoanCOBManagerConfiguration {
@Autowired @Autowired
@@ -22,6 +22,7 @@ import org.apache.fineract.cob.COBBusinessStepService;
import org.apache.fineract.cob.common.CustomJobParameterResolver; import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.common.InitialisationTasklet; import org.apache.fineract.cob.common.InitialisationTasklet;
import org.apache.fineract.cob.common.ResetContextTasklet; import org.apache.fineract.cob.common.ResetContextTasklet;
import org.apache.fineract.cob.conditions.LoanCOBWorkerCondition;
import org.apache.fineract.cob.listener.ChunkProcessingLoanItemListener; import org.apache.fineract.cob.listener.ChunkProcessingLoanItemListener;
import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.infrastructure.jobs.service.JobName;
@@ -39,8 +40,8 @@ import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.integration.partition.RemotePartitioningWorkerStepBuilderFactory; import org.springframework.batch.integration.partition.RemotePartitioningWorkerStepBuilderFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SyncTaskExecutor; import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.core.task.TaskExecutor; import org.springframework.core.task.TaskExecutor;
@@ -50,7 +51,7 @@ import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
@Configuration @Configuration
@ConditionalOnProperty(value = "fineract.mode.batch-worker-enabled", havingValue = "true") @Conditional(LoanCOBWorkerCondition.class)
public class LoanCOBWorkerConfiguration { public class LoanCOBWorkerConfiguration {
@Autowired @Autowired
@@ -21,6 +21,7 @@ package org.apache.fineract.cob.loan;
import org.apache.fineract.cob.COBBusinessStepService; import org.apache.fineract.cob.COBBusinessStepService;
import org.apache.fineract.cob.common.CustomJobParameterResolver; import org.apache.fineract.cob.common.CustomJobParameterResolver;
import org.apache.fineract.cob.common.ResetContextTasklet; import org.apache.fineract.cob.common.ResetContextTasklet;
import org.apache.fineract.cob.conditions.LoanCOBEnabledCondition;
import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LoanAccountLockRepository;
import org.apache.fineract.cob.listener.InlineCOBLoanItemListener; import org.apache.fineract.cob.listener.InlineCOBLoanItemListener;
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository; import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
@@ -38,12 +39,14 @@ import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.integration.config.annotation.EnableBatchIntegration; import org.springframework.batch.integration.config.annotation.EnableBatchIntegration;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate; import org.springframework.transaction.support.TransactionTemplate;
@Configuration @Configuration
@EnableBatchIntegration @EnableBatchIntegration
@Conditional(LoanCOBEnabledCondition.class)
public class LoanInlineCOBConfig { public class LoanInlineCOBConfig {
@Autowired @Autowired
@@ -25,6 +25,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.cob.conditions.LoanCOBEnabledCondition;
import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate; import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate;
import org.apache.fineract.cob.loan.LoanCOBConstant; import org.apache.fineract.cob.loan.LoanCOBConstant;
import org.apache.fineract.cob.loan.RetrieveLoanIdService; import org.apache.fineract.cob.loan.RetrieveLoanIdService;
@@ -45,11 +46,13 @@ import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException;
import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException;
import org.springframework.batch.core.repository.JobRestartException; import org.springframework.batch.core.repository.JobRestartException;
import org.springframework.context.annotation.Conditional;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@Conditional(LoanCOBEnabledCondition.class)
public class AsyncLoanCOBExecutorServiceImpl implements AsyncLoanCOBExecutorService { public class AsyncLoanCOBExecutorServiceImpl implements AsyncLoanCOBExecutorService {
private final JobLocator jobLocator; private final JobLocator jobLocator;
@@ -35,6 +35,7 @@ import java.util.Set;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.cob.conditions.LoanCOBEnabledCondition;
import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate; import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate;
import org.apache.fineract.cob.domain.LoanAccountLock; import org.apache.fineract.cob.domain.LoanAccountLock;
import org.apache.fineract.cob.domain.LoanAccountLockRepository; import org.apache.fineract.cob.domain.LoanAccountLockRepository;
@@ -68,6 +69,7 @@ import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.explore.JobExplorer; import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.NoSuchJobException; import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
@@ -78,6 +80,7 @@ import org.springframework.transaction.support.TransactionTemplate;
@Service @Service
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Conditional(LoanCOBEnabledCondition.class)
public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<Long> { public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<Long> {
private static final String JOB_EXECUTION_FAILED_MESSAGE = "Job execution failed for job with name: "; private static final String JOB_EXECUTION_FAILED_MESSAGE = "Job execution failed for job with name: ";
@@ -22,6 +22,7 @@ import com.google.gson.Gson;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.cob.conditions.LoanCOBEnabledCondition;
import org.apache.fineract.cob.data.IsCatchUpRunningDTO; import org.apache.fineract.cob.data.IsCatchUpRunningDTO;
import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate; import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate;
import org.apache.fineract.cob.data.OldestCOBProcessedLoanDTO; import org.apache.fineract.cob.data.OldestCOBProcessedLoanDTO;
@@ -34,10 +35,12 @@ import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository; import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
import org.apache.fineract.infrastructure.jobs.domain.JobExecutionRepository; import org.apache.fineract.infrastructure.jobs.domain.JobExecutionRepository;
import org.springframework.batch.core.explore.JobExplorer; import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
@Conditional(LoanCOBEnabledCondition.class)
public class LoanCOBCatchUpServiceImpl implements LoanCOBCatchUpService { public class LoanCOBCatchUpServiceImpl implements LoanCOBCatchUpService {
private final AsyncLoanCOBExecutorService asyncLoanCOBExecutorService; private final AsyncLoanCOBExecutorService asyncLoanCOBExecutorService;
@@ -24,6 +24,7 @@ import static org.springframework.security.authorization.AuthorityAuthorizationM
import static org.springframework.security.authorization.AuthorizationManagers.allOf; import static org.springframework.security.authorization.AuthorizationManagers.allOf;
import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher;
import java.util.Objects;
import org.apache.fineract.commands.domain.CommandSourceRepository; import org.apache.fineract.commands.domain.CommandSourceRepository;
import org.apache.fineract.commands.service.CommandSourceService; import org.apache.fineract.commands.service.CommandSourceService;
import org.apache.fineract.infrastructure.businessdate.service.BusinessDateReadPlatformService; import org.apache.fineract.infrastructure.businessdate.service.BusinessDateReadPlatformService;
@@ -107,7 +108,7 @@ public class SecurityConfig {
@Autowired @Autowired
private FineractRequestContextHolder fineractRequestContextHolder; private FineractRequestContextHolder fineractRequestContextHolder;
@Autowired @Autowired(required = false)
private LoanCOBFilterHelper loanCOBFilterHelper; private LoanCOBFilterHelper loanCOBFilterHelper;
@Autowired @Autowired
private PlatformSecurityContext context; private PlatformSecurityContext context;
@@ -136,9 +137,13 @@ public class SecurityConfig {
.addFilterAfter(requestResponseFilter(), ExceptionTranslationFilter.class) // .addFilterAfter(requestResponseFilter(), ExceptionTranslationFilter.class) //
.addFilterAfter(correlationHeaderFilter(), RequestResponseFilter.class) // .addFilterAfter(correlationHeaderFilter(), RequestResponseFilter.class) //
.addFilterAfter(responseCorsFilter(), CorrelationHeaderFilter.class) // .addFilterAfter(responseCorsFilter(), CorrelationHeaderFilter.class) //
.addFilterAfter(fineractInstanceModeApiFilter(), ResponseCorsFilter.class) // .addFilterAfter(fineractInstanceModeApiFilter(), ResponseCorsFilter.class); //
.addFilterAfter(loanCOBApiFilter(), FineractInstanceModeApiFilter.class) // if (!Objects.isNull(loanCOBFilterHelper)) {
.addFilterAfter(idempotencyStoreFilter(), LoanCOBApiFilter.class); // http.addFilterAfter(loanCOBApiFilter(), FineractInstanceModeApiFilter.class) //
.addFilterAfter(idempotencyStoreFilter(), LoanCOBApiFilter.class); //
} else {
http.addFilterAfter(idempotencyStoreFilter(), FineractInstanceModeApiFilter.class); //
}
if (fineractProperties.getSecurity().getTwoFactor().isEnabled()) { if (fineractProperties.getSecurity().getTwoFactor().isEnabled()) {
http.addFilterAfter(twoFactorAuthenticationFilter(), ResponseCorsFilter.class); http.addFilterAfter(twoFactorAuthenticationFilter(), ResponseCorsFilter.class);
@@ -25,13 +25,16 @@ import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.cob.conditions.LoanCOBEnabledCondition;
import org.apache.fineract.infrastructure.core.data.ApiGlobalErrorResponse; import org.apache.fineract.infrastructure.core.data.ApiGlobalErrorResponse;
import org.apache.fineract.infrastructure.jobs.exception.LoanIdsHardLockedException; import org.apache.fineract.infrastructure.jobs.exception.LoanIdsHardLockedException;
import org.apache.fineract.useradministration.exception.UnAuthenticatedUserException; import org.apache.fineract.useradministration.exception.UnAuthenticatedUserException;
import org.apache.http.HttpStatus; import org.apache.http.HttpStatus;
import org.springframework.context.annotation.Conditional;
import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.filter.OncePerRequestFilter;
@RequiredArgsConstructor @RequiredArgsConstructor
@Conditional(LoanCOBEnabledCondition.class)
public class LoanCOBApiFilter extends OncePerRequestFilter { public class LoanCOBApiFilter extends OncePerRequestFilter {
private final LoanCOBFilterHelper helper; private final LoanCOBFilterHelper helper;
@@ -24,8 +24,10 @@ import io.github.resilience4j.core.functions.Either;
import java.util.List; import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.domain.BatchRequest; import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.cob.conditions.LoanCOBEnabledCondition;
import org.apache.fineract.infrastructure.core.filters.BatchRequestPreprocessor; import org.apache.fineract.infrastructure.core.filters.BatchRequestPreprocessor;
import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException; import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
@@ -33,6 +35,7 @@ import org.springframework.transaction.support.TransactionTemplate;
@Component @Component
@RequiredArgsConstructor @RequiredArgsConstructor
@Conditional(LoanCOBEnabledCondition.class)
public class LoanCOBBatchPreprocessor implements BatchRequestPreprocessor { public class LoanCOBBatchPreprocessor implements BatchRequestPreprocessor {
private final LoanCOBFilterHelper helper; private final LoanCOBFilterHelper helper;
@@ -28,6 +28,7 @@ import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.cob.conditions.LoanCOBEnabledCondition;
import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate; import org.apache.fineract.cob.data.LoanIdAndLastClosedBusinessDate;
import org.apache.fineract.cob.loan.RetrieveLoanIdService; import org.apache.fineract.cob.loan.RetrieveLoanIdService;
import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl; import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl;
@@ -43,11 +44,13 @@ import org.apache.fineract.portfolio.loanaccount.domain.GroupLoanIndividualMonit
import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.Loan;
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
import org.springframework.context.annotation.Conditional;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@RequiredArgsConstructor @RequiredArgsConstructor
@Component @Component
@Conditional(LoanCOBEnabledCondition.class)
public class LoanCOBFilterHelper { public class LoanCOBFilterHelper {
private final GLIMAccountInfoRepository glimAccountInfoRepository; private final GLIMAccountInfoRepository glimAccountInfoRepository;
@@ -23,6 +23,8 @@ import org.apache.fineract.commands.annotation.CommandType;
import org.apache.fineract.commands.handler.NewCommandSourceHandler; import org.apache.fineract.commands.handler.NewCommandSourceHandler;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.exception.JobIsNotFoundOrNotEnabledException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -36,7 +38,11 @@ public class InlineJobExecuteHandler implements NewCommandSourceHandler {
@Override @Override
public CommandProcessingResult processCommand(JsonCommand command) { public CommandProcessingResult processCommand(JsonCommand command) {
InlineJobType inlineJobType = InlineJobType.getInlineJobType(command.getJobName()); InlineJobType inlineJobType = InlineJobType.getInlineJobType(command.getJobName());
InlineExecutorService inlineJobExecutorService = applicationContext.getBean(inlineJobType.getExecutorServiceClass()); try {
return inlineJobExecutorService.executeInlineJob(command, inlineJobType.getInlineJobName()); InlineExecutorService inlineJobExecutorService = applicationContext.getBean(inlineJobType.getExecutorServiceClass());
return inlineJobExecutorService.executeInlineJob(command, inlineJobType.getInlineJobName());
} catch (NoSuchBeanDefinitionException e) {
throw new JobIsNotFoundOrNotEnabledException(e, inlineJobType.getInlineJobName());
}
} }
} }
@@ -29,6 +29,7 @@ import java.util.TimeZone;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.exception.JobIsNotFoundOrNotEnabledException;
import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException; import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO; import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
@@ -48,6 +49,7 @@ import org.quartz.Trigger;
import org.quartz.TriggerListener; import org.quartz.TriggerListener;
import org.springframework.batch.core.Job; import org.springframework.batch.core.Job;
import org.springframework.batch.core.configuration.JobLocator; import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.launch.NoSuchJobException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener; import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextClosedEvent;
@@ -118,6 +120,10 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
scheduler.addJob(jobDetail, true); scheduler.addJob(jobDetail, true);
scheduler.triggerJob(jobKey, jobDataMap); scheduler.triggerJob(jobKey, jobDataMap);
} }
} catch (JobIsNotFoundOrNotEnabledException e) {
final String msg = "Job is not found or it is disabled with job ID: " + scheduledJobDetail.getId();
log.error("{}", msg, e);
throw e;
} catch (final Exception e) { } catch (final Exception e) {
final String msg = "Job execution failed for job with id:" + scheduledJobDetail.getId(); final String msg = "Job execution failed for job with id:" + scheduledJobDetail.getId();
log.error("{}", msg, e); log.error("{}", msg, e);
@@ -327,7 +333,12 @@ public class JobRegisterServiceImpl implements JobRegisterService, ApplicationLi
final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant(); final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
JobNameData jobName = jobNameService.getJobByHumanReadableName(scheduledJobDetail.getJobName()); JobNameData jobName = jobNameService.getJobByHumanReadableName(scheduledJobDetail.getJobName());
Job job = jobLocator.getJob(jobName.getEnumStyleName()); Job job;
try {
job = jobLocator.getJob(jobName.getEnumStyleName());
} catch (NoSuchJobException e) {
throw new JobIsNotFoundOrNotEnabledException(e, jobName.getEnumStyleName());
}
final MethodInvokingJobDetailFactoryBean jobDetailFactoryBean = new MethodInvokingJobDetailFactoryBean(); final MethodInvokingJobDetailFactoryBean jobDetailFactoryBean = new MethodInvokingJobDetailFactoryBean();
jobDetailFactoryBean.setName(scheduledJobDetail.getJobName() + "JobDetail" + tenant.getId()); jobDetailFactoryBean.setName(scheduledJobDetail.getJobName() + "JobDetail" + tenant.getId());
@@ -28,6 +28,7 @@ import org.apache.fineract.infrastructure.businessdate.service.BusinessDateReadP
import org.apache.fineract.infrastructure.core.config.FineractProperties; import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.domain.ActionContext; import org.apache.fineract.infrastructure.core.domain.ActionContext;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.exception.JobIsNotFoundOrNotEnabledException;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.core.service.tenant.TenantDetailsService; import org.apache.fineract.infrastructure.core.service.tenant.TenantDetailsService;
import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail; import org.apache.fineract.infrastructure.jobs.domain.ScheduledJobDetail;
@@ -63,7 +64,11 @@ public class JobSchedulerServiceImpl implements ApplicationListener<ContextRefre
final List<ScheduledJobDetail> scheduledJobDetails = schedularWritePlatformService final List<ScheduledJobDetail> scheduledJobDetails = schedularWritePlatformService
.retrieveAllJobs(fineractProperties.getNodeId()); .retrieveAllJobs(fineractProperties.getNodeId());
for (final ScheduledJobDetail jobDetails : scheduledJobDetails) { for (final ScheduledJobDetail jobDetails : scheduledJobDetails) {
jobRegisterService.scheduleJob(jobDetails); try {
jobRegisterService.scheduleJob(jobDetails);
} catch (JobIsNotFoundOrNotEnabledException e) {
log.warn("{}", e.getMessage());
}
jobDetails.setTriggerMisfired(false); jobDetails.setTriggerMisfired(false);
schedularWritePlatformService.saveOrUpdate(jobDetails); schedularWritePlatformService.saveOrUpdate(jobDetails);
} }
@@ -60,6 +60,7 @@ fineract.correlation.enabled=${FINERACT_LOGGING_HTTP_CORRELATION_ID_ENABLED:fals
fineract.correlation.header-name=${FINERACT_LOGGING_HTTP_CORRELATION_ID_HEADER_NAME:X-Correlation-ID} fineract.correlation.header-name=${FINERACT_LOGGING_HTTP_CORRELATION_ID_HEADER_NAME:X-Correlation-ID}
fineract.job.stuck-retry-threshold=${FINERACT_JOB_STUCK_RETRY_THRESHOLD:5} fineract.job.stuck-retry-threshold=${FINERACT_JOB_STUCK_RETRY_THRESHOLD:5}
fineract.job.loan-cob-enabled=${FINERACT_JOB_LOAN_COB_ENABLED:true}
fineract.partitioned-job.partitioned-job-properties[0].job-name=LOAN_COB fineract.partitioned-job.partitioned-job-properties[0].job-name=LOAN_COB
fineract.partitioned-job.partitioned-job-properties[0].chunk-size=${LOAN_COB_CHUNK_SIZE:100} fineract.partitioned-job.partitioned-job-properties[0].chunk-size=${LOAN_COB_CHUNK_SIZE:100}
@@ -0,0 +1,67 @@
/**
* 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.cob.conditions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.mock.env.MockEnvironment;
@ExtendWith(MockitoExtension.class)
class LoanCOBEnabledConditionTest {
@Mock
private ConditionContext conditionContext;
@Mock
private AnnotatedTypeMetadata metadata;
private MockEnvironment environment;
@InjectMocks
private LoanCOBEnabledCondition testObj = new LoanCOBEnabledCondition();
@BeforeEach
public void setUp() {
environment = new MockEnvironment();
given(conditionContext.getEnvironment()).willReturn(environment);
}
@Test
public void testMatchesShouldReturnFalseWhenLoanCobIsDisabled() {
environment.withProperty("fineract.job.loan-cob-enabled", "false");
boolean result = testObj.matches(conditionContext, metadata);
assertThat(result).isFalse();
}
@Test
public void testMatchesShouldReturnTrueWhenLoanCobIsEnabled() {
environment.withProperty("fineract.job.loan-cob-enabled", "true");
boolean result = testObj.matches(conditionContext, metadata);
assertThat(result).isTrue();
}
}
@@ -0,0 +1,77 @@
/**
* 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.cob.conditions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.mock.env.MockEnvironment;
@ExtendWith(MockitoExtension.class)
class LoanCOBManagerConditionTest {
@Mock
private ConditionContext conditionContext;
@Mock
private AnnotatedTypeMetadata metadata;
private MockEnvironment environment;
@InjectMocks
private LoanCOBManagerCondition testObj = new LoanCOBManagerCondition();
@BeforeEach
public void setUp() {
environment = new MockEnvironment();
given(conditionContext.getEnvironment()).willReturn(environment);
}
@Test
public void testMatchesShouldReturnFalseWhenLoanCobIsDisabledAndManager() {
environment.withProperty("fineract.job.loan-cob-enabled", "false");
environment.withProperty("fineract.mode.batch-manager-enabled", "true");
boolean result = testObj.matches(conditionContext, metadata);
assertThat(result).isFalse();
}
@Test
public void testMatchesShouldReturnFalseWhenLoanCobIsEnabledAndNotManager() {
environment.withProperty("fineract.job.loan-cob-enabled", "true");
environment.withProperty("fineract.mode.batch-manager-enabled", "false");
boolean result = testObj.matches(conditionContext, metadata);
assertThat(result).isFalse();
}
@Test
public void testMatchesShouldReturnTrueWhenLoanCobIsEnabledAndManager() {
environment.withProperty("fineract.job.loan-cob-enabled", "true");
environment.withProperty("fineract.mode.batch-manager-enabled", "true");
boolean result = testObj.matches(conditionContext, metadata);
assertThat(result).isTrue();
}
}
@@ -0,0 +1,77 @@
/**
* 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.cob.conditions;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.mock.env.MockEnvironment;
@ExtendWith(MockitoExtension.class)
class LoanCOBWorkerConditionTest {
@Mock
private ConditionContext conditionContext;
@Mock
private AnnotatedTypeMetadata metadata;
private MockEnvironment environment;
@InjectMocks
private LoanCOBWorkerCondition testObj = new LoanCOBWorkerCondition();
@BeforeEach
public void setUp() {
environment = new MockEnvironment();
given(conditionContext.getEnvironment()).willReturn(environment);
}
@Test
public void testMatchesShouldReturnFalseWhenLoanCobIsDisabledAndWorker() {
environment.withProperty("fineract.job.loan-cob-enabled", "false");
environment.withProperty("fineract.mode.batch-worker-enabled", "true");
boolean result = testObj.matches(conditionContext, metadata);
assertThat(result).isFalse();
}
@Test
public void testMatchesShouldReturnFalseWhenLoanCobIsEnabledAndNotWorker() {
environment.withProperty("fineract.job.loan-cob-enabled", "true");
environment.withProperty("fineract.mode.batch-worker-enabled", "false");
boolean result = testObj.matches(conditionContext, metadata);
assertThat(result).isFalse();
}
@Test
public void testMatchesShouldReturnTrueWhenLoanCobIsEnabledAndWorker() {
environment.withProperty("fineract.job.loan-cob-enabled", "true");
environment.withProperty("fineract.mode.batch-worker-enabled", "true");
boolean result = testObj.matches(conditionContext, metadata);
assertThat(result).isTrue();
}
}
@@ -87,6 +87,7 @@ fineract.report.export.s3.enabled=${FINERACT_REPORT_EXPORT_S3_ENABLED:false}
fineract.jpa.statementLoggingEnabled=${FINERACT_STATEMENT_LOGGING_ENABLED:false} fineract.jpa.statementLoggingEnabled=${FINERACT_STATEMENT_LOGGING_ENABLED:false}
fineract.job.loan-cob-enabled=${FINERACT_JOB_LOAN_COB_ENABLED:true}
fineract.sampling.enabled=false fineract.sampling.enabled=false
fineract.sampling.sampledClasses= fineract.sampling.sampledClasses=