You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ar...@apache.org on 2023/06/16 18:58:43 UTC

[fineract] branch develop updated: FINERACT-1926: Fix request validation

This is an automated email from the ASF dual-hosted git repository.

arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new d5686ed45 FINERACT-1926: Fix request validation
d5686ed45 is described below

commit d5686ed45ad202e29b687486c91b34772bd82dc4
Author: Adam Saghy <ad...@gmail.com>
AuthorDate: Fri Jun 16 18:57:59 2023 +0200

    FINERACT-1926: Fix request validation
---
 .../ExternalAssetOwnersWriteServiceImpl.java       |  34 ++++++
 .../InitiateExternalAssetOwnerTransferTest.java    | 126 ++++++++++++++++++---
 2 files changed, 143 insertions(+), 17 deletions(-)

diff --git a/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java
index 1a026023b..d3db1547e 100644
--- a/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java
+++ b/fineract-investor/src/main/java/org/apache/fineract/investor/service/ExternalAssetOwnersWriteServiceImpl.java
@@ -89,7 +89,9 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
     @Transactional
     public CommandProcessingResult buybackLoanByLoanId(JsonCommand command) {
         final JsonElement json = fromApiJsonHelper.parse(command.json());
+        validateBuybackRequestBody(command.json());
         Long loanId = command.getLoanId();
+        validateLoan(loanId);
         LocalDate settlementDate = getSettlementDateFromJson(json);
         ExternalId externalId = getTransferExternalIdFromJson(json);
         validateSettlementDate(settlementDate);
@@ -99,6 +101,12 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
         return buildResponseData(externalAssetOwnerTransfer);
     }
 
+    private void validateLoan(Long loanId) {
+        if (!loanRepository.existsById(loanId)) {
+            throw new LoanNotFoundException(loanId);
+        }
+    }
+
     private void validateEffectiveTransferForSale(final ExternalAssetOwnerTransfer externalAssetOwnerTransfer) {
         List<ExternalAssetOwnerTransfer> effectiveTransfers = externalAssetOwnerTransferRepository
                 .findEffectiveTransfers(externalAssetOwnerTransfer.getLoanId(), externalAssetOwnerTransfer.getSettlementDate());
@@ -244,6 +252,32 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
         }
     }
 
+    private void validateBuybackRequestBody(String apiRequestBodyAsJson) {
+        final Set<String> requestParameters = new HashSet<>(
+                Arrays.asList(ExternalTransferRequestParameters.SETTLEMENT_DATE, ExternalTransferRequestParameters.TRANSFER_EXTERNAL_ID,
+                        ExternalTransferRequestParameters.DATEFORMAT, ExternalTransferRequestParameters.LOCALE));
+        final Type typeOfMap = new TypeToken<Map<String, Object>>() {
+
+        }.getType();
+        fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, apiRequestBodyAsJson, requestParameters);
+
+        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+        final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loantransfer");
+        final JsonElement json = fromApiJsonHelper.parse(apiRequestBodyAsJson);
+
+        String transferExternalId = fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.TRANSFER_EXTERNAL_ID, json);
+        baseDataValidator.reset().parameter(ExternalTransferRequestParameters.TRANSFER_EXTERNAL_ID).value(transferExternalId).ignoreIfNull()
+                .notExceedingLengthOf(100);
+
+        LocalDate settlementDate = fromApiJsonHelper.extractLocalDateNamed(ExternalTransferRequestParameters.SETTLEMENT_DATE, json);
+        baseDataValidator.reset().parameter(ExternalTransferRequestParameters.SETTLEMENT_DATE).value(settlementDate).notNull();
+
+        if (!dataValidationErrors.isEmpty()) {
+            throw new PlatformApiDataValidationException("validation.msg.validation.errors.exist", "Validation errors exist.",
+                    dataValidationErrors);
+        }
+    }
+
     private LocalDate getSettlementDateFromJson(JsonElement json) {
         String dateFormat = fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.DATEFORMAT, json);
         String locale = fromApiJsonHelper.extractStringNamed(ExternalTransferRequestParameters.LOCALE, json);
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
index 45d3fcb45..c662a73c0 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/investor/externalassetowner/InitiateExternalAssetOwnerTransferTest.java
@@ -146,7 +146,7 @@ public class InitiateExternalAssetOwnerTransferTest {
             Integer loanID = createLoanForClient(clientID);
             addPenaltyForLoan(loanID, "10");
 
-            PostInitiateTransferResponse saleTransferResponse = createExternalAssetOwnerTransfer(loanID, "sale", "2020-03-02");
+            PostInitiateTransferResponse saleTransferResponse = createSaleTransfer(loanID, "2020-03-02");
             validateResponse(saleTransferResponse, loanID);
             getAndValidateExternalAssetOwnerTransferByLoan(loanID,
                     ExpectedExternalTransferData.expected(PENDING, saleTransferResponse.getResourceExternalId(), "2020-03-02", "2020-03-02",
@@ -184,7 +184,7 @@ public class InitiateExternalAssetOwnerTransferTest {
                     ExpectedJournalEntryData.expected((long) TRANSFER_ACCOUNT.getAccountID(), (long) JournalEntryType.CREDIT.getValue(),
                             BigDecimal.valueOf(15767.420000), expectedDate, expectedDate));
 
-            PostInitiateTransferResponse buybackTransferResponse = createExternalAssetOwnerTransfer(loanID, "buyback", "2020-03-03");
+            PostInitiateTransferResponse buybackTransferResponse = createBuybackTransfer(loanID, "2020-03-03");
             validateResponse(buybackTransferResponse, loanID);
             getAndValidateExternalAssetOwnerTransferByLoan(loanID,
                     ExpectedExternalTransferData.expected(PENDING, saleTransferResponse.getResourceExternalId(), "2020-03-02", "2020-03-02",
@@ -278,10 +278,10 @@ public class InitiateExternalAssetOwnerTransferTest {
             Integer clientID = createClient();
             Integer loanID = createLoanForClient(clientID);
 
-            createExternalAssetOwnerTransfer(loanID, "sale", "2020-03-02");
+            createSaleTransfer(loanID, "2020-03-02");
 
             CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class,
-                    () -> createExternalAssetOwnerTransfer(loanID, "sale", "2020-03-02"));
+                    () -> createSaleTransfer(loanID, "2020-03-02"));
             assertTrue(exception.getMessage().contains("External asset owner transfer is already in PENDING state for this loan"));
         } finally {
             cleanUpAndRestoreBusinessDate();
@@ -302,7 +302,7 @@ public class InitiateExternalAssetOwnerTransferTest {
             LOAN_TRANSACTION_HELPER.makeRepayment("04 March 2020", 16000.0f, loanID);
 
             CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class,
-                    () -> createExternalAssetOwnerTransfer(loanID, "sale", "2020-03-02"));
+                    () -> createSaleTransfer(loanID, "2020-03-02"));
             assertTrue(exception.getMessage().contains("Loan is not in active status"));
         } finally {
             cleanUpAndRestoreBusinessDate();
@@ -318,9 +318,9 @@ public class InitiateExternalAssetOwnerTransferTest {
             Integer clientID = createClient();
             Integer loanID = createLoanForClient(clientID);
 
-            PostInitiateTransferResponse saleTransferResponse = createExternalAssetOwnerTransfer(loanID, "sale", "2020-03-02");
+            PostInitiateTransferResponse saleTransferResponse = createSaleTransfer(loanID, "2020-03-02");
             validateResponse(saleTransferResponse, loanID);
-            PostInitiateTransferResponse buybackTransferResponse = createExternalAssetOwnerTransfer(loanID, "buyback", "2020-03-02");
+            PostInitiateTransferResponse buybackTransferResponse = createBuybackTransfer(loanID, "2020-03-02");
             validateResponse(buybackTransferResponse, loanID);
 
             getAndValidateExternalAssetOwnerTransferByLoan(loanID,
@@ -369,8 +369,8 @@ public class InitiateExternalAssetOwnerTransferTest {
             Integer clientID = createClient();
             Integer loanID = createLoanForClient(clientID);
 
-            PostInitiateTransferResponse saleTransferResponse = createExternalAssetOwnerTransfer(loanID, "sale", "2020-03-04");
-            PostInitiateTransferResponse buybackTransferResponse = createExternalAssetOwnerTransfer(loanID, "buyback", "2020-03-04");
+            PostInitiateTransferResponse saleTransferResponse = createSaleTransfer(loanID, "2020-03-04");
+            PostInitiateTransferResponse buybackTransferResponse = createBuybackTransfer(loanID, "2020-03-04");
 
             getAndValidateExternalAssetOwnerTransferByLoan(loanID,
                     ExpectedExternalTransferData.expected(PENDING, saleTransferResponse.getResourceExternalId(), "2020-03-04", "2020-03-02",
@@ -383,11 +383,11 @@ public class InitiateExternalAssetOwnerTransferTest {
                             new BigDecimal("0.000000")));
 
             CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class,
-                    () -> createExternalAssetOwnerTransfer(loanID, "sale", "2020-03-04"));
+                    () -> createSaleTransfer(loanID, "2020-03-04"));
             assertTrue(exception.getMessage().contains("This loan cannot be sold, there is already an in progress transfer"));
 
             CallFailedRuntimeException exception2 = assertThrows(CallFailedRuntimeException.class,
-                    () -> createExternalAssetOwnerTransfer(loanID, "buyback", "2020-03-04"));
+                    () -> createBuybackTransfer(loanID, "2020-03-04"));
             assertTrue(exception2.getMessage()
                     .contains("This loan cannot be bought back, external asset owner buyback transfer is already in progress"));
         } finally {
@@ -395,19 +395,111 @@ public class InitiateExternalAssetOwnerTransferTest {
         }
     }
 
+    @Test
+    public void buybackExceptionHandling() {
+        try {
+            GlobalConfigurationHelper.manageConfigurations(REQUEST_SPEC, RESPONSE_SPEC,
+                    GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+            setInitialBusinessDate("2020-03-02");
+
+            CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class, () -> createBuybackTransfer(1, null));
+            assertTrue(exception.getMessage().contains("The parameter `settlementDate` is mandatory."));
+
+            CallFailedRuntimeException exception2 = assertThrows(CallFailedRuntimeException.class,
+                    () -> createBuybackTransfer(1, "1970-01-01"));
+            assertTrue(exception2.getMessage().contains("Settlement date cannot be in the past"));
+
+            CallFailedRuntimeException exception3 = assertThrows(CallFailedRuntimeException.class, () -> {
+                Integer clientID = createClient();
+                Integer loanID = createLoanForClient(clientID);
+                createSaleTransfer(loanID, "2020-03-03");
+                createBuybackTransfer(loanID, "2020-03-02");
+            });
+            assertTrue(exception3.getMessage().contains(
+                    "This loan cannot be bought back, settlement date is earlier than effective transfer settlement date: 2020-03-03"));
+
+            CallFailedRuntimeException exception4 = assertThrows(CallFailedRuntimeException.class, () -> {
+                Integer clientID = createClient();
+                Integer loanID = createLoanForClient(clientID);
+                createBuybackTransfer(loanID, "2020-03-03");
+            });
+            assertTrue(exception4.getMessage().contains("This loan cannot be bought back, it is not owned by an external asset owner"));
+
+            CallFailedRuntimeException exception5 = assertThrows(CallFailedRuntimeException.class,
+                    () -> createBuybackTransfer(-1, "2020-03-03"));
+            assertTrue(exception5.getMessage().contains("Loan with identifier -1 does not exist"));
+        } finally {
+            cleanUpAndRestoreBusinessDate();
+        }
+    }
+
+    @Test
+    public void saleExceptionHandling() {
+        try {
+            GlobalConfigurationHelper.manageConfigurations(REQUEST_SPEC, RESPONSE_SPEC,
+                    GlobalConfigurationHelper.ENABLE_AUTOGENERATED_EXTERNAL_ID, true);
+            setInitialBusinessDate("2020-03-02");
+            Integer clientID = createClient();
+            Integer loanID = createLoanForClient(clientID);
+
+            CallFailedRuntimeException exception = assertThrows(CallFailedRuntimeException.class, () -> createSaleTransfer(loanID, null));
+            assertTrue(exception.getMessage().contains("The parameter `settlementDate` is mandatory."));
+
+            CallFailedRuntimeException exception2 = assertThrows(CallFailedRuntimeException.class,
+                    () -> createSaleTransfer(loanID, "2020-03-02", UUID.randomUUID().toString(), null, "1.0"));
+            assertTrue(exception2.getMessage().contains("The parameter `ownerExternalId` is mandatory."));
+
+            CallFailedRuntimeException exception3 = assertThrows(CallFailedRuntimeException.class,
+                    () -> createSaleTransfer(loanID, "2020-03-02", null, UUID.randomUUID().toString(), null));
+            assertTrue(exception3.getMessage().contains("The parameter `purchasePriceRatio` is mandatory."));
+
+            CallFailedRuntimeException exception4 = assertThrows(CallFailedRuntimeException.class,
+                    () -> createSaleTransfer(loanID, "1970-01-01"));
+            assertTrue(exception4.getMessage().contains("Settlement date cannot be in the past"));
+
+            CallFailedRuntimeException exception5 = assertThrows(CallFailedRuntimeException.class, () -> {
+                createSaleTransfer(loanID, "2020-03-03");
+                createBuybackTransfer(loanID, "2020-03-04");
+                createSaleTransfer(loanID, "2020-03-05");
+            });
+            assertTrue(exception5.getMessage().contains("This loan cannot be sold, there is already an in progress transfer"));
+            CallFailedRuntimeException exception6 = assertThrows(CallFailedRuntimeException.class, () -> {
+                Integer loanID2 = createLoanForClient(clientID);
+                createSaleTransfer(loanID2, "2020-03-03");
+                updateBusinessDateAndExecuteCOBJob("2020-03-04");
+                createSaleTransfer(loanID2, "2020-03-05");
+            });
+            assertTrue(exception6.getMessage().contains("This loan cannot be sold, because it is owned by an external asset owner"));
+        } finally {
+            cleanUpAndRestoreBusinessDate();
+        }
+    }
+
     private void updateBusinessDateAndExecuteCOBJob(String date) {
         BusinessDateHelper.updateBusinessDate(REQUEST_SPEC, RESPONSE_SPEC, BUSINESS_DATE, LocalDate.parse(date));
         SCHEDULER_JOB_HELPER.executeAndAwaitJob("Loan COB");
     }
 
-    private PostInitiateTransferResponse createExternalAssetOwnerTransfer(Integer loanID, String command, String settlementDate) {
+    private PostInitiateTransferResponse createSaleTransfer(Integer loanID, String settlementDate) {
         String transferExternalId = UUID.randomUUID().toString();
-        if (command.equals("sale")) {
-            ownerExternalId = UUID.randomUUID().toString();
-        }
-        PostInitiateTransferResponse saleResponse = EXTERNAL_ASSET_OWNER_HELPER.initiateTransferByLoanId(loanID.longValue(), command,
+        ownerExternalId = UUID.randomUUID().toString();
+        return createSaleTransfer(loanID, settlementDate, transferExternalId, ownerExternalId, "1.0");
+    }
+
+    private PostInitiateTransferResponse createSaleTransfer(Integer loanID, String settlementDate, String transferExternalId,
+            String ownerExternalId, String purchasePriceRatio) {
+        PostInitiateTransferResponse saleResponse = EXTERNAL_ASSET_OWNER_HELPER.initiateTransferByLoanId(loanID.longValue(), "sale",
+                new PostInitiateTransferRequest().settlementDate(settlementDate).dateFormat("yyyy-MM-dd").locale("en")
+                        .transferExternalId(transferExternalId).ownerExternalId(ownerExternalId).purchasePriceRatio(purchasePriceRatio));
+        assertEquals(transferExternalId, saleResponse.getResourceExternalId());
+        return saleResponse;
+    }
+
+    private PostInitiateTransferResponse createBuybackTransfer(Integer loanID, String settlementDate) {
+        String transferExternalId = UUID.randomUUID().toString();
+        PostInitiateTransferResponse saleResponse = EXTERNAL_ASSET_OWNER_HELPER.initiateTransferByLoanId(loanID.longValue(), "buyback",
                 new PostInitiateTransferRequest().settlementDate(settlementDate).dateFormat("yyyy-MM-dd").locale("en")
-                        .transferExternalId(transferExternalId).ownerExternalId(ownerExternalId).purchasePriceRatio("1.0"));
+                        .transferExternalId(transferExternalId));
         assertEquals(transferExternalId, saleResponse.getResourceExternalId());
         return saleResponse;
     }