[FINERACT-1678] Fix Inline COB execution

This commit is contained in:
taskain7
2022-11-30 08:52:36 +01:00
committed by Arnold Gálovics
parent 53b474b461
commit 47108e0ac9
5 changed files with 76 additions and 15 deletions
@@ -18,8 +18,14 @@
*/ */
package org.apache.fineract.cob.common; package org.apache.fineract.cob.common;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Objects;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.cob.loan.LoanCOBConstant;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
import org.apache.fineract.infrastructure.core.domain.ActionContext; import org.apache.fineract.infrastructure.core.domain.ActionContext;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.useradministration.domain.AppUser; import org.apache.fineract.useradministration.domain.AppUser;
@@ -41,11 +47,18 @@ public class InitialisationTasklet implements Tasklet {
@Override @Override
public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception { public RepeatStatus execute(@NotNull StepContribution contribution, @NotNull ChunkContext chunkContext) throws Exception {
HashMap<BusinessDateType, LocalDate> businessDates = ThreadLocalContextUtil.getBusinessDates();
AppUser user = userRepository.fetchSystemUser(); AppUser user = userRepository.fetchSystemUser();
UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, user.getPassword(), UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(user, user.getPassword(),
new NullAuthoritiesMapper().mapAuthorities(user.getAuthorities())); new NullAuthoritiesMapper().mapAuthorities(user.getAuthorities()));
SecurityContextHolder.getContext().setAuthentication(auth); SecurityContextHolder.getContext().setAuthentication(auth);
ThreadLocalContextUtil.setActionContext(ActionContext.COB); ThreadLocalContextUtil.setActionContext(ActionContext.COB);
String businessDateString = Objects.requireNonNull((String) chunkContext.getStepContext().getStepExecution().getJobExecution()
.getExecutionContext().get(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME));
LocalDate businessDate = LocalDate.parse(businessDateString, DateTimeFormatter.ISO_DATE);
businessDates.put(BusinessDateType.COB_DATE, businessDate);
businessDates.put(BusinessDateType.BUSINESS_DATE, businessDate.plusDays(1));
ThreadLocalContextUtil.setBusinessDates(businessDates);
return RepeatStatus.FINISHED; return RepeatStatus.FINISHED;
} }
} }
@@ -20,12 +20,16 @@ package org.apache.fineract.cob.loan;
import com.google.common.reflect.TypeToken; import com.google.common.reflect.TypeToken;
import com.google.gson.Gson; import com.google.gson.Gson;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.TreeMap; import java.util.TreeMap;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.cob.COBBusinessStepService; import org.apache.fineract.cob.COBBusinessStepService;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
import org.apache.fineract.infrastructure.core.domain.ActionContext; import org.apache.fineract.infrastructure.core.domain.ActionContext;
import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper; import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
@@ -37,14 +41,11 @@ import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext; import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus; import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.stereotype.Component;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@Component
public class InlineLoanCOBBuildExecutionContextTasklet implements Tasklet { public class InlineLoanCOBBuildExecutionContextTasklet implements Tasklet {
private final GoogleGsonSerializerHelper gsonFactory;
private final COBBusinessStepService cobBusinessStepService; private final COBBusinessStepService cobBusinessStepService;
private final CustomJobParameterRepository customJobParameterRepository; private final CustomJobParameterRepository customJobParameterRepository;
@@ -52,15 +53,30 @@ public class InlineLoanCOBBuildExecutionContextTasklet implements Tasklet {
@Override @Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
HashMap<BusinessDateType, LocalDate> businessDates = ThreadLocalContextUtil.getBusinessDates();
ThreadLocalContextUtil.setActionContext(ActionContext.COB); ThreadLocalContextUtil.setActionContext(ActionContext.COB);
TreeMap<Long, String> cobBusinessStepMap = cobBusinessStepService.getCOBBusinessStepMap(LoanCOBBusinessStep.class, TreeMap<Long, String> cobBusinessStepMap = cobBusinessStepService.getCOBBusinessStepMap(LoanCOBBusinessStep.class,
LoanCOBConstant.LOAN_COB_JOB_NAME); LoanCOBConstant.LOAN_COB_JOB_NAME);
contribution.getStepExecution().getExecutionContext().put(LoanCOBConstant.LOAN_IDS, getLoanIdsFromJobParameters(chunkContext)); contribution.getStepExecution().getExecutionContext().put(LoanCOBConstant.LOAN_IDS, getLoanIdsFromJobParameters(chunkContext));
contribution.getStepExecution().getExecutionContext().put(LoanCOBConstant.BUSINESS_STEP_MAP, cobBusinessStepMap); contribution.getStepExecution().getExecutionContext().put(LoanCOBConstant.BUSINESS_STEP_MAP, cobBusinessStepMap);
String businessDateString = getBusinessDateFromJobParameters(chunkContext);
contribution.getStepExecution().getExecutionContext().put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME, businessDateString);
LocalDate businessDate = LocalDate.parse(businessDateString, DateTimeFormatter.ISO_DATE);
businessDates.put(BusinessDateType.COB_DATE, businessDate);
businessDates.put(BusinessDateType.BUSINESS_DATE, businessDate.plusDays(1));
ThreadLocalContextUtil.setBusinessDates(businessDates);
return RepeatStatus.FINISHED; return RepeatStatus.FINISHED;
} }
private String getBusinessDateFromJobParameters(ChunkContext chunkContext) {
Long customJobParameterId = (Long) chunkContext.getStepContext().getJobParameters()
.get(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME);
CustomJobParameter customJobParameter = customJobParameterRepository.findById(customJobParameterId)
.orElseThrow(() -> new LoanNotFoundException(customJobParameterId));
String parameterJson = customJobParameter.getParameterJson();
return gson.fromJson(parameterJson, new TypeToken<String>() {}.getType());
}
private List<Long> getLoanIdsFromJobParameters(ChunkContext chunkContext) { private List<Long> getLoanIdsFromJobParameters(ChunkContext chunkContext) {
Long customJobParameterId = (Long) chunkContext.getStepContext().getJobParameters() Long customJobParameterId = (Long) chunkContext.getStepContext().getJobParameters()
.get(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY); .get(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY);
@@ -22,7 +22,6 @@ import org.apache.fineract.cob.COBBusinessStepService;
import org.apache.fineract.cob.common.ResetContextTasklet; import org.apache.fineract.cob.common.ResetContextTasklet;
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.core.serialization.GoogleGsonSerializerHelper;
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository; import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.infrastructure.jobs.service.JobName;
import org.apache.fineract.infrastructure.springbatch.PropertyService; import org.apache.fineract.infrastructure.springbatch.PropertyService;
@@ -60,12 +59,10 @@ public class LoanInlineCOBConfig {
private TransactionTemplate transactionTemplate; private TransactionTemplate transactionTemplate;
@Autowired @Autowired
private CustomJobParameterRepository loanIdListRepository; private CustomJobParameterRepository loanIdListRepository;
@Autowired
private GoogleGsonSerializerHelper googleGsonSerializerHelper;
@Bean @Bean
public InlineLoanCOBBuildExecutionContextTasklet inlineLoanCOBBuildExecutionContextTasklet() { public InlineLoanCOBBuildExecutionContextTasklet inlineLoanCOBBuildExecutionContextTasklet() {
return new InlineLoanCOBBuildExecutionContextTasklet(googleGsonSerializerHelper, cobBusinessStepService, loanIdListRepository); return new InlineLoanCOBBuildExecutionContextTasklet(cobBusinessStepService, loanIdListRepository);
} }
@Bean @Bean
@@ -124,7 +121,8 @@ public class LoanInlineCOBConfig {
@Bean @Bean
public ExecutionContextPromotionListener inlineCobPromotionListener() { public ExecutionContextPromotionListener inlineCobPromotionListener() {
ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener(); ExecutionContextPromotionListener listener = new ExecutionContextPromotionListener();
listener.setKeys(new String[] { LoanCOBConstant.LOAN_IDS, LoanCOBConstant.BUSINESS_STEP_MAP }); listener.setKeys(
new String[] { LoanCOBConstant.LOAN_IDS, LoanCOBConstant.BUSINESS_STEP_MAP, LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME });
return listener; return listener;
} }
} }
@@ -21,6 +21,8 @@ package org.apache.fineract.cob.service;
import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW; import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW;
import com.google.gson.Gson; import com.google.gson.Gson;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -102,7 +104,9 @@ public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<L
throw new JobNotFoundException(jobName, e); throw new JobNotFoundException(jobName, e);
} }
JobParameters jobParameters = new JobParametersBuilder(jobExplorer).getNextJobParameters(inlineLoanCOBJob) JobParameters jobParameters = new JobParametersBuilder(jobExplorer).getNextJobParameters(inlineLoanCOBJob)
.addJobParameters(new JobParameters(getJobParametersMap(loanIds))).toJobParameters(); .addJobParameters(new JobParameters(
getJobParametersMap(loanIds, ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE))))
.toJobParameters();
JobExecution jobExecution; JobExecution jobExecution;
try { try {
jobExecution = jobLauncher.run(inlineLoanCOBJob, jobParameters); jobExecution = jobLauncher.run(inlineLoanCOBJob, jobParameters);
@@ -141,16 +145,18 @@ public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<L
return loanAccountLocks; return loanAccountLocks;
} }
private Map<String, JobParameter> getJobParametersMap(List<Long> loanIds) { private Map<String, JobParameter> getJobParametersMap(List<Long> loanIds, LocalDate businessDate) {
// TODO: refactor for a more generic solution // TODO: refactor for a more generic solution
String parameterJson = gson.toJson(loanIds); String parameterJson = gson.toJson(loanIds);
CustomJobParameter loanIdsJobParameter = new CustomJobParameter(); CustomJobParameter loanIdsJobParameter = new CustomJobParameter();
loanIdsJobParameter.setParameterJson(parameterJson); loanIdsJobParameter.setParameterJson(parameterJson);
Long loanIdsJobParameterId = customJobParameterRepository.saveAndFlush(loanIdsJobParameter).getId(); Long loanIdsJobParameterId = customJobParameterRepository.saveAndFlush(loanIdsJobParameter).getId();
CustomJobParameter businessDateJobParameter = new CustomJobParameter();
businessDateJobParameter.setParameterJson(gson.toJson(businessDate.format(DateTimeFormatter.ISO_DATE)));
Long businessDateJobParameterId = customJobParameterRepository.saveAndFlush(businessDateJobParameter).getId();
Map<String, JobParameter> jobParameterMap = new HashMap<>(); Map<String, JobParameter> jobParameterMap = new HashMap<>();
jobParameterMap.put(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY, new JobParameter(loanIdsJobParameterId)); jobParameterMap.put(SpringBatchJobConstants.CUSTOM_JOB_PARAMETER_ID_KEY, new JobParameter(loanIdsJobParameterId));
String businessDate = ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE).toString(); jobParameterMap.put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME, new JobParameter(businessDateJobParameterId));
jobParameterMap.put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME, new JobParameter(businessDate));
return jobParameterMap; return jobParameterMap;
} }
@@ -24,21 +24,34 @@ import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import io.cucumber.java8.En; import io.cucumber.java8.En;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import org.apache.fineract.cob.loan.LoanCOBConstant;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
import org.apache.fineract.infrastructure.core.domain.ActionContext; import org.apache.fineract.infrastructure.core.domain.ActionContext;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.useradministration.domain.AppUser; import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper; import org.apache.fineract.useradministration.domain.AppUserRepositoryWrapper;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.scope.context.StepContext;
import org.springframework.batch.repeat.RepeatStatus; import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
public class InitialisationTaskletStepDefinitions implements En { public class InitialisationTaskletStepDefinitions implements En {
private static final LocalDate TODAY = LocalDate.now(ZoneId.systemDefault());
private AppUserRepositoryWrapper userRepository = mock(AppUserRepositoryWrapper.class); private AppUserRepositoryWrapper userRepository = mock(AppUserRepositoryWrapper.class);
private InitialisationTasklet initialisationTasklet = new InitialisationTasklet(userRepository); private InitialisationTasklet initialisationTasklet = new InitialisationTasklet(userRepository);
private AppUser appUser = mock(AppUser.class); private AppUser appUser = mock(AppUser.class);
private RepeatStatus resultItem; private RepeatStatus resultItem;
private ChunkContext chunkContext;
public InitialisationTaskletStepDefinitions() { public InitialisationTaskletStepDefinitions() {
Given("/^The InitialisationTasklet.execute method with action (.*)$/", (String action) -> { Given("/^The InitialisationTasklet.execute method with action (.*)$/", (String action) -> {
@@ -48,16 +61,29 @@ public class InitialisationTaskletStepDefinitions implements En {
} else { } else {
lenient().when(this.userRepository.fetchSystemUser()).thenReturn(appUser); lenient().when(this.userRepository.fetchSystemUser()).thenReturn(appUser);
} }
HashMap<BusinessDateType, LocalDate> businessDates = new HashMap<>();
LocalDate businessDate = TODAY;
LocalDate cobBusinessDate = businessDate.minusDays(1);
businessDates.put(BusinessDateType.BUSINESS_DATE, businessDate);
businessDates.put(BusinessDateType.COB_DATE, cobBusinessDate);
ThreadLocalContextUtil.setBusinessDates(businessDates);
JobExecution jobExecution = new JobExecution(1L);
jobExecution.getExecutionContext().put(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME,
cobBusinessDate.format(DateTimeFormatter.ISO_DATE));
StepExecution stepExecution = new StepExecution("step", jobExecution);
StepContext stepContext = new StepContext(stepExecution);
chunkContext = new ChunkContext(stepContext);
}); });
When("InitialisationTasklet.execute method executed", () -> { When("InitialisationTasklet.execute method executed", () -> {
resultItem = this.initialisationTasklet.execute(null, null); resultItem = this.initialisationTasklet.execute(null, chunkContext);
}); });
Then("InitialisationTasklet.execute result should match", () -> { Then("InitialisationTasklet.execute result should match", () -> {
assertEquals(RepeatStatus.FINISHED, resultItem); assertEquals(RepeatStatus.FINISHED, resultItem);
assertEquals(appUser, SecurityContextHolder.getContext().getAuthentication().getPrincipal()); assertEquals(appUser, SecurityContextHolder.getContext().getAuthentication().getPrincipal());
assertEquals(TODAY, ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.BUSINESS_DATE));
assertEquals(TODAY.minusDays(1), ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE));
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT); ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
}); });
@@ -65,6 +91,8 @@ public class InitialisationTaskletStepDefinitions implements En {
assertThrows(RuntimeException.class, () -> { assertThrows(RuntimeException.class, () -> {
resultItem = this.initialisationTasklet.execute(null, null); resultItem = this.initialisationTasklet.execute(null, null);
}); });
assertEquals(TODAY, ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.BUSINESS_DATE));
assertEquals(TODAY.minusDays(1), ThreadLocalContextUtil.getBusinessDateByType(BusinessDateType.COB_DATE));
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT); ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
}); });
} }