FINERACT-1927: Incorrect exception info in BatchAPI response
This commit is contained in:
+5
-17
@@ -48,23 +48,11 @@ public class ApplySavingsCommandStrategy implements CommandStrategy {
|
||||
|
||||
@Override
|
||||
public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
|
||||
// Calls 'submitApplication' function from 'SavingsAccountsApiResource' to Apply Savings to an existing client
|
||||
String responseBody = savingsAccountsApiResource.submitApplication(request.getBody());
|
||||
|
||||
final BatchResponse response = new BatchResponse();
|
||||
final String responseBody;
|
||||
|
||||
response.setRequestId(request.getRequestId());
|
||||
response.setHeaders(request.getHeaders());
|
||||
|
||||
// Calls 'submitApplication' function from
|
||||
// 'SavingsAccountsApiResource' to Apply Savings to an existing
|
||||
// client
|
||||
responseBody = savingsAccountsApiResource.submitApplication(request.getBody());
|
||||
|
||||
response.setStatusCode(HttpStatus.SC_OK);
|
||||
// Sets the body of the response after savings is successfully
|
||||
// applied
|
||||
response.setBody(responseBody);
|
||||
|
||||
return response;
|
||||
// Sets the body of the response after savings is successfully applied
|
||||
return new BatchResponse().setRequestId(request.getRequestId()).setStatusCode(HttpStatus.SC_OK).setBody(responseBody)
|
||||
.setHeaders(request.getHeaders());
|
||||
}
|
||||
}
|
||||
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.batch.command.internal;
|
||||
|
||||
import static org.apache.fineract.batch.command.CommandStrategyUtils.relativeUrlWithoutVersion;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.batch.command.CommandStrategy;
|
||||
import org.apache.fineract.batch.domain.BatchRequest;
|
||||
import org.apache.fineract.batch.domain.BatchResponse;
|
||||
import org.apache.fineract.infrastructure.dataqueries.api.DatatablesApiResource;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Implements {@link CommandStrategy} and deletes a new datatable entry for a given entity. It passes the contents of
|
||||
* the body from the BatchRequest to {@link DatatablesApiResource} and gets back the response. This class will also
|
||||
* catch any errors raised by {@link DatatablesApiResource} and map those errors to appropriate status codes in
|
||||
* BatchResponse.
|
||||
*
|
||||
* @see CommandStrategy
|
||||
* @see BatchRequest
|
||||
* @see BatchResponse
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DeleteDatatableEntryOneToManyCommandStrategy implements CommandStrategy {
|
||||
|
||||
private final DatatablesApiResource datatablesApiResource;
|
||||
|
||||
@Override
|
||||
public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
|
||||
final BatchResponse response = new BatchResponse();
|
||||
final String responseBody;
|
||||
|
||||
response.setRequestId(request.getRequestId());
|
||||
response.setHeaders(request.getHeaders());
|
||||
|
||||
final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrlWithoutVersion(request));
|
||||
// Pluck out the datatable name & entityId out of the relative path
|
||||
final String datatableName = pathParameters.get(1);
|
||||
final Long entityId = Long.parseLong(pathParameters.get(2));
|
||||
final Long entryId = Long.parseLong(pathParameters.get(3));
|
||||
|
||||
// Calls 'deleteDatatableEntry' function from 'DatatablesApiResource' to delete a datatable entry on an existing
|
||||
// entity
|
||||
responseBody = datatablesApiResource.deleteDatatableEntry(datatableName, entityId, entryId);
|
||||
|
||||
response.setStatusCode(HttpStatus.SC_OK);
|
||||
// Sets the body of the response after datatable entry is successfully deleted created
|
||||
response.setBody(responseBody);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* 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.batch.command.internal;
|
||||
|
||||
import static org.apache.fineract.batch.command.CommandStrategyUtils.relativeUrlWithoutVersion;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.batch.command.CommandStrategy;
|
||||
import org.apache.fineract.batch.domain.BatchRequest;
|
||||
import org.apache.fineract.batch.domain.BatchResponse;
|
||||
import org.apache.fineract.infrastructure.dataqueries.api.DatatablesApiResource;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Implements {@link CommandStrategy} and deletes a new datatable entry for a given entity. It passes the contents of
|
||||
* the body from the BatchRequest to {@link DatatablesApiResource} and gets back the response. This class will also
|
||||
* catch any errors raised by {@link DatatablesApiResource} and map those errors to appropriate status codes in
|
||||
* BatchResponse.
|
||||
*
|
||||
* @see CommandStrategy
|
||||
* @see BatchRequest
|
||||
* @see BatchResponse
|
||||
*/
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DeleteDatatableEntryOneToOneCommandStrategy implements CommandStrategy {
|
||||
|
||||
private final DatatablesApiResource datatablesApiResource;
|
||||
|
||||
@Override
|
||||
public BatchResponse execute(BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) {
|
||||
final BatchResponse response = new BatchResponse();
|
||||
final String responseBody;
|
||||
|
||||
response.setRequestId(request.getRequestId());
|
||||
response.setHeaders(request.getHeaders());
|
||||
|
||||
final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrlWithoutVersion(request));
|
||||
// Pluck out the datatable name & entityId out of the relative path
|
||||
final String datatableName = pathParameters.get(1);
|
||||
final Long entityId = Long.parseLong(pathParameters.get(2));
|
||||
|
||||
// Calls 'deleteDatatableEntry' function from 'DatatablesApiResource' to delete a datatable entry on an existing
|
||||
// entity
|
||||
responseBody = datatablesApiResource.deleteDatatableEntries(datatableName, entityId);
|
||||
|
||||
response.setStatusCode(HttpStatus.SC_OK);
|
||||
// Sets the body of the response after datatable entry is successfully deleted created
|
||||
response.setBody(responseBody);
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
+3
-11
@@ -43,14 +43,9 @@ public class GetSavingsAccountByIdCommandStrategy implements CommandStrategy {
|
||||
@Override
|
||||
public BatchResponse execute(BatchRequest batchRequest, UriInfo uriInfo) {
|
||||
final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo);
|
||||
final BatchResponse response = new BatchResponse();
|
||||
|
||||
response.setRequestId(batchRequest.getRequestId());
|
||||
response.setHeaders(batchRequest.getHeaders());
|
||||
|
||||
final String relativeUrl = relativeUrlWithoutVersion(batchRequest);
|
||||
|
||||
final Long savingsAccountId;
|
||||
final long savingsAccountId;
|
||||
Map<String, String> queryParameters = new HashMap<>();
|
||||
if (relativeUrl.indexOf('?') > 0) {
|
||||
savingsAccountId = Long.parseLong(StringUtils.substringBetween(relativeUrl, "/", "?"));
|
||||
@@ -74,10 +69,7 @@ public class GetSavingsAccountByIdCommandStrategy implements CommandStrategy {
|
||||
final String responseBody = savingsAccountsApiResource.retrieveOne(savingsAccountId,
|
||||
Boolean.parseBoolean(staffInSelectedOfficeOnly), chargeStatus, uriInfo);
|
||||
|
||||
response.setStatusCode(HttpStatus.SC_OK);
|
||||
|
||||
response.setBody(responseBody);
|
||||
|
||||
return response;
|
||||
return new BatchResponse().setRequestId(batchRequest.getRequestId()).setStatusCode(HttpStatus.SC_OK).setBody(responseBody)
|
||||
.setHeaders(batchRequest.getHeaders());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-9
@@ -42,10 +42,6 @@ public class SavingsAccountAdjustTransactionCommandStrategy implements CommandSt
|
||||
|
||||
@Override
|
||||
public BatchResponse execute(BatchRequest batchRequest, UriInfo uriInfo) {
|
||||
final BatchResponse response = new BatchResponse();
|
||||
|
||||
response.setRequestId(batchRequest.getRequestId());
|
||||
response.setHeaders(batchRequest.getHeaders());
|
||||
String relativeUrl = relativeUrlWithoutVersion(batchRequest);
|
||||
final List<String> pathParameters;
|
||||
String command = null;
|
||||
@@ -63,10 +59,7 @@ public class SavingsAccountAdjustTransactionCommandStrategy implements CommandSt
|
||||
final String responseBody = savingsAccountTransactionsApiResource.adjustTransaction(savingsAccountId, transactionId, command,
|
||||
batchRequest.getBody());
|
||||
|
||||
response.setStatusCode(HttpStatus.SC_OK);
|
||||
|
||||
response.setBody(responseBody);
|
||||
|
||||
return response;
|
||||
return new BatchResponse().setRequestId(batchRequest.getRequestId()).setStatusCode(HttpStatus.SC_OK).setBody(responseBody)
|
||||
.setHeaders(batchRequest.getHeaders());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-9
@@ -41,10 +41,6 @@ public class SavingsAccountTransactionCommandStrategy implements CommandStrategy
|
||||
|
||||
@Override
|
||||
public BatchResponse execute(BatchRequest batchRequest, UriInfo uriInfo) {
|
||||
final BatchResponse response = new BatchResponse();
|
||||
|
||||
response.setRequestId(batchRequest.getRequestId());
|
||||
response.setHeaders(batchRequest.getHeaders());
|
||||
String relativeUrl = relativeUrlWithoutVersion(batchRequest);
|
||||
final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrl);
|
||||
String command = null;
|
||||
@@ -56,10 +52,7 @@ public class SavingsAccountTransactionCommandStrategy implements CommandStrategy
|
||||
Long savingsAccountId = Long.parseLong(pathParameters.get(1));
|
||||
final String responseBody = savingsAccountTransactionsApiResource.transaction(savingsAccountId, command, batchRequest.getBody());
|
||||
|
||||
response.setStatusCode(HttpStatus.SC_OK);
|
||||
|
||||
response.setBody(responseBody);
|
||||
|
||||
return response;
|
||||
return new BatchResponse().setRequestId(batchRequest.getRequestId()).setStatusCode(HttpStatus.SC_OK).setBody(responseBody)
|
||||
.setHeaders(batchRequest.getHeaders());
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ public class DocumentManagementApiResource {
|
||||
final DocumentCommand documentCommand = new DocumentCommand(null, null, entityType, entityId, name, fileDetails.getFileName(),
|
||||
fileSize, bodyPart.getMediaType().toString(), description, null);
|
||||
final Long documentId = this.documentWritePlatformService.createDocument(documentCommand, inputStream);
|
||||
return this.toApiJsonSerializer.serialize(CommandProcessingResult.resourceResult(documentId, null));
|
||||
return this.toApiJsonSerializer.serialize(CommandProcessingResult.resourceResult(documentId));
|
||||
}
|
||||
|
||||
@PUT
|
||||
|
||||
+1
-1
@@ -201,7 +201,7 @@ public class ImagesApiResource {
|
||||
public String deleteClientImage(@PathParam("entity") final String entityName, @PathParam("entityId") final Long entityId) {
|
||||
validateEntityTypeforImage(entityName);
|
||||
this.imageWritePlatformService.deleteImage(entityName, entityId);
|
||||
return this.toApiJsonSerializer.serialize(new CommandProcessingResult(entityId));
|
||||
return this.toApiJsonSerializer.serialize(CommandProcessingResult.resourceResult(entityId));
|
||||
}
|
||||
|
||||
/*** Entities for document Management **/
|
||||
|
||||
+2
-2
@@ -133,7 +133,7 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
|
||||
this.documentRepository.saveAndFlush(documentForUpdate);
|
||||
|
||||
return new CommandProcessingResult(documentForUpdate.getId());
|
||||
return CommandProcessingResult.resourceResult(documentForUpdate.getId());
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||
LOG.error("Error occured.", dve);
|
||||
throw new PlatformDataIntegrityException("error.msg.document.unknown.data.integrity.issue",
|
||||
@@ -158,7 +158,7 @@ public class DocumentWritePlatformServiceJpaRepositoryImpl implements DocumentWr
|
||||
|
||||
final ContentRepository contentRepository = this.contentRepositoryFactory.getRepository(document.storageType());
|
||||
contentRepository.deleteFile(document.getLocation());
|
||||
return new CommandProcessingResult(document.getId());
|
||||
return CommandProcessingResult.resourceResult(document.getId());
|
||||
}
|
||||
|
||||
private void validateParentEntityType(final DocumentCommand documentCommand) {
|
||||
|
||||
+2
-2
@@ -103,7 +103,7 @@ public class ImageWritePlatformServiceJpaRepositoryImpl implements ImageWritePla
|
||||
this.imageRepository.delete(image);
|
||||
}
|
||||
|
||||
return new CommandProcessingResult(clientId);
|
||||
return CommandProcessingResult.resourceResult(clientId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +151,7 @@ public class ImageWritePlatformServiceJpaRepositoryImpl implements ImageWritePla
|
||||
}
|
||||
|
||||
this.imageRepository.save(image);
|
||||
return new CommandProcessingResult(clientId);
|
||||
return CommandProcessingResult.resourceResult(clientId);
|
||||
}
|
||||
|
||||
private Image createImage(Image image, final String imageLocation, final StorageType storageType) {
|
||||
|
||||
+1
-1
@@ -164,7 +164,7 @@ public class ClientIdentifierWritePlatformServiceJpaRepositoryImpl implements Cl
|
||||
.build();
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||
handleClientIdentifierDataIntegrityViolation(documentTypeLabel, documentTypeId, documentKey, dve.getMostSpecificCause(), dve);
|
||||
return new CommandProcessingResult(Long.valueOf(-1));
|
||||
return CommandProcessingResult.resourceResult(-1L);
|
||||
} catch (final PersistenceException dve) {
|
||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||
handleClientIdentifierDataIntegrityViolation(documentTypeLabel, documentTypeId, documentKey, throwable, dve);
|
||||
|
||||
+1
-1
@@ -138,7 +138,7 @@ public class CollateralWritePlatformServiceJpaRepositoryImpl implements Collater
|
||||
.build();
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||
handleCollateralDataIntegrityViolation(dve);
|
||||
return new CommandProcessingResult(Long.valueOf(-1));
|
||||
return CommandProcessingResult.resourceResult(-1L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-8
@@ -40,6 +40,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||
import org.apache.fineract.batch.exception.ErrorHandler;
|
||||
import org.apache.fineract.cob.exceptions.LoanAccountLockCannotBeOverruledException;
|
||||
import org.apache.fineract.cob.service.LoanAccountLockService;
|
||||
import org.apache.fineract.infrastructure.codes.domain.CodeValue;
|
||||
@@ -193,7 +194,6 @@ import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
|
||||
import org.apache.fineract.portfolio.transfer.api.TransferApiConstants;
|
||||
import org.apache.fineract.useradministration.domain.AppUser;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -247,7 +247,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
private final ExternalIdFactory externalIdFactory;
|
||||
private final ReplayedTransactionBusinessEventService replayedTransactionBusinessEventService;
|
||||
private final LoanAccrualTransactionBusinessEventService loanAccrualTransactionBusinessEventService;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final ErrorHandler errorHandler;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -2426,13 +2426,8 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
|
||||
@SuppressWarnings("unused")
|
||||
private void fallbackRecalculateInterest(Throwable t) {
|
||||
// NOTE: allow caller to catch the exceptions
|
||||
|
||||
if (t instanceof RuntimeException re) {
|
||||
throw re;
|
||||
}
|
||||
|
||||
// NOTE: wrap throwable only if really necessary
|
||||
throw new RuntimeException(t);
|
||||
throw errorHandler.getMappable(t, null, null, "loan.recalculateinterest");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -255,7 +255,7 @@ public class LoanProductWritePlatformServiceJpaRepositoryImpl implements LoanPro
|
||||
|
||||
} catch (final DataIntegrityViolationException | JpaSystemException dve) {
|
||||
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||
return new CommandProcessingResult((long) -1);
|
||||
return CommandProcessingResult.resourceResult(-1L);
|
||||
} catch (final PersistenceException dve) {
|
||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||
handleDataIntegrityIssues(command, throwable, dve);
|
||||
|
||||
+1
-1
@@ -125,7 +125,7 @@ public class RateWriteServiceImpl implements RateWriteService {
|
||||
|
||||
} catch (final JpaSystemException | DataIntegrityViolationException dve) {
|
||||
handleRateDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||
return new CommandProcessingResult((long) -1);
|
||||
return CommandProcessingResult.resourceResult(-1L);
|
||||
} catch (final PersistenceException dve) {
|
||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||
handleRateDataIntegrityIssues(command, throwable, dve);
|
||||
|
||||
+25
-37
@@ -50,7 +50,6 @@ import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformS
|
||||
import org.apache.fineract.infrastructure.core.api.ApiParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.api.ApiRequestParameterHelper;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
|
||||
import org.apache.fineract.infrastructure.core.exception.UnrecognizedQueryParamException;
|
||||
import org.apache.fineract.infrastructure.core.serialization.ApiRequestJsonSerializationSettings;
|
||||
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
|
||||
@@ -66,10 +65,8 @@ import org.apache.fineract.portfolio.savings.service.SavingsAccountReadPlatformS
|
||||
import org.apache.fineract.portfolio.savings.service.search.SavingsAccountTransactionSearchService;
|
||||
import org.apache.fineract.portfolio.search.data.AdvancedQueryRequest;
|
||||
import org.apache.fineract.portfolio.search.data.TransactionSearchRequest;
|
||||
import org.springframework.dao.CannotAcquireLockException;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Path("/v1/savingsaccounts/{savingsId}/transactions")
|
||||
@@ -185,42 +182,33 @@ public class SavingsAccountTransactionsApiResource {
|
||||
@ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = SavingsAccountTransactionsApiResourceSwagger.PostSavingsAccountTransactionsResponse.class))) })
|
||||
public String transaction(@PathParam("savingsId") final Long savingsId, @QueryParam("command") final String commandParam,
|
||||
final String apiRequestBodyAsJson) {
|
||||
try {
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||
final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
if (is(commandParam, "deposit")) {
|
||||
final CommandWrapper commandRequest = builder.savingsAccountDeposit(savingsId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "gsimDeposit")) {
|
||||
final CommandWrapper commandRequest = builder.gsimSavingsAccountDeposit(savingsId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "withdrawal")) {
|
||||
final CommandWrapper commandRequest = builder.savingsAccountWithdrawal(savingsId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "postInterestAsOn")) {
|
||||
final CommandWrapper commandRequest = builder.savingsAccountInterestPosting(savingsId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, SavingsApiConstants.COMMAND_HOLD_AMOUNT)) {
|
||||
final CommandWrapper commandRequest = builder.holdAmount(savingsId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
//
|
||||
throw new UnrecognizedQueryParamException("command", commandParam,
|
||||
new Object[] { "deposit", "withdrawal", SavingsApiConstants.COMMAND_HOLD_AMOUNT });
|
||||
}
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
} catch (ObjectOptimisticLockingFailureException lockingFailureException) {
|
||||
throw new PlatformDataIntegrityException("error.msg.savings.concurrent.operations",
|
||||
"Concurrent Transactions being made on this savings account: " + lockingFailureException.getMessage(),
|
||||
lockingFailureException);
|
||||
} catch (CannotAcquireLockException cannotAcquireLockException) {
|
||||
throw new PlatformDataIntegrityException("error.msg.savings.concurrent.operations.unable.to.acquire.lock",
|
||||
"Unable to acquir lock for this transaction: " + cannotAcquireLockException.getMessage(), cannotAcquireLockException);
|
||||
CommandProcessingResult result = null;
|
||||
if (is(commandParam, "deposit")) {
|
||||
final CommandWrapper commandRequest = builder.savingsAccountDeposit(savingsId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "gsimDeposit")) {
|
||||
final CommandWrapper commandRequest = builder.gsimSavingsAccountDeposit(savingsId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "withdrawal")) {
|
||||
final CommandWrapper commandRequest = builder.savingsAccountWithdrawal(savingsId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, "postInterestAsOn")) {
|
||||
final CommandWrapper commandRequest = builder.savingsAccountInterestPosting(savingsId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
} else if (is(commandParam, SavingsApiConstants.COMMAND_HOLD_AMOUNT)) {
|
||||
final CommandWrapper commandRequest = builder.holdAmount(savingsId).build();
|
||||
result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
//
|
||||
throw new UnrecognizedQueryParamException("command", commandParam,
|
||||
new Object[] { "deposit", "withdrawal", SavingsApiConstants.COMMAND_HOLD_AMOUNT });
|
||||
}
|
||||
|
||||
return this.toApiJsonSerializer.serialize(result);
|
||||
}
|
||||
|
||||
@POST
|
||||
|
||||
+4
-4
@@ -450,11 +450,11 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
.build();
|
||||
} catch (final DataAccessException dve) {
|
||||
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||
return new CommandProcessingResult((long) -1);
|
||||
return CommandProcessingResult.resourceResult(-1L);
|
||||
} catch (final PersistenceException dve) {
|
||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||
handleDataIntegrityIssues(command, throwable, dve);
|
||||
return new CommandProcessingResult((long) -1);
|
||||
return CommandProcessingResult.resourceResult(-1L);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,11 +521,11 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
.build();
|
||||
} catch (final DataAccessException dve) {
|
||||
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||
return new CommandProcessingResult(Long.valueOf(-1));
|
||||
return CommandProcessingResult.resourceResult(-1L);
|
||||
} catch (final PersistenceException dve) {
|
||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||
handleDataIntegrityIssues(command, throwable, dve);
|
||||
return new CommandProcessingResult(Long.valueOf(-1));
|
||||
return CommandProcessingResult.resourceResult(-1L);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-39
@@ -51,6 +51,7 @@ import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.fineract.accounting.journalentry.service.JournalEntryWritePlatformService;
|
||||
import org.apache.fineract.batch.exception.ErrorHandler;
|
||||
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.ApiParameterError;
|
||||
@@ -165,6 +166,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
private final BusinessEventNotifierService businessEventNotifierService;
|
||||
private final GSIMRepositoy gsimRepository;
|
||||
private final SavingsAccountInterestPostingService savingsAccountInterestPostingService;
|
||||
private final ErrorHandler errorHandler;
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
@@ -233,7 +235,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
@Override
|
||||
public void processPostActiveActions(final SavingsAccount account, final DateTimeFormatter fmt, final Set<Long> existingTransactionIds,
|
||||
final Set<Long> existingReversedTransactionIds) {
|
||||
|
||||
AppUser user = getAppUserIfPresent();
|
||||
|
||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
||||
@@ -264,7 +265,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult gsimDeposit(final Long gsimId, final JsonCommand command) {
|
||||
|
||||
Long parentSavingId = gsimId;
|
||||
// GroupSavingsIndividualMonitoringparentSavings=gsimRepository.findById(parentSavingId).get();
|
||||
List<SavingsAccount> childSavings = this.savingAccountRepositoryWrapper.findByGsimId(gsimId);
|
||||
@@ -276,7 +276,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
CommandProcessingResult result = null;
|
||||
for (JsonElement element : savingsArray) {
|
||||
|
||||
result = deposit(element.getAsJsonObject().get("childAccountId").getAsLong(),
|
||||
JsonCommand.fromExistingCommand(command, element));
|
||||
}
|
||||
@@ -286,7 +285,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult deposit(final Long savingsId, final JsonCommand command) {
|
||||
|
||||
this.context.authenticatedUser();
|
||||
|
||||
this.savingsAccountTransactionDataValidator.validate(command);
|
||||
@@ -347,7 +345,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
.withSavingsId(savingsId) //
|
||||
.with(changes) //
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
private Long saveTransactionToGenerateTransactionId(final SavingsAccountTransaction transaction) {
|
||||
@@ -420,7 +417,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult applyAnnualFee(final Long savingsAccountChargeId, final Long accountId) {
|
||||
|
||||
AppUser user = getAppUserIfPresent();
|
||||
|
||||
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
|
||||
@@ -445,7 +441,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult calculateInterest(final Long savingsId) {
|
||||
|
||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
||||
.isSavingsInterestPostingAtCurrentPeriodEnd();
|
||||
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
|
||||
@@ -480,7 +475,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
@Override
|
||||
@Transactional
|
||||
public CommandProcessingResult postInterest(final JsonCommand command) {
|
||||
|
||||
Long savingsId = command.getSavingsId();
|
||||
final boolean postInterestAs = command.booleanPrimitiveValueOfParameterNamed("isPostInterestAsOn");
|
||||
final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
|
||||
@@ -491,13 +485,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
this.savingsAccountTransactionDataValidator.validateTransactionWithPivotDate(transactionDate, account);
|
||||
|
||||
if (postInterestAs == true) {
|
||||
|
||||
if (postInterestAs) {
|
||||
if (transactionDate == null) {
|
||||
|
||||
throw new PostInterestAsOnDateException(PostInterestAsOnExceptionType.VALID_DATE);
|
||||
}
|
||||
if (transactionDate.isBefore(account.accountSubmittedOrActivationDate())) {
|
||||
if (DateUtils.isBefore(transactionDate, account.accountSubmittedOrActivationDate())) {
|
||||
throw new PostInterestAsOnDateException(PostInterestAsOnExceptionType.ACTIVATION_DATE);
|
||||
}
|
||||
|
||||
@@ -509,16 +501,14 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
}
|
||||
|
||||
for (SavingsAccountTransaction savingTransaction : savingTransactions) {
|
||||
if (transactionDate.isBefore(savingTransaction.getDateOf())) {
|
||||
if (DateUtils.isBefore(transactionDate, savingTransaction.getDateOf())) {
|
||||
throw new PostInterestAsOnDateException(PostInterestAsOnExceptionType.LAST_TRANSACTION_DATE);
|
||||
}
|
||||
}
|
||||
|
||||
LocalDate today = DateUtils.getBusinessLocalDate();
|
||||
if (transactionDate.isAfter(today)) {
|
||||
if (DateUtils.isDateInTheFuture(transactionDate)) {
|
||||
throw new PostInterestAsOnDateException(PostInterestAsOnExceptionType.FUTURE_DATE);
|
||||
}
|
||||
|
||||
}
|
||||
postInterest(account, postInterestAs, transactionDate, backdatedTxnsAllowedTill);
|
||||
|
||||
@@ -536,7 +526,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
@Override
|
||||
public void postInterest(final SavingsAccount account, final boolean postInterestAs, final LocalDate transactionDate,
|
||||
final boolean backdatedTxnsAllowedTill) {
|
||||
|
||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
||||
.isSavingsInterestPostingAtCurrentPeriodEnd();
|
||||
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
|
||||
@@ -757,7 +746,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult adjustSavingsTransaction(final Long savingsId, final Long transactionId, final JsonCommand command) {
|
||||
|
||||
AppUser user = getAppUserIfPresent();
|
||||
|
||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
||||
@@ -939,12 +927,12 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
List<SavingsAccountTransaction> savingTransactions = account.getTransactions();
|
||||
for (SavingsAccountTransaction savingTransaction : savingTransactions) {
|
||||
if (savingTransaction.isInterestPosting() && savingTransaction.isNotReversed()
|
||||
&& closedDate.isEqual(savingTransaction.getTransactionLocalDate())) {
|
||||
&& DateUtils.isEqual(closedDate, savingTransaction.getTransactionLocalDate())) {
|
||||
postInterestOnClosingDate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (postInterestOnClosingDate == false) {
|
||||
if (!postInterestOnClosingDate) {
|
||||
throw new PostInterestClosingDateException();
|
||||
}
|
||||
}
|
||||
@@ -997,7 +985,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
@Override
|
||||
public SavingsAccountTransaction initiateSavingsTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate) {
|
||||
|
||||
AppUser user = getAppUserIfPresent();
|
||||
|
||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
||||
@@ -1032,7 +1019,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
@Override
|
||||
public SavingsAccountTransaction withdrawSavingsTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate) {
|
||||
|
||||
AppUser user = getAppUserIfPresent();
|
||||
|
||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
||||
@@ -1072,7 +1058,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
@Override
|
||||
public SavingsAccountTransaction acceptSavingsTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate,
|
||||
final Office acceptedInOffice, final Staff fieldOfficer) {
|
||||
|
||||
AppUser user = getAppUserIfPresent();
|
||||
|
||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
||||
@@ -1108,7 +1093,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult addSavingsAccountCharge(final JsonCommand command) {
|
||||
|
||||
this.context.authenticatedUser();
|
||||
final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
|
||||
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
|
||||
@@ -1233,7 +1217,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult waiveCharge(final Long savingsAccountId, final Long savingsAccountChargeId) {
|
||||
|
||||
AppUser user = getAppUserIfPresent();
|
||||
|
||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
|
||||
@@ -1325,7 +1308,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult payCharge(final Long savingsAccountId, final Long savingsAccountChargeId, final JsonCommand command) {
|
||||
|
||||
AppUser user = getAppUserIfPresent();
|
||||
|
||||
this.savingsAccountChargeDataValidator.validatePayCharge(command.json());
|
||||
@@ -1393,7 +1375,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
|
||||
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MM yyyy").withZone(DateUtils.getDateTimeZoneOfTenant());
|
||||
|
||||
while (transactionDate.isAfter(savingsAccountCharge.getDueLocalDate()) && savingsAccountCharge.isNotFullyPaid()) {
|
||||
while (savingsAccountCharge.isNotFullyPaid() && DateUtils.isBeforeBusinessDate(savingsAccountCharge.getDueLocalDate())) {
|
||||
payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt, user, false);
|
||||
}
|
||||
}
|
||||
@@ -1402,13 +1384,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
private SavingsAccountData fallbackPostInterest(SavingsAccountData savingsAccountData, boolean postInterestAs,
|
||||
LocalDate transactionDate, boolean backdatedTxnsAllowedTill, Throwable t) {
|
||||
// NOTE: allow caller to catch the exceptions
|
||||
|
||||
if (t instanceof RuntimeException re) {
|
||||
throw re;
|
||||
}
|
||||
|
||||
// NOTE: wrap throwable only if really necessary
|
||||
throw new RuntimeException(t);
|
||||
throw errorHandler.getMappable(t, null, null, "savings.postinterest");
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@@ -1817,7 +1794,6 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
@Transactional
|
||||
@Override
|
||||
public CommandProcessingResult releaseAmount(final Long savingsId, final Long savingsTransactionId) {
|
||||
|
||||
final AppUser submittedBy = this.context.authenticatedUser();
|
||||
SavingsAccountTransaction holdTransaction = this.savingsAccountTransactionRepository
|
||||
.findOneByIdAndSavingsAccountId(savingsTransactionId, savingsId);
|
||||
@@ -1935,16 +1911,14 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
}
|
||||
|
||||
private void validateTransactionsForTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate) {
|
||||
|
||||
for (SavingsAccountTransaction transaction : savingsAccount.getTransactions()) {
|
||||
if ((transaction.getTransactionLocalDate().isEqual(transferDate) && transaction.getTransactionLocalDate().isAfter(transferDate))
|
||||
|| transaction.getTransactionLocalDate().isAfter(transferDate)) {
|
||||
if ((DateUtils.isEqual(transferDate, transaction.getTransactionLocalDate())
|
||||
&& DateUtils.isEqual(transferDate, transaction.getSubmittedOnDate()))
|
||||
|| DateUtils.isBefore(transferDate, transaction.getTransactionLocalDate())) {
|
||||
throw new GeneralPlatformDomainRuleException(TransferApiConstants.transferClientSavingsException,
|
||||
TransferApiConstants.transferClientSavingsException, transaction.getCreatedDate(), transferDate);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void validateReasonForHold(String reasonForBlock) {
|
||||
|
||||
+1
-1
@@ -397,7 +397,7 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
|
||||
.build();
|
||||
} catch (final DataAccessException dve) {
|
||||
handleDataIntegrityIssues(command, dve.getMostSpecificCause(), dve);
|
||||
return new CommandProcessingResult(Long.valueOf(-1));
|
||||
return CommandProcessingResult.resourceResult(-1L);
|
||||
} catch (final PersistenceException dve) {
|
||||
Throwable throwable = ExceptionUtils.getRootCause(dve.getCause());
|
||||
handleDataIntegrityIssues(command, throwable, dve);
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ public class SelfServiceRegistrationApiResource {
|
||||
@Produces({ MediaType.APPLICATION_JSON })
|
||||
public String createSelfServiceUser(final String apiRequestBodyAsJson) {
|
||||
AppUser user = this.selfServiceRegistrationWritePlatformService.createUser(apiRequestBodyAsJson);
|
||||
return this.toApiJsonSerializer.serialize(CommandProcessingResult.resourceResult(user.getId(), null));
|
||||
return this.toApiJsonSerializer.serialize(CommandProcessingResult.resourceResult(user.getId()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -287,7 +287,7 @@ resilience4j.retry.instances.executeCommand.max-attempts=${FINERACT_COMMAND_PROC
|
||||
resilience4j.retry.instances.executeCommand.wait-duration=${FINERACT_COMMAND_PROCESSING_RETRY_WAIT_DURATION:1s}
|
||||
resilience4j.retry.instances.executeCommand.enable-exponential-backoff=${FINERACT_COMMAND_PROCESSING_RETRY_ENABLE_EXPONENTIAL_BACKOFF:true}
|
||||
resilience4j.retry.instances.executeCommand.exponential-backoff-multiplier=${FINERACT_COMMAND_PROCESSING_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER:2}
|
||||
resilience4j.retry.instances.executeCommand.retryExceptions=${FINERACT_COMMAND_PROCESSING_RETRY_EXCEPTIONS:org.springframework.dao.CannotAcquireLockException,org.springframework.orm.ObjectOptimisticLockingFailureException}
|
||||
resilience4j.retry.instances.executeCommand.retryExceptions=${FINERACT_COMMAND_PROCESSING_RETRY_EXCEPTIONS:org.springframework.dao.CannotAcquireLockException,org.springframework.orm.ObjectOptimisticLockingFailureException,org.eclipse.persistence.exceptions.OptimisticLockException}
|
||||
|
||||
resilience4j.retry.instances.processJobDetailForExecution.max-attempts=${FINERACT_PROCESS_JOB_DETAIL_RETRY_MAX_ATTEMPTS:3}
|
||||
resilience4j.retry.instances.processJobDetailForExecution.wait-duration=${FINERACT_PROCESS_JOB_DETAIL_RETRY_WAIT_DURATION:1s}
|
||||
@@ -298,10 +298,10 @@ resilience4j.retry.instances.recalculateInterest.max-attempts=${FINERACT_PROCESS
|
||||
resilience4j.retry.instances.recalculateInterest.wait-duration=${FINERACT_PROCESS_RECALCULATE_INTEREST_RETRY_WAIT_DURATION:1s}
|
||||
resilience4j.retry.instances.recalculateInterest.enable-exponential-backoff=${FINERACT_PROCESS_RECALCULATE_INTEREST_RETRY_ENABLE_EXPONENTIAL_BACKOFF:true}
|
||||
resilience4j.retry.instances.recalculateInterest.exponential-backoff-multiplier=${FINERACT_PROCESS_RECALCULATE_INTEREST_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER:2}
|
||||
resilience4j.retry.instances.recalculateInterest.retryExceptions=${FINERACT_PROCESS_RECALCULATE_INTEREST_RETRY_EXCEPTIONS:org.springframework.dao.CannotAcquireLockException,org.springframework.orm.ObjectOptimisticLockingFailureException}
|
||||
resilience4j.retry.instances.recalculateInterest.retryExceptions=${FINERACT_PROCESS_RECALCULATE_INTEREST_RETRY_EXCEPTIONS:org.springframework.dao.CannotAcquireLockException,org.springframework.orm.ObjectOptimisticLockingFailureException,org.eclipse.persistence.exceptions.OptimisticLockException}
|
||||
|
||||
resilience4j.retry.instances.postInterest.max-attempts=${FINERACT_PROCESS_POST_INTEREST_RETRY_MAX_ATTEMPTS:3}
|
||||
resilience4j.retry.instances.postInterest.wait-duration=${FINERACT_PROCESS_POST_INTEREST_RETRY_WAIT_DURATION:1s}
|
||||
resilience4j.retry.instances.postInterest.enable-exponential-backoff=${FINERACT_PROCESS_POST_INTEREST_RETRY_ENABLE_EXPONENTIAL_BACKOFF:true}
|
||||
resilience4j.retry.instances.postInterest.exponential-backoff-multiplier=${FINERACT_PROCESS_POST_INTEREST_RETRY_EXPONENTIAL_BACKOFF_MULTIPLIER:2}
|
||||
resilience4j.retry.instances.postInterest.retryExceptions=${FINERACT_PROCESS_POST_INTEREST_RETRY_EXCEPTIONS:org.springframework.dao.CannotAcquireLockException,org.springframework.orm.ObjectOptimisticLockingFailureException}
|
||||
resilience4j.retry.instances.postInterest.retryExceptions=${FINERACT_PROCESS_POST_INTEREST_RETRY_EXCEPTIONS:org.springframework.dao.CannotAcquireLockException,org.springframework.orm.ObjectOptimisticLockingFailureException,org.eclipse.persistence.exceptions.OptimisticLockException}
|
||||
|
||||
+14
-28
@@ -20,6 +20,7 @@ package org.apache.fineract.commands.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
|
||||
import io.cucumber.java8.En;
|
||||
import io.github.resilience4j.retry.RetryRegistry;
|
||||
@@ -33,11 +34,14 @@ import org.apache.fineract.commands.domain.CommandWrapper;
|
||||
import org.apache.fineract.commands.exception.RollbackTransactionAsCommandIsNotApprovedByCheckerException;
|
||||
import org.apache.fineract.infrastructure.core.api.JsonCommand;
|
||||
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
|
||||
import org.apache.fineract.infrastructure.core.domain.FineractRequestContextHolder;
|
||||
import org.mockito.Mockito;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.CannotAcquireLockException;
|
||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
public class CommandServiceStepDefinitions implements En {
|
||||
|
||||
@@ -55,16 +59,24 @@ public class CommandServiceStepDefinitions implements En {
|
||||
|
||||
private RetryEvent retryEvent;
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
public CommandServiceStepDefinitions() {
|
||||
Given("/^A command source write service$/", () -> {
|
||||
this.commandSourceWritePlatformService = new DummyCommandSourceWriteService(processAndLogCommandService);
|
||||
this.command = new DummyCommand();
|
||||
FineractRequestContextHolder contextHolder = Mockito.spy(FineractRequestContextHolder.class);
|
||||
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()));
|
||||
|
||||
this.retryRegistry.retry("executeCommand").getEventPublisher().onRetry(event -> {
|
||||
log.warn("... retry event: {}", event);
|
||||
|
||||
counter.incrementAndGet();
|
||||
CommandServiceStepDefinitions.this.retryEvent = event;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
When("/^The user executes the command via a command write service with exceptions$/", () -> {
|
||||
@@ -84,47 +96,21 @@ public class CommandServiceStepDefinitions implements En {
|
||||
});
|
||||
|
||||
Then("/^The command processing service execute function should be called 3 times$/", () -> {
|
||||
assertEquals(3, command.getCount());
|
||||
assertEquals(2, counter.get());
|
||||
});
|
||||
}
|
||||
|
||||
public static class DummyCommand extends CommandWrapper {
|
||||
|
||||
private AtomicInteger counter = new AtomicInteger();
|
||||
|
||||
public DummyCommand() {
|
||||
super(null, null, null, null, null, null, null, null, null, null, "{}", null, null, null, null, null, null,
|
||||
UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String taskPermissionName() {
|
||||
// NOTE: simulating a failure scenario that triggers retries; using this function, because it is the first
|
||||
// called in the command processing service
|
||||
|
||||
int step = counter.incrementAndGet();
|
||||
|
||||
log.warn("Round: {}", step);
|
||||
|
||||
if (step == 1) {
|
||||
throw new CannotAcquireLockException("BLOW IT UP!!!");
|
||||
} else if (step == 2) {
|
||||
throw new ObjectOptimisticLockingFailureException("Dummy", new RuntimeException("BLOW IT UP!!!"));
|
||||
} else if (step == 3) {
|
||||
throw new RollbackTransactionAsCommandIsNotApprovedByCheckerException(new DummyCommandSource());
|
||||
}
|
||||
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String actionName() {
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return counter.get();
|
||||
}
|
||||
}
|
||||
|
||||
public static class DummyCommandSourceWriteService implements PortfolioCommandSourceWritePlatformService {
|
||||
|
||||
+5
-4
@@ -74,7 +74,7 @@ public class CommandSourceServiceTest {
|
||||
ThreadLocalContextUtil.setTenant(ft);
|
||||
|
||||
String idk = "idk";
|
||||
underTest.saveInitial(wrapper, jsonCommand, appUser, idk);
|
||||
underTest.saveInitialNewTransaction(wrapper, jsonCommand, appUser, idk);
|
||||
|
||||
ArgumentCaptor<CommandSource> commandSourceArgumentCaptor = ArgumentCaptor.forClass(CommandSource.class);
|
||||
Mockito.verify(commandSourceRepository).saveAndFlush(commandSourceArgumentCaptor.capture());
|
||||
@@ -97,7 +97,7 @@ public class CommandSourceServiceTest {
|
||||
|
||||
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "t1", "n1", ZoneId.systemDefault().toString(), null));
|
||||
|
||||
CommandSource actual = underTest.saveInitial(wrapper, jsonCommand, appUser, "idk");
|
||||
CommandSource actual = underTest.saveInitialNewTransaction(wrapper, jsonCommand, appUser, "idk");
|
||||
|
||||
ArgumentCaptor<CommandSource> commandSourceArgumentCaptor = ArgumentCaptor.forClass(CommandSource.class);
|
||||
Mockito.verify(commandSourceRepository).saveAndFlush(commandSourceArgumentCaptor.capture());
|
||||
@@ -108,9 +108,10 @@ public class CommandSourceServiceTest {
|
||||
|
||||
@Test
|
||||
public void testGenerateErrorException() {
|
||||
Mockito.when(errorHandler.getMappable(any())).thenAnswer(i -> i.getArguments()[0]);
|
||||
Mockito.when(errorHandler.handle(any(CodeNotFoundException.class)))
|
||||
.thenReturn(new ErrorInfo(404, 1001, "Code with name `foo` does not exist"));
|
||||
ErrorInfo result = underTest.generateErrorException(new CodeNotFoundException("foo"));
|
||||
.thenReturn(new ErrorInfo(404, 1001, "Code with name `foo` does not exist", null));
|
||||
ErrorInfo result = underTest.generateErrorInfo(new CodeNotFoundException("foo"));
|
||||
Assertions.assertEquals(404, result.getStatusCode());
|
||||
Assertions.assertEquals(1001, result.getErrorCode());
|
||||
Assertions.assertTrue(result.getMessage().contains("Code with name `foo` does not exist"));
|
||||
|
||||
+15
-10
@@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.apache.fineract.commands.service;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -85,7 +86,6 @@ public class SynchronousCommandProcessingServiceTest {
|
||||
|
||||
@Test
|
||||
public void testExecuteCommandSuccess() {
|
||||
|
||||
CommandWrapper commandWrapper = Mockito.mock(CommandWrapper.class);
|
||||
when(commandWrapper.isDatatableResource()).thenReturn(false);
|
||||
when(commandWrapper.isNoteResource()).thenReturn(false);
|
||||
@@ -103,19 +103,24 @@ public class SynchronousCommandProcessingServiceTest {
|
||||
String idk = "idk";
|
||||
when(idempotencyKeyResolver.resolve(commandWrapper)).thenReturn(idk);
|
||||
CommandSource commandSource = Mockito.mock(CommandSource.class);
|
||||
when(commandSourceService.findCommandSource(commandWrapper, idk)).thenReturn(null).thenReturn(commandSource);
|
||||
long commandId = 1L;
|
||||
when(commandSource.getId()).thenReturn(commandId);
|
||||
when(commandSourceService.findCommandSource(commandWrapper, idk)).thenReturn(null);
|
||||
when(commandSourceService.getCommandSource(commandId)).thenReturn(commandSource);
|
||||
|
||||
AppUser appUser = Mockito.mock(AppUser.class);
|
||||
when(commandSourceService.saveInitial(commandWrapper, jsonCommand, appUser, idk)).thenReturn(commandSource);
|
||||
when(commandSourceService.saveInitialNewTransaction(commandWrapper, jsonCommand, appUser, idk)).thenReturn(commandSource);
|
||||
when(commandSourceService.saveResultSameTransaction(commandSource)).thenReturn(commandSource);
|
||||
when(commandSource.getStatus()).thenReturn(CommandProcessingResultType.PROCESSED.getValue());
|
||||
when(context.authenticatedUser(Mockito.any(CommandWrapper.class))).thenReturn(appUser);
|
||||
|
||||
CommandProcessingResult actualCommandProcessingResult = underTest.executeCommand(commandWrapper, jsonCommand, false);
|
||||
|
||||
verify(commandSourceService).saveInitial(commandWrapper, jsonCommand, appUser, idk);
|
||||
verify(commandSource).setStatus(CommandProcessingResultType.PROCESSED.getValue());
|
||||
verify(commandSourceService).saveResult(commandSource);
|
||||
verify(commandSourceService).saveInitialNewTransaction(commandWrapper, jsonCommand, appUser, idk);
|
||||
assertEquals(CommandProcessingResultType.PROCESSED.getValue(), commandSource.getStatus());
|
||||
verify(commandSourceService).saveResultSameTransaction(commandSource);
|
||||
|
||||
Assertions.assertEquals(commandProcessingResult, actualCommandProcessingResult);
|
||||
assertEquals(commandProcessingResult, actualCommandProcessingResult);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -142,7 +147,7 @@ public class SynchronousCommandProcessingServiceTest {
|
||||
|
||||
AppUser appUser = Mockito.mock(AppUser.class);
|
||||
when(context.authenticatedUser(Mockito.any(CommandWrapper.class))).thenReturn(appUser);
|
||||
when(commandSourceService.saveInitial(commandWrapper, jsonCommand, appUser, idk)).thenReturn(commandSource);
|
||||
when(commandSourceService.saveInitialNewTransaction(commandWrapper, jsonCommand, appUser, idk)).thenReturn(commandSource);
|
||||
|
||||
CommandSource initialCommandSource = Mockito.mock(CommandSource.class);
|
||||
|
||||
@@ -152,7 +157,7 @@ public class SynchronousCommandProcessingServiceTest {
|
||||
underTest.executeCommand(commandWrapper, jsonCommand, false);
|
||||
});
|
||||
|
||||
verify(commandSourceService).saveInitial(commandWrapper, jsonCommand, appUser, idk);
|
||||
verify(commandSourceService).generateErrorException(runtimeException);
|
||||
verify(commandSourceService).saveInitialNewTransaction(commandWrapper, jsonCommand, appUser, idk);
|
||||
verify(commandSourceService).generateErrorInfo(runtimeException);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ public class InvalidInstanceTypeMethodExceptionMapperTest {
|
||||
assertEquals(Response.Status.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatus());
|
||||
assertNotNull(response.getEntity());
|
||||
final ApiGlobalErrorResponse errorResponse = (ApiGlobalErrorResponse) response.getEntity();
|
||||
assertEquals(String.valueOf(Response.Status.METHOD_NOT_ALLOWED.getReasonPhrase()), errorResponse.getHttpStatusCode());
|
||||
assertEquals(String.valueOf(Response.Status.METHOD_NOT_ALLOWED.getStatusCode()), errorResponse.getHttpStatusCode());
|
||||
assertEquals("Invalid instance type called in api request for the method POST", errorResponse.getDeveloperMessage());
|
||||
assertEquals("error.msg.invalid.instance.type", errorResponse.getUserMessageGlobalisationCode());
|
||||
assertEquals("Invalid method POST used with request to this instance type.", errorResponse.getDefaultUserMessage());
|
||||
|
||||
Reference in New Issue
Block a user