From 8c023e74a8f070294f40db7708a9d479554564e6 Mon Sep 17 00:00:00 2001 From: Arnold Galovics Date: Wed, 13 Jul 2022 08:58:40 +0200 Subject: [PATCH] Batch API extensions and a couple of OpenAPI fixes + minor datatable fixes --- .../org/apache/fineract/client/util/JSON.java | 6 + .../api/GLAccountsApiResourceSwagger.java | 2 +- .../command/CommandStrategyProvider.java | 10 +- .../ActivateClientCommandStrategy.java | 29 +- .../internal/ApplyLoanCommandStrategy.java | 27 +- .../internal/ApplySavingsCommandStrategy.java | 31 +- .../internal/ApproveLoanCommandStrategy.java | 29 +- .../ApproveLoanRescheduleCommandStrategy.java | 31 +- .../CollectChargesCommandStrategy.java | 31 +- .../internal/CreateChargeCommandStrategy.java | 31 +- .../internal/CreateClientCommandStrategy.java | 29 +- ...CreateTransactionLoanCommandStrategy.java} | 49 +-- .../internal/DisburseLoanCommandStrategy.java | 29 +- ...tableEntryByAppTableIdCommandStrategy.java | 93 ++++++ .../internal/GetLoanByIdCommandStrategy.java | 102 +++--- .../GetTransactionByIdCommandStrategy.java | 24 +- .../internal/UpdateClientCommandStrategy.java | 29 +- .../batch/service/BatchApiServiceImpl.java | 31 +- .../api/CodeValuesApiResourceSwagger.java | 2 + .../api/GlobalConfigurationApiResource.java | 3 +- .../data/ResultsetColumnHeaderData.java | 4 + .../ReadWriteNonCoreDataServiceImpl.java | 8 +- .../api/LoanProductsApiResourceSwagger.java | 22 +- .../command/CommandStrategyProviderTest.java | 20 +- ...ateTransactionLoanCommandStrategyTest.java | 119 +++++++ ...eEntryByAppTableIdCommandStrategyTest.java | 160 ++++++++++ .../GetLoanByIdCommandStrategyTest.java | 60 ---- ...GetTransactionByIdCommandStrategyTest.java | 93 ------ .../integrationtests/BatchApiTest.java | 292 +++++++++++++++++- .../integrationtests/common/BatchHelper.java | 217 ++++++++++++- 30 files changed, 1084 insertions(+), 529 deletions(-) rename fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/{RepayLoanCommandStrategy.java => CreateTransactionLoanCommandStrategy.java} (56%) create mode 100644 fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategy.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategyTest.java create mode 100644 fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategyTest.java diff --git a/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java b/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java index de7611f16..c478a7bb5 100644 --- a/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java +++ b/fineract-client/src/main/java/org/apache/fineract/client/util/JSON.java @@ -120,6 +120,12 @@ public class JSON { case NULL: in.nextNull(); return null; + case STRING: + String dateString = in.nextString(); + if (dateString != null && dateString.length() != 0) { + return LocalDate.parse(dateString); + } + return null; case BEGIN_ARRAY: in.beginArray(); int year = in.nextInt(); diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/api/GLAccountsApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/api/GLAccountsApiResourceSwagger.java index 7a1d54236..34c85fe2e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/api/GLAccountsApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/glaccount/api/GLAccountsApiResourceSwagger.java @@ -129,7 +129,7 @@ final class GLAccountsApiResourceSwagger { public Long parentId; @Schema(example = "1") - public EnumOptionData usage; + public String usage; @Schema(example = "Desc") public String description; diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java index d14afd0d7..634450304 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/CommandStrategyProvider.java @@ -92,7 +92,10 @@ public class CommandStrategyProvider { this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/charges").method("GET").build(), "collectChargesCommandStrategy"); this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\?command=repayment").method("POST").build(), - "repayLoanCommandStrategy"); + "createTransactionLoanCommandStrategy"); + this.commandStrategies.put( + CommandContext.resource("loans\\/\\d+\\/transactions\\?command=creditBalanceRefund").method("POST").build(), + "createTransactionLoanCommandStrategy"); this.commandStrategies.put(CommandContext.resource("clients\\/\\d+\\?command=activate").method("POST").build(), "activateClientCommandStrategy"); this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\?command=approve").method("POST").build(), @@ -103,6 +106,11 @@ public class CommandStrategyProvider { "approveLoanRescheduleCommandStrategy"); this.commandStrategies.put(CommandContext.resource("loans\\/\\d+\\/transactions\\/\\d+").method("GET").build(), "getTransactionByIdCommandStrategy"); + this.commandStrategies.put(CommandContext.resource("datatables\\/[a-zA-Z0-9_]*\\/\\d+").method("GET").build(), + "getDatatableEntryByAppTableIdCommandStrategy"); + this.commandStrategies.put( + CommandContext.resource("datatables\\/[a-zA-Z0-9_]*\\/\\d+(\\?(\\w+(?:\\=[\\w,]+|&)+)+)").method("GET").build(), + "getDatatableEntryByAppTableIdCommandStrategy"); } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ActivateClientCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ActivateClientCommandStrategy.java index d5601e06f..20ab7e3ea 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ActivateClientCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ActivateClientCommandStrategy.java @@ -25,8 +25,6 @@ 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.portfolio.client.api.ClientsApiResource; import org.springframework.stereotype.Component; @@ -61,27 +59,14 @@ public class ActivateClientCommandStrategy implements CommandStrategy { final List pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl()); Long clientId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?"))); - // Try-catch blocks to map exceptions to appropriate status codes - try { + // Calls 'activate' function from 'ClientsApiResource' to activate a + // client + responseBody = clientsApiResource.activate(clientId, "activate", request.getBody()); - // Calls 'activate' function from 'ClientsApiResource' to activate a - // client - responseBody = clientsApiResource.activate(clientId, "activate", request.getBody()); - - response.setStatusCode(200); - // Sets the body of the response after the successful activation of - // the client - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); - } + response.setStatusCode(200); + // Sets the body of the response after the successful activation of + // the client + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplyLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplyLoanCommandStrategy.java index 0a0d45cea..c6e9639b5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplyLoanCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplyLoanCommandStrategy.java @@ -23,8 +23,6 @@ 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource; import org.springframework.stereotype.Component; @@ -56,26 +54,13 @@ public class ApplyLoanCommandStrategy implements CommandStrategy { response.setRequestId(request.getRequestId()); response.setHeaders(request.getHeaders()); - // Try-catch blocks to map exceptions to appropriate status codes - try { + // Calls 'SubmitLoanFunction' function from 'LoansApiResource' to + // Apply Loan to an existing client + responseBody = loansApiResource.calculateLoanScheduleOrSubmitLoanApplication(null, null, request.getBody()); - // Calls 'SubmitLoanFunction' function from 'LoansApiResource' to - // Apply Loan to an existing client - responseBody = loansApiResource.calculateLoanScheduleOrSubmitLoanApplication(null, null, request.getBody()); - - response.setStatusCode(200); - // Sets the body of the response after loan is successfully applied - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); - } + response.setStatusCode(200); + // Sets the body of the response after loan is successfully applied + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplySavingsCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplySavingsCommandStrategy.java index 32359c81f..a5c2a914f 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplySavingsCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApplySavingsCommandStrategy.java @@ -23,8 +23,6 @@ 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.portfolio.savings.api.SavingsAccountsApiResource; import org.springframework.stereotype.Component; @@ -56,28 +54,15 @@ public class ApplySavingsCommandStrategy implements CommandStrategy { response.setRequestId(request.getRequestId()); response.setHeaders(request.getHeaders()); - // Try-catch blocks to map exceptions to appropriate status codes - try { + // Calls 'submitApplication' function from + // 'SavingsAccountsApiResource' to Apply Savings to an existing + // client + responseBody = savingsAccountsApiResource.submitApplication(request.getBody()); - // Calls 'submitApplication' function from - // 'SavingsAccountsApiResource' to Apply Savings to an existing - // client - responseBody = savingsAccountsApiResource.submitApplication(request.getBody()); - - response.setStatusCode(200); - // Sets the body of the response after savings is successfully - // applied - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); - } + response.setStatusCode(200); + // Sets the body of the response after savings is successfully + // applied + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanCommandStrategy.java index 26dac2941..c517e4119 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanCommandStrategy.java @@ -25,8 +25,6 @@ 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource; import org.springframework.stereotype.Component; @@ -61,27 +59,14 @@ public class ApproveLoanCommandStrategy implements CommandStrategy { final List pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl()); Long loanId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?"))); - // Try-catch blocks to map exceptions to appropriate status codes - try { + // Calls 'approve' function from 'LoansApiResource' to approve a + // loan + responseBody = loansApiResource.stateTransitions(loanId, "approve", request.getBody()); - // Calls 'approve' function from 'LoansApiResource' to approve a - // loan - responseBody = loansApiResource.stateTransitions(loanId, "approve", request.getBody()); - - response.setStatusCode(200); - // Sets the body of the response after the successful approval of a - // loan - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); - } + response.setStatusCode(200); + // Sets the body of the response after the successful approval of a + // loan + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanRescheduleCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanRescheduleCommandStrategy.java index 4220f9c13..15748e7da 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanRescheduleCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/ApproveLoanRescheduleCommandStrategy.java @@ -25,8 +25,6 @@ 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.api.RescheduleLoansApiResource; import org.springframework.stereotype.Component; @@ -47,28 +45,15 @@ public class ApproveLoanRescheduleCommandStrategy implements CommandStrategy { final List pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl()); Long scheduleId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?"))); - // Try-catch blocks to map exceptions to appropriate status codes - try { + // Calls 'approve' function from 'Loans reschedule Request' to + // approve a + // loan + responseBody = rescheduleLoansApiResource.updateLoanRescheduleRequest(scheduleId, "approve", request.getBody()); - // Calls 'approve' function from 'Loans reschedule Request' to - // approve a - // loan - responseBody = rescheduleLoansApiResource.updateLoanRescheduleRequest(scheduleId, "approve", request.getBody()); - - response.setStatusCode(200); - // Sets the body of the response after the successful approval of a - // Loans reschedule Request - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); - } + response.setStatusCode(200); + // Sets the body of the response after the successful approval of a + // Loans reschedule Request + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesCommandStrategy.java index 9d516cb7d..593ed0c69 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CollectChargesCommandStrategy.java @@ -25,8 +25,6 @@ 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.portfolio.loanaccount.api.LoanChargesApiResource; import org.springframework.stereotype.Component; @@ -63,28 +61,15 @@ public class CollectChargesCommandStrategy implements CommandStrategy { // Pluck out the loanId out of the relative path Long loanId = Long.parseLong(pathParameters.get(1)); - // Try-catch blocks to map exceptions to appropriate status codes - try { + // Calls 'retrieveAllLoanCharges' function from + // 'LoanChargesApiResource' to Collect + // Charges for a loan + responseBody = loanChargesApiResource.retrieveAllLoanCharges(loanId, uriInfo); - // Calls 'retrieveAllLoanCharges' function from - // 'LoanChargesApiResource' to Collect - // Charges for a loan - responseBody = loanChargesApiResource.retrieveAllLoanCharges(loanId, uriInfo); - - response.setStatusCode(200); - // Sets the body of the response after Charges have been - // successfully collected - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); - } + response.setStatusCode(200); + // Sets the body of the response after Charges have been + // successfully collected + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeCommandStrategy.java index 0edc2b9a8..7e7be136e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateChargeCommandStrategy.java @@ -25,8 +25,6 @@ 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.portfolio.loanaccount.api.LoanChargesApiResource; import org.springframework.stereotype.Component; @@ -61,28 +59,15 @@ public class CreateChargeCommandStrategy implements CommandStrategy { final List pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl()); Long loanId = Long.parseLong(pathParameters.get(1)); - // Try-catch blocks to map exceptions to appropriate status codes - try { + // Calls 'executeLoanCharge' function from 'LoanChargesApiResource' + // to create + // a new charge for a loan + responseBody = loanChargesApiResource.executeLoanCharge(loanId, null, request.getBody()); - // Calls 'executeLoanCharge' function from 'LoanChargesApiResource' - // to create - // a new charge for a loan - responseBody = loanChargesApiResource.executeLoanCharge(loanId, null, request.getBody()); - - response.setStatusCode(200); - // Sets the body of the response after Charge has been successfully - // created - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); - } + response.setStatusCode(200); + // Sets the body of the response after Charge has been successfully + // created + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateClientCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateClientCommandStrategy.java index 3020a2f20..fc0d71757 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateClientCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateClientCommandStrategy.java @@ -23,8 +23,6 @@ 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.portfolio.client.api.ClientsApiResource; import org.springframework.stereotype.Component; @@ -56,27 +54,14 @@ public class CreateClientCommandStrategy implements CommandStrategy { response.setRequestId(request.getRequestId()); response.setHeaders(request.getHeaders()); - // Try-catch blocks to map exceptions to appropriate status codes - try { + // Calls 'create' function from 'ClientsApiResource' to create a new + // client + responseBody = clientsApiResource.create(request.getBody()); - // Calls 'create' function from 'ClientsApiResource' to create a new - // client - responseBody = clientsApiResource.create(request.getBody()); - - response.setStatusCode(200); - // Sets the body of the response after the successful creation of - // the client - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); - } + response.setStatusCode(200); + // Sets the body of the response after the successful creation of + // the client + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/RepayLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java similarity index 56% rename from fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/RepayLoanCommandStrategy.java rename to fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java index c91269cf7..a192b2624 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/RepayLoanCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategy.java @@ -20,20 +20,22 @@ package org.apache.fineract.batch.command.internal; import com.google.common.base.Splitter; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.ws.rs.core.UriInfo; 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource; +import org.apache.http.HttpStatus; import org.springframework.stereotype.Component; /** - * Implements {@link CommandStrategy} and handles repayment for a Loan. It passes the contents of the body from the - * BatchRequest to {@link LoanTransactionsApiResource} and gets back the response. This class will also catch any errors - * raised by {@link LoanTransactionsApiResource} and map those errors to appropriate status codes in BatchResponse. + * Implements {@link CommandStrategy} and handles the creation of a transaction for a Loan. It passes the contents of + * the body from the BatchRequest to {@link LoanTransactionsApiResource} and gets back the response. This class will + * also catch any errors raised by {@link LoanTransactionsApiResource} and map those errors to appropriate status codes + * in BatchResponse. * * @author Mohit Sinha * @@ -43,7 +45,7 @@ import org.springframework.stereotype.Component; */ @Component @RequiredArgsConstructor -public class RepayLoanCommandStrategy implements CommandStrategy { +public class CreateTransactionLoanCommandStrategy implements CommandStrategy { private final LoanTransactionsApiResource loanTransactionsApiResource; @@ -59,25 +61,26 @@ public class RepayLoanCommandStrategy implements CommandStrategy { final List pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl()); Long loanId = Long.parseLong(pathParameters.get(1)); - // Try-catch blocks to map exceptions to appropriate status codes - try { + Pattern commandPattern = Pattern.compile("^?command=[a-zA-Z]+"); + Matcher commandMatcher = commandPattern.matcher(pathParameters.get(2)); - responseBody = loanTransactionsApiResource.executeLoanTransaction(loanId, "repayment", request.getBody()); - - response.setStatusCode(200); - // Sets the body of the response after Charge has been successfully - // created - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); + if (!commandMatcher.find()) { + // This would only occur if the CommandStrategyProvider is incorrectly configured. + response.setRequestId(request.getRequestId()); + response.setStatusCode(HttpStatus.SC_NOT_IMPLEMENTED); + response.setBody( + "Resource with method " + request.getMethod() + " and relativeUrl " + request.getRelativeUrl() + " doesn't exist"); + return response; } + String commandQueryParam = commandMatcher.group(0); + String command = commandQueryParam.substring(commandQueryParam.indexOf("=") + 1); + + responseBody = loanTransactionsApiResource.executeLoanTransaction(loanId, command, request.getBody()); + + response.setStatusCode(200); + // Sets the body of the response after Charge has been successfully + // created + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/DisburseLoanCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/DisburseLoanCommandStrategy.java index e4244be90..cffd07bd2 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/DisburseLoanCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/DisburseLoanCommandStrategy.java @@ -25,8 +25,6 @@ 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource; import org.springframework.stereotype.Component; @@ -61,28 +59,15 @@ public class DisburseLoanCommandStrategy implements CommandStrategy { final List pathParameters = Splitter.on('/').splitToList(request.getRelativeUrl()); Long loanId = Long.parseLong(pathParameters.get(1).substring(0, pathParameters.get(1).indexOf("?"))); - // Try-catch blocks to map exceptions to appropriate status codes - try { + // Calls 'disburse' function from 'LoansApiResource' to disburse a + // loan + responseBody = loansApiResource.stateTransitions(loanId, "disburse", request.getBody()); - // Calls 'disburse' function from 'LoansApiResource' to disburse a - // loan - responseBody = loansApiResource.stateTransitions(loanId, "disburse", request.getBody()); + response.setStatusCode(200); - response.setStatusCode(200); - - // Sets the body of the response after the successful disbursal of - // the loan - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); - } + // Sets the body of the response after the successful disbursal of + // the loan + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategy.java new file mode 100644 index 000000000..58c8e5b5c --- /dev/null +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategy.java @@ -0,0 +1,93 @@ +/** + * 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 java.util.Map; +import javax.ws.rs.core.UriInfo; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.apache.fineract.batch.command.CommandStrategy; +import org.apache.fineract.batch.command.CommandStrategyUtils; +import org.apache.fineract.batch.domain.BatchRequest; +import org.apache.fineract.batch.domain.BatchResponse; +import org.apache.fineract.infrastructure.core.api.MutableUriInfo; +import org.apache.fineract.infrastructure.dataqueries.api.DatatablesApiResource; +import org.apache.http.HttpStatus; +import org.springframework.stereotype.Component; + +/** + * Implements {@link CommandStrategy} and get datatable by appTableId. 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. + */ +@Component +@RequiredArgsConstructor +public class GetDatatableEntryByAppTableIdCommandStrategy implements CommandStrategy { + + /** + * Data table api resource {@link DatatablesApiResource}. + */ + private final DatatablesApiResource dataTablesApiResource; + + @Override + public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") UriInfo uriInfo) { + final MutableUriInfo parameterizedUriInfo = new MutableUriInfo(uriInfo); + final BatchResponse response = new BatchResponse(); + final String responseBody; + + response.setRequestId(request.getRequestId()); + response.setHeaders(request.getHeaders()); + + final String relativeUrl = request.getRelativeUrl(); + final String relativeUrlSubString = StringUtils.substringAfter(relativeUrl, "/"); + + // uriInfo will contain the query parameter value(s) that are sent in the actual batch uri. + // for example: batches?enclosingTransaction=true + // But the query parameters that are sent in the batch relative url has to be sent to + // datatablesApiResource.getDatatable + // To use the relative url query parameters + // - Parse and fetch the query parameters sent in the relative url + // (datatables/dt_123/1?genericResultSet=true) + // - Add them to the MutableUriInfo query parameters list + // - Call datatablesApiResource.getDatatable(dataTable, appTableId, null, uriInfo) + long appTableId; + if (relativeUrl.indexOf('?') > 0) { + appTableId = Long.parseLong(StringUtils.substringBetween(relativeUrlSubString, "/", "?")); + Map queryParameters = CommandStrategyUtils.getQueryParameters(relativeUrl); + + // Add the query parameters sent in the relative URL to MutableUriInfo + CommandStrategyUtils.addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters); + } else { + appTableId = Long.parseLong(StringUtils.substringAfter(relativeUrlSubString, "/")); + } + + String dataTableName = relativeUrlSubString.substring(0, relativeUrlSubString.indexOf("/")); + + // Calls 'getDatatable' function from 'DatatablesApiResource' to + // get the datatable details based on the appTableId + responseBody = dataTablesApiResource.getDatatable(dataTableName, appTableId, null, parameterizedUriInfo); + + response.setStatusCode(HttpStatus.SC_OK); + + // Sets the response after retrieving the datatable + response.setBody(responseBody); + + return response; + } +} diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategy.java index 66781ec3a..15cea55f0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategy.java @@ -26,8 +26,6 @@ import org.apache.commons.lang3.StringUtils; 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.infrastructure.core.api.MutableUriInfo; import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource; import org.apache.http.HttpStatus; @@ -60,65 +58,53 @@ public class GetLoanByIdCommandStrategy implements CommandStrategy { final String relativeUrl = request.getRelativeUrl(); Long loanId; - // Try-catch blocks to map exceptions to appropriate status codes - try { - // uriInfo will contain the query parameter value(s) that are sent in the actual batch uri. - // for example: batches?enclosingTransaction=true - // But the query parameters that are sent in the batch relative url has to be sent to - // LoansApiResource.retrieveLoan - // To use the relative url query parameters - // - Parse and fetch the query parameters sent in the relative url - // (loans/66?fields=id,principal,annualInterestRate) - // - Add them to the UriInfo query parameters list - // - Call loansApiResource.retrieveLoan(loanId, false, uriInfo) - // - Remove the relative url query parameters from UriInfo in the finally (after loan details are retrieved) - Map queryParameters = null; - if (relativeUrl.indexOf('?') > 0) { - loanId = Long.parseLong(StringUtils.substringBetween(relativeUrl, "/", "?")); - queryParameters = getQueryParameters(relativeUrl); + // uriInfo will contain the query parameter value(s) that are sent in the actual batch uri. + // for example: batches?enclosingTransaction=true + // But the query parameters that are sent in the batch relative url has to be sent to + // LoansApiResource.retrieveLoan + // To use the relative url query parameters + // - Parse and fetch the query parameters sent in the relative url + // (loans/66?fields=id,principal,annualInterestRate) + // - Add them to the UriInfo query parameters list + // - Call loansApiResource.retrieveLoan(loanId, false, uriInfo) + // - Remove the relative url query parameters from UriInfo in the finally (after loan details are retrieved) + Map queryParameters = null; + if (relativeUrl.indexOf('?') > 0) { + loanId = Long.parseLong(StringUtils.substringBetween(relativeUrl, "/", "?")); + queryParameters = getQueryParameters(relativeUrl); - // Add the query parameters sent in the relative URL to UriInfo - addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters); - } else { - loanId = Long.parseLong(StringUtils.substringAfter(relativeUrl, "/")); - } - - // Calls 'retrieveLoan' function from 'LoansApiResource' to - // get the loan details based on the loan id - final boolean staffInSelectedOfficeOnly = false; - String associations = null; - String exclude = null; - String fields = null; - if (queryParameters != null && queryParameters.size() > 0) { - if (queryParameters.containsKey("associations")) { - associations = queryParameters.get("associations"); - } - if (queryParameters.containsKey("exclude")) { - exclude = queryParameters.get("exclude"); - } - if (queryParameters.containsKey("fields")) { - fields = queryParameters.get("fields"); - } - } - - responseBody = loansApiResource.retrieveLoan(loanId, staffInSelectedOfficeOnly, associations, exclude, fields, - parameterizedUriInfo); - - response.setStatusCode(HttpStatus.SC_OK); - - // Sets the response after retrieving the loan - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); + // Add the query parameters sent in the relative URL to UriInfo + addQueryParametersToUriInfo(parameterizedUriInfo, queryParameters); + } else { + loanId = Long.parseLong(StringUtils.substringAfter(relativeUrl, "/")); } + // Calls 'retrieveLoan' function from 'LoansApiResource' to + // get the loan details based on the loan id + final boolean staffInSelectedOfficeOnly = false; + String associations = null; + String exclude = null; + String fields = null; + if (queryParameters != null && queryParameters.size() > 0) { + if (queryParameters.containsKey("associations")) { + associations = queryParameters.get("associations"); + } + if (queryParameters.containsKey("exclude")) { + exclude = queryParameters.get("exclude"); + } + if (queryParameters.containsKey("fields")) { + fields = queryParameters.get("fields"); + } + } + + responseBody = loansApiResource.retrieveLoan(loanId, staffInSelectedOfficeOnly, associations, exclude, fields, + parameterizedUriInfo); + + response.setStatusCode(HttpStatus.SC_OK); + + // Sets the response after retrieving the loan + response.setBody(responseBody); + return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategy.java index 3f7f22a89..bec9b431c 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategy.java @@ -29,8 +29,6 @@ import org.apache.fineract.batch.command.CommandStrategy; import org.apache.fineract.batch.command.CommandStrategyUtils; import org.apache.fineract.batch.domain.BatchRequest; import org.apache.fineract.batch.domain.BatchResponse; -import org.apache.fineract.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.infrastructure.core.api.MutableUriInfo; import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource; import org.apache.http.HttpStatus; @@ -88,25 +86,13 @@ public class GetTransactionByIdCommandStrategy implements CommandStrategy { } } - // Try-catch blocks to map exceptions to appropriate status codes - try { - // Calls 'retrieveTransaction' function from 'loanTransactionsApiResource' - responseBody = loanTransactionsApiResource.retrieveTransaction(loanId, transactionId, fields, uriInfo); + // Calls 'retrieveTransaction' function from 'loanTransactionsApiResource' + responseBody = loanTransactionsApiResource.retrieveTransaction(loanId, transactionId, fields, uriInfo); - response.setStatusCode(HttpStatus.SC_OK); + response.setStatusCode(HttpStatus.SC_OK); - // Sets the body of the response after retrieving the transaction - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); - } + // Sets the body of the response after retrieving the transaction + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateClientCommandStrategy.java b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateClientCommandStrategy.java index 2977ed58b..9d0e3aae7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateClientCommandStrategy.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/command/internal/UpdateClientCommandStrategy.java @@ -23,8 +23,6 @@ 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.batch.exception.ErrorHandler; -import org.apache.fineract.batch.exception.ErrorInfo; import org.apache.fineract.portfolio.client.api.ClientsApiResource; import org.springframework.stereotype.Component; @@ -60,27 +58,14 @@ public class UpdateClientCommandStrategy implements CommandStrategy { final String relativeUrl = request.getRelativeUrl(); final Long clientId = Long.parseLong(relativeUrl.substring(relativeUrl.indexOf('/') + 1)); - // Try-catch blocks to map exceptions to appropriate status codes - try { + // Calls 'update' function from 'ClientsApiResource' to update a + // client + responseBody = clientsApiResource.update(clientId, request.getBody()); - // Calls 'update' function from 'ClientsApiResource' to update a - // client - responseBody = clientsApiResource.update(clientId, request.getBody()); - - response.setStatusCode(200); - // Sets the body of the response after the successful update of - // client information - response.setBody(responseBody); - - } catch (RuntimeException e) { - - // Gets an object of type ErrorInfo, containing information about - // raised exception - ErrorInfo ex = ErrorHandler.handler(e); - - response.setStatusCode(ex.getStatusCode()); - response.setBody(ex.getMessage()); - } + response.setStatusCode(200); + // Sets the body of the response after the successful update of + // client information + response.setBody(responseBody); return response; } diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/service/BatchApiServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/batch/service/BatchApiServiceImpl.java index 44ef98191..20502e3c1 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/batch/service/BatchApiServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/batch/service/BatchApiServiceImpl.java @@ -27,6 +27,7 @@ import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import javax.ws.rs.core.UriInfo; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.apache.fineract.batch.command.CommandContext; import org.apache.fineract.batch.command.CommandStrategy; import org.apache.fineract.batch.command.CommandStrategyProvider; @@ -55,6 +56,7 @@ import org.springframework.transaction.support.TransactionTemplate; */ @Service @RequiredArgsConstructor +@Slf4j public class BatchApiServiceImpl implements BatchApiService { private final CommandStrategyProvider strategyProvider; @@ -86,24 +88,35 @@ public class BatchApiServiceImpl implements BatchApiService { final BatchRequest rootRequest = rootNode.getRequest(); final CommandStrategy commandStrategy = this.strategyProvider .getCommandStrategy(CommandContext.resource(rootRequest.getRelativeUrl()).method(rootRequest.getMethod()).build()); - final BatchResponse rootResponse = commandStrategy.execute(rootRequest, uriInfo); + final BatchResponse rootResponse = safelyExecuteStrategy(commandStrategy, rootRequest, uriInfo); responseList.add(rootResponse); responseList.addAll(this.processChildRequests(rootNode, rootResponse, uriInfo)); } - Collections.sort(responseList, new Comparator() { - - @Override - public int compare(BatchResponse source, BatchResponse testee) { - return source.getRequestId().compareTo(testee.getRequestId()); - } - }); + Collections.sort(responseList, Comparator.comparing(BatchResponse::getRequestId)); return responseList; } + private BatchResponse safelyExecuteStrategy(CommandStrategy commandStrategy, BatchRequest request, UriInfo uriInfo) { + try { + return commandStrategy.execute(request, uriInfo); + } catch (RuntimeException e) { + log.warn("Exception while executing batch strategy {}", commandStrategy.getClass().getSimpleName(), e); + + ErrorInfo ex = ErrorHandler.handler(e); + + final BatchResponse response = new BatchResponse(); + response.setRequestId(request.getRequestId()); + response.setHeaders(request.getHeaders()); + response.setStatusCode(ex.getStatusCode()); + response.setBody(ex.getMessage()); + return response; + } + } + private List processChildRequests(final BatchRequestNode rootRequest, BatchResponse rootResponse, UriInfo uriInfo) { final List childResponses = new ArrayList<>(); @@ -121,7 +134,7 @@ public class BatchApiServiceImpl implements BatchApiService { final CommandStrategy commandStrategy = this.strategyProvider.getCommandStrategy( CommandContext.resource(childRequest.getRelativeUrl()).method(childRequest.getMethod()).build()); - childResponse = commandStrategy.execute(childRequest, uriInfo); + childResponse = safelyExecuteStrategy(commandStrategy, childRequest, uriInfo); } else { // Something went wrong with the parent request, create diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/api/CodeValuesApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/api/CodeValuesApiResourceSwagger.java index ee8e48af1..26df57634 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/api/CodeValuesApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/api/CodeValuesApiResourceSwagger.java @@ -55,6 +55,8 @@ final class CodeValuesApiResourceSwagger { @Schema(example = "Passport") public String name; + @Schema(example = "true") + public Boolean isActive; @Schema(example = "Passport information") public String description; @Schema(example = "0") diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiResource.java index 7d6fc0778..f26438d48 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiResource.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationApiResource.java @@ -20,7 +20,6 @@ package org.apache.fineract.infrastructure.configuration.api; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.parameters.RequestBody; @@ -103,7 +102,7 @@ public class GlobalConfigurationApiResource { + "Returns the list global enable/disable survey configurations.\n" + "\n" + "Example Requests:\n" + "\n" + "configurations/survey") @ApiResponses({ - @ApiResponse(responseCode = "200", description = "List of example \\n response \\nsurveys response \\ngiven below", content = @Content(array = @ArraySchema(schema = @Schema(implementation = GlobalConfigurationApiResourceSwagger.GetGlobalConfigurationsResponse.class)))) }) + @ApiResponse(responseCode = "200", description = "List of example \\n response \\nsurveys response \\ngiven below", content = @Content(schema = @Schema(implementation = GlobalConfigurationApiResourceSwagger.GetGlobalConfigurationsResponse.class))) }) public String retrieveConfiguration(@Context final UriInfo uriInfo, @DefaultValue("false") @QueryParam("survey") @Parameter(description = "survey") final boolean survey) { diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java index e318cd02d..7ae84bf38 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/data/ResultsetColumnHeaderData.java @@ -326,4 +326,8 @@ public final class ResultsetColumnHeaderData implements Serializable { public String getColumnCode() { return this.columnCode; } + + public List getColumnValues() { + return this.columnValues; + } } diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java index cbae01e23..ca887a6b0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java @@ -1652,7 +1652,13 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ pValueWrite = "null"; } else { if ("bit".equalsIgnoreCase(pColumnHeader.getColumnType())) { - pValueWrite = BooleanUtils.toString(BooleanUtils.toBooleanObject(pValue), "1", "0", "null"); + if (databaseTypeResolver.isMySQL()) { + pValueWrite = BooleanUtils.toString(BooleanUtils.toBooleanObject(pValue), "1", "0", "null"); + } else if (databaseTypeResolver.isPostgreSQL()) { + pValueWrite = BooleanUtils.toString(BooleanUtils.toBooleanObject(pValue), "B'1'", "B'0'", "null"); + } else { + throw new IllegalStateException("Current database is not supported"); + } } else { pValueWrite = singleQuote + this.genericDataService.replace(pValue, singleQuote, singleQuote + singleQuote) + singleQuote; diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java index e786f9bad..3aa826ef5 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java @@ -25,7 +25,7 @@ import java.util.Collection; import java.util.List; import java.util.Set; import org.apache.fineract.accounting.producttoaccountmapping.data.ChargeToGLAccountMapper; -import org.apache.fineract.portfolio.rate.data.RateData; +import org.apache.fineract.infrastructure.core.data.EnumOptionData; /** * Created by Chirag Gupta on 12/27/17. @@ -69,6 +69,26 @@ final class LoanProductsApiResourceSwagger { public Integer id; } + static final class RateData { + + private RateData() {} + + @Schema(example = "1") + public Long id; + + @Schema(example = "some name") + public String name; + + @Schema(example = "20") + public BigDecimal percentage; + + @Schema(description = "Apply specific product using its id, code, and value.") + public EnumOptionData productApply; + + @Schema(example = "false") + public boolean active; + } + @Schema(example = "LP Accrual Accounting") public String name; @Schema(example = "LPAA") diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java index 7ce584cd1..22e8811d8 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/CommandStrategyProviderTest.java @@ -32,10 +32,11 @@ import org.apache.fineract.batch.command.internal.ApproveLoanRescheduleCommandSt import org.apache.fineract.batch.command.internal.CollectChargesCommandStrategy; import org.apache.fineract.batch.command.internal.CreateChargeCommandStrategy; import org.apache.fineract.batch.command.internal.CreateClientCommandStrategy; +import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy; import org.apache.fineract.batch.command.internal.DisburseLoanCommandStrategy; +import org.apache.fineract.batch.command.internal.GetDatatableEntryByAppTableIdCommandStrategy; import org.apache.fineract.batch.command.internal.GetLoanByIdCommandStrategy; import org.apache.fineract.batch.command.internal.GetTransactionByIdCommandStrategy; -import org.apache.fineract.batch.command.internal.RepayLoanCommandStrategy; import org.apache.fineract.batch.command.internal.UnknownCommandStrategy; import org.apache.fineract.batch.command.internal.UpdateClientCommandStrategy; import org.junit.jupiter.params.ParameterizedTest; @@ -66,8 +67,10 @@ public class CommandStrategyProviderTest { Arguments.of("loans/123/charges", HttpMethod.POST, "createChargeCommandStrategy", mock(CreateChargeCommandStrategy.class)), Arguments.of("loans/123/charges", HttpMethod.GET, "collectChargesCommandStrategy", mock(CollectChargesCommandStrategy.class)), - Arguments.of("loans/123/transactions?command=repayment", HttpMethod.POST, "repayLoanCommandStrategy", - mock(RepayLoanCommandStrategy.class)), + Arguments.of("loans/123/transactions?command=repayment", HttpMethod.POST, "createTransactionLoanCommandStrategy", + mock(CreateTransactionLoanCommandStrategy.class)), + Arguments.of("loans/123/transactions?command=creditBalanceRefund", HttpMethod.POST, "createTransactionLoanCommandStrategy", + mock(CreateTransactionLoanCommandStrategy.class)), Arguments.of("clients/456?command=activate", HttpMethod.POST, "activateClientCommandStrategy", mock(ActivateClientCommandStrategy.class)), Arguments.of("loans/123?command=approve", HttpMethod.POST, "approveLoanCommandStrategy", @@ -77,7 +80,11 @@ public class CommandStrategyProviderTest { Arguments.of("rescheduleloans/123?command=approve", HttpMethod.POST, "approveLoanRescheduleCommandStrategy", mock(ApproveLoanRescheduleCommandStrategy.class)), Arguments.of("loans/123/transactions/123", HttpMethod.GET, "getTransactionByIdCommandStrategy", - mock(GetTransactionByIdCommandStrategy.class))); + mock(GetTransactionByIdCommandStrategy.class)), + Arguments.of("datatables/test_dt_table/123", HttpMethod.GET, "getDatatableEntryByAppTableIdCommandStrategy", + mock(GetDatatableEntryByAppTableIdCommandStrategy.class)), + Arguments.of("datatables/test_dt_table/123?genericResultSet=true", HttpMethod.GET, + "getDatatableEntryByAppTableIdCommandStrategy", mock(GetDatatableEntryByAppTableIdCommandStrategy.class))); } /** @@ -99,7 +106,6 @@ public class CommandStrategyProviderTest { final ApplicationContext applicationContext = mock(ApplicationContext.class); final CommandStrategyProvider commandStrategyProvider = new CommandStrategyProvider(applicationContext); when(applicationContext.getBean(beanName)).thenReturn(commandStrategy); - final CommandStrategy result = commandStrategyProvider.getCommandStrategy(CommandContext.resource(url).method(httpMethod).build()); assertEquals(commandStrategy, result); } @@ -111,7 +117,9 @@ public class CommandStrategyProviderTest { */ private static Stream provideCommandStrategyResourceDetailsForErrors() { return Stream.of(Arguments.of("loans/123?command=reject", HttpMethod.POST), - Arguments.of("loans/glimAccount/746?command=approve", HttpMethod.POST), Arguments.of("loans/123", HttpMethod.PUT)); + Arguments.of("loans/glimAccount/746?command=approve", HttpMethod.POST), Arguments.of("loans/123", HttpMethod.PUT), + Arguments.of("datatables/test_dt_table", HttpMethod.GET), Arguments.of("datatables", HttpMethod.GET)); + } /** diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategyTest.java new file mode 100644 index 000000000..c081a2e0c --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/CreateTransactionLoanCommandStrategyTest.java @@ -0,0 +1,119 @@ +/** + * 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.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.UriInfo; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.fineract.batch.domain.BatchRequest; +import org.apache.fineract.batch.domain.BatchResponse; +import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource; +import org.apache.http.HttpStatus; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Test class fpr {@link CreateTransactionLoanCommandStrategy}. + */ +public class CreateTransactionLoanCommandStrategyTest { + + /** + * Test {@link CreateTransactionLoanCommandStrategy#execute} happy path scenario. + */ + @Test + public void testExecuteSuccessScenario() { + final TestContext testContext = new TestContext(); + final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4)); + final String command = "myCommand"; + final BatchRequest batchRequest = getBatchRequest(loanId, command); + final String responseBody = "myResponseBody"; + + when(testContext.loanTransactionsApiResource.executeLoanTransaction(loanId, command, batchRequest.getBody())) + .thenReturn(responseBody); + + BatchResponse batchResponse = testContext.subjectToTest.execute(batchRequest, testContext.uriInfo); + + assertEquals(HttpStatus.SC_OK, batchResponse.getStatusCode()); + assertSame(responseBody, batchResponse.getBody()); + assertEquals(batchRequest.getRequestId(), batchResponse.getRequestId()); + assertEquals(batchRequest.getHeaders(), batchResponse.getHeaders()); + + verify(testContext.loanTransactionsApiResource).executeLoanTransaction(loanId, command, batchRequest.getBody()); + } + + /** + * Creates and returns a request with the given loan id and command value. + * + * @param loanId + * the loan id + * @param command + * the transaction id + * @return BatchRequest + */ + private BatchRequest getBatchRequest(final Long loanId, final String command) { + + final BatchRequest br = new BatchRequest(); + String relativeUrl = "loans/" + loanId + "/transactions?command=" + command; + + br.setRequestId(Long.valueOf(RandomStringUtils.randomNumeric(5))); + br.setRelativeUrl(relativeUrl); + br.setMethod(HttpMethod.POST); + br.setReference(Long.valueOf(RandomStringUtils.randomNumeric(5))); + br.setBody("{}"); + + return br; + } + + /** + * Private test context class used since testng runs in parallel to avoid state between tests + */ + private static class TestContext { + + /** + * Mock URI info. + */ + @Mock + private UriInfo uriInfo; + + /** + * Mock loan transactions API resource. + */ + @Mock + private LoanTransactionsApiResource loanTransactionsApiResource; + + /** + * The {@link CreateTransactionLoanCommandStrategy} under test. + */ + private final CreateTransactionLoanCommandStrategy subjectToTest; + + /** + * Constructor. + */ + TestContext() { + MockitoAnnotations.openMocks(this); + subjectToTest = new CreateTransactionLoanCommandStrategy(loanTransactionsApiResource); + } + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategyTest.java new file mode 100644 index 000000000..7699dec0c --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetDatatableEntryByAppTableIdCommandStrategyTest.java @@ -0,0 +1,160 @@ +/** + * 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 com.google.common.truth.Truth.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; + +import java.util.List; +import java.util.stream.Stream; +import javax.ws.rs.HttpMethod; +import javax.ws.rs.core.UriInfo; +import org.apache.commons.lang3.RandomUtils; +import org.apache.fineract.batch.domain.BatchRequest; +import org.apache.fineract.batch.domain.BatchResponse; +import org.apache.fineract.infrastructure.core.api.MutableUriInfo; +import org.apache.fineract.infrastructure.dataqueries.api.DatatablesApiResource; +import org.apache.http.HttpStatus; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests {GetDatatableEntryByAppTableIdCommandStrategy}. + */ +public class GetDatatableEntryByAppTableIdCommandStrategyTest { + + /** + * Query parameter provider. + * + * @return the test data stream + */ + private static Stream provideQueryParameters() { + return Stream.of(Arguments.of(null, 0), Arguments.of("genericResultSet=true", 1)); + } + + /** + * Test {@link GetDatatableEntryByAppTableIdCommandStrategy#execute} happy path scenario. + * + * @param queryParameter + * the query parameter + * @param numberOfQueryParams + * number of query params are provided + */ + @ParameterizedTest + @MethodSource("provideQueryParameters") + public void testExecuteSuccessScenario(final String queryParameter, final int numberOfQueryParams) { + final TestContext testContext = new TestContext(); + + final Long loanId = RandomUtils.nextLong(); + final String datatableName = "dt_loan_xyz"; + final BatchRequest request = getBatchRequest(loanId, queryParameter, datatableName); + final String responseBody = "{\\\"columnHeaders\\\":[{}],\\\"data\\\":\\\"{}\\\"}"; + + given(testContext.dataTableApiResource.getDatatable(eq(datatableName), eq(loanId), eq(null), any(UriInfo.class))) + .willReturn(responseBody); + + final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo); + + assertEquals(HttpStatus.SC_OK, response.getStatusCode()); + assertEquals(request.getRequestId(), response.getRequestId()); + assertEquals(request.getHeaders(), response.getHeaders()); + assertEquals(responseBody, response.getBody()); + + verify(testContext.dataTableApiResource).getDatatable(eq(datatableName), eq(loanId), eq(null), testContext.uriInfoCaptor.capture()); + MutableUriInfo mutableUriInfo = testContext.uriInfoCaptor.getValue(); + assertThat(mutableUriInfo.getAdditionalQueryParameters()).hasSize(numberOfQueryParams); + if (numberOfQueryParams > 0) { + List param = mutableUriInfo.getAdditionalQueryParameters().get("genericResultSet"); + assertEquals(param.get(0), "true"); + } + } + + /** + * Creates and returns a request with the given loan id. + * + * @param loanId + * the loan id + * @param queryParameter + * the query parameter + * @param datatableName + * the datatable name + * @return the {@link BatchRequest} + */ + private BatchRequest getBatchRequest(final Long loanId, final String queryParameter, final String datatableName) { + final BatchRequest br = new BatchRequest(); + String relativeUrl = String.format("datatables/%s/%s", datatableName, loanId); + if (queryParameter != null) { + relativeUrl = relativeUrl + "?" + queryParameter; + } + + br.setRequestId(RandomUtils.nextLong()); + br.setRelativeUrl(relativeUrl); + br.setMethod(HttpMethod.GET); + br.setReference(RandomUtils.nextLong()); + br.setBody("{}"); + + return br; + } + + /** + * Private test context class used since testng runs in parallel to avoid state between tests + */ + private static final class TestContext { + + /** + * The subject under test. + */ + private final GetDatatableEntryByAppTableIdCommandStrategy subjectToTest; + + /** + * Mock of {@link UriInfo} + */ + @Mock + private UriInfo uriInfo; + + /** + * Captor of {@link MutableUriInfo}. + */ + @Captor + private ArgumentCaptor uriInfoCaptor; + + /** + * {@link DatatablesApiResource} mock. + */ + @Mock + private DatatablesApiResource dataTableApiResource; + + /** + * Constructor. + */ + private TestContext() { + MockitoAnnotations.openMocks(this); + subjectToTest = new GetDatatableEntryByAppTableIdCommandStrategy(dataTableApiResource); + } + } +} diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategyTest.java index 40a381488..d8327f89c 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategyTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetLoanByIdCommandStrategyTest.java @@ -34,9 +34,7 @@ import org.apache.fineract.batch.domain.BatchRequest; import org.apache.fineract.batch.domain.BatchResponse; import org.apache.fineract.infrastructure.core.api.MutableUriInfo; import org.apache.fineract.portfolio.loanaccount.api.LoansApiResource; -import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException; import org.apache.http.HttpStatus; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -87,64 +85,6 @@ public class GetLoanByIdCommandStrategyTest { assertThat(mutableUriInfo.getAdditionalQueryParameters()).hasSize(noOfQueryParams); } - /** - * Test {@link GetLoanByIdCommandStrategy#execute} for internal server error. - */ - @Test - public void testExecuteForInternalServerError() { - // given - final TestContext testContext = new TestContext(); - final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4)); - final BatchRequest request = getBatchRequest(loanId, null, null, null); - - given(testContext.loansApiResource.retrieveLoan(eq(loanId), eq(false), eq(null), eq(null), eq(null), any(UriInfo.class))) - .willThrow(new RuntimeException("Some error")); - - // when - final BatchResponse response = testContext.underTest.execute(request, testContext.uriInfo); - - // then - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_INTERNAL_SERVER_ERROR); - assertThat(response.getBody()).isEqualTo("{\"Exception\": java.lang.RuntimeException: Some error}"); - assertThat(response.getRequestId()).isEqualTo(request.getRequestId()); - assertThat(response.getHeaders()).isEqualTo(request.getHeaders()); - } - - /** - * Test {@link GetLoanByIdCommandStrategy#execute} for loan not found exception. - */ - @Test - public void testExecuteForLoanNotFoundException() { - // given - final TestContext testContext = new TestContext(); - final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4)); - final BatchRequest request = getBatchRequest(loanId, null, null, null); - - given(testContext.loansApiResource.retrieveLoan(eq(loanId), eq(false), eq(null), eq(null), eq(null), any(UriInfo.class))) - .willThrow(new LoanNotFoundException(loanId)); - - // when - final BatchResponse response = testContext.underTest.execute(request, testContext.uriInfo); - - // then - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND); - assertThat(response.getBody()).isEqualTo(buildLoanNotFoundError(loanId)); - assertThat(response.getRequestId()).isEqualTo(request.getRequestId()); - assertThat(response.getHeaders()).isEqualTo(request.getHeaders()); - } - - private String buildLoanNotFoundError(final Long loanId) { - return String.format( - "{\n \"developerMessage\": \"The requested resource is not available.\",\n" - + " \"httpStatusCode\": \"404\",\n \"defaultUserMessage\": \"The requested resource is not available.\",\n" - + " \"userMessageGlobalisationCode\": \"error.msg.resource.not.found\",\n \"errors\": [\n {\n" - + " \"developerMessage\": \"Loan with identifier %s does not exist\",\n" - + " \"defaultUserMessage\": \"Loan with identifier %s does not exist\",\n" - + " \"userMessageGlobalisationCode\": \"error.msg.loan.id.invalid\",\n \"parameterName\": \"id\",\n" - + " \"args\": [\n {\n \"value\": %s\n }\n ]\n }\n ]\n" + "}", - loanId, loanId, loanId); - } - /** * Creates and returns a request with the given loan id. * diff --git a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategyTest.java b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategyTest.java index fe92be9dc..60ef582f8 100644 --- a/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategyTest.java +++ b/fineract-provider/src/test/java/org/apache/fineract/batch/command/internal/GetTransactionByIdCommandStrategyTest.java @@ -19,11 +19,9 @@ package org.apache.fineract.batch.command.internal; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.when; import java.util.stream.Stream; import javax.ws.rs.HttpMethod; @@ -32,10 +30,7 @@ import org.apache.commons.lang3.RandomStringUtils; import org.apache.fineract.batch.domain.BatchRequest; import org.apache.fineract.batch.domain.BatchResponse; import org.apache.fineract.portfolio.loanaccount.api.LoanTransactionsApiResource; -import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException; -import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException; import org.apache.http.HttpStatus; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -84,75 +79,6 @@ public class GetTransactionByIdCommandStrategyTest { assertThat(response.getBody()).isEqualTo(responseBody); } - /** - * Test {@link GetTransactionByIdCommandStrategy#execute} for internal server error. - */ - @Test - public void testExecuteForInternalServerError() { - // given - final TestContext testContext = new TestContext(); - final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4)); - final Long transactionId = Long.valueOf(RandomStringUtils.randomNumeric(4)); - final BatchRequest request = getBatchRequest(loanId, transactionId, null); - - given(testContext.loanTransactionsApiResource.retrieveTransaction(loanId, transactionId, null, testContext.uriInfo)) - .willThrow(new RuntimeException("Some error")); - - // when - final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo); - - // then - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_INTERNAL_SERVER_ERROR); - assertThat(response.getRequestId()).isEqualTo(request.getRequestId()); - assertThat(response.getHeaders()).isEqualTo(request.getHeaders()); - assertThat(response.getBody()).isEqualTo("{\"Exception\": java.lang.RuntimeException: Some error}"); - } - - /** - * Test {@link GetTransactionByIdCommandStrategy#execute} for loan not found exception. - */ - @Test - public void testExecuteForLoanNotFoundException() { - // given - final TestContext testContext = new TestContext(); - final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4)); - final Long transactionId = Long.valueOf(RandomStringUtils.randomNumeric(4)); - final BatchRequest request = getBatchRequest(loanId, transactionId, null); - - given(testContext.loanTransactionsApiResource.retrieveTransaction(loanId, transactionId, null, testContext.uriInfo)) - .willThrow(new LoanNotFoundException(loanId)); - - // when - final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo); - - // then - assertThat(response.getStatusCode()).isEqualTo(HttpStatus.SC_NOT_FOUND); - assertThat(response.getRequestId()).isEqualTo(request.getRequestId()); - assertThat(response.getHeaders()).isEqualTo(request.getHeaders()); - assertThat(response.getBody()).isEqualTo(build404NotFoundError("Loan", loanId)); - } - - /** - * Test {@link GetTransactionByIdCommandStrategy#execute} for transaction not found exception. - */ - @Test - public void testExecuteForTransactionNotFoundException() { - final TestContext testContext = new TestContext(); - final Long loanId = Long.valueOf(RandomStringUtils.randomNumeric(4)); - final Long transactionId = Long.valueOf(RandomStringUtils.randomNumeric(4)); - final BatchRequest request = getBatchRequest(loanId, transactionId, null); - - when(testContext.loanTransactionsApiResource.retrieveTransaction(loanId, transactionId, null, testContext.uriInfo)) - .thenThrow(new LoanTransactionNotFoundException(transactionId)); - - final BatchResponse response = testContext.subjectToTest.execute(request, testContext.uriInfo); - - assertEquals(HttpStatus.SC_NOT_FOUND, response.getStatusCode()); - assertEquals(build404NotFoundError("Transaction", transactionId), response.getBody()); - assertEquals(request.getRequestId(), response.getRequestId()); - assertEquals(request.getHeaders(), response.getHeaders()); - } - /** * Creates and returns a request with the given loan id and transaction id. * @@ -179,25 +105,6 @@ public class GetTransactionByIdCommandStrategyTest { return br; } - /** - * Builds the 404 not found error. - * - * @param field - * the field name - * @param id - * the id - */ - private String build404NotFoundError(final String field, final Long id) { - return String.format("{\n" + " \"developerMessage\": \"The requested resource is not available.\",\n" - + " \"httpStatusCode\": \"404\",\n" + " \"defaultUserMessage\": \"The requested resource is not available.\",\n" - + " \"userMessageGlobalisationCode\": \"error.msg.resource.not.found\",\n" + " \"errors\": [\n" + " {\n" - + " \"developerMessage\": \"%s with identifier %s does not exist\",\n" - + " \"defaultUserMessage\": \"%s with identifier %s does not exist\",\n" - + " \"userMessageGlobalisationCode\": \"error.msg.loan.id.invalid\",\n" + " \"parameterName\": \"id\",\n" - + " \"args\": [\n" + " {\n" + " \"value\": %s\n" + " }\n" + " ]\n" + " }\n" + " ]\n" - + "}", field, id, field, id, id); - } - /** * Private test context class used since testng runs in parallel to avoid state between tests */ diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java index 59d0ce052..49bded81a 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BatchApiTest.java @@ -18,6 +18,8 @@ */ package org.apache.fineract.integrationtests; +import static org.junit.jupiter.api.Assertions.assertEquals; + import com.google.gson.JsonElement; import com.google.gson.JsonObject; import io.restassured.builder.RequestSpecBuilder; @@ -28,6 +30,7 @@ import io.restassured.specification.ResponseSpecification; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy; import org.apache.fineract.batch.domain.BatchRequest; import org.apache.fineract.batch.domain.BatchResponse; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; @@ -38,6 +41,7 @@ import org.apache.fineract.integrationtests.common.Utils; import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; import org.apache.fineract.integrationtests.common.savings.SavingsProductHelper; +import org.apache.fineract.integrationtests.common.system.DatatableHelper; import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; import org.apache.http.HttpStatus; import org.junit.jupiter.api.Assertions; @@ -55,9 +59,26 @@ import org.junit.jupiter.api.Test; */ public class BatchApiTest { + /** + * The response specification + */ private ResponseSpecification responseSpec; + + /** + * The request specification + */ private RequestSpecification requestSpec; + /** + * The datatable helper + */ + private DatatableHelper datatableHelper; + + /** + * Loan app datatable + */ + private static final String LOAN_APP_TABLE_NAME = "m_loan"; + /** * Sets up the essential settings for the TEST like contentType, expectedStatusCode. It uses the '@BeforeEach' * annotation provided by jUnit. @@ -68,6 +89,7 @@ public class BatchApiTest { this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + this.datatableHelper = new DatatableHelper(this.requestSpec, this.responseSpec); } /** @@ -240,7 +262,8 @@ public class BatchApiTest { // Get the clientId parameter from createClient Response final JsonElement clientId = new FromJsonHelper().parse(response.get(0).getBody()).getAsJsonObject().get("clientId"); - Assertions.assertEquals(200L, (long) response.get(1).getStatusCode(), "Verify Status Code 200" + clientId.getAsString()); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(1).getStatusCode(), + "Verify Status Code 200" + clientId.getAsString()); } /** @@ -281,7 +304,7 @@ public class BatchApiTest { final List response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec, jsonifiedRequest); - Assertions.assertEquals(200L, (long) response.get(1).getStatusCode(), "Verify Status Code 200"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(1).getStatusCode(), "Verify Status Code 200"); } /** @@ -341,7 +364,7 @@ public class BatchApiTest { final List response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec, jsonifiedRequest); - Assertions.assertEquals(200L, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for Create Loan Charge"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for Create Loan Charge"); } /** @@ -349,7 +372,7 @@ public class BatchApiTest { * successful responses. It first creates a new loan and then makes two repayments for it and then verifies that * 200(OK) is returned for the repayment requests. * - * @see org.apache.fineract.batch.command.internal.RepayLoanCommandStrategy + * @see CreateTransactionLoanCommandStrategy */ @Test public void shouldReturnOkStatusForBatchRepayment() { @@ -392,10 +415,10 @@ public class BatchApiTest { final BatchRequest br5 = BatchHelper.disburseLoanRequest(4734L, 4733L); // Create a loanRepay Request - final BatchRequest br6 = BatchHelper.repayLoanRequest(4735L, 4734L); + final BatchRequest br6 = BatchHelper.repayLoanRequest(4735L, 4734L, "500"); // Create a loanRepay Request - final BatchRequest br7 = BatchHelper.repayLoanRequest(4736L, 4734L); + final BatchRequest br7 = BatchHelper.repayLoanRequest(4736L, 4734L, "500"); final List batchRequests = new ArrayList<>(); @@ -412,8 +435,85 @@ public class BatchApiTest { final List response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec, jsonifiedRequest); - Assertions.assertEquals(200L, (long) response.get(5).getStatusCode(), "Verify Status Code 200 for Repayment"); - Assertions.assertEquals(200L, (long) response.get(6).getStatusCode(), "Verify Status Code 200 for Repayment"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(5).getStatusCode(), "Verify Status Code 200 for Repayment"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(6).getStatusCode(), "Verify Status Code 200 for Repayment"); + } + + /** + * Tests that batch credit balance refund for loans is happening properly. Collected properly 200(OK) status was + * returned for successful responses. It first creates a new loan and then makes an overpayment, before creating a + * credit balance refund to refund a portion of the over-payment. + * + * @see CreateTransactionLoanCommandStrategy + */ + @Test + public void shouldReturnOkStatusForBatchCreditBalanceRefund() { + + final String loanProductJSON = new LoanProductTestBuilder() // + .withPrincipal("1000.00") // + .withNumberOfRepayments("24") // + .withRepaymentAfterEvery("1") // + .withRepaymentTypeAsMonth() // + .withinterestRatePerPeriod("2") // + .withInterestRateFrequencyTypeAsMonths() // + .withAmortizationTypeAsEqualPrincipalPayment() // + .withInterestTypeAsDecliningBalance() // + .currencyDetails("0", "100").build(null); + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec); + ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID); + + final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec); + Assertions.assertNotNull(collateralId); + final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec, + clientID.toString(), collateralId); + Assertions.assertNotNull(clientCollateralId); + + final Integer productId = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON); + + final Long createActiveClientRequestId = 4730L; + final Long applyLoanRequestId = createActiveClientRequestId + 1; + final Long approveLoanRequestId = applyLoanRequestId + 1; + final Long disburseLoanRequestId = approveLoanRequestId + 1; + final Long repayLoanRequestId = disburseLoanRequestId + 1; + final Long creditBalanceRefundRequestId = repayLoanRequestId + 1; + + // Create a createClient Request + final BatchRequest br1 = BatchHelper.createActiveClientRequest(createActiveClientRequestId, ""); + + // Create a ApplyLoan Request + final BatchRequest br2 = BatchHelper.applyLoanRequest(applyLoanRequestId, createActiveClientRequestId, productId, + clientCollateralId); + + // Create a approveLoan Request + final BatchRequest br3 = BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId); + + // Create a disburseLoan Request + final BatchRequest br4 = BatchHelper.disburseLoanRequest(disburseLoanRequestId, approveLoanRequestId); + + // Create a loanRepay Request which will result in an overpay. + final BatchRequest br5 = BatchHelper.repayLoanRequest(repayLoanRequestId, disburseLoanRequestId, "20000"); + + // Create a credit balance refund request + final BatchRequest br6 = BatchHelper.creditBalanceRefundRequest(creditBalanceRefundRequestId, repayLoanRequestId, "500"); + + final List batchRequests = new ArrayList<>(); + + batchRequests.add(br1); + batchRequests.add(br2); + batchRequests.add(br3); + batchRequests.add(br4); + batchRequests.add(br5); + batchRequests.add(br6); + + final String jsonifiedRequest = BatchHelper.toJsonString(batchRequests); + + final List response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec, + jsonifiedRequest); + + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(4).getStatusCode(), "Verify Status Code 200 for Repayment"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(5).getStatusCode(), + "Verify Status Code 200 for Credit Balance Refund"); } /** @@ -441,8 +541,8 @@ public class BatchApiTest { final List response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec, jsonifiedRequest); - Assertions.assertEquals(200L, (long) response.get(0).getStatusCode(), "Verify Status Code 200 for Create Client"); - Assertions.assertEquals(200L, (long) response.get(1).getStatusCode(), "Verify Status Code 200 for Activate Client"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(0).getStatusCode(), "Verify Status Code 200 for Create Client"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(1).getStatusCode(), "Verify Status Code 200 for Activate Client"); } /** @@ -504,8 +604,8 @@ public class BatchApiTest { final List response = BatchHelper.postBatchRequestsWithoutEnclosingTransaction(this.requestSpec, this.responseSpec, jsonifiedRequest); - Assertions.assertEquals(200L, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for Approve Loan"); - Assertions.assertEquals(200L, (long) response.get(4).getStatusCode(), "Verify Status Code 200 for Disburse Loan"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for Approve Loan"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(4).getStatusCode(), "Verify Status Code 200 for Disburse Loan"); } /** @@ -558,10 +658,10 @@ public class BatchApiTest { final List response = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, this.responseSpec, jsonifiedRequest); - Assertions.assertEquals(200L, (long) response.get(0).getStatusCode(), "Verify Status Code 200 for create client"); - Assertions.assertEquals(200L, (long) response.get(1).getStatusCode(), "Verify Status Code 200 for apply Loan"); - Assertions.assertEquals(200L, (long) response.get(2).getStatusCode(), "Verify Status Code 200 for approve Loan"); - Assertions.assertEquals(200L, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for disburse Loan"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(0).getStatusCode(), "Verify Status Code 200 for create client"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(1).getStatusCode(), "Verify Status Code 200 for apply Loan"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(2).getStatusCode(), "Verify Status Code 200 for approve Loan"); + Assertions.assertEquals(HttpStatus.SC_OK, (long) response.get(3).getStatusCode(), "Verify Status Code 200 for disburse Loan"); } /** @@ -754,4 +854,164 @@ public class BatchApiTest { // Repayment schedule information will be available in the response based on the query parameter Assertions.assertTrue(responses.get(3).getBody().contains("repaymentSchedule")); } + + /** + * Test for the successful get loan and get datatable entry. A '200' status code is expected on successful + * responses. + * + * @see org.apache.fineract.batch.command.internal.ApplyLoanCommandStrategy + * @see org.apache.fineract.batch.command.internal.ApproveLoanCommandStrategy + * @see org.apache.fineract.batch.command.internal.GetLoanByIdCommandStrategy + */ + @Test + public void shouldReturnOkStatusOnSuccessfulGetDataTableEntry() { + final FromJsonHelper jsonHelper = new FromJsonHelper(); + final Long loanId = jsonHelper.extractLongNamed("loanId", jsonHelper.parse(setupAccount()).getAsJsonObject()); + final String datatableName = this.datatableHelper.createDatatable(LOAN_APP_TABLE_NAME, false); + try { + + // Get loan by id Request with query param + final BatchRequest getLoanBatchRequest = BatchHelper.getLoanByIdRequest(loanId, "associations=repaymentSchedule,transactions"); + + // Get datatable batch request + final BatchRequest getDatatableBatchRequest = BatchHelper.getDatatableByIdRequest(loanId, datatableName, + "genericResultSet=true"); + + final List batchRequestsGetLoan = Arrays.asList(getLoanBatchRequest, getDatatableBatchRequest); + + final List responsesGetLoan = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, + this.responseSpec, BatchHelper.toJsonString(batchRequestsGetLoan)); + + final String getLoanResponse = responsesGetLoan.get(0).getBody(); + final String getDatatableResponse = responsesGetLoan.get(1).getBody(); + + Assertions.assertEquals(HttpStatus.SC_OK, responsesGetLoan.get(0).getStatusCode(), "Verify Status Code 200 for get loan"); + Assertions.assertEquals(HttpStatus.SC_OK, responsesGetLoan.get(1).getStatusCode(), "Verify Status Code 200 for datatable"); + + final Long loanIdInGetResponse = jsonHelper.extractLongNamed("id", jsonHelper.parse(getLoanResponse).getAsJsonObject()); + Assertions.assertEquals(loanId, loanIdInGetResponse); + + // Repayment schedule information will be available in the response based on the query parameter + Assertions.assertTrue(getLoanResponse.contains("repaymentSchedule")); + + // Transaction will be available in the response based on the query parameter + Assertions.assertTrue(getLoanResponse.contains("transactions")); + + // datatable info will be available in the response based on the query parameter + Assertions.assertTrue(getDatatableResponse.contains("columnHeaders")); + + // datatable info will be available in the response based on the query parameter + Assertions.assertTrue(getDatatableResponse.contains("data")); + } finally { + deleteDatatable(datatableName); + } + } + + /** + * Test for the successful get loan and get datatable entry where get datatable request have no query param. A '200' + * status code is expected on successful responses. + * + * @see org.apache.fineract.batch.command.internal.ApplyLoanCommandStrategy + * @see org.apache.fineract.batch.command.internal.ApproveLoanCommandStrategy + * @see org.apache.fineract.batch.command.internal.GetLoanByIdCommandStrategy + */ + @Test + public void shouldReturnOkStatusOnSuccessfulGetDatatableEntryWithNoQueryParam() { + final FromJsonHelper jsonHelper = new FromJsonHelper(); + final Long loanId = jsonHelper.extractLongNamed("loanId", jsonHelper.parse(setupAccount()).getAsJsonObject()); + final String datatableName = this.datatableHelper.createDatatable(LOAN_APP_TABLE_NAME, false); + try { + // Get loan by id Request with query param + final BatchRequest getLoanBatchRequest = BatchHelper.getLoanByIdRequest(loanId, "associations=repaymentSchedule,transactions"); + + // Get datatable batch request + final BatchRequest getDatatableBatchRequest = BatchHelper.getDatatableByIdRequest(loanId, datatableName, null); + + final List batchRequestsGetLoan = Arrays.asList(getLoanBatchRequest, getDatatableBatchRequest); + + final List responsesGetLoan = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, + this.responseSpec, BatchHelper.toJsonString(batchRequestsGetLoan)); + + final String getLoanResponse = responsesGetLoan.get(0).getBody(); + + Assertions.assertEquals(HttpStatus.SC_OK, responsesGetLoan.get(0).getStatusCode(), "Verify Status Code 200 for Get Loan"); + Assertions.assertEquals(HttpStatus.SC_OK, responsesGetLoan.get(1).getStatusCode(), "Verify Status Code 200 for Get Datatable"); + + final Long loanIdInGetResponse = jsonHelper.extractLongNamed("id", jsonHelper.parse(getLoanResponse).getAsJsonObject()); + Assertions.assertEquals(loanId, loanIdInGetResponse); + + Assertions.assertTrue(getLoanResponse.contains("repaymentSchedule")); + + Assertions.assertTrue(getLoanResponse.contains("transactions")); + } finally { + deleteDatatable(datatableName); + } + + } + + /** + * Delete datatable + * + * @param datatableName + * the datatable name + */ + private void deleteDatatable(final String datatableName) { + String deletedDatatableName = this.datatableHelper.deleteDatatable(datatableName); + assertEquals(datatableName, deletedDatatableName, "Fail to delete the datatable"); + } + + /** + * Setup account to test get loan and get datatable batch call + * + * @return the response body + */ + private String setupAccount() { + final String loanProductJSON = new LoanProductTestBuilder() // + .withPrincipal("10000000.00") // + .withNumberOfRepayments("24") // + .withRepaymentAfterEvery("1") // + .withRepaymentTypeAsMonth() // + .withinterestRatePerPeriod("2") // + .withInterestRateFrequencyTypeAsMonths() // + .withAmortizationTypeAsEqualPrincipalPayment() // + .withInterestTypeAsDecliningBalance() // + .currencyDetails("0", "100").build(null); + + final Long applyLoanRequestId = 5730L; + final Long approveLoanRequestId = 5731L; + final Long disburseLoanRequestId = 5734L; + final Long repayLoanRequestId = 5735L; + + // Create product + final Integer productId = new LoanTransactionHelper(this.requestSpec, this.responseSpec).getLoanProductId(loanProductJSON); + + // Create client + final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec); + ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientId); + + // Create an ApplyLoan Request + final BatchRequest applyLoanBatchRequest = BatchHelper.applyLoanRequestWithClientId(applyLoanRequestId, clientId, productId); + + // Create an approveLoan Request + final BatchRequest approveLoanBatchRequest = BatchHelper.approveLoanRequest(approveLoanRequestId, applyLoanRequestId); + + // Create a disburseLoan Request + final BatchRequest disburseLoanBatchRequest = BatchHelper.disburseLoanRequest(disburseLoanRequestId, applyLoanRequestId); + + // Create a repayment Request + final BatchRequest repaymentBatchRequest = BatchHelper.repayLoanRequest(repayLoanRequestId, applyLoanRequestId, "500"); + + final List batchRequests = Arrays.asList(applyLoanBatchRequest, approveLoanBatchRequest, disburseLoanBatchRequest, + repaymentBatchRequest); + + final List responses = BatchHelper.postBatchRequestsWithEnclosingTransaction(this.requestSpec, this.responseSpec, + BatchHelper.toJsonString(batchRequests)); + + Assertions.assertEquals(HttpStatus.SC_OK, responses.get(0).getStatusCode(), "Verify Status Code 200 for Apply Loan"); + Assertions.assertEquals(HttpStatus.SC_OK, responses.get(1).getStatusCode(), "Verify Status Code 200 for Approve Loan"); + Assertions.assertEquals(HttpStatus.SC_OK, responses.get(2).getStatusCode(), "Verify Status Code 200 for Disburse Loan"); + Assertions.assertEquals(HttpStatus.SC_OK, responses.get(3).getStatusCode(), "Verify Status Code 200 for Repay Loan"); + + return responses.get(0).getBody(); + } } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java index 5b37a620d..ff45e9974 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/BatchHelper.java @@ -23,10 +23,14 @@ import com.google.gson.reflect.TypeToken; import io.restassured.specification.RequestSpecification; import io.restassured.specification.ResponseSpecification; import java.security.SecureRandom; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; import java.util.Map; import javax.ws.rs.HttpMethod; +import org.apache.fineract.batch.command.internal.CreateTransactionLoanCommandStrategy; import org.apache.fineract.batch.domain.BatchRequest; import org.apache.fineract.batch.domain.BatchResponse; import org.junit.jupiter.api.Assertions; @@ -223,12 +227,37 @@ public final class BatchHelper { * given requestId and reference. * * @param requestId + * the request ID * @param reference + * the reference ID * @param productId - * @return BatchRequest + * the product ID + * @return BatchRequest the batch request */ public static BatchRequest applyLoanRequest(final Long requestId, final Long reference, final Integer productId, final Integer clientCollateralId) { + return applyLoanRequest(requestId, reference, productId, clientCollateralId, LocalDate.now(ZoneId.systemDefault()).minusDays(10), + "10,000.00"); + } + + /** + * Creates and returns a {@link org.apache.fineract.batch.command.internal.ApplyLoanCommandStrategy} Request with + * given requestId and reference. + * + * @param requestId + * the request ID + * @param reference + * the reference ID + * @param productId + * the product ID + * @param date + * the loan submitted on date + * @param loanAmount + * the loan amount + * @return BatchRequest the batch request + */ + public static BatchRequest applyLoanRequest(final Long requestId, final Long reference, final Integer productId, + final Integer clientCollateralId, final LocalDate date, final String loanAmount) { final BatchRequest br = new BatchRequest(); @@ -236,19 +265,20 @@ public final class BatchHelper { br.setRelativeUrl("loans"); br.setMethod("POST"); br.setReference(reference); + String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy")); String body = "{\"dateFormat\": \"dd MMMM yyyy\", \"locale\": \"en_GB\", \"clientId\": \"$.clientId\"," + "\"productId\": " - + productId + ", \"principal\": \"10,000.00\", \"loanTermFrequency\": 10," + + productId + ", \"principal\": \"" + loanAmount + "\", \"loanTermFrequency\": 10," + "\"loanTermFrequencyType\": 2, \"loanType\": \"individual\", \"numberOfRepayments\": 10," + "\"repaymentEvery\": 1, \"repaymentFrequencyType\": 2, \"interestRatePerPeriod\": 10," + "\"amortizationType\": 1, \"interestType\": 0, \"interestCalculationPeriodType\": 1," - + "\"transactionProcessingStrategyId\": 1, \"expectedDisbursementDate\": \"10 Jun 2013\","; + + "\"transactionProcessingStrategyId\": 1, \"expectedDisbursementDate\": \"" + dateString + "\","; if (clientCollateralId != null) { - body = body + "\"collateral\": [{\"clientCollateralId\": \"" + clientCollateralId.toString() + "\", \"quantity\": \"1\"}],"; + body = body + "\"collateral\": [{\"clientCollateralId\": \"" + clientCollateralId + "\", \"quantity\": \"1\"}],"; } - body = body + "\"submittedOnDate\": \"10 Jun 2013\"}"; + body = body + "\"submittedOnDate\": \"" + dateString + "\"}"; br.setBody(body); @@ -385,17 +415,37 @@ public final class BatchHelper { * * * @param requestId + * the request ID * @param reference - * @return BatchRequest + * the reference ID + * @return BatchRequest the batch request */ public static BatchRequest approveLoanRequest(final Long requestId, final Long reference) { + return approveLoanRequest(requestId, reference, LocalDate.now(ZoneId.systemDefault()).minusDays(10)); + } + + /** + * Creates and returns a {@link org.apache.fineract.batch.command.internal.ApproveLoanCommandStrategy} Request with + * given requestId and reference. + * + * + * @param requestId + * the request ID + * @param reference + * the reference ID + * @param date + * the approved on date + * @return BatchRequest the batch request + */ + public static BatchRequest approveLoanRequest(final Long requestId, final Long reference, LocalDate date) { final BatchRequest br = new BatchRequest(); br.setRequestId(requestId); br.setRelativeUrl("loans/$.loanId?command=approve"); br.setReference(reference); br.setMethod("POST"); - br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"approvedOnDate\": \"12 September 2013\"," + String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy")); + br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"approvedOnDate\": \"" + dateString + "\"," + "\"note\": \"Loan approval note\"}"); return br; @@ -407,39 +457,123 @@ public final class BatchHelper { * * * @param requestId + * the request ID * @param reference - * @return BatchRequest + * the reference ID + * @return BatchRequest the batch request */ public static BatchRequest disburseLoanRequest(final Long requestId, final Long reference) { + return disburseLoanRequest(requestId, reference, LocalDate.now(ZoneId.systemDefault()).minusDays(8)); + } + + /** + * Creates and returns a {@link org.apache.fineract.batch.command.internal.DisburseLoanCommandStrategy} Request with + * given requestId and reference. + * + * + * @param requestId + * the request ID + * @param reference + * the reference ID + * @param date + * the actual disbursement date + * @return BatchRequest the batch request + */ + public static BatchRequest disburseLoanRequest(final Long requestId, final Long reference, final LocalDate date) { final BatchRequest br = new BatchRequest(); br.setRequestId(requestId); br.setRelativeUrl("loans/$.loanId?command=disburse"); br.setReference(reference); br.setMethod("POST"); - br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"actualDisbursementDate\": \"15 September 2013\"}"); + String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy")); + br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", \"actualDisbursementDate\": \"" + dateString + "\"}"); return br; } /** - * Creates and returns a {@link org.apache.fineract.batch.command.internal.RepayLoanCommandStrategy} Request with - * given requestId. - * + * Creates and returns a {@link CreateTransactionLoanCommandStrategy} Request with given requestId. * * @param requestId + * the request ID * @param reference - * @return BatchRequest + * the reference ID + * @param amount + * the amount + * @param date + * the transaction date + * @return BatchRequest the batch request */ - public static BatchRequest repayLoanRequest(final Long requestId, final Long reference) { + public static BatchRequest repayLoanRequest(final Long requestId, final Long reference, final String amount) { + return repayLoanRequest(requestId, reference, amount, LocalDate.now(ZoneId.systemDefault())); + } + + /** + * Creates and returns a {@link CreateTransactionLoanCommandStrategy} Request with given requestId. + * + * @param requestId + * the request ID + * @param reference + * the reference ID + * @param amount + * the amount + * @param date + * the transaction date + * @return BatchRequest the batch request + */ + public static BatchRequest repayLoanRequest(final Long requestId, final Long reference, final String amount, final LocalDate date) { final BatchRequest br = new BatchRequest(); br.setRequestId(requestId); br.setReference(reference); br.setRelativeUrl("loans/$.loanId/transactions?command=repayment"); br.setMethod("POST"); - br.setBody("{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", " - + "\"transactionDate\": \"15 September 2013\", \"transactionAmount\": 500}"); + String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy")); + br.setBody(String.format( + "{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", " + "\"transactionDate\": \"%s\", \"transactionAmount\": %s}", + dateString, amount)); + + return br; + } + + /** + * Creates and returns a {@link CreateTransactionLoanCommandStrategy} request with given request ID. + * + * + * @param requestId + * the request ID + * @param reference + * teh reference + * @return BatchRequest the created {@link BatchRequest} + */ + public static BatchRequest creditBalanceRefundRequest(final Long requestId, final Long reference, final String amount) { + return creditBalanceRefundRequest(requestId, reference, amount, LocalDate.now(ZoneId.systemDefault())); + } + + /** + * Creates and returns a {@link CreateTransactionLoanCommandStrategy} request with given request ID. + * + * + * @param requestId + * the request ID + * @param reference + * teh reference + * @param date + * the transaction date + * @return BatchRequest the created {@link BatchRequest} + */ + public static BatchRequest creditBalanceRefundRequest(final Long requestId, final Long reference, final String amount, LocalDate date) { + final BatchRequest br = new BatchRequest(); + + br.setRequestId(requestId); + br.setReference(reference); + br.setRelativeUrl("loans/$.loanId/transactions?command=creditBalanceRefund"); + br.setMethod("POST"); + String dateString = date.format(DateTimeFormatter.ofPattern("dd MMMM yyyy")); + br.setBody(String.format( + "{\"locale\": \"en\", \"dateFormat\": \"dd MMMM yyyy\", " + "\"transactionDate\": \"%s\", \"transactionAmount\": %s}", + dateString, amount)); return br; } @@ -511,4 +645,55 @@ public final class BatchHelper { return br; } + + /** + * Creates and returns a {@link org.apache.fineract.batch.command.internal.GetLoanByIdCommandStrategy} request with + * given loan id and query param. + * + * @param loanId + * the loan id + * @param queryParameter + * the query parameters + * @return the {@link BatchRequest} + */ + public static BatchRequest getLoanByIdRequest(final Long loanId, final String queryParameter) { + final BatchRequest br = new BatchRequest(); + String relativeUrl = String.format("loans/%s", loanId); + if (queryParameter != null) { + relativeUrl = relativeUrl + "?" + queryParameter; + } + + br.setRequestId(4567L); + br.setRelativeUrl(relativeUrl); + br.setMethod(HttpMethod.GET); + br.setBody("{}"); + + return br; + } + + /** + * Creates and returns a batch request to get datatable entry. + * + * @param loanId + * the loan id + * @param datatableName + * the name of datatable + * @param queryParameter + * the query parameters + * @return the {@link BatchRequest} + */ + public static BatchRequest getDatatableByIdRequest(final Long loanId, final String datatableName, final String queryParameter) { + final BatchRequest br = new BatchRequest(); + String relativeUrl = String.format("datatables/%s/%s", datatableName, loanId); + if (queryParameter != null) { + relativeUrl = relativeUrl + "?" + queryParameter; + } + + br.setRequestId(4568L); + br.setRelativeUrl(relativeUrl); + br.setMethod(HttpMethod.GET); + br.setBody("{}"); + + return br; + } }