FINERACT-1977: 4-eye-principle, maker-checker permission
This commit is contained in:
+42
-54
@@ -52,7 +52,6 @@ import org.apache.fineract.batch.exception.BatchReferenceInvalidException;
|
||||
import org.apache.fineract.batch.exception.ErrorInfo;
|
||||
import org.apache.fineract.batch.service.ResolutionHelper.BatchRequestNode;
|
||||
import org.apache.fineract.infrastructure.core.domain.BatchRequestContextHolder;
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractIdempotentCommandException;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.core.filters.BatchCallHandler;
|
||||
import org.apache.fineract.infrastructure.core.filters.BatchFilter;
|
||||
@@ -62,10 +61,8 @@ import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.dao.NonTransientDataAccessException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
import org.springframework.transaction.TransactionExecution;
|
||||
import org.springframework.transaction.TransactionStatus;
|
||||
import org.springframework.transaction.TransactionSystemException;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
@@ -104,8 +101,7 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
*/
|
||||
@Override
|
||||
public List<BatchResponse> handleBatchRequestsWithoutEnclosingTransaction(final List<BatchRequest> requestList, UriInfo uriInfo) {
|
||||
BatchRequestContextHolder.setEnclosingTransaction(Optional.empty());
|
||||
return handleBatchRequests(false, requestList, uriInfo);
|
||||
return handleBatchRequests(requestList, uriInfo, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -117,7 +113,18 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
*/
|
||||
@Override
|
||||
public List<BatchResponse> handleBatchRequestsWithEnclosingTransaction(final List<BatchRequest> requestList, final UriInfo uriInfo) {
|
||||
return callInTransaction(Function.identity()::apply, () -> handleBatchRequests(true, requestList, uriInfo));
|
||||
return handleBatchRequests(requestList, uriInfo, true);
|
||||
}
|
||||
|
||||
private List<BatchResponse> handleBatchRequests(final List<BatchRequest> requestList, final UriInfo uriInfo,
|
||||
boolean enclosingTransaction) {
|
||||
BatchRequestContextHolder.setIsEnclosingTransaction(enclosingTransaction);
|
||||
try {
|
||||
return enclosingTransaction ? callInTransaction(Function.identity()::apply, () -> handleRequestNodes(requestList, uriInfo))
|
||||
: handleRequestNodes(requestList, uriInfo);
|
||||
} finally {
|
||||
BatchRequestContextHolder.resetIsEnclosingTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,18 +143,15 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
|
||||
transactionConfigurator.accept(transactionTemplate);
|
||||
return transactionTemplate.execute(status -> {
|
||||
BatchRequestContextHolder.setEnclosingTransaction(Optional.of(status));
|
||||
BatchRequestContextHolder.setEnclosingTransaction(status);
|
||||
try {
|
||||
responseList.addAll(request.get());
|
||||
return responseList;
|
||||
} catch (BatchExecutionException ex) {
|
||||
status.setRollbackOnly();
|
||||
return List.of(buildErrorResponse(ex.getCause(), ex.getRequest()));
|
||||
} catch (RuntimeException ex) {
|
||||
status.setRollbackOnly();
|
||||
return buildErrorResponses(ex, responseList);
|
||||
responseList.add(buildErrorResponse(ex.getCause(), ex.getRequest()));
|
||||
return responseList;
|
||||
} finally {
|
||||
BatchRequestContextHolder.setEnclosingTransaction(Optional.empty());
|
||||
BatchRequestContextHolder.resetTransaction();
|
||||
}
|
||||
});
|
||||
} catch (TransactionException | NonTransientDataAccessException ex) {
|
||||
@@ -163,24 +167,17 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
* @param uriInfo
|
||||
* @return {@code List<BatchResponse>}
|
||||
*/
|
||||
private List<BatchResponse> handleBatchRequests(boolean enclosingTransaction, final List<BatchRequest> requestList,
|
||||
final UriInfo uriInfo) {
|
||||
private List<BatchResponse> handleRequestNodes(final List<BatchRequest> requestList, final UriInfo uriInfo) {
|
||||
final List<BatchRequestNode> rootNodes;
|
||||
try {
|
||||
rootNodes = this.resolutionHelper.buildNodesTree(requestList);
|
||||
} catch (BatchReferenceInvalidException e) {
|
||||
return List.of(buildErrorResponse(e));
|
||||
return List.of(buildOrThrowErrorResponse(e, null));
|
||||
}
|
||||
|
||||
final ArrayList<BatchResponse> responseList = new ArrayList<>(requestList.size());
|
||||
for (BatchRequestNode rootNode : rootNodes) {
|
||||
if (enclosingTransaction) {
|
||||
this.callRequestRecursive(rootNode.getRequest(), rootNode, responseList, uriInfo, enclosingTransaction);
|
||||
} else {
|
||||
ArrayList<BatchResponse> localResponseList = new ArrayList<>();
|
||||
this.callRequestRecursive(rootNode.getRequest(), rootNode, localResponseList, uriInfo, enclosingTransaction);
|
||||
responseList.addAll(localResponseList);
|
||||
}
|
||||
this.callRequestRecursive(rootNode.getRequest(), rootNode, responseList, uriInfo);
|
||||
}
|
||||
responseList.sort(Comparator.comparing(BatchResponse::getRequestId));
|
||||
return responseList;
|
||||
@@ -197,18 +194,10 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
* the collected responses
|
||||
* @return {@code BatchResponse}
|
||||
*/
|
||||
private void callRequestRecursive(BatchRequest request, BatchRequestNode requestNode, List<BatchResponse> responseList, UriInfo uriInfo,
|
||||
boolean enclosingTransaction) {
|
||||
private void callRequestRecursive(BatchRequest request, BatchRequestNode requestNode, List<BatchResponse> responseList,
|
||||
UriInfo uriInfo) {
|
||||
// run current node
|
||||
BatchResponse response;
|
||||
if (enclosingTransaction) {
|
||||
response = executeRequest(request, uriInfo);
|
||||
} else {
|
||||
List<BatchResponse> transactionResponse = callInTransaction(
|
||||
transactionTemplate -> transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
|
||||
() -> List.of(executeRequest(request, uriInfo)));
|
||||
response = transactionResponse.get(0);
|
||||
}
|
||||
BatchResponse response = executeRequest(request, uriInfo);
|
||||
responseList.add(response);
|
||||
if (response.getStatusCode() != null && response.getStatusCode() == SC_OK) {
|
||||
// run child nodes
|
||||
@@ -217,13 +206,10 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
BatchRequest resolvedChildRequest;
|
||||
try {
|
||||
resolvedChildRequest = this.resolutionHelper.resolveRequest(childRequest, response);
|
||||
callRequestRecursive(resolvedChildRequest, childNode, responseList, uriInfo);
|
||||
} catch (JsonPathException jpex) {
|
||||
responseList.add(buildErrorResponse(jpex, childRequest));
|
||||
return;
|
||||
} catch (RuntimeException ex) {
|
||||
throw new BatchExecutionException(childRequest, ex);
|
||||
responseList.add(buildOrThrowErrorResponse(jpex, childRequest));
|
||||
}
|
||||
callRequestRecursive(resolvedChildRequest, childNode, responseList, uriInfo, enclosingTransaction);
|
||||
});
|
||||
} else {
|
||||
responseList.addAll(parentRequestFailedRecursive(request, requestNode, response, null));
|
||||
@@ -245,7 +231,7 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
log.debug("Batch request: method [{}], relative url [{}]", request.getMethod(), request.getRelativeUrl());
|
||||
Either<RuntimeException, BatchRequest> preprocessorResult = runPreprocessors(request);
|
||||
if (preprocessorResult.isLeft()) {
|
||||
throw new BatchExecutionException(request, preprocessorResult.getLeft());
|
||||
return buildOrThrowErrorResponse(preprocessorResult.getLeft(), request);
|
||||
} else {
|
||||
request = preprocessorResult.get();
|
||||
}
|
||||
@@ -253,19 +239,16 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
BatchRequestContextHolder.setRequestAttributes(new HashMap<>(Optional.ofNullable(request.getHeaders())
|
||||
.map(list -> list.stream().collect(Collectors.toMap(Header::getName, Header::getValue)))
|
||||
.orElse(Collections.emptyMap())));
|
||||
BatchCallHandler callHandler = new BatchCallHandler(this.batchFilters, commandStrategy::execute);
|
||||
Optional<TransactionStatus> transaction = BatchRequestContextHolder.getEnclosingTransaction();
|
||||
if (transaction.isPresent()) {
|
||||
if (BatchRequestContextHolder.isEnclosingTransaction()) {
|
||||
entityManager.flush();
|
||||
}
|
||||
BatchCallHandler callHandler = new BatchCallHandler(this.batchFilters, commandStrategy::execute);
|
||||
final BatchResponse rootResponse = callHandler.serviceCall(request, uriInfo);
|
||||
log.debug("Batch response: status code [{}], method [{}], relative url [{}]", rootResponse.getStatusCode(), request.getMethod(),
|
||||
request.getRelativeUrl());
|
||||
return rootResponse;
|
||||
} catch (AbstractIdempotentCommandException idempotentException) {
|
||||
return buildErrorResponse(idempotentException, request);
|
||||
} catch (RuntimeException ex) {
|
||||
throw new BatchExecutionException(request, ex);
|
||||
return buildOrThrowErrorResponse(ex, request);
|
||||
} finally {
|
||||
BatchRequestContextHolder.resetRequestAttributes();
|
||||
}
|
||||
@@ -313,11 +296,6 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
return responseList;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private BatchResponse buildErrorResponse(@NotNull Throwable ex) {
|
||||
return buildErrorResponse(ex, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the response when any exception raised
|
||||
*
|
||||
@@ -346,6 +324,15 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
return buildErrorResponse(requestId, statusCode, body, headers);
|
||||
}
|
||||
|
||||
private BatchResponse buildOrThrowErrorResponse(RuntimeException ex, BatchRequest request) {
|
||||
BatchResponse response = buildErrorResponse(ex, request);
|
||||
if (response.getStatusCode() != SC_OK && BatchRequestContextHolder.isEnclosingTransaction()) {
|
||||
BatchRequestContextHolder.getTransaction().ifPresent(TransactionExecution::setRollbackOnly);
|
||||
throw new BatchExecutionException(request, ex);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<BatchResponse> buildErrorResponses(Throwable ex, @NotNull List<BatchResponse> responseList) {
|
||||
BatchResponse response = responseList.isEmpty() ? null
|
||||
@@ -368,9 +355,10 @@ public class BatchApiServiceImpl implements BatchApiService {
|
||||
}
|
||||
if (response != null) {
|
||||
requestId = response.getRequestId();
|
||||
if (response.getStatusCode() == null || response.getStatusCode() != SC_OK) {
|
||||
if (response.getStatusCode() != null) {
|
||||
statusCode = response.getStatusCode();
|
||||
Integer responseCode = response.getStatusCode();
|
||||
if (responseCode == null || responseCode != SC_OK) {
|
||||
if (responseCode != null) {
|
||||
statusCode = responseCode;
|
||||
}
|
||||
body = "Transaction is being rolled back. First erroneous request: \n" + new Gson().toJson(response);
|
||||
}
|
||||
|
||||
@@ -202,6 +202,14 @@ public class CommandSource extends AbstractPersistableCustom {
|
||||
this.commandAsJson = json;
|
||||
}
|
||||
|
||||
public AppUser getMaker() {
|
||||
return maker;
|
||||
}
|
||||
|
||||
public AppUser getChecker() {
|
||||
return checker;
|
||||
}
|
||||
|
||||
public String getActionName() {
|
||||
return this.actionName;
|
||||
}
|
||||
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* 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.commands.exception;
|
||||
|
||||
import org.apache.fineract.commands.domain.CommandSource;
|
||||
|
||||
public class RollbackTransactionAsCommandIsNotApprovedByCheckerException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* When maker-checker is configured globally and also for the current transaction.
|
||||
*
|
||||
* An initial save determines if there are any integrity rule or data problems.
|
||||
*
|
||||
* If there isn't... and the transaction is from a maker... then this roll back is issued and the
|
||||
* commandSourceResult is used to write the audit entry.
|
||||
*/
|
||||
private final CommandSource commandSourceResult;
|
||||
|
||||
public RollbackTransactionAsCommandIsNotApprovedByCheckerException(final CommandSource commandSourceResult) {
|
||||
this.commandSourceResult = commandSourceResult;
|
||||
}
|
||||
|
||||
public CommandSource getCommandSourceResult() {
|
||||
return this.commandSourceResult;
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.commands.exception;
|
||||
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
|
||||
/**
|
||||
* When maker-checker is configured globally and also for the current transaction. An initial save determines if there
|
||||
* are any integrity rule or data problems. If there isn't... and the transaction is from a maker... then this roll back
|
||||
* is issued and the commandSourceResult is used to write the audit entry.
|
||||
*/
|
||||
public class RollbackTransactionNotApprovedException extends RuntimeException {
|
||||
|
||||
private final CommandProcessingResult result;
|
||||
|
||||
public RollbackTransactionNotApprovedException(Long commandId, Long entityId) {
|
||||
this.result = new CommandProcessingResultBuilder().withCommandId(commandId).withEntityId(entityId).setRollbackTransaction(true)
|
||||
.build();
|
||||
}
|
||||
|
||||
public CommandProcessingResult getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+5
@@ -30,6 +30,11 @@ public class UnsupportedCommandException extends RuntimeException {
|
||||
this.unsupportedCommandName = unsupportedCommandName;
|
||||
}
|
||||
|
||||
public UnsupportedCommandException(final String unsupportedCommandName, String message) {
|
||||
super(message);
|
||||
this.unsupportedCommandName = unsupportedCommandName;
|
||||
}
|
||||
|
||||
public String getUnsupportedCommandName() {
|
||||
return this.unsupportedCommandName;
|
||||
}
|
||||
|
||||
+1
-4
@@ -18,7 +18,6 @@
|
||||
*/
|
||||
package org.apache.fineract.commands.service;
|
||||
|
||||
import org.apache.fineract.commands.domain.CommandSource;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
@@ -28,8 +27,6 @@ public interface CommandProcessingService {
|
||||
|
||||
CommandProcessingResult executeCommand(CommandWrapper wrapper, JsonCommand command, boolean isApprovedByChecker);
|
||||
|
||||
CommandProcessingResult logCommand(CommandSource commandSourceResult);
|
||||
|
||||
boolean validateCommand(CommandWrapper commandWrapper, AppUser user);
|
||||
boolean validateRollbackCommand(CommandWrapper commandWrapper, AppUser user);
|
||||
|
||||
}
|
||||
|
||||
+17
-9
@@ -26,7 +26,10 @@ import org.apache.fineract.commands.domain.CommandSource;
|
||||
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.exception.CommandNotFoundException;
|
||||
import org.apache.fineract.commands.exception.RollbackTransactionNotApprovedException;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -95,19 +98,24 @@ public class CommandSourceService {
|
||||
idempotencyKey);
|
||||
}
|
||||
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public CommandSource getInitialCommandSource(CommandWrapper wrapper, JsonCommand jsonCommand, AppUser maker, String idempotencyKey) {
|
||||
CommandSource commandSourceResult;
|
||||
if (jsonCommand.commandId() != null) {
|
||||
commandSourceResult = commandSourceRepository.findById(jsonCommand.commandId())
|
||||
.orElseThrow(() -> new CommandNotFoundException(jsonCommand.commandId()));
|
||||
commandSourceResult.markAsChecked(maker);
|
||||
} else {
|
||||
commandSourceResult = CommandSource.fullEntryFrom(wrapper, jsonCommand, maker, idempotencyKey, UNDER_PROCESSING.getValue());
|
||||
}
|
||||
CommandSource commandSourceResult = CommandSource.fullEntryFrom(wrapper, jsonCommand, maker, idempotencyKey,
|
||||
UNDER_PROCESSING.getValue());
|
||||
if (commandSourceResult.getCommandJson() == null) {
|
||||
commandSourceResult.setCommandJson("{}");
|
||||
}
|
||||
return commandSourceResult;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public CommandProcessingResult processCommand(NewCommandSourceHandler handler, JsonCommand command, CommandSource commandSource,
|
||||
AppUser user, boolean isApprovedByChecker, boolean isMakerChecker) {
|
||||
final CommandProcessingResult result = handler.processCommand(command);
|
||||
boolean isRollback = !isApprovedByChecker && !user.isCheckerSuperUser() && (isMakerChecker || result.isRollbackTransaction());
|
||||
if (isRollback) {
|
||||
commandSource.markAsAwaitingApproval();
|
||||
throw new RollbackTransactionNotApprovedException(commandSource.getId(), commandSource.getResourceId());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+19
-9
@@ -19,6 +19,7 @@
|
||||
package org.apache.fineract.commands.service;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import java.util.Objects;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.fineract.commands.domain.CommandSource;
|
||||
@@ -26,6 +27,8 @@ import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.exception.CommandNotAwaitingApprovalException;
|
||||
import org.apache.fineract.commands.exception.CommandNotFoundException;
|
||||
import org.apache.fineract.commands.exception.UnsupportedCommandException;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
|
||||
@@ -45,10 +48,10 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio
|
||||
private final FromJsonHelper fromApiJsonHelper;
|
||||
private final CommandProcessingService processAndLogCommandService;
|
||||
private final SchedulerJobRunnerReadService schedulerJobRunnerReadService;
|
||||
private final ConfigurationDomainService configurationService;
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult logCommandSource(final CommandWrapper wrapper) {
|
||||
|
||||
boolean isApprovedByChecker = false;
|
||||
|
||||
// check if is update of own account details
|
||||
@@ -77,7 +80,6 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult approveEntry(final Long makerCheckerId) {
|
||||
|
||||
final CommandSource commandSourceInput = validateMakerCheckerTransaction(makerCheckerId);
|
||||
validateIsUpdateAllowed();
|
||||
|
||||
@@ -111,16 +113,24 @@ public class PortfolioCommandSourceWritePlatformServiceImpl implements Portfolio
|
||||
}
|
||||
|
||||
private CommandSource validateMakerCheckerTransaction(final Long makerCheckerId) {
|
||||
|
||||
final CommandSource commandSourceInput = this.commandSourceRepository.findById(makerCheckerId)
|
||||
final CommandSource commandSource = this.commandSourceRepository.findById(makerCheckerId)
|
||||
.orElseThrow(() -> new CommandNotFoundException(makerCheckerId));
|
||||
if (!commandSourceInput.isMarkedAsAwaitingApproval()) {
|
||||
if (!commandSource.isMarkedAsAwaitingApproval()) {
|
||||
throw new CommandNotAwaitingApprovalException(makerCheckerId);
|
||||
}
|
||||
|
||||
this.context.authenticatedUser().validateHasCheckerPermissionTo(commandSourceInput.getPermissionCode());
|
||||
|
||||
return commandSourceInput;
|
||||
AppUser appUser = this.context.authenticatedUser();
|
||||
String permissionCode = commandSource.getPermissionCode();
|
||||
appUser.validateHasCheckerPermissionTo(permissionCode);
|
||||
if (!configurationService.isSameMakerCheckerEnabled() && !appUser.isCheckerSuperUser()) {
|
||||
AppUser maker = commandSource.getMaker();
|
||||
if (maker == null) {
|
||||
throw new UnsupportedCommandException(permissionCode, "Maker user is missing.");
|
||||
}
|
||||
if (Objects.equals(appUser.getId(), maker.getId())) {
|
||||
throw new UnsupportedCommandException(permissionCode, "Can not be checked by the same user.");
|
||||
}
|
||||
}
|
||||
return commandSource;
|
||||
}
|
||||
|
||||
private void validateIsUpdateAllowed() {
|
||||
|
||||
+31
-57
@@ -35,14 +35,12 @@ import org.apache.fineract.batch.exception.ErrorInfo;
|
||||
import org.apache.fineract.commands.domain.CommandProcessingResultType;
|
||||
import org.apache.fineract.commands.domain.CommandSource;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.exception.RollbackTransactionAsCommandIsNotApprovedByCheckerException;
|
||||
import org.apache.fineract.commands.exception.UnsupportedCommandException;
|
||||
import org.apache.fineract.commands.handler.NewCommandSourceHandler;
|
||||
import org.apache.fineract.commands.provider.CommandHandlerProvider;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
|
||||
import org.apache.fineract.infrastructure.core.domain.BatchRequestContextHolder;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractRequestContextHolder;
|
||||
import org.apache.fineract.infrastructure.core.exception.ErrorHandler;
|
||||
@@ -58,7 +56,6 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
@@ -76,9 +73,7 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
||||
private final ConfigurationDomainService configurationDomainService;
|
||||
private final CommandHandlerProvider commandHandlerProvider;
|
||||
private final IdempotencyKeyResolver idempotencyKeyResolver;
|
||||
private final IdempotencyKeyGenerator idempotencyKeyGenerator;
|
||||
private final CommandSourceService commandSourceService;
|
||||
private final ErrorHandler errorHandler;
|
||||
|
||||
private final FineractRequestContextHolder fineractRequestContextHolder;
|
||||
private final Gson gson = GoogleGsonSerializerHelper.createSimpleGson();
|
||||
@@ -92,73 +87,68 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
||||
|
||||
Long commandId = (Long) fineractRequestContextHolder.getAttribute(COMMAND_SOURCE_ID, null);
|
||||
boolean isRetry = commandId != null;
|
||||
boolean isEnclosingTransaction = BatchRequestContextHolder.isEnclosingTransaction();
|
||||
|
||||
CommandSource commandSource = null;
|
||||
String idempotencyKey;
|
||||
if (isRetry) {
|
||||
commandSource = commandSourceService.getCommandSource(commandId);
|
||||
idempotencyKey = commandSource.getIdempotencyKey();
|
||||
} else if ((commandId = command.commandId()) != null) { // action on the command itself
|
||||
commandSource = commandSourceService.getCommandSource(commandId);
|
||||
idempotencyKey = commandSource.getIdempotencyKey();
|
||||
} else {
|
||||
idempotencyKey = idempotencyKeyResolver.resolve(wrapper);
|
||||
}
|
||||
exceptionWhenTheRequestAlreadyProcessed(wrapper, idempotencyKey, isRetry);
|
||||
|
||||
boolean sameTransaction = BatchRequestContextHolder.getEnclosingTransaction().isPresent();
|
||||
AppUser user = context.authenticatedUser(wrapper);
|
||||
if (commandSource == null) {
|
||||
AppUser user = context.authenticatedUser(wrapper);
|
||||
if (sameTransaction) {
|
||||
if (isEnclosingTransaction) {
|
||||
commandSource = commandSourceService.getInitialCommandSource(wrapper, command, user, idempotencyKey);
|
||||
} else {
|
||||
commandSource = commandSourceService.saveInitialNewTransaction(wrapper, command, user, idempotencyKey);
|
||||
storeCommandIdInContext(commandSource); // Store command id as a request attribute
|
||||
commandId = commandSource.getId();
|
||||
}
|
||||
}
|
||||
if (commandId != null) {
|
||||
storeCommandIdInContext(commandSource); // Store command id as a request attribute
|
||||
}
|
||||
|
||||
boolean isMakerChecker = configurationDomainService.isMakerCheckerEnabledForTask(wrapper.taskPermissionName());
|
||||
if (isApprovedByChecker || (isMakerChecker && user.isCheckerSuperUser())) {
|
||||
commandSource.markAsChecked(user);
|
||||
}
|
||||
setIdempotencyKeyStoreFlag(true);
|
||||
|
||||
final CommandProcessingResult result;
|
||||
try {
|
||||
result = findCommandHandler(wrapper).processCommand(command);
|
||||
result = commandSourceService.processCommand(findCommandHandler(wrapper), command, commandSource, user, isApprovedByChecker,
|
||||
isMakerChecker);
|
||||
} catch (Throwable t) { // NOSONAR
|
||||
RuntimeException mappable = ErrorHandler.getMappable(t);
|
||||
ErrorInfo errorInfo = commandSourceService.generateErrorInfo(mappable);
|
||||
commandSource.setResultStatusCode(errorInfo.getStatusCode());
|
||||
Integer statusCode = errorInfo.getStatusCode();
|
||||
commandSource.setResultStatusCode(statusCode);
|
||||
commandSource.setResult(errorInfo.getMessage());
|
||||
commandSource.setStatus(ERROR);
|
||||
if (!sameTransaction) { // TODO: temporary solution
|
||||
if (statusCode != SC_OK) {
|
||||
commandSource.setStatus(ERROR);
|
||||
}
|
||||
if (!isEnclosingTransaction) { // TODO: temporary solution
|
||||
commandSource = commandSourceService.saveResultNewTransaction(commandSource);
|
||||
}
|
||||
publishHookErrorEvent(wrapper, command, errorInfo); // TODO must be performed in a new transaction
|
||||
// must not throw any exception; must persist in new transaction as the current transaction was already
|
||||
// marked as rollback
|
||||
publishHookErrorEvent(wrapper, command, errorInfo);
|
||||
throw mappable;
|
||||
}
|
||||
|
||||
commandSource.updateForAudit(result);
|
||||
commandSource.setResultStatusCode(SC_OK);
|
||||
commandSource.updateForAudit(result);
|
||||
commandSource.setResult(toApiJsonSerializer.serializeResult(result));
|
||||
commandSource.setStatus(PROCESSED);
|
||||
|
||||
boolean isRollback = !isApprovedByChecker && (result.isRollbackTransaction()
|
||||
|| configurationDomainService.isMakerCheckerEnabledForTask(wrapper.taskPermissionName()));
|
||||
// TODO: this should be removed, can not override audit information (and maker-checker does not work)
|
||||
if (!isRollback && result.hasChanges()) {
|
||||
commandSource.setCommandJson(toApiJsonSerializer.serializeResult(result.getChanges()));
|
||||
}
|
||||
|
||||
commandSource = commandSourceService.saveResultSameTransaction(commandSource);
|
||||
if (sameTransaction) {
|
||||
storeCommandIdInContext(commandSource); // Store command id as a request attribute
|
||||
}
|
||||
|
||||
if (isRollback) {
|
||||
/*
|
||||
* JournalEntry will generate a new transactionId every time. Updating the transactionId with old
|
||||
* transactionId, because as there are no entries are created with new transactionId, will throw an error
|
||||
* when checker approves the transaction
|
||||
*/
|
||||
commandSource.setTransactionId(command.getTransactionId());
|
||||
// TODO: this should be removed together with lines 147-149
|
||||
commandSource.setCommandJson(command.json()); // Set back CommandSource json data
|
||||
throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(commandSource);
|
||||
}
|
||||
storeCommandIdInContext(commandSource); // Store command id as a request attribute
|
||||
|
||||
result.setRollbackTransaction(null);
|
||||
publishHookEvent(wrapper.entityName(), wrapper.actionName(), command, result); // TODO must be performed in a
|
||||
@@ -201,24 +191,8 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
||||
fineractRequestContextHolder.setAttribute(IDEMPOTENCY_KEY_STORE_FLAG, flag);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult logCommand(CommandSource commandSource) {
|
||||
commandSource.markAsAwaitingApproval();
|
||||
if (commandSource.getIdempotencyKey() == null) {
|
||||
commandSource.setIdempotencyKey(idempotencyKeyGenerator.create());
|
||||
}
|
||||
commandSource = commandSourceService.saveResultSameTransaction(commandSource);
|
||||
|
||||
return new CommandProcessingResultBuilder().withCommandId(commandSource.getId()).withEntityId(commandSource.getResourceId())
|
||||
.build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public CommandProcessingResult fallbackExecuteCommand(Exception e) {
|
||||
if (e instanceof RollbackTransactionAsCommandIsNotApprovedByCheckerException ex) {
|
||||
return logCommand(ex.getCommandSourceResult());
|
||||
}
|
||||
throw ErrorHandler.getMappable(e);
|
||||
}
|
||||
|
||||
@@ -281,10 +255,10 @@ public class SynchronousCommandProcessingService implements CommandProcessingSer
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateCommand(final CommandWrapper commandWrapper, final AppUser user) {
|
||||
boolean rollbackTransaction = configurationDomainService.isMakerCheckerEnabledForTask(commandWrapper.taskPermissionName());
|
||||
public boolean validateRollbackCommand(final CommandWrapper commandWrapper, final AppUser user) {
|
||||
user.validateHasPermissionTo(commandWrapper.getTaskPermissionName());
|
||||
return rollbackTransaction;
|
||||
boolean isMakerChecker = configurationDomainService.isMakerCheckerEnabledForTask(commandWrapper.taskPermissionName());
|
||||
return isMakerChecker && !user.isCheckerSuperUser();
|
||||
}
|
||||
|
||||
private void publishHookEvent(final String entityName, final String actionName, JsonCommand command, final Object result) {
|
||||
|
||||
+2
@@ -25,6 +25,8 @@ public interface ConfigurationDomainService {
|
||||
|
||||
boolean isMakerCheckerEnabledForTask(String taskPermissionCode);
|
||||
|
||||
boolean isSameMakerCheckerEnabled();
|
||||
|
||||
boolean isAmazonS3Enabled();
|
||||
|
||||
boolean isRescheduleFutureRepaymentsEnabled();
|
||||
|
||||
+65
-18
@@ -27,15 +27,16 @@ public final class BatchRequestContextHolder {
|
||||
|
||||
private BatchRequestContextHolder() {}
|
||||
|
||||
private static final ThreadLocal<Map<String, Object>> batchAttributes = new NamedThreadLocal<>("BatchAttributesForProcessing");
|
||||
private static final ThreadLocal<Map<String, Object>> batchAttributes = new NamedThreadLocal<>("batchAttributes");
|
||||
|
||||
private static final ThreadLocal<Optional<TransactionStatus>> enclosingTransaction = new NamedThreadLocal<>("EnclosingTransaction") {
|
||||
private static final ThreadLocal<Optional<TransactionStatus>> batchTransaction = new NamedThreadLocal<>("batchTransaction") {
|
||||
|
||||
@Override
|
||||
protected Optional<TransactionStatus> initialValue() {
|
||||
return Optional.empty();
|
||||
}
|
||||
};
|
||||
private static final ThreadLocal<Boolean> isEnclosingTransaction = new NamedThreadLocal<>("isEnclosingTransaction");
|
||||
|
||||
/**
|
||||
* True if the batch attributes are set
|
||||
@@ -47,12 +48,12 @@ public final class BatchRequestContextHolder {
|
||||
}
|
||||
|
||||
/**
|
||||
* True if the batch attributes are set and the enclosing transaction is set to true
|
||||
* Returns the batch attributes for the current thread.
|
||||
*
|
||||
* @return
|
||||
* @return the batch attributes for the current thread, cna be null
|
||||
*/
|
||||
public static Optional<TransactionStatus> getEnclosingTransaction() {
|
||||
return enclosingTransaction.get();
|
||||
public static Map<String, Object> getRequestAttributes() {
|
||||
return batchAttributes.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,15 +66,6 @@ public final class BatchRequestContextHolder {
|
||||
batchAttributes.set(requestAttributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the batch attributes for the current thread.
|
||||
*
|
||||
* @return the batch attributes for the current thread, cna be null
|
||||
*/
|
||||
public static Map<String, Object> getRequestAttributes() {
|
||||
return batchAttributes.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the batch attributes for the current thread.
|
||||
*/
|
||||
@@ -82,11 +74,66 @@ public final class BatchRequestContextHolder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the enclosing transaction flag for the current thread.
|
||||
* True if the batch attributes are set and the enclosing transaction is set to true
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isEnclosingTransaction() {
|
||||
return Boolean.TRUE.equals(isEnclosingTransaction.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the isEnclosingTransaction flag for the current thread.
|
||||
*
|
||||
* @param isEnclosingTransaction
|
||||
*/
|
||||
public static void setIsEnclosingTransaction(boolean isEnclosingTransaction) {
|
||||
BatchRequestContextHolder.isEnclosingTransaction.set(isEnclosingTransaction);
|
||||
}
|
||||
|
||||
public static void resetIsEnclosingTransaction() {
|
||||
isEnclosingTransaction.remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Optional<TransactionStatus> getTransaction() {
|
||||
return batchTransaction.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the enclosing transaction
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static Optional<TransactionStatus> getEnclosingTransaction() {
|
||||
return isEnclosingTransaction() ? getTransaction() : Optional.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the transaction for the current thread.
|
||||
*
|
||||
* @param enclosingTransaction
|
||||
*/
|
||||
public static void setEnclosingTransaction(Optional<TransactionStatus> enclosingTransaction) {
|
||||
BatchRequestContextHolder.enclosingTransaction.set(enclosingTransaction);
|
||||
public static void setTransaction(TransactionStatus enclosingTransaction) {
|
||||
batchTransaction.set(Optional.ofNullable(enclosingTransaction));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the enclosing transaction for the current thread.
|
||||
*
|
||||
* @param enclosingTransaction
|
||||
*/
|
||||
public static void setEnclosingTransaction(TransactionStatus enclosingTransaction) {
|
||||
if (isEnclosingTransaction()) {
|
||||
setTransaction(enclosingTransaction);
|
||||
}
|
||||
}
|
||||
|
||||
public static void resetTransaction() {
|
||||
batchTransaction.set(Optional.empty());
|
||||
}
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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 com.google.gson.Gson;
|
||||
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.commands.exception.RollbackTransactionNotApprovedException;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* An {@link ExceptionMapper} to map {@link PlatformApiDataValidationException} thrown by platform into a HTTP API
|
||||
* friendly format.
|
||||
*
|
||||
* The {@link PlatformApiDataValidationException} is typically thrown in data validation of the parameters passed in
|
||||
* with an api request.
|
||||
*/
|
||||
@Provider
|
||||
@Component
|
||||
@Scope("singleton")
|
||||
@Slf4j
|
||||
public class RollbackTransactionNotApprovedExceptionMapper
|
||||
implements FineractExceptionMapper, ExceptionMapper<RollbackTransactionNotApprovedException> {
|
||||
|
||||
@Override
|
||||
public Response toResponse(final RollbackTransactionNotApprovedException exception) {
|
||||
log.warn("Exception: {}", exception.getClass().getName());
|
||||
return Response.ok().entity(new Gson().toJson(exception.getResult())).type(MediaType.APPLICATION_JSON).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int errorCode() {
|
||||
return 2000;
|
||||
}
|
||||
}
|
||||
+4
@@ -47,8 +47,12 @@ public class UnsupportedCommandExceptionMapper implements ExceptionMapper<Unsupp
|
||||
final List<ApiParameterError> errors = new ArrayList<>();
|
||||
|
||||
final StringBuilder validationErrorCode = new StringBuilder("error.msg.command.unsupported");
|
||||
String message = exception.getMessage();
|
||||
final StringBuilder defaultEnglishMessage = new StringBuilder("The command ").append(exception.getUnsupportedCommandName())
|
||||
.append(" is not supported.");
|
||||
if (message != null) {
|
||||
defaultEnglishMessage.append(" ").append(message);
|
||||
}
|
||||
log.warn("Exception: {}, Message: {}", exception.getClass().getName(), defaultEnglishMessage);
|
||||
final ApiParameterError error = ApiParameterError.parameterError(validationErrorCode.toString(), defaultEnglishMessage.toString(),
|
||||
exception.getUnsupportedCommandName(), exception.getUnsupportedCommandName());
|
||||
|
||||
+1
-4
@@ -25,7 +25,6 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.commands.domain.CommandSourceRepository;
|
||||
import org.apache.fineract.commands.service.CommandSourceService;
|
||||
import org.apache.fineract.commands.service.SynchronousCommandProcessingService;
|
||||
import org.apache.fineract.infrastructure.core.domain.BatchRequestContextHolder;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractRequestContextHolder;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@@ -39,11 +38,9 @@ public class IdempotencyStoreHelper {
|
||||
|
||||
public void storeCommandResult(Integer response, String body, Long commandId) {
|
||||
commandSourceRepository.findById(commandId).ifPresent(commandSource -> {
|
||||
boolean sameTransaction = BatchRequestContextHolder.getEnclosingTransaction().isPresent();
|
||||
commandSource.setResultStatusCode(response);
|
||||
commandSource.setResult(body);
|
||||
commandSource = sameTransaction ? commandSourceService.saveResultSameTransaction(commandSource)
|
||||
: commandSourceService.saveResultNewTransaction(commandSource);
|
||||
commandSourceService.saveResultSameTransaction(commandSource);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -657,6 +657,10 @@ public class AppUser extends AbstractPersistableCustom implements PlatformUser {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCheckerSuperUser() {
|
||||
return hasPermissionTo("CHECKER_SUPER_USER");
|
||||
}
|
||||
|
||||
public void validateHasDatatableReadPermission(final String datatable) {
|
||||
if (hasNotPermissionForDatatable(datatable, "READ")) {
|
||||
throw new NoAuthorizationException("Not authorised to read datatable: " + datatable);
|
||||
|
||||
+6
@@ -34,6 +34,7 @@ import org.apache.fineract.infrastructure.core.domain.ActionContext;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.exceptionmapper.IdempotentCommandExceptionMapper;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -48,6 +49,11 @@ public class IdempotencyCommandProcessFailedExceptionTest {
|
||||
ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, actualDate)));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInconsistentStatus() {
|
||||
IdempotentCommandExceptionMapper mapper = new IdempotentCommandExceptionMapper();
|
||||
|
||||
+6
@@ -54,6 +54,7 @@ import org.apache.fineract.investor.service.AccountingService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -90,6 +91,11 @@ public class LoanAccountOwnerTransferBusinessStepTest {
|
||||
externalAssetOwnerTransferLoanMappingRepository, accountingService, businessEventNotifierService);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLoanNoTransfer() {
|
||||
// given
|
||||
|
||||
+4
-1
@@ -38,6 +38,7 @@ import org.apache.fineract.infrastructure.bulkimport.importhandler.helper.EnumOp
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
|
||||
import org.apache.fineract.portfolio.savings.SavingsPostingInterestPeriodType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountChargeData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountData;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsActivation;
|
||||
@@ -147,7 +148,9 @@ public class SavingsImportHandler implements ImportHandler {
|
||||
Long interestPostingPeriodTypeId = null;
|
||||
EnumOptionData interestPostingPeriodTypeEnum = null;
|
||||
if (interestPostingPeriodType != null) {
|
||||
if (interestPostingPeriodType.equalsIgnoreCase(MONTHLY)) {
|
||||
if (interestPostingPeriodType.equalsIgnoreCase(DAILY)) {
|
||||
interestPostingPeriodTypeId = SavingsPostingInterestPeriodType.DAILY.getValue().longValue();
|
||||
} else if (interestPostingPeriodType.equalsIgnoreCase(MONTHLY)) {
|
||||
interestPostingPeriodTypeId = 4L;
|
||||
} else if (interestPostingPeriodType.equalsIgnoreCase(QUARTERLY)) {
|
||||
interestPostingPeriodTypeId = 5L;
|
||||
|
||||
+8
-6
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.infrastructure.configuration.domain;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.time.LocalDate;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -74,6 +75,11 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameMakerCheckerEnabled() {
|
||||
return getGlobalConfigurationPropertyData("enable-same-maker-checker").isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAmazonS3Enabled() {
|
||||
return getGlobalConfigurationPropertyData("amazon-S3").isEnabled();
|
||||
@@ -379,18 +385,14 @@ public class ConfigurationDomainServiceJpa implements ConfigurationDomainService
|
||||
return property.getValue();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private GlobalConfigurationPropertyData getGlobalConfigurationPropertyData(final String propertyName) {
|
||||
return globalConfigurationRepository.findOneByNameWithNotFoundDetection(propertyName).toData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSubRatesEnabled() {
|
||||
GlobalConfigurationPropertyData configuration = getGlobalConfigurationPropertyData("sub-rates");
|
||||
if (configuration == null) {
|
||||
return false;
|
||||
} else {
|
||||
return configuration.isEnabled();
|
||||
}
|
||||
return getGlobalConfigurationPropertyData("sub-rates").isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -294,7 +294,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
|
||||
validateParentGroupRulesBeforeClientActivation(newClient);
|
||||
runEntityDatatableCheck(newClient.getId(), newClient.getLegalForm());
|
||||
final CommandWrapper commandWrapper = new CommandWrapperBuilder().activateClient(null).build();
|
||||
rollbackTransaction = this.commandProcessingService.validateCommand(commandWrapper, currentUser);
|
||||
rollbackTransaction = this.commandProcessingService.validateRollbackCommand(commandWrapper, currentUser);
|
||||
}
|
||||
|
||||
this.clientRepository.saveAndFlush(newClient);
|
||||
|
||||
+3
-3
@@ -172,16 +172,16 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
|
||||
|
||||
if (newGroup.isCenter()) {
|
||||
final CommandWrapper commandWrapper = new CommandWrapperBuilder().activateCenter(null).build();
|
||||
rollbackTransaction = this.commandProcessingService.validateCommand(commandWrapper, currentUser);
|
||||
rollbackTransaction = this.commandProcessingService.validateRollbackCommand(commandWrapper, currentUser);
|
||||
} else {
|
||||
final CommandWrapper commandWrapper = new CommandWrapperBuilder().activateGroup(null).build();
|
||||
rollbackTransaction = this.commandProcessingService.validateCommand(commandWrapper, currentUser);
|
||||
rollbackTransaction = this.commandProcessingService.validateRollbackCommand(commandWrapper, currentUser);
|
||||
}
|
||||
}
|
||||
|
||||
if (!newGroup.isCenter() && newGroup.hasActiveClients()) {
|
||||
final CommandWrapper commandWrapper = new CommandWrapperBuilder().associateClientsToGroup(newGroup.getId()).build();
|
||||
rollbackTransaction = this.commandProcessingService.validateCommand(commandWrapper, currentUser);
|
||||
rollbackTransaction = this.commandProcessingService.validateRollbackCommand(commandWrapper, currentUser);
|
||||
}
|
||||
|
||||
// pre-save to generate id for use in group hierarchy
|
||||
|
||||
+2
-1
@@ -656,7 +656,8 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
public CommandProcessingResult createActiveApplication(final SavingsAccountDataDTO savingsAccountDataDTO) {
|
||||
|
||||
final CommandWrapper commandWrapper = new CommandWrapperBuilder().savingsAccountActivation(null).build();
|
||||
boolean rollbackTransaction = this.commandProcessingService.validateCommand(commandWrapper, savingsAccountDataDTO.getAppliedBy());
|
||||
boolean rollbackTransaction = this.commandProcessingService.validateRollbackCommand(commandWrapper,
|
||||
savingsAccountDataDTO.getAppliedBy());
|
||||
|
||||
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsAccountDataDTO.getClient(),
|
||||
savingsAccountDataDTO.getGroup(), savingsAccountDataDTO.getSavingsProduct(), savingsAccountDataDTO.getApplicationDate(),
|
||||
|
||||
@@ -150,4 +150,5 @@
|
||||
<include file="parts/0128_savings_audit.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0129_transaction_summary_with_asset_owner_report_overpaid_amount.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0130_add_create_delinquency_action_permission.xml" relativeToChangelogFile="true" />
|
||||
<include file="parts/0131_add_configuration_maker_checker.xml" relativeToChangelogFile="true" />
|
||||
</databaseChangeLog>
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
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.
|
||||
|
||||
-->
|
||||
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
|
||||
<changeSet author="fineract" id="1">
|
||||
<insert tableName="c_configuration">
|
||||
<column name="name" value="enable-same-maker-checker"/>
|
||||
<column name="value"/>
|
||||
<column name="date_value"/>
|
||||
<column name="string_value"/>
|
||||
<column name="enabled" valueBoolean="false"/>
|
||||
<column name="is_trap_door" valueBoolean="false"/>
|
||||
<column name="description"/>
|
||||
</insert>
|
||||
</changeSet>
|
||||
</databaseChangeLog>
|
||||
+15
-3
@@ -66,7 +66,11 @@ public class LoanItemListenerStepDefinitions implements En {
|
||||
});
|
||||
|
||||
When("LoanItemListener.onReadError method executed", () -> {
|
||||
loanItemListener.onReadError(exception);
|
||||
try {
|
||||
loanItemListener.onReadError(exception);
|
||||
} finally {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
});
|
||||
|
||||
Then("LoanItemListener.onReadError result should match", () -> {
|
||||
@@ -87,7 +91,11 @@ public class LoanItemListenerStepDefinitions implements En {
|
||||
});
|
||||
|
||||
When("LoanItemListener.onProcessError method executed", () -> {
|
||||
loanItemListener.onProcessError(loan, exception);
|
||||
try {
|
||||
loanItemListener.onProcessError(loan, exception);
|
||||
} finally {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
});
|
||||
|
||||
Then("LoanItemListener.onProcessError result should match", () -> {
|
||||
@@ -108,7 +116,11 @@ public class LoanItemListenerStepDefinitions implements En {
|
||||
});
|
||||
|
||||
When("LoanItemListener.onWriteError method executed", () -> {
|
||||
loanItemListener.onWriteError(exception, new Chunk<>(List.of(loan)));
|
||||
try {
|
||||
loanItemListener.onWriteError(exception, new Chunk<>(List.of(loan)));
|
||||
} finally {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
});
|
||||
|
||||
Then("LoanItemListener.onWriteError result should match", () -> {
|
||||
|
||||
+6
@@ -42,6 +42,7 @@ import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanAccrualPlatformService;
|
||||
import org.junit.Assert;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -66,6 +67,11 @@ public class AddPeriodicAccrualEntriesBusinessStepTest {
|
||||
underTest = new AddPeriodicAccrualEntriesBusinessStep(loanAccrualPlatformService);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLoanWithAccrual() throws MultiException {
|
||||
// given
|
||||
|
||||
+5
-1
@@ -122,7 +122,11 @@ public class ApplyLoanLockTaskletStepDefinitions implements En {
|
||||
});
|
||||
|
||||
When("ApplyLoanLockTasklet.execute method executed", () -> {
|
||||
resultItem = applyLoanLockTasklet.execute(stepContribution, null);
|
||||
try {
|
||||
resultItem = applyLoanLockTasklet.execute(stepContribution, null);
|
||||
} finally {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
});
|
||||
|
||||
Then("ApplyLoanLockTasklet.execute result should match", () -> {
|
||||
|
||||
+6
@@ -46,6 +46,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanSummary;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -72,6 +73,11 @@ public class CheckLoanRepaymentDueBusinessStepTest {
|
||||
underTest = new CheckLoanRepaymentDueBusinessStep(configurationDomainService, businessEventNotifierService);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLoanWithInstallmentDueAfterConfiguredDaysWhenStepExecutionThenBusinessEventIsRaised() {
|
||||
ArgumentCaptor<LoanRepaymentDueBusinessEvent> loanRepaymentDueEvent = ArgumentCaptor.forClass(LoanRepaymentDueBusinessEvent.class);
|
||||
|
||||
+6
-1
@@ -43,6 +43,7 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -69,6 +70,11 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
||||
underTest = new CheckLoanRepaymentOverdueBusinessStep(configurationDomainService, businessEventNotifierService);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLoanWithInstallmentOverdueAfterConfiguredDaysWhenStepExecutionThenBusinessEventIsRaised() {
|
||||
ArgumentCaptor<LoanRepaymentOverdueBusinessEvent> loanRepaymentDueBusinessEventArgumentCaptor = ArgumentCaptor
|
||||
@@ -169,5 +175,4 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
|
||||
assertEquals(repaymentInstallment, loanPayloadForEvent);
|
||||
assertEquals(processedLoan, loanForProcessing);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-1
@@ -40,6 +40,7 @@ import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -73,6 +74,11 @@ class LoanItemReaderTest {
|
||||
@Mock
|
||||
private Loan loan;
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoanItemReaderSimple() throws Exception {
|
||||
// given
|
||||
@@ -163,5 +169,4 @@ class LoanItemReaderTest {
|
||||
|
||||
Mockito.verifyNoMoreInteractions(loanRepository);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -47,6 +47,7 @@ import org.apache.fineract.portfolio.delinquency.helper.DelinquencyEffectivePaus
|
||||
import org.apache.fineract.portfolio.delinquency.service.DelinquencyReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanAccountDomainService;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -100,6 +101,11 @@ public class SetLoanDelinquencyTagsBusinessStepTest {
|
||||
delinquencyReadPlatformService, businessEventNotifierService);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link SetLoanDelinquencyTagsBusinessStep#execute(Loan)} success scenario.
|
||||
*
|
||||
|
||||
+1
-1
@@ -85,7 +85,7 @@ public class COBBulkEventConfigurationTest {
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+6
@@ -42,6 +42,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.config.FineractProperties;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
@@ -73,6 +74,11 @@ class InlineLoanCOBExecutorServiceImplTest {
|
||||
@Mock
|
||||
private FineractProperties.FineractBodyItemSizeLimitProperties fineractBodyItemSizeLimitProperties;
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExceptionThrownIfLoanIsAlreadyLocked() {
|
||||
JsonCommand command = mock(JsonCommand.class);
|
||||
|
||||
+6
-1
@@ -37,6 +37,7 @@ import org.apache.fineract.infrastructure.core.domain.ActionContext;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -71,6 +72,11 @@ public class PurgeProcessedCommandsTaskletTest {
|
||||
underTest = new PurgeProcessedCommandsTasklet(repository, configurationDomainService);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenEventsForPurgeWhenTaskExecutionThenEventsPurgeForDaysCriteria() {
|
||||
// given
|
||||
@@ -97,5 +103,4 @@ public class PurgeProcessedCommandsTaskletTest {
|
||||
// then
|
||||
assertEquals(RepeatStatus.FINISHED, resultStatus);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-14
@@ -25,13 +25,10 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import io.cucumber.java8.En;
|
||||
import io.github.resilience4j.retry.RetryRegistry;
|
||||
import io.github.resilience4j.retry.event.RetryEvent;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Table;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.apache.fineract.commands.domain.CommandSource;
|
||||
import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.exception.RollbackTransactionAsCommandIsNotApprovedByCheckerException;
|
||||
import org.apache.fineract.commands.exception.RollbackTransactionNotApprovedException;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractRequestContextHolder;
|
||||
@@ -69,7 +66,7 @@ public class CommandServiceStepDefinitions implements En {
|
||||
ReflectionTestUtils.setField(processAndLogCommandService, "fineractRequestContextHolder", contextHolder);
|
||||
Mockito.when(contextHolder.getAttribute(any(), any())).thenThrow(new CannotAcquireLockException("BLOW IT UP!!!"))
|
||||
.thenThrow(new ObjectOptimisticLockingFailureException("Dummy", new RuntimeException("BLOW IT UP!!!")))
|
||||
.thenThrow(new RollbackTransactionAsCommandIsNotApprovedByCheckerException(new DummyCommandSource()));
|
||||
.thenThrow(new RollbackTransactionNotApprovedException(1L, null));
|
||||
|
||||
this.retryRegistry.retry("executeCommand").getEventPublisher().onRetry(event -> {
|
||||
log.warn("... retry event: {}", event);
|
||||
@@ -147,13 +144,4 @@ public class CommandServiceStepDefinitions implements En {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name = "m_portfolio_command_source")
|
||||
public static class DummyCommandSource extends CommandSource {
|
||||
|
||||
public DummyCommandSource() {
|
||||
setId(1L);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-14
@@ -89,24 +89,12 @@ public class CommandSourceServiceTest {
|
||||
|
||||
@Test
|
||||
public void testCreateFromExisting() {
|
||||
CommandWrapper wrapper = CommandWrapper.wrap("act", "ent", 1L, 1L);
|
||||
long commandId = 1L;
|
||||
JsonCommand jsonCommand = JsonCommand.fromExistingCommand(commandId, "", null, null, null, 1L, null, null, null, null, null, null,
|
||||
null, null, null, null, null);
|
||||
CommandSource commandMock = Mockito.mock(CommandSource.class);
|
||||
Mockito.when(commandSourceRepository.saveAndFlush(commandMock)).thenReturn(commandMock);
|
||||
Mockito.when(commandSourceRepository.findById(commandId)).thenReturn(Optional.of(commandMock));
|
||||
AppUser appUser = Mockito.mock(AppUser.class);
|
||||
|
||||
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "t1", "n1", ZoneId.systemDefault().toString(), null));
|
||||
|
||||
CommandSource actual = underTest.saveInitialNewTransaction(wrapper, jsonCommand, appUser, "idk");
|
||||
|
||||
ArgumentCaptor<CommandSource> commandSourceArgumentCaptor = ArgumentCaptor.forClass(CommandSource.class);
|
||||
Mockito.verify(commandSourceRepository).saveAndFlush(commandSourceArgumentCaptor.capture());
|
||||
|
||||
CommandSource captured = commandSourceArgumentCaptor.getValue();
|
||||
Assertions.assertEquals(actual, captured);
|
||||
CommandSource actual = underTest.getCommandSource(commandId);
|
||||
Assertions.assertEquals(commandMock, actual);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+20
-12
@@ -67,8 +67,6 @@ public class SynchronousCommandProcessingServiceTest {
|
||||
@Mock
|
||||
private IdempotencyKeyResolver idempotencyKeyResolver;
|
||||
@Mock
|
||||
private IdempotencyKeyGenerator idempotencyKeyGenerator;
|
||||
@Mock
|
||||
private CommandSourceService commandSourceService;
|
||||
|
||||
@Spy
|
||||
@@ -93,19 +91,21 @@ public class SynchronousCommandProcessingServiceTest {
|
||||
when(commandWrapper.isNoteResource()).thenReturn(false);
|
||||
when(commandWrapper.isSurveyResource()).thenReturn(false);
|
||||
when(commandWrapper.isLoanDisburseDetailResource()).thenReturn(false);
|
||||
JsonCommand jsonCommand = Mockito.mock(JsonCommand.class);
|
||||
|
||||
NewCommandSourceHandler newCommandSourceHandler = Mockito.mock(NewCommandSourceHandler.class);
|
||||
long commandId = 1L;
|
||||
JsonCommand jsonCommand = Mockito.mock(JsonCommand.class);
|
||||
when(jsonCommand.commandId()).thenReturn(commandId);
|
||||
|
||||
NewCommandSourceHandler commandHandler = Mockito.mock(NewCommandSourceHandler.class);
|
||||
CommandProcessingResult commandProcessingResult = Mockito.mock(CommandProcessingResult.class);
|
||||
when(commandProcessingResult.isRollbackTransaction()).thenReturn(false);
|
||||
when(newCommandSourceHandler.processCommand(jsonCommand)).thenReturn(commandProcessingResult);
|
||||
when(commandHandlerProvider.getHandler(Mockito.any(), Mockito.any())).thenReturn(newCommandSourceHandler);
|
||||
when(commandHandler.processCommand(jsonCommand)).thenReturn(commandProcessingResult);
|
||||
when(commandHandlerProvider.getHandler(Mockito.any(), Mockito.any())).thenReturn(commandHandler);
|
||||
|
||||
when(configurationDomainService.isMakerCheckerEnabledForTask(Mockito.any())).thenReturn(false);
|
||||
String idk = "idk";
|
||||
when(idempotencyKeyResolver.resolve(commandWrapper)).thenReturn(idk);
|
||||
CommandSource commandSource = Mockito.mock(CommandSource.class);
|
||||
long commandId = 1L;
|
||||
when(commandSource.getId()).thenReturn(commandId);
|
||||
when(commandSourceService.findCommandSource(commandWrapper, idk)).thenReturn(null);
|
||||
when(commandSourceService.getCommandSource(commandId)).thenReturn(commandSource);
|
||||
@@ -116,9 +116,12 @@ public class SynchronousCommandProcessingServiceTest {
|
||||
when(commandSource.getStatus()).thenReturn(CommandProcessingResultType.PROCESSED.getValue());
|
||||
when(context.authenticatedUser(Mockito.any(CommandWrapper.class))).thenReturn(appUser);
|
||||
|
||||
when(commandSourceService.processCommand(commandHandler, jsonCommand, commandSource, appUser, false, false))
|
||||
.thenReturn(commandProcessingResult);
|
||||
|
||||
CommandProcessingResult actualCommandProcessingResult = underTest.executeCommand(commandWrapper, jsonCommand, false);
|
||||
|
||||
verify(commandSourceService).saveInitialNewTransaction(commandWrapper, jsonCommand, appUser, idk);
|
||||
verify(commandSourceService).getCommandSource(commandId);
|
||||
assertEquals(CommandProcessingResultType.PROCESSED.getValue(), commandSource.getStatus());
|
||||
verify(commandSourceService).saveResultSameTransaction(commandSource);
|
||||
|
||||
@@ -133,19 +136,21 @@ public class SynchronousCommandProcessingServiceTest {
|
||||
when(commandWrapper.isSurveyResource()).thenReturn(false);
|
||||
when(commandWrapper.isLoanDisburseDetailResource()).thenReturn(false);
|
||||
JsonCommand jsonCommand = Mockito.mock(JsonCommand.class);
|
||||
Long commandId = jsonCommand.commandId();
|
||||
|
||||
NewCommandSourceHandler newCommandSourceHandler = Mockito.mock(NewCommandSourceHandler.class);
|
||||
NewCommandSourceHandler commandHandler = Mockito.mock(NewCommandSourceHandler.class);
|
||||
CommandProcessingResult commandProcessingResult = Mockito.mock(CommandProcessingResult.class);
|
||||
CommandSource commandSource = Mockito.mock(CommandSource.class);
|
||||
when(commandProcessingResult.isRollbackTransaction()).thenReturn(false);
|
||||
RuntimeException runtimeException = new RuntimeException("foo");
|
||||
when(newCommandSourceHandler.processCommand(jsonCommand)).thenThrow(runtimeException);
|
||||
when(commandHandlerProvider.getHandler(Mockito.any(), Mockito.any())).thenReturn(newCommandSourceHandler);
|
||||
when(commandHandler.processCommand(jsonCommand)).thenThrow(runtimeException);
|
||||
when(commandHandlerProvider.getHandler(Mockito.any(), Mockito.any())).thenReturn(commandHandler);
|
||||
|
||||
when(configurationDomainService.isMakerCheckerEnabledForTask(Mockito.any())).thenReturn(false);
|
||||
String idk = "idk";
|
||||
when(idempotencyKeyResolver.resolve(commandWrapper)).thenReturn(idk);
|
||||
when(commandSourceService.findCommandSource(commandWrapper, idk)).thenReturn(null);
|
||||
when(commandSourceService.getCommandSource(commandId)).thenReturn(commandSource);
|
||||
|
||||
AppUser appUser = Mockito.mock(AppUser.class);
|
||||
when(context.authenticatedUser(Mockito.any(CommandWrapper.class))).thenReturn(appUser);
|
||||
@@ -155,11 +160,14 @@ public class SynchronousCommandProcessingServiceTest {
|
||||
|
||||
when(commandSourceService.findCommandSource(commandWrapper, idk)).thenReturn(initialCommandSource);
|
||||
|
||||
when(commandSourceService.processCommand(commandHandler, jsonCommand, commandSource, appUser, false, false))
|
||||
.thenThrow(runtimeException);
|
||||
|
||||
Assertions.assertThrows(RuntimeException.class, () -> {
|
||||
underTest.executeCommand(commandWrapper, jsonCommand, false);
|
||||
});
|
||||
|
||||
verify(commandSourceService).saveInitialNewTransaction(commandWrapper, jsonCommand, appUser, idk);
|
||||
verify(commandSourceService).getCommandSource(commandId);
|
||||
verify(commandSourceService).generateErrorInfo(runtimeException);
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -42,6 +42,7 @@ import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -79,6 +80,11 @@ public class BusinessDateWritePlatformServiceTest {
|
||||
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void businessDateIsNotEnabled() {
|
||||
JsonCommand command = JsonCommand.from("");
|
||||
|
||||
+6
-1
@@ -33,6 +33,7 @@ import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDa
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
@@ -48,6 +49,11 @@ public class CustomAuditingHandlerTest {
|
||||
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.of("Asia/Kolkata")))));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void markCreated() {
|
||||
MappingContext mappingContext = Mockito.mock(MappingContext.class);
|
||||
@@ -111,5 +117,4 @@ public class CustomAuditingHandlerTest {
|
||||
assertEquals(now.getHour(), targetObject.getCreatedDate().get().getHour());
|
||||
assertEquals(now.getMinute(), targetObject.getCreatedDate().get().getMinute());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-1
@@ -28,6 +28,7 @@ import java.util.Optional;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -38,6 +39,11 @@ public class CustomDateTimeProviderTest {
|
||||
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void instanceDateProvider() {
|
||||
Optional<TemporalAccessor> dateTimeProvider = CustomDateTimeProvider.INSTANCE.getNow();
|
||||
@@ -65,5 +71,4 @@ public class CustomDateTimeProviderTest {
|
||||
assertEquals(now.getHour(), ((OffsetDateTime) dateTimeProvider.get()).getHour());
|
||||
assertEquals(now.getMinute(), ((OffsetDateTime) dateTimeProvider.get()).getMinute());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -32,6 +32,7 @@ import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -43,6 +44,11 @@ public class DateUtilsTest {
|
||||
ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.of(2022, 6, 12))));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSystemZoneIdTest() {
|
||||
TimeZone.setDefault(TimeZone.getTimeZone("GMT+02:00"));
|
||||
|
||||
+14
@@ -21,12 +21,26 @@ package org.apache.fineract.infrastructure.dataqueries.service;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.infrastructure.dataqueries.service.export.DatatableExportUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class DatatableExportUtilTest {
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyFolderTest() {
|
||||
Assert.assertEquals("", DatatableExportUtil.normalizeFolderName(""));
|
||||
|
||||
+6
-1
@@ -35,6 +35,7 @@ import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.infrastructure.event.external.repository.ExternalEventRepository;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -69,6 +70,11 @@ public class PurgeExternalEventsTaskletTest {
|
||||
underTest = new PurgeExternalEventsTasklet(repository, configurationDomainService);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenEventsForPurgeWhenTaskExecutionThenEventsPurgeForDaysCriteria() {
|
||||
// given
|
||||
@@ -95,5 +101,4 @@ public class PurgeExternalEventsTaskletTest {
|
||||
// then
|
||||
assertEquals(RepeatStatus.FINISHED, resultStatus);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -47,6 +47,7 @@ import org.apache.fineract.infrastructure.event.external.repository.ExternalEven
|
||||
import org.apache.fineract.infrastructure.event.external.repository.domain.ExternalEventView;
|
||||
import org.apache.fineract.infrastructure.event.external.service.message.MessageFactory;
|
||||
import org.apache.fineract.infrastructure.event.external.service.support.ByteBufferConverter;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -97,6 +98,11 @@ class SendAsynchronousEventsTaskletTest {
|
||||
configurationDomainService);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
private void configureExternalEventsProducerReadBatchSizeProperty() {
|
||||
FineractProperties.FineractEventsProperties eventsProperties = new FineractProperties.FineractEventsProperties();
|
||||
FineractProperties.FineractExternalEventsProperties externalProperties = new FineractProperties.FineractExternalEventsProperties();
|
||||
|
||||
+6
-1
@@ -26,10 +26,16 @@ import java.util.Map;
|
||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class ExternalEventTest {
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructorWorks() {
|
||||
// given
|
||||
@@ -56,6 +62,5 @@ class ExternalEventTest {
|
||||
assertThat(result.getBusinessDate()).isEqualTo(currentBusinessDate);
|
||||
assertThat(result.getCreatedAt()).isNotNull();
|
||||
assertThat(result.getSentAt()).isNull();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+6
@@ -57,6 +57,7 @@ import org.apache.fineract.infrastructure.event.external.service.support.ByteBuf
|
||||
import org.apache.fineract.investor.enricher.LoanAccountDataV1Enricher;
|
||||
import org.apache.fineract.investor.enricher.LoanTransactionAdjustmentDataV1Enricher;
|
||||
import org.apache.fineract.investor.enricher.LoanTransactionDataV1Enricher;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -110,6 +111,11 @@ class ExternalEventServiceTest {
|
||||
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostEventShouldFailWhenNullEventIsGiven() {
|
||||
// given
|
||||
|
||||
+6
-1
@@ -70,6 +70,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanInstallmentCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargeReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -105,6 +106,11 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
|
||||
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoanDelinquencyRangeEventPayloadSerialization() throws IOException {
|
||||
// given
|
||||
@@ -340,5 +346,4 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
|
||||
ReflectionTestUtils.setField(loanCharge, "id", 1L);
|
||||
return loanCharge;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-1
@@ -47,6 +47,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
|
||||
import org.apache.fineract.portfolio.loanproduct.service.LoanEnumerations;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -78,6 +79,11 @@ public class LoanAdjustTransactionBusinessEventSerializerTest {
|
||||
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void loanTransactionReversedOnDateSerializationTest() {
|
||||
Loan loanForProcessing = Mockito.mock(Loan.class);
|
||||
@@ -107,5 +113,4 @@ public class LoanAdjustTransactionBusinessEventSerializerTest {
|
||||
.toAvroDTO(businessEvent);
|
||||
assertEquals(reversedLocalDate, loanTransactionAdjustmentDataV1.getTransactionToAdjust().getReversedOnDate());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -90,6 +90,7 @@ public class LoanRepaymentBusinessEventSerializerTest {
|
||||
|
||||
@AfterEach
|
||||
public void reset() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
moneyHelper.close();
|
||||
}
|
||||
|
||||
@@ -181,5 +182,4 @@ public class LoanRepaymentBusinessEventSerializerTest {
|
||||
AvroRuntimeException exceptionThrown = assertThrows(AvroRuntimeException.class, () -> serializer.toAvroDTO(event));
|
||||
assertTrue(exceptionThrown.getMessage().contains("does not accept null values"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -56,6 +56,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository;
|
||||
import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -99,6 +100,11 @@ class LoanCOBApiFilterTest {
|
||||
testObj = new LoanCOBApiFilter(helper);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldLoanAndExternalMatchToo() {
|
||||
String externalId = UUID.randomUUID().toString();
|
||||
|
||||
+6
-1
@@ -45,6 +45,7 @@ import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.investor.enricher.LoanAccountDataV1Enricher;
|
||||
import org.apache.fineract.investor.enricher.LoanChargeDataV1Enricher;
|
||||
import org.apache.fineract.investor.enricher.LoanTransactionDataV1Enricher;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -66,6 +67,11 @@ public class EnricherTest {
|
||||
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoanAccountDataEnricher() {
|
||||
LoanAccountDataV1Enricher loanAccountDataV1Enricher = mock(LoanAccountDataV1Enricher.class);
|
||||
@@ -178,5 +184,4 @@ public class EnricherTest {
|
||||
assertEquals(PURCHASE_PRICE_RATIO, data.getPurchasePriceRatio());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-1
@@ -76,6 +76,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleIns
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRepository;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
@@ -130,6 +131,11 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
|
||||
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void givenLoanAccountWithDelinquencyBucketWhenRangeChangeThenEventIsRaised() {
|
||||
ArgumentCaptor<LoanDelinquencyRangeChangeBusinessEvent> loanDeliquencyRangeChangeEvent = ArgumentCaptor
|
||||
@@ -614,5 +620,4 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
|
||||
Loan loanPayloadForEvent = loanDelinquencyRangeChangeEvent.getValue().get();
|
||||
assertEquals(loanForProcessing, loanPayloadForEvent);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
@@ -105,6 +105,7 @@ public class LoanDelinquencyDomainServiceTest {
|
||||
|
||||
@AfterEach
|
||||
public void deregister() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
moneyHelperStatic.close();
|
||||
}
|
||||
|
||||
|
||||
+17
@@ -27,16 +27,23 @@ import static org.mockito.Mockito.when;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||
import org.apache.fineract.infrastructure.core.domain.ExternalId;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||
import org.apache.fineract.portfolio.charge.domain.Charge;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeCalculationType;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargePaymentMode;
|
||||
import org.apache.fineract.portfolio.charge.domain.ChargeTimeType;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
@@ -46,11 +53,21 @@ import org.springframework.test.util.ReflectionTestUtils;
|
||||
*/
|
||||
public class LoanTest {
|
||||
|
||||
private final LocalDate actualDate = LocalDate.now(ZoneId.systemDefault());
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link Loan#getCharges()} with charges.
|
||||
*/
|
||||
@Test
|
||||
public void testGetChargesWithCharges() {
|
||||
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
|
||||
ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, actualDate)));
|
||||
|
||||
Loan loan = new Loan();
|
||||
ReflectionTestUtils.setField(loan, "charges", Collections.singleton(buildLoanCharge()));
|
||||
|
||||
|
||||
+6
@@ -48,6 +48,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationTransactionType;
|
||||
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -83,6 +84,11 @@ class AdvancedPaymentScheduleTransactionProcessorTest {
|
||||
ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, transactionDate)));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chargePaymentTransactionTestWithExactAmount() {
|
||||
final BigDecimal chargeAmount = BigDecimal.valueOf(100);
|
||||
|
||||
+6
@@ -44,6 +44,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleIns
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -104,6 +105,11 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
|
||||
ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, transactionDate)));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
// IN ADVANCE
|
||||
@Test
|
||||
public void inAdvancePaymentOfPrincipal() {
|
||||
|
||||
+6
@@ -44,6 +44,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleIns
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -105,6 +106,11 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
|
||||
ThreadLocalContextUtil.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, transactionDate)));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
}
|
||||
|
||||
// IN ADVANCE
|
||||
@Test
|
||||
public void inAdvancePaymentOfPrincipal() {
|
||||
|
||||
+1
@@ -69,6 +69,7 @@ public class LoanCalculateRepaymentPastDueServiceTest {
|
||||
|
||||
@AfterEach
|
||||
public void reset() {
|
||||
ThreadLocalContextUtil.reset();
|
||||
moneyHelper.close();
|
||||
}
|
||||
|
||||
|
||||
+1
-6
@@ -2153,12 +2153,7 @@ public class BatchApiTest {
|
||||
|
||||
LOG.info("Batch Response : {}", new Gson().toJson(responseOfQueryAndUpdateDatatableBatch));
|
||||
|
||||
final BatchResponse queryResponse = responseOfQueryAndUpdateDatatableBatch.get(0);
|
||||
|
||||
Assertions.assertEquals(1L, queryResponse.getRequestId());
|
||||
Assertions.assertEquals(HttpStatus.SC_OK, queryResponse.getStatusCode(), "Verify Status Code 200 for query datatable entry");
|
||||
|
||||
final BatchResponse updateResponse = responseOfQueryAndUpdateDatatableBatch.get(1);
|
||||
final BatchResponse updateResponse = responseOfQueryAndUpdateDatatableBatch.get(0);
|
||||
|
||||
Assertions.assertEquals(2L, updateResponse.getRequestId());
|
||||
Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST, updateResponse.getStatusCode(),
|
||||
|
||||
+5
-2
@@ -21,6 +21,7 @@ package org.apache.fineract.integrationtests;
|
||||
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.PAUSE;
|
||||
import static org.apache.fineract.portfolio.delinquency.domain.DelinquencyAction.RESUME;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
@@ -109,8 +110,10 @@ public class DelinquencyBucketsIntegrationTest {
|
||||
// then
|
||||
assertNotNull(delinquencyRangeResponse01);
|
||||
assertNotNull(ranges);
|
||||
assertEquals(1, ranges.get(0).getMinimumAgeDays(), "Expected Min Age Days to 1");
|
||||
assertEquals(3, ranges.get(0).getMaximumAgeDays(), "Expected Max Age Days to 3");
|
||||
assertFalse(ranges.isEmpty());
|
||||
GetDelinquencyRangesResponse range = ranges.get(ranges.size() - 1);
|
||||
assertEquals(1, range.getMinimumAgeDays(), "Expected Min Age Days to 1");
|
||||
assertEquals(3, range.getMaximumAgeDays(), "Expected Max Age Days to 3");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+22
-18
@@ -19,22 +19,25 @@
|
||||
package org.apache.fineract.integrationtests;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import io.restassured.builder.RequestSpecBuilder;
|
||||
import io.restassured.builder.ResponseSpecBuilder;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.response.Response;
|
||||
import io.restassured.response.ResponseBody;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.apache.fineract.cob.data.BusinessStep;
|
||||
import org.apache.fineract.cob.data.JobBusinessStepConfigData;
|
||||
import org.apache.fineract.infrastructure.core.exception.AbstractIdempotentCommandException;
|
||||
import org.apache.fineract.integrationtests.common.IdempotencyHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -70,9 +73,9 @@ public class IdempotencyTest {
|
||||
IdempotencyHelper.toJsonString(requestBody), idempotencyKeyHeader);
|
||||
Response responseSecond = IdempotencyHelper.updateBusinessStepOrder(requestSpec, updateResponseSpec, LOAN_JOB_NAME,
|
||||
IdempotencyHelper.toJsonString(requestBody), idempotencyKeyHeader);
|
||||
Assertions.assertEquals(response.getBody().asString(), responseSecond.getBody().asString());
|
||||
Assertions.assertNull(response.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
Assertions.assertNotNull(responseSecond.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
assertEquals(response.getBody().asString(), responseSecond.getBody().asString());
|
||||
assertNull(response.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
assertNotNull(responseSecond.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
|
||||
idempotencyKeyHeader = UUID.randomUUID().toString();
|
||||
|
||||
@@ -89,9 +92,9 @@ public class IdempotencyTest {
|
||||
IdempotencyHelper.toJsonString(requestBody), idempotencyKeyHeader);
|
||||
Response updateSecond = IdempotencyHelper.updateBusinessStepOrder(requestSpec, updateResponseSpec, LOAN_JOB_NAME,
|
||||
IdempotencyHelper.toJsonString(requestBody), idempotencyKeyHeader);
|
||||
Assertions.assertNull(update.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
Assertions.assertNotNull(updateSecond.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
Assertions.assertEquals(update.getBody().asString(), updateSecond.getBody().asString());
|
||||
assertNull(update.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
assertNotNull(updateSecond.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
assertEquals(update.getBody().asString(), updateSecond.getBody().asString());
|
||||
|
||||
newStepConfig = IdempotencyHelper.getConfiguredBusinessStepsByJobName(requestSpec, responseSpec, LOAN_JOB_NAME);
|
||||
applyChargeStep = newStepConfig.getBusinessSteps().stream()
|
||||
@@ -109,9 +112,9 @@ public class IdempotencyTest {
|
||||
updateSecond = IdempotencyHelper.updateBusinessStepOrder(requestSpec, updateResponseSpec, LOAN_JOB_NAME,
|
||||
IdempotencyHelper.toJsonString(requestBody), idempotencyKeyHeader);
|
||||
|
||||
Assertions.assertNull(update.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
Assertions.assertNotNull(updateSecond.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
Assertions.assertEquals(update.getBody().asString(), updateSecond.getBody().asString());
|
||||
assertNull(update.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
assertNotNull(updateSecond.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
assertEquals(update.getBody().asString(), updateSecond.getBody().asString());
|
||||
|
||||
newStepConfig = IdempotencyHelper.getConfiguredBusinessStepsByJobName(requestSpec, responseSpec, LOAN_JOB_NAME);
|
||||
applyChargeStep = newStepConfig.getBusinessSteps().stream()
|
||||
@@ -126,14 +129,14 @@ public class IdempotencyTest {
|
||||
updateSecond = IdempotencyHelper.updateBusinessStepOrder(requestSpec, updateResponseSpec, LOAN_JOB_NAME,
|
||||
IdempotencyHelper.toJsonString(originalStepConfig.getBusinessSteps()), idempotencyKeyHeader);
|
||||
|
||||
Assertions.assertNull(update.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
Assertions.assertNotNull(updateSecond.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
Assertions.assertEquals(update.getBody().asString(), updateSecond.getBody().asString());
|
||||
assertNull(update.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
assertNotNull(updateSecond.header(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
assertEquals(update.getBody().asString(), updateSecond.getBody().asString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shoudTheSecondRequestWithSameIdempotencyKeyWillFailureToo() {
|
||||
public void shouldTheSecondRequestWithSameIdempotencyKeyWillFailureToo() {
|
||||
ResponseSpecification responseSpecForError = new ResponseSpecBuilder().expectStatusCode(400).build();
|
||||
List<BusinessStep> requestBody = new ArrayList<>();
|
||||
String idempotencyKey = UUID.randomUUID().toString();
|
||||
@@ -141,13 +144,14 @@ public class IdempotencyTest {
|
||||
|
||||
Response response1 = IdempotencyHelper.updateBusinessStepOrderWithError(requestSpec, responseSpecForError, LOAN_JOB_NAME,
|
||||
IdempotencyHelper.toJsonString(requestBody), idempotencyKey);
|
||||
Assertions.assertNull(response1.getHeader(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
String originalBody = response1.getBody().asString();
|
||||
assertNull(response1.getHeader(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
ResponseBody body1 = response1.getBody();
|
||||
assertNotNull(body1);
|
||||
|
||||
Response response2 = IdempotencyHelper.updateBusinessStepOrderWithError(requestSpec, responseSpecForError, LOAN_JOB_NAME,
|
||||
IdempotencyHelper.toJsonString(requestBody), idempotencyKey);
|
||||
Assertions.assertNotNull(response2.getHeader(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
Assertions.assertEquals(originalBody, response2.getBody().asString());
|
||||
assertNotNull(response2.getHeader(AbstractIdempotentCommandException.IDEMPOTENT_CACHE_HEADER));
|
||||
assertEquals((Map) body1.jsonPath().get(""), response2.getBody().jsonPath().get(""));
|
||||
}
|
||||
|
||||
private BusinessStep getBusinessSteps(Long order, String stepName) {
|
||||
|
||||
+8
-8
@@ -181,8 +181,8 @@ public class LoanChargePaymentWithAdvancedPaymentAllocationTest {
|
||||
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
|
||||
.transactionAmount(BigDecimal.valueOf(1000.00)).locale("en"));
|
||||
|
||||
final float feePortion = 50.0f;
|
||||
final float penaltyPortion = 100.0f;
|
||||
final double feePortion = 50.0d;
|
||||
final double penaltyPortion = 100.0d;
|
||||
|
||||
Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
|
||||
ChargesHelper.getLoanSpecifiedDueDateWithAccountTransferJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
|
||||
@@ -209,8 +209,8 @@ public class LoanChargePaymentWithAdvancedPaymentAllocationTest {
|
||||
assertEquals(feePortion, loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesOutstanding());
|
||||
assertEquals(penaltyPortion, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesDue());
|
||||
assertEquals(penaltyPortion, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesOutstanding());
|
||||
assertEquals(400.0f, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalDueForPeriod());
|
||||
assertEquals(400.0f, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalOutstandingForPeriod());
|
||||
assertEquals(400.0d, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalDueForPeriod());
|
||||
assertEquals(400.0d, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalOutstandingForPeriod());
|
||||
assertEquals(LocalDate.of(2023, 1, 16), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
|
||||
|
||||
scheduleJobHelper.executeAndAwaitJob(jobName);
|
||||
@@ -218,11 +218,11 @@ public class LoanChargePaymentWithAdvancedPaymentAllocationTest {
|
||||
loanDetails = loanTransactionHelper.getLoanDetails((long) loanId);
|
||||
assertEquals(5, loanDetails.getRepaymentSchedule().getPeriods().size());
|
||||
assertEquals(feePortion, loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesDue());
|
||||
assertEquals(0, loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesOutstanding());
|
||||
assertEquals(0.0d, loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesOutstanding());
|
||||
assertEquals(penaltyPortion, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesDue());
|
||||
assertEquals(0, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesOutstanding());
|
||||
assertEquals(400, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalDueForPeriod());
|
||||
assertEquals(250, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalOutstandingForPeriod());
|
||||
assertEquals(0.0d, loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesOutstanding());
|
||||
assertEquals(400.0d, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalDueForPeriod());
|
||||
assertEquals(250.0d, loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalOutstandingForPeriod());
|
||||
assertEquals(LocalDate.of(2023, 1, 16), loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
|
||||
} finally {
|
||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||
|
||||
+162
-5
@@ -18,17 +18,31 @@
|
||||
*/
|
||||
package org.apache.fineract.integrationtests;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
import io.restassured.builder.RequestSpecBuilder;
|
||||
import io.restassured.builder.ResponseSpecBuilder;
|
||||
import io.restassured.http.ContentType;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.util.ArrayList;
|
||||
import org.apache.fineract.client.models.GetMakerCheckerResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.fineract.client.models.GlobalConfigurationPropertyData;
|
||||
import org.apache.fineract.client.models.PutPermissionsRequest;
|
||||
import org.apache.fineract.integrationtests.common.AuditHelper;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.CommonConstants;
|
||||
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.commands.MakercheckersHelper;
|
||||
import org.apache.fineract.integrationtests.common.organisation.StaffHelper;
|
||||
import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
|
||||
import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper;
|
||||
import org.apache.fineract.integrationtests.useradministration.roles.RolesHelper;
|
||||
import org.apache.fineract.integrationtests.useradministration.users.UserHelper;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -38,6 +52,12 @@ public class MakercheckerTest {
|
||||
private ResponseSpecification responseSpec;
|
||||
private RequestSpecification requestSpec;
|
||||
private MakercheckersHelper makercheckersHelper;
|
||||
private RolesHelper rolesHelper;
|
||||
private AuditHelper auditHelper;
|
||||
private SavingsProductHelper savingsProductHelper;
|
||||
private SavingsAccountHelper savingsAccountHelper;
|
||||
private static final String START_DATE_STRING = "03 June 2023";
|
||||
private static final String TRANSACTION_DATE_STRING = "05 June 2023";
|
||||
|
||||
@BeforeEach
|
||||
public void setup() {
|
||||
@@ -46,14 +66,151 @@ public class MakercheckerTest {
|
||||
this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
|
||||
this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
|
||||
this.makercheckersHelper = new MakercheckersHelper(this.requestSpec, this.responseSpec);
|
||||
this.rolesHelper = new RolesHelper();
|
||||
this.auditHelper = new AuditHelper(requestSpec, responseSpec);
|
||||
this.savingsProductHelper = new SavingsProductHelper();
|
||||
this.savingsAccountHelper = new SavingsAccountHelper(this.requestSpec, this.responseSpec);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMakerchekerInboxList() {
|
||||
public void testMakercheckerInboxList() {
|
||||
// given
|
||||
// when
|
||||
final ArrayList<GetMakerCheckerResponse> makerCheckerList = this.makercheckersHelper.getMakerCheckerList();
|
||||
|
||||
List<Map<String, Object>> makerCheckerList = this.makercheckersHelper.getMakerCheckerList(null);
|
||||
assertNotNull(makerCheckerList);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMakerCheckerOn() {
|
||||
GlobalConfigurationPropertyData mcConfig = GlobalConfigurationHelper.getGlobalConfigurationByName(requestSpec, responseSpec,
|
||||
"maker-checker");
|
||||
Long mcConfigId = mcConfig.getId();
|
||||
boolean mcConfigUpdate = false;
|
||||
if (!Boolean.TRUE.equals(mcConfig.getEnabled())) {
|
||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, mcConfigId, true);
|
||||
mcConfigUpdate = true;
|
||||
}
|
||||
GlobalConfigurationPropertyData sameMcConfig = GlobalConfigurationHelper.getGlobalConfigurationByName(requestSpec, responseSpec,
|
||||
"enable-same-maker-checker");
|
||||
Long sameMcConfigId = mcConfig.getId();
|
||||
boolean sameMcConfigUpdate = false;
|
||||
if (Boolean.TRUE.equals(sameMcConfig.getEnabled())) {
|
||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, sameMcConfigId, false);
|
||||
sameMcConfigUpdate = true;
|
||||
}
|
||||
|
||||
try {
|
||||
// client permission - maker-checker disabled
|
||||
PutPermissionsRequest putPermissionsRequest = new PutPermissionsRequest().putPermissionsItem("CREATE_CLIENT", false);
|
||||
rolesHelper.updatePermissions(putPermissionsRequest);
|
||||
putPermissionsRequest = new PutPermissionsRequest().putPermissionsItem("ACTIVATE_CLIENT", false);
|
||||
rolesHelper.updatePermissions(putPermissionsRequest);
|
||||
|
||||
Integer roleId = RolesHelper.createRole(requestSpec, responseSpec);
|
||||
Map<String, Boolean> permissionMap = Map.of("CREATE_CLIENT", true, "CREATE_CLIENT_CHECKER", true, "ACTIVATE_CLIENT", true,
|
||||
"ACTIVATE_CLIENT_CHECKER", true, "WITHDRAWAL_SAVINGSACCOUNT", true, "WITHDRAWAL_SAVINGSACCOUNT_CHECKER", true);
|
||||
RolesHelper.addPermissionsToRole(requestSpec, responseSpec, roleId, permissionMap);
|
||||
final Integer staffId = StaffHelper.createStaff(this.requestSpec, this.responseSpec);
|
||||
// create maker user
|
||||
String maker = Utils.uniqueRandomStringGenerator("user", 8);
|
||||
final Integer makerUserId = (Integer) UserHelper.createUser(this.requestSpec, this.responseSpec, roleId, staffId, maker,
|
||||
"P4ssw0rd", "resourceId");
|
||||
|
||||
// create client - maker-checker disabled
|
||||
RequestSpecification makerRequestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build()
|
||||
.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey(maker, "P4ssw0rd"));
|
||||
Integer clientId = ClientHelper.createClient(makerRequestSpec, this.responseSpec);
|
||||
assertNotNull(clientId);
|
||||
ClientHelper.verifyClientCreatedOnServer(requestSpec, this.responseSpec, clientId);
|
||||
|
||||
final Integer savingsId = createApproveActivateSavingsAccountDailyPosting(clientId, START_DATE_STRING);
|
||||
assertNotNull(savingsId);
|
||||
Integer transactionId = (Integer) savingsAccountHelper.depositToSavingsAccount(savingsId, "1000", TRANSACTION_DATE_STRING,
|
||||
CommonConstants.RESPONSE_RESOURCE_ID);
|
||||
assertNotNull(transactionId);
|
||||
|
||||
// client and saving permission - maker-checker enabled
|
||||
putPermissionsRequest = new PutPermissionsRequest().putPermissionsItem("ACTIVATE_CLIENT", true);
|
||||
rolesHelper.updatePermissions(putPermissionsRequest);
|
||||
putPermissionsRequest = new PutPermissionsRequest().putPermissionsItem("WITHDRAWAL_SAVINGSACCOUNT", true);
|
||||
rolesHelper.updatePermissions(putPermissionsRequest);
|
||||
|
||||
// create client - maker-checker enabled
|
||||
clientId = ClientHelper.createClient(makerRequestSpec, this.responseSpec);
|
||||
assertNull(clientId, "Client is created on the server");
|
||||
|
||||
List<Map<String, Object>> auditDetails = makercheckersHelper
|
||||
.getMakerCheckerList(Map.of("actionName", "CREATE", "entityName", "CLIENT", "makerId", makerUserId.toString()));
|
||||
assertEquals(1, auditDetails.size(), "More than one command exists");
|
||||
Long clientCommandId = ((Double) auditDetails.get(0).get("id")).longValue();
|
||||
|
||||
// savings withdrawal - maker-checker enabled
|
||||
SavingsAccountHelper makerSavingsHelper = new SavingsAccountHelper(makerRequestSpec, this.responseSpec);
|
||||
Integer withdrawalId = (Integer) makerSavingsHelper.withdrawalFromSavingsAccount(savingsId, "100", TRANSACTION_DATE_STRING,
|
||||
CommonConstants.RESPONSE_RESOURCE_ID);
|
||||
assertNull(withdrawalId, "Withdrawal performed on the server");
|
||||
|
||||
auditDetails = makercheckersHelper.getMakerCheckerList(
|
||||
Map.of("actionName", "WITHDRAWAL", "entityName", "SAVINGSACCOUNT", "makerId", makerUserId.toString()));
|
||||
assertEquals(1, auditDetails.size(), "More than one command exists");
|
||||
Long savingCommandId = ((Double) auditDetails.get(0).get("id")).longValue();
|
||||
|
||||
// check by the same user should fail
|
||||
ResponseSpecification failedResponseSpec = new ResponseSpecBuilder().expectStatusCode(400).build();
|
||||
MakercheckersHelper.approveMakerCheckerEntry(makerRequestSpec, failedResponseSpec, clientCommandId);
|
||||
MakercheckersHelper.approveMakerCheckerEntry(makerRequestSpec, failedResponseSpec, savingCommandId);
|
||||
|
||||
// create checker user
|
||||
String checker = Utils.uniqueRandomStringGenerator("user", 8);
|
||||
final Integer checkerUserId = (Integer) UserHelper.createUser(this.requestSpec, this.responseSpec, roleId, staffId, checker,
|
||||
"P4ssw0rd", "resourceId");
|
||||
RequestSpecification checkerRequestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build()
|
||||
.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey(checker, "P4ssw0rd"));
|
||||
|
||||
// check by another checker user should succeed
|
||||
HashMap<?, ?> response = MakercheckersHelper.approveMakerCheckerEntry(checkerRequestSpec, responseSpec, clientCommandId);
|
||||
assertNotNull(response);
|
||||
clientId = (Integer) response.get("clientId");
|
||||
assertNotNull(clientId);
|
||||
ClientHelper.verifyClientCreatedOnServer(requestSpec, responseSpec, clientId);
|
||||
|
||||
response = MakercheckersHelper.approveMakerCheckerEntry(checkerRequestSpec, responseSpec, savingCommandId);
|
||||
assertNotNull(response);
|
||||
withdrawalId = (Integer) response.get("resourceId");
|
||||
assertNotNull(withdrawalId);
|
||||
|
||||
// add checker superuser permission - actions are performed in one step
|
||||
permissionMap = Map.of("CHECKER_SUPER_USER", true);
|
||||
RolesHelper.addPermissionsToRole(requestSpec, responseSpec, roleId, permissionMap);
|
||||
clientId = ClientHelper.createClient(makerRequestSpec, this.responseSpec);
|
||||
assertNotNull(clientId);
|
||||
ClientHelper.verifyClientCreatedOnServer(requestSpec, this.responseSpec, clientId);
|
||||
|
||||
withdrawalId = (Integer) makerSavingsHelper.withdrawalFromSavingsAccount(savingsId, "100", TRANSACTION_DATE_STRING,
|
||||
CommonConstants.RESPONSE_RESOURCE_ID);
|
||||
assertNotNull(withdrawalId);
|
||||
} finally {
|
||||
if (mcConfigUpdate) {
|
||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, mcConfigId, false);
|
||||
}
|
||||
if (sameMcConfigUpdate) {
|
||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, sameMcConfigId, true);
|
||||
}
|
||||
PutPermissionsRequest putPermissionsRequest = new PutPermissionsRequest().putPermissionsItem("WITHDRAWAL_SAVINGSACCOUNT",
|
||||
false);
|
||||
rolesHelper.updatePermissions(putPermissionsRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private Integer createSavingsProductDailyPosting() {
|
||||
final String savingsProductJSON = this.savingsProductHelper.withInterestCompoundingPeriodTypeAsDaily()
|
||||
.withInterestPostingPeriodTypeAsDaily().withInterestCalculationPeriodTypeAsDailyBalance().build();
|
||||
return SavingsProductHelper.createSavingsProduct(savingsProductJSON, requestSpec, responseSpec);
|
||||
}
|
||||
|
||||
private Integer createApproveActivateSavingsAccountDailyPosting(final Integer clientID, final String startDate) {
|
||||
final Integer savingsProductID = createSavingsProductDailyPosting();
|
||||
assertNotNull(savingsProductID);
|
||||
return savingsAccountHelper.createApproveActivateSavingsAccount(clientID, savingsProductID, startDate);
|
||||
}
|
||||
}
|
||||
|
||||
+31
-36
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.integrationtests;
|
||||
|
||||
import static org.apache.fineract.integrationtests.common.CommonConstants.RESPONSE_RESOURCE_ID;
|
||||
import static org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper.PAYMENT_TYPE_ID;
|
||||
import static org.apache.fineract.integrationtests.common.system.DatatableHelper.addDatatableColumn;
|
||||
import static org.apache.http.HttpStatus.SC_CONFLICT;
|
||||
@@ -60,7 +61,6 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.integrationtests.common.BatchHelper;
|
||||
import org.apache.fineract.integrationtests.common.BusinessDateHelper;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.CommonConstants;
|
||||
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.savings.SavingsAccountHelper;
|
||||
@@ -118,7 +118,7 @@ public class SavingsAccountTransactionTest {
|
||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, startDateString);
|
||||
assertNotNull(clientID);
|
||||
|
||||
final Integer savingsId = createSavingsAccountDailyPosting(clientID, startDateString);
|
||||
final Integer savingsId = createApproveActivateSavingsAccountDailyPosting(clientID, startDateString);
|
||||
assertNotNull(savingsId);
|
||||
|
||||
performSavingsTransaction(savingsId, "100", depositDate, true);
|
||||
@@ -176,7 +176,7 @@ public class SavingsAccountTransactionTest {
|
||||
|
||||
// creating datatable for client entity
|
||||
final HashMap<String, Object> columnMap = new HashMap<>();
|
||||
String datatableName = Utils.uniqueRandomStringGenerator("savings_transaction" + "_", 5).toLowerCase();
|
||||
String datatableName = Utils.uniqueRandomStringGenerator("dt_savings_transaction_", 5).toLowerCase();
|
||||
columnMap.put("datatableName", datatableName);
|
||||
columnMap.put("apptableName", "m_savings_account_transaction");
|
||||
columnMap.put("multiRow", false);
|
||||
@@ -284,10 +284,9 @@ public class SavingsAccountTransactionTest {
|
||||
private void performSavingsTransaction(Integer savingsId, String amount, LocalDate transactionDate, boolean isDeposit) {
|
||||
String transactionType = isDeposit ? "Deposit" : "Withdrawal";
|
||||
Integer transactionId = isDeposit
|
||||
? (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, amount, depositDateString,
|
||||
CommonConstants.RESPONSE_RESOURCE_ID)
|
||||
? (Integer) this.savingsAccountHelper.depositToSavingsAccount(savingsId, amount, depositDateString, RESPONSE_RESOURCE_ID)
|
||||
: (Integer) this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, amount, withdrawDateString,
|
||||
CommonConstants.RESPONSE_RESOURCE_ID);
|
||||
RESPONSE_RESOURCE_ID);
|
||||
|
||||
assertNotNull(transactionId);
|
||||
|
||||
@@ -311,17 +310,10 @@ public class SavingsAccountTransactionTest {
|
||||
return extractedDate;
|
||||
}
|
||||
|
||||
private Integer createSavingsAccountDailyPosting(final Integer clientID, final String startDate) {
|
||||
private Integer createApproveActivateSavingsAccountDailyPosting(final Integer clientID, final String startDate) {
|
||||
final Integer savingsProductID = createSavingsProductDailyPosting();
|
||||
assertNotNull(savingsProductID);
|
||||
final Integer savingsId = this.savingsAccountHelper.applyForSavingsApplicationOnDate(clientID, savingsProductID,
|
||||
ACCOUNT_TYPE_INDIVIDUAL, startDate);
|
||||
assertNotNull(savingsId);
|
||||
HashMap savingsStatusHashMap = this.savingsAccountHelper.approveSavingsOnDate(savingsId, startDate);
|
||||
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
|
||||
savingsStatusHashMap = this.savingsAccountHelper.activateSavingsAccount(savingsId, startDate);
|
||||
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
|
||||
return savingsId;
|
||||
return savingsAccountHelper.createApproveActivateSavingsAccount(clientID, savingsProductID, startDate);
|
||||
}
|
||||
|
||||
private Integer createSavingsProductDailyPosting() {
|
||||
@@ -382,28 +374,31 @@ public class SavingsAccountTransactionTest {
|
||||
final List<BatchResponse> responses = enclosingTransaction
|
||||
? BatchHelper.postBatchRequestsWithEnclosingTransaction(requestSpec, responseSpec, json)
|
||||
: BatchHelper.postBatchRequestsWithoutEnclosingTransaction(requestSpec, responseSpec, json);
|
||||
assertNotNull(responses);
|
||||
assertNotNull(responses, "Responses");
|
||||
if (enclosingTransaction) {
|
||||
Integer statusCode1 = responses.get(0).getStatusCode();
|
||||
assertNotNull(statusCode1);
|
||||
assertNotNull(statusCode1, "First enlosingTransaction response status code");
|
||||
assertTrue(SC_OK == statusCode1 || SC_CONFLICT == statusCode1, "Status code: " + statusCode1);
|
||||
if (SC_OK == statusCode1) {
|
||||
assertEquals(4, responses.size());
|
||||
assertEquals(4, responses.size(), "Response size for enlosingTransaction OK response");
|
||||
Integer statusCode4 = responses.get(3).getStatusCode();
|
||||
assertNotNull(statusCode4);
|
||||
assertEquals(SC_OK, statusCode4);
|
||||
assertNotNull(statusCode4, "Last enlosingTransaction OK response status code");
|
||||
assertEquals(SC_OK, statusCode4, "Last enlosingTransaction OK response status code");
|
||||
} else {
|
||||
assertEquals(1, responses.size());
|
||||
assertEquals(1, responses.size(), "Response size for enlosingTransaction failed response");
|
||||
}
|
||||
} else {
|
||||
assertEquals(4, responses.size());
|
||||
assertEquals(4, responses.size(), "Response size for without-enlosingTransaction response");
|
||||
Integer statusCode1 = responses.get(0).getStatusCode();
|
||||
assertNotNull(statusCode1);
|
||||
assertTrue(SC_OK == statusCode1 || SC_CONFLICT == statusCode1, "Status code: " + statusCode1);
|
||||
assertNotNull(statusCode1, "First without-enlosingTransaction response status code");
|
||||
assertTrue(SC_OK == statusCode1 || SC_CONFLICT == statusCode1,
|
||||
"First without-enlosingTransaction response status code: " + statusCode1);
|
||||
Integer statusCode4 = responses.get(3).getStatusCode();
|
||||
assertNotNull(statusCode4);
|
||||
assertTrue(SC_OK == statusCode1 ? (SC_OK == statusCode4 || SC_CONFLICT == statusCode4)
|
||||
: (SC_FORBIDDEN == statusCode4 || SC_CONFLICT == statusCode4), "Status code: " + statusCode4);
|
||||
assertNotNull(statusCode4, "Last without-enlosingTransaction response status code");
|
||||
assertTrue(
|
||||
SC_OK == statusCode1 ? (SC_OK == statusCode4 || SC_CONFLICT == statusCode4)
|
||||
: (SC_FORBIDDEN == statusCode4 || SC_CONFLICT == statusCode4),
|
||||
"Last without-enlosingTransaction response status code: " + statusCode4);
|
||||
}
|
||||
} else {
|
||||
String json = transactionData.getJson();
|
||||
@@ -417,14 +412,14 @@ public class SavingsAccountTransactionTest {
|
||||
}
|
||||
|
||||
private static boolean checkConcurrentResponse(String response) {
|
||||
assertNotNull(response);
|
||||
assertNotNull(response, "Single response");
|
||||
JsonPath res = JsonPath.from(response);
|
||||
String statusCode = res.get("httpStatusCode");
|
||||
if (statusCode == null) {
|
||||
assertNotNull(res.get(CommonConstants.RESPONSE_RESOURCE_ID));
|
||||
assertNotNull(res.get(RESPONSE_RESOURCE_ID), "Single response " + RESPONSE_RESOURCE_ID);
|
||||
return true;
|
||||
}
|
||||
assertEquals(String.valueOf(SC_CONFLICT), statusCode);
|
||||
assertEquals(String.valueOf(SC_CONFLICT), statusCode, "Single response status code");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -439,22 +434,22 @@ public class SavingsAccountTransactionTest {
|
||||
RequestSpecification requestSpec = savingsHelper.getRequestSpec();
|
||||
ResponseSpecification responseSpec = savingsHelper.getResponseSpec();
|
||||
final List<BatchResponse> responses = BatchHelper.postBatchRequestsWithEnclosingTransaction(requestSpec, responseSpec, json);
|
||||
assertNotNull(responses);
|
||||
assertNotNull(responses, "Responses");
|
||||
BatchResponse response1 = responses.get(0);
|
||||
Integer statusCode = response1.getStatusCode();
|
||||
String msg = Strings.nullToEmpty(response1.getBody());
|
||||
assertNotNull(statusCode);
|
||||
assertNotNull(statusCode, "First response status code");
|
||||
assertTrue(
|
||||
SC_OK == statusCode || SC_CONFLICT == statusCode
|
||||
|| (SC_FORBIDDEN == statusCode && msg.contains("Cannot add or update a child row")),
|
||||
"Status code: " + statusCode + ", message: " + msg);
|
||||
if (SC_OK == statusCode) {
|
||||
assertEquals(4, responses.size());
|
||||
assertEquals(4, responses.size(), "Response size for OK response");
|
||||
Integer statusCode4 = responses.get(3).getStatusCode();
|
||||
assertNotNull(statusCode4);
|
||||
assertEquals(SC_OK, statusCode4);
|
||||
assertNotNull(statusCode4, "Last OK response status code");
|
||||
assertEquals(SC_OK, statusCode4, "Last OK response status code");
|
||||
} else {
|
||||
assertEquals(1, responses.size());
|
||||
assertEquals(1, responses.size(), "Response size for failed response");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -20,6 +20,7 @@ package org.apache.fineract.integrationtests.common;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.util.Collections;
|
||||
@@ -27,6 +28,7 @@ import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import org.apache.fineract.client.util.JSON;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -38,13 +40,15 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class AuditHelper {
|
||||
|
||||
private ResponseSpecification responseSpec;
|
||||
private RequestSpecification requestSpec;
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AuditHelper.class);
|
||||
private static final String AUDIT_BASE_URL = "/fineract-provider/api/v1/audits?" + Utils.TENANT_IDENTIFIER;
|
||||
private static final String AUDITSEARCH_BASE_URL = "/fineract-provider/api/v1/audits/searchtemplate?" + Utils.TENANT_IDENTIFIER;
|
||||
|
||||
private static final Gson GSON = new JSON().getGson();
|
||||
|
||||
private ResponseSpecification responseSpec;
|
||||
private RequestSpecification requestSpec;
|
||||
|
||||
public AuditHelper(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||
this.requestSpec = requestSpec;
|
||||
this.responseSpec = responseSpec;
|
||||
|
||||
+10
-3
@@ -119,8 +119,8 @@ public class GlobalConfigurationHelper {
|
||||
ArrayList<HashMap> expectedGlobalConfigurations = getAllDefaultGlobalConfigurations();
|
||||
ArrayList<HashMap> actualGlobalConfigurations = getAllGlobalConfigurations(requestSpec, responseSpec);
|
||||
|
||||
Assertions.assertEquals(52, expectedGlobalConfigurations.size());
|
||||
Assertions.assertEquals(52, actualGlobalConfigurations.size());
|
||||
Assertions.assertEquals(53, expectedGlobalConfigurations.size());
|
||||
Assertions.assertEquals(53, actualGlobalConfigurations.size());
|
||||
|
||||
for (int i = 0; i < expectedGlobalConfigurations.size(); i++) {
|
||||
|
||||
@@ -571,9 +571,16 @@ public class GlobalConfigurationHelper {
|
||||
assetExternalizationOfNonActiveLoans.put("value", 0);
|
||||
assetExternalizationOfNonActiveLoans.put("enabled", true);
|
||||
assetExternalizationOfNonActiveLoans.put("trapDoor", false);
|
||||
assetExternalizationOfNonActiveLoans.put("string_value", "due-date");
|
||||
defaults.add(assetExternalizationOfNonActiveLoans);
|
||||
|
||||
HashMap<String, Object> enableSameMakerChecker = new HashMap<>();
|
||||
enableSameMakerChecker.put("id", 58);
|
||||
enableSameMakerChecker.put("name", "enable-same-maker-checker");
|
||||
enableSameMakerChecker.put("value", 0);
|
||||
enableSameMakerChecker.put("enabled", false);
|
||||
enableSameMakerChecker.put("trapDoor", false);
|
||||
defaults.add(enableSameMakerChecker);
|
||||
|
||||
return defaults;
|
||||
}
|
||||
|
||||
|
||||
+22
-6
@@ -23,8 +23,9 @@ import com.linecorp.armeria.internal.shaded.guava.reflect.TypeToken;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import org.apache.fineract.client.models.GetMakerCheckerResponse;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.fineract.client.util.JSON;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
|
||||
@@ -32,7 +33,7 @@ public class MakercheckersHelper {
|
||||
|
||||
private static final Gson GSON = new JSON().getGson();
|
||||
|
||||
private static final String MAKERCHECKER_URL = "/fineract-provider/api/v1/makercheckers?" + Utils.TENANT_IDENTIFIER;
|
||||
private static final String MAKERCHECKER_URL = "/fineract-provider/api/v1/makercheckers";
|
||||
|
||||
private final RequestSpecification requestSpec;
|
||||
private final ResponseSpecification responseSpec;
|
||||
@@ -42,10 +43,25 @@ public class MakercheckersHelper {
|
||||
this.responseSpec = responseSpec;
|
||||
}
|
||||
|
||||
public ArrayList<GetMakerCheckerResponse> getMakerCheckerList() {
|
||||
final String response = Utils.performServerGet(this.requestSpec, this.responseSpec, MAKERCHECKER_URL);
|
||||
Type makerCheckerList = new TypeToken<ArrayList<GetMakerCheckerResponse>>() {}.getType();
|
||||
public List<Map<String, Object>> getMakerCheckerList(Map<String, String> queryParams) {
|
||||
StringBuilder url = new StringBuilder(MAKERCHECKER_URL).append("?").append(Utils.TENANT_IDENTIFIER);
|
||||
if (queryParams != null) {
|
||||
for (Map.Entry<String, String> entry : queryParams.entrySet()) {
|
||||
url.append("&").append(entry.getKey()).append("=").append(entry.getValue());
|
||||
}
|
||||
}
|
||||
final String response = Utils.performServerGet(this.requestSpec, this.responseSpec, url.toString());
|
||||
Type makerCheckerList = new TypeToken<List<Map<String, Object>>>() {}.getType();
|
||||
return GSON.fromJson(response, makerCheckerList);
|
||||
}
|
||||
|
||||
public void approveMakerCheckerEntry(Long auditId) {
|
||||
approveMakerCheckerEntry(this.requestSpec, this.responseSpec, auditId);
|
||||
}
|
||||
|
||||
public static HashMap<?, ?> approveMakerCheckerEntry(RequestSpecification requestSpec, ResponseSpecification responseSpec,
|
||||
Long auditId) {
|
||||
String url = MAKERCHECKER_URL + "/" + auditId + "?command=approve&" + Utils.TENANT_IDENTIFIER;
|
||||
return Utils.performServerPost(requestSpec, responseSpec, url, "", "");
|
||||
}
|
||||
}
|
||||
|
||||
+11
@@ -20,6 +20,7 @@ package org.apache.fineract.integrationtests.common.savings;
|
||||
|
||||
import static org.apache.fineract.integrationtests.common.Utils.DEFAULT_TENANT;
|
||||
import static org.apache.fineract.integrationtests.common.Utils.TENANT_PARAM_NAME;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.gson.Gson;
|
||||
@@ -174,6 +175,16 @@ public class SavingsAccountHelper extends IntegrationTest {
|
||||
savingsApplicationJSON, responseAttribute);
|
||||
}
|
||||
|
||||
public Integer createApproveActivateSavingsAccount(final Integer clientId, Integer savingsProductId, final String startDate) {
|
||||
final Integer savingsId = applyForSavingsApplicationOnDate(clientId, savingsProductId, ACCOUNT_TYPE_INDIVIDUAL, startDate);
|
||||
assertNotNull(savingsId);
|
||||
HashMap savingsStatusHashMap = approveSavingsOnDate(savingsId, startDate);
|
||||
SavingsStatusChecker.verifySavingsIsApproved(savingsStatusHashMap);
|
||||
savingsStatusHashMap = activateSavingsAccount(savingsId, startDate);
|
||||
SavingsStatusChecker.verifySavingsIsActive(savingsStatusHashMap);
|
||||
return savingsId;
|
||||
}
|
||||
|
||||
public HashMap updateSavingsAccount(final Integer id, final Integer savingsProductID, final Integer savingsId,
|
||||
final String accountType) {
|
||||
final String savingsApplicationJSON = new SavingsApplicationTestBuilder() //
|
||||
|
||||
+29
-6
@@ -19,24 +19,36 @@
|
||||
package org.apache.fineract.integrationtests.useradministration.roles;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.restassured.specification.RequestSpecification;
|
||||
import io.restassured.specification.ResponseSpecification;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.fineract.client.models.CommandProcessingResult;
|
||||
import org.apache.fineract.client.models.PutPermissionsRequest;
|
||||
import org.apache.fineract.client.util.JSON;
|
||||
import org.apache.fineract.integrationtests.client.IntegrationTest;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.useradministration.data.PermissionData;
|
||||
|
||||
public final class RolesHelper {
|
||||
public final class RolesHelper extends IntegrationTest {
|
||||
|
||||
public static final long SUPER_USER_ROLE_ID = 1L; // This is hardcoded into the initial Liquibase migration
|
||||
|
||||
private RolesHelper() {
|
||||
public RolesHelper() {
|
||||
|
||||
}
|
||||
|
||||
private static final String CREATE_ROLE_URL = "/fineract-provider/api/v1/roles?" + Utils.TENANT_IDENTIFIER;
|
||||
private static final String ROLE_URL = "/fineract-provider/api/v1/roles";
|
||||
private static final String PERMISSIONS_URL = "/fineract-provider/api/v1/permissions";
|
||||
private static final String DISABLE_ROLE_COMMAND = "disable";
|
||||
private static final String ENABLE_ROLE_COMMAND = "enable";
|
||||
|
||||
private static final Gson GSON = new JSON().getGson();
|
||||
|
||||
public static Integer createRole(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
|
||||
return Utils.performServerPost(requestSpec, responseSpec, CREATE_ROLE_URL, getTestCreateRoleAsJSON(), "resourceId");
|
||||
}
|
||||
@@ -70,13 +82,25 @@ public final class RolesHelper {
|
||||
}
|
||||
|
||||
public static String addPermissionsToRole(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
final Integer roleId, final HashMap<String, Boolean> permissionMap) {
|
||||
final Integer roleId, final Map<String, Boolean> permissionMap) {
|
||||
return Utils.performServerPut(requestSpec, responseSpec, ROLE_URL + "/" + roleId + "/permissions?" + Utils.TENANT_IDENTIFIER,
|
||||
getAddPermissionsToRoleJSON(permissionMap));
|
||||
}
|
||||
|
||||
private static String getAddPermissionsToRoleJSON(HashMap<String, Boolean> permissionMap) {
|
||||
final HashMap<String, HashMap<String, Boolean>> map = new HashMap<>();
|
||||
public static List<PermissionData> getPermissions(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
|
||||
boolean makerCheckerable) {
|
||||
String response = Utils.performServerGet(requestSpec, responseSpec,
|
||||
PERMISSIONS_URL + "?" + makerCheckerable + "=" + makerCheckerable);
|
||||
final Type listType = new TypeToken<List<PermissionData>>() {}.getType();
|
||||
return GSON.fromJson(response, listType);
|
||||
}
|
||||
|
||||
public CommandProcessingResult updatePermissions(PutPermissionsRequest request) {
|
||||
return ok(fineract().permissions.updatePermissionsDetails(request));
|
||||
}
|
||||
|
||||
private static String getAddPermissionsToRoleJSON(Map<String, Boolean> permissionMap) {
|
||||
final HashMap<String, Map<String, Boolean>> map = new HashMap<>();
|
||||
map.put("permissions", permissionMap);
|
||||
return new Gson().toJson(map);
|
||||
}
|
||||
@@ -84,5 +108,4 @@ public final class RolesHelper {
|
||||
private static String createRoleOperationURL(final String command, final Integer roleId) {
|
||||
return ROLE_URL + "/" + roleId + "?command=" + command + "&" + Utils.TENANT_IDENTIFIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user