FINERACT-2181: Batch Api - Add Interest pause API support

This commit is contained in:
Adam Saghy
2025-07-21 12:26:03 +02:00
parent 46e1bd0492
commit 81d6d2e54e
11 changed files with 645 additions and 7 deletions
@@ -228,12 +228,12 @@ public class CommandStrategyProvider {
.resource("v1\\/datatables\\/" + ALPHANUMBERIC_WITH_UNDERSCORE_REGEX + "\\/query" + MANDATORY_QUERY_PARAM_REGEX).method(GET)
.build(), "getDatatableEntryByQueryCommandStrategy");
commandStrategies.put(CommandContext.resource("v1\\/loans\\/" + NUMBER_REGEX + "\\/interest-pauses").method(GET).build(),
"getLoanInterestPausesByExternalIdCommandStrategy");
"getLoanInterestPausesByLoanIdCommandStrategy");
commandStrategies.put(CommandContext.resource("v1\\/loans\\/" + NUMBER_REGEX + "\\/interest-pauses").method(POST).build(),
"createLoanInterestPauseByExternalIdCommandStrategy");
"createLoanInterestPauseByLoanIdCommandStrategy");
commandStrategies.put(
CommandContext.resource("v1\\/loans\\/" + NUMBER_REGEX + "\\/interest-pauses\\/" + NUMBER_REGEX).method(PUT).build(),
"updateLoanInterestPauseByExternalIdCommandStrategy");
"updateLoanInterestPauseByLoanIdCommandStrategy");
commandStrategies.put(
CommandContext.resource("v1\\/loans\\/external-id\\/" + UUID_PARAM_REGEX + "\\/interest-pauses").method(GET).build(),
"getLoanInterestPausesByExternalIdCommandStrategy");
@@ -26,6 +26,7 @@ import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
@@ -954,6 +955,112 @@ public class BatchApiStepDef extends AbstractStepDef {
}
}
@When("Run Batch API with steps: createClient, createLoan, approveLoan, disburseLoan, applyInterestPause by external ids")
public void runBatchApiWithInterestPauseByExternalIds() throws IOException {
String idempotencyKey = UUID.randomUUID().toString();
String clientExternalId = UUID.randomUUID().toString();
String loanExternalId = UUID.randomUUID().toString();
List<BatchRequest> requestList = new ArrayList<>();
// Create client
requestList.add(createClient(1L, idempotencyKey, clientExternalId));
// Create loan
requestList.add(createProgressiveLoan(2L, 1L, idempotencyKey, loanExternalId));
// Approve loan
requestList.add(approveLoanByExternalId(3L, 2L, idempotencyKey));
// Disburse loan
PostLoansLoanIdRequest loanDisburseRequest = LoanRequestFactory.defaultLoanDisburseRequest();
String bodyLoanDisburseRequest = GSON.toJson(loanDisburseRequest);
BatchRequest disburseRequest = new BatchRequest();
disburseRequest.requestId(4L);
disburseRequest.relativeUrl("loans/external-id/$.resourceExternalId?command=disburse");
disburseRequest.method(BATCH_API_METHOD_POST);
disburseRequest.reference(2L);
disburseRequest.headers(setHeaders(idempotencyKey));
disburseRequest.body(bodyLoanDisburseRequest);
requestList.add(disburseRequest);
// Apply interest pause (1 day starting from tomorrow)
String startDate = DateTimeFormatter.ofPattern(DATE_FORMAT).format(Utils.now().minusMonths(1).plusDays(1));
String endDate = DateTimeFormatter.ofPattern(DATE_FORMAT).format(Utils.now().minusMonths(1).plusDays(2));
requestList.add(applyInterestPauseByExternalId(5L, 2L, idempotencyKey, startDate, endDate));
// Execute batch request
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, true).execute();
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
testContext().set(TestContextKey.BATCH_API_CALL_CLIENT_EXTERNAL_ID, clientExternalId);
testContext().set(TestContextKey.BATCH_API_CALL_LOAN_EXTERNAL_ID, loanExternalId);
// Log response for debugging
if (batchResponseList.isSuccessful() && batchResponseList.body() != null && !batchResponseList.body().isEmpty()) {
for (int i = 0; i < batchResponseList.body().size(); i++) {
BatchResponse response = batchResponseList.body().get(i);
log.debug("Batch step {} status code: {}", i + 1, response.getStatusCode());
log.debug("Batch step {} response body: {}", i + 1, response.getBody());
}
} else {
log.warn("Batch API call failed or returned empty response");
}
}
@When("Run Batch API with steps: createClient, createLoan, approveLoan, disburseLoan, applyInterestPause")
public void runBatchApiWithInterestPause() throws IOException {
String idempotencyKey = UUID.randomUUID().toString();
String clientExternalId = UUID.randomUUID().toString();
String loanExternalId = UUID.randomUUID().toString();
List<BatchRequest> requestList = new ArrayList<>();
// Create client
requestList.add(createClient(1L, idempotencyKey, clientExternalId));
// Create loan
requestList.add(createProgressiveLoan(2L, 1L, idempotencyKey, loanExternalId));
// Approve loan
requestList.add(approveLoanByExternalId(3L, 2L, idempotencyKey));
// Disburse loan
PostLoansLoanIdRequest loanDisburseRequest = LoanRequestFactory.defaultLoanDisburseRequest();
String bodyLoanDisburseRequest = GSON.toJson(loanDisburseRequest);
BatchRequest disburseRequest = new BatchRequest();
disburseRequest.requestId(4L);
disburseRequest.relativeUrl("loans/$.loanId?command=disburse");
disburseRequest.method(BATCH_API_METHOD_POST);
disburseRequest.reference(2L);
disburseRequest.headers(setHeaders(idempotencyKey));
disburseRequest.body(bodyLoanDisburseRequest);
requestList.add(disburseRequest);
// Apply interest pause (1 day starting from tomorrow)
String startDate = DateTimeFormatter.ofPattern(DATE_FORMAT).format(Utils.now().minusMonths(1).plusDays(1));
String endDate = DateTimeFormatter.ofPattern(DATE_FORMAT).format(Utils.now().minusMonths(1).plusDays(2));
requestList.add(applyInterestPause(5L, 2L, idempotencyKey, startDate, endDate));
// Execute batch request
Response<List<BatchResponse>> batchResponseList = batchApiApi.handleBatchRequests(requestList, true).execute();
testContext().set(TestContextKey.BATCH_API_CALL_RESPONSE, batchResponseList);
testContext().set(TestContextKey.BATCH_API_CALL_IDEMPOTENCY_KEY, idempotencyKey);
testContext().set(TestContextKey.BATCH_API_CALL_CLIENT_EXTERNAL_ID, clientExternalId);
testContext().set(TestContextKey.BATCH_API_CALL_LOAN_EXTERNAL_ID, loanExternalId);
// Log response for debugging
if (batchResponseList.isSuccessful() && batchResponseList.body() != null && !batchResponseList.body().isEmpty()) {
for (int i = 0; i < batchResponseList.body().size(); i++) {
BatchResponse response = batchResponseList.body().get(i);
log.debug("Batch step {} status code: {}", i + 1, response.getStatusCode());
log.debug("Batch step {} response body: {}", i + 1, response.getBody());
}
} else {
log.warn("Batch API call failed or returned empty response");
}
}
private BatchRequest createChargeOffRequest(Long requestId, Long loanId, String idempotencyKey, String chargeOffDate) {
// Create a charge-off request with the specified date
Map<String, Object> requestMap = new HashMap<>();
@@ -1043,6 +1150,24 @@ public class BatchApiStepDef extends AbstractStepDef {
return batchRequest;
}
private BatchRequest createProgressiveLoan(Long requestId, Long referenceId, String idempotencyKey, String loanExternalId) {
PostLoansRequest loansRequest = loanExternalId == null ? loanRequestFactory.defaultProgressiveLoansRequest(1L)
: loanRequestFactory.defaultProgressiveLoansRequest(1L).externalId(loanExternalId);
loansRequest.setInterestRatePerPeriod(BigDecimal.ONE);
String bodyLoansRequest = GSON.toJson(loansRequest);
String bodyLoansRequestMod = bodyLoansRequest.replace("\"clientId\":1", "\"clientId\":\"$.clientId\"");
BatchRequest batchRequest = new BatchRequest();
batchRequest.requestId(requestId);
batchRequest.relativeUrl(BATCH_API_SAMPLE_RELATIVE_URL_LOANS);
batchRequest.method(BATCH_API_METHOD_POST);
batchRequest.headers(setHeaders(idempotencyKey));
batchRequest.reference(referenceId);
batchRequest.body(bodyLoansRequestMod);
return batchRequest;
}
private BatchRequest queryDatatable(Long requestId) {
String datatableName = testContext().get(DATATABLE_NAME);
@@ -1113,6 +1238,37 @@ public class BatchApiStepDef extends AbstractStepDef {
return batchRequest;
}
private BatchRequest applyInterestPause(Long requestId, Long referenceId, String idempotencyKey, String startDate, String endDate) {
BatchRequest batchRequest = new BatchRequest();
batchRequest.requestId(requestId);
batchRequest.relativeUrl("loans/$.loanId/interest-pauses");
batchRequest.method(BATCH_API_METHOD_POST);
batchRequest.reference(referenceId);
batchRequest.headers(setHeaders(idempotencyKey));
String interestPauseRequest = String
.format("{\"dateFormat\":\"dd MMMM yyyy\",\"locale\":\"en\",\"startDate\":\"%s\",\"endDate\":\"%s\"}", startDate, endDate);
batchRequest.body(interestPauseRequest);
return batchRequest;
}
private BatchRequest applyInterestPauseByExternalId(Long requestId, Long referenceId, String idempotencyKey, String startDate,
String endDate) {
BatchRequest batchRequest = new BatchRequest();
batchRequest.requestId(requestId);
batchRequest.relativeUrl("loans/external-id/$.resourceExternalId/interest-pauses");
batchRequest.method(BATCH_API_METHOD_POST);
batchRequest.reference(referenceId);
batchRequest.headers(setHeaders(idempotencyKey));
String interestPauseRequest = String
.format("{\"dateFormat\":\"dd MMMM yyyy\",\"locale\":\"en\",\"startDate\":\"%s\",\"endDate\":\"%s\"}", startDate, endDate);
batchRequest.body(interestPauseRequest);
return batchRequest;
}
private Set<Header> setHeaders(String idempotencyKey) {
Set<Header> headers = new HashSet<>();
headers.add(HEADER);
@@ -1122,4 +1278,43 @@ public class BatchApiStepDef extends AbstractStepDef {
return headers;
}
@Then("Loan should have an active interest pause period starting on {int}st day and ending on {int}nd day")
public void verifyInterestPausePeriod(int startDay, int endDay) throws IOException {
// Get the loan ID from the batch response
Response<List<BatchResponse>> batchResponseList = testContext().get(TestContextKey.BATCH_API_CALL_RESPONSE);
assertThat(batchResponseList.isSuccessful()).isTrue();
assertThat(batchResponseList.body()).isNotNull();
// The loan creation response is the second response in the batch (index 1)
BatchResponse loanCreateResponse = batchResponseList.body().get(1);
assertThat(loanCreateResponse.getStatusCode()).isEqualTo(200);
// Parse the loan ID from the response
String loanCreateResponseBody = loanCreateResponse.getBody();
com.google.gson.JsonObject loanCreateJson = com.google.gson.JsonParser.parseString(loanCreateResponseBody).getAsJsonObject();
long loanId = loanCreateJson.get("loanId").getAsLong();
// Get the loan details
Response<GetLoansLoanIdResponse> loanResponse = loansApi.retrieveLoan(loanId, false, "all", "", "").execute();
assertThat(loanResponse.isSuccessful()).isTrue();
assertThat(loanResponse.body()).isNotNull();
// Verify the interest pause period
GetLoansLoanIdResponse loan = loanResponse.body();
assertThat(loan.getLoanTermVariations().get(0).getTermType().getValue().equals("interestPause")).isTrue();
// Verify the start date is the specified day of the previous month
LocalDate today = Utils.now();
LocalDate expectedStartDate = today.minusMonths(1).plusDays(startDay);
LocalDate actualStartDate = loan.getLoanTermVariations().get(0).getTermVariationApplicableFrom();
assertThat(actualStartDate).isEqualTo(expectedStartDate);
// Verify the end date is the specified day of the previous month
LocalDate expectedEndDate = today.minusMonths(1).plusDays(endDay);
LocalDate actualEndDate = loan.getLoanTermVariations().get(0).getDateValue();
assertThat(actualEndDate).isEqualTo(expectedEndDate);
log.debug("Verified interest pause period from {} to {}", actualStartDate, actualEndDate);
}
}
@@ -48,6 +48,18 @@ Feature: Batch API
When Batch API call with steps done twice: createClient, createLoan, approveLoan, getLoanDetails runs with enclosingTransaction: "false"
Then Admin checks that all steps result 200OK
@TestRailId:C3876
Scenario: Create loan, approve, disburse and apply interest pause in a single Batch API call
And Run Batch API with steps: createClient, createLoan, approveLoan, disburseLoan, applyInterestPause
Then Admin checks that all steps result 200OK
And Loan should have an active interest pause period starting on 1st day and ending on 2nd day
@TestRailId:C3877
Scenario: Create loan, approve, disburse and apply interest pause in a single Batch API call by external ids
And Run Batch API with steps: createClient, createLoan, approveLoan, disburseLoan, applyInterestPause by external ids
Then Admin checks that all steps result 200OK
And Loan should have an active interest pause period starting on 1st day and ending on 2nd day
@TestRailId:C2645
Scenario: Verify Batch API call in case of enclosing transaction is FALSE, there are two reference-trees and one of the steps in second tree fails
When Batch API call with steps done twice: createClient, createLoan, approveLoan, getLoanDetails runs with enclosingTransaction: "false", with failed approve step in second tree
@@ -141,4 +153,4 @@ Feature: Batch API
| 01 February 2024 | Repayment | 17.01 | 16.43 | 0.58 | 0.0 | 0.0 | 83.57 |
| 01 February 2024 | Accrual | 0.58 | 0.0 | 0.58 | 0.0 | 0.0 | 0.0 |
| 01 February 2024 | Charge-off | 83.57 | 83.57 | 0.0 | 0.0 | 0.0 | 0.0 |
And Admin checks the loan has been charged-off on "01 February 2024"
And Admin checks the loan has been charged-off on "01 February 2024"
@@ -50,4 +50,12 @@ public class InterestPauseRequestDto {
throw new IllegalArgumentException("Error serializing request to JSON", e);
}
}
public static InterestPauseRequestDto fromJson(String json) {
try {
return new ObjectMapper().readValue(json, InterestPauseRequestDto.class);
} catch (JsonProcessingException e) {
throw new IllegalArgumentException("Error deserializing request from JSON", e);
}
}
}
@@ -0,0 +1,71 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.batch.command.internal;
import static org.apache.fineract.batch.command.CommandStrategyUtils.relativeUrlWithoutVersion;
import com.google.common.base.Splitter;
import jakarta.ws.rs.core.UriInfo;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.command.CommandStrategy;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.portfolio.interestpauses.api.LoanInterestPauseApiResource;
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseRequestDto;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;
/**
* Implements {@link CommandStrategy} and creates a loan interest pause by external id. It passes the contents of the
* body from the BatchRequest to {@link LoanInterestPauseApiResource} and gets back the response. This class will also
* catch any errors raised by {@link LoanInterestPauseApiResource} and map those errors to appropriate status codes in
* BatchResponse.
*/
@Component
@RequiredArgsConstructor
public class CreateLoanInterestPauseByExternalIdCommandStrategy implements CommandStrategy {
private final LoanInterestPauseApiResource loanInterestPauseApiResource;
private final DefaultToApiJsonSerializer<CommandProcessingResult> toApiJsonSerializer;
@Override
public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") final UriInfo uriInfo) {
final BatchResponse response = new BatchResponse();
response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());
// Expected pattern - loans\/external-id\/[\w\d_-]+\/interest-pauses
final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrlWithoutVersion(request));
final String loanExternalId = pathParameters.get(2);
final InterestPauseRequestDto interestPauseRequestDto = InterestPauseRequestDto.fromJson(request.getBody());
final CommandProcessingResult commandProcessingResult = loanInterestPauseApiResource.createInterestPauseByExternalId(loanExternalId,
interestPauseRequestDto);
response.setStatusCode(HttpStatus.SC_OK);
response.setBody(toApiJsonSerializer.serialize(commandProcessingResult));
return response;
}
}
@@ -0,0 +1,71 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.batch.command.internal;
import static org.apache.fineract.batch.command.CommandStrategyUtils.relativeUrlWithoutVersion;
import com.google.common.base.Splitter;
import jakarta.ws.rs.core.UriInfo;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.command.CommandStrategy;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.portfolio.interestpauses.api.LoanInterestPauseApiResource;
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseRequestDto;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;
/**
* Implements {@link CommandStrategy} and creates a loan interest pause by loan id. It passes the contents of the body
* from the BatchRequest to {@link LoanInterestPauseApiResource} and gets back the response. This class will also catch
* any errors raised by {@link LoanInterestPauseApiResource} and map those errors to appropriate status codes in
* BatchResponse.
*/
@Component
@RequiredArgsConstructor
public class CreateLoanInterestPauseByLoanIdCommandStrategy implements CommandStrategy {
private final LoanInterestPauseApiResource loanInterestPauseApiResource;
private final DefaultToApiJsonSerializer<CommandProcessingResult> toApiJsonSerializer;
@Override
public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") final UriInfo uriInfo) {
final BatchResponse response = new BatchResponse();
response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());
// Expected pattern - loans\/\d+\/interest-pauses
final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrlWithoutVersion(request));
final Long loanId = Long.parseLong(pathParameters.get(1));
final InterestPauseRequestDto interestPauseRequestDto = InterestPauseRequestDto.fromJson(request.getBody());
final CommandProcessingResult commandProcessingResult = loanInterestPauseApiResource.createInterestPause(loanId,
interestPauseRequestDto);
response.setStatusCode(HttpStatus.SC_OK);
response.setBody(toApiJsonSerializer.serialize(commandProcessingResult));
return response;
}
}
@@ -0,0 +1,69 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.batch.command.internal;
import static org.apache.fineract.batch.command.CommandStrategyUtils.relativeUrlWithoutVersion;
import com.google.common.base.Splitter;
import jakarta.ws.rs.core.UriInfo;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.command.CommandStrategy;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.portfolio.interestpauses.api.LoanInterestPauseApiResource;
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseResponseDto;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;
/**
* Implements {@link CommandStrategy} and retrieves loan interest pauses by external id. It passes the contents of the
* body from the BatchRequest to {@link LoanInterestPauseApiResource} and gets back the response. This class will also
* catch any errors raised by {@link LoanInterestPauseApiResource} and map those errors to appropriate status codes in
* BatchResponse.
*/
@Component
@RequiredArgsConstructor
public class GetLoanInterestPausesByExternalIdCommandStrategy implements CommandStrategy {
private final LoanInterestPauseApiResource loanInterestPauseApiResource;
private final DefaultToApiJsonSerializer<List<InterestPauseResponseDto>> toApiJsonSerializer;
@Override
public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") final UriInfo uriInfo) {
final BatchResponse response = new BatchResponse();
response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());
// Expected pattern - loans\/external-id\/[\w\d_-]+\/interest-pauses
final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrlWithoutVersion(request));
final String loanExternalId = pathParameters.get(2);
final List<InterestPauseResponseDto> responseDtos = loanInterestPauseApiResource.retrieveInterestPausesByExternalId(loanExternalId);
response.setStatusCode(HttpStatus.SC_OK);
response.setBody(toApiJsonSerializer.serialize(responseDtos));
return response;
}
}
@@ -0,0 +1,68 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.batch.command.internal;
import static org.apache.fineract.batch.command.CommandStrategyUtils.relativeUrlWithoutVersion;
import com.google.common.base.Splitter;
import jakarta.ws.rs.core.UriInfo;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.command.CommandStrategy;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.portfolio.interestpauses.api.LoanInterestPauseApiResource;
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseResponseDto;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;
/**
* Implements {@link CommandStrategy} and retrieves loan interest pauses by loan id. It passes the contents of the body
* from the BatchRequest to {@link LoanInterestPauseApiResource} and gets back the response. This class will also catch
* any errors raised by {@link LoanInterestPauseApiResource} and map those errors to appropriate status codes in
* BatchResponse.
*/
@Component
@RequiredArgsConstructor
public class GetLoanInterestPausesByLoanIdCommandStrategy implements CommandStrategy {
private final LoanInterestPauseApiResource loanInterestPauseApiResource;
private final DefaultToApiJsonSerializer<List<InterestPauseResponseDto>> toApiJsonSerializer;
@Override
public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") final UriInfo uriInfo) {
final BatchResponse response = new BatchResponse();
response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());
// Expected pattern - loans\/\d+\/interest-pauses
final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrlWithoutVersion(request));
final Long loanId = Long.parseLong(pathParameters.get(1));
final List<InterestPauseResponseDto> responseDtos = loanInterestPauseApiResource.retrieveInterestPauses(loanId);
response.setStatusCode(HttpStatus.SC_OK);
response.setBody(toApiJsonSerializer.serialize(responseDtos));
return response;
}
}
@@ -0,0 +1,72 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.batch.command.internal;
import static org.apache.fineract.batch.command.CommandStrategyUtils.relativeUrlWithoutVersion;
import com.google.common.base.Splitter;
import jakarta.ws.rs.core.UriInfo;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.command.CommandStrategy;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.portfolio.interestpauses.api.LoanInterestPauseApiResource;
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseRequestDto;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;
/**
* Implements {@link CommandStrategy} and updates a loan interest pause by external id. It passes the contents of the
* body from the BatchRequest to {@link LoanInterestPauseApiResource} and gets back the response. This class will also
* catch any errors raised by {@link LoanInterestPauseApiResource} and map those errors to appropriate status codes in
* BatchResponse.
*/
@Component
@RequiredArgsConstructor
public class UpdateLoanInterestPauseByExternalIdCommandStrategy implements CommandStrategy {
private final LoanInterestPauseApiResource loanInterestPauseApiResource;
private final DefaultToApiJsonSerializer<CommandProcessingResult> toApiJsonSerializer;
@Override
public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") final UriInfo uriInfo) {
final BatchResponse response = new BatchResponse();
response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());
// Expected pattern - loans\/external-id\/[\w\d_-]+\/interest-pauses\/\d+
final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrlWithoutVersion(request));
final String loanExternalId = pathParameters.get(2);
final Long variationId = Long.parseLong(pathParameters.get(4));
final InterestPauseRequestDto interestPauseRequestDto = InterestPauseRequestDto.fromJson(request.getBody());
final CommandProcessingResult commandProcessingResult = loanInterestPauseApiResource.updateInterestPauseByExternalId(loanExternalId,
variationId, interestPauseRequestDto);
response.setStatusCode(HttpStatus.SC_OK);
response.setBody(toApiJsonSerializer.serialize(commandProcessingResult));
return response;
}
}
@@ -0,0 +1,72 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.batch.command.internal;
import static org.apache.fineract.batch.command.CommandStrategyUtils.relativeUrlWithoutVersion;
import com.google.common.base.Splitter;
import jakarta.ws.rs.core.UriInfo;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.apache.fineract.batch.command.CommandStrategy;
import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
import org.apache.fineract.portfolio.interestpauses.api.LoanInterestPauseApiResource;
import org.apache.fineract.portfolio.interestpauses.data.InterestPauseRequestDto;
import org.apache.http.HttpStatus;
import org.springframework.stereotype.Component;
/**
* Implements {@link CommandStrategy} and updates a loan interest pause by loan id. It passes the contents of the body
* from the BatchRequest to {@link LoanInterestPauseApiResource} and gets back the response. This class will also catch
* any errors raised by {@link LoanInterestPauseApiResource} and map those errors to appropriate status codes in
* BatchResponse.
*/
@Component
@RequiredArgsConstructor
public class UpdateLoanInterestPauseByLoanIdCommandStrategy implements CommandStrategy {
private final LoanInterestPauseApiResource loanInterestPauseApiResource;
private final DefaultToApiJsonSerializer<CommandProcessingResult> toApiJsonSerializer;
@Override
public BatchResponse execute(final BatchRequest request, @SuppressWarnings("unused") final UriInfo uriInfo) {
final BatchResponse response = new BatchResponse();
response.setRequestId(request.getRequestId());
response.setHeaders(request.getHeaders());
// Expected pattern - loans\/\d+\/interest-pauses\/\d+
final List<String> pathParameters = Splitter.on('/').splitToList(relativeUrlWithoutVersion(request));
final Long loanId = Long.parseLong(pathParameters.get(1));
final Long variationId = Long.parseLong(pathParameters.get(3));
final InterestPauseRequestDto interestPauseRequestDto = InterestPauseRequestDto.fromJson(request.getBody());
final CommandProcessingResult commandProcessingResult = loanInterestPauseApiResource.updateInterestPause(loanId, variationId,
interestPauseRequestDto);
response.setStatusCode(HttpStatus.SC_OK);
response.setBody(toApiJsonSerializer.serialize(commandProcessingResult));
return response;
}
}
@@ -195,11 +195,11 @@ public class CommandStrategyProviderTest {
"getDatatableEntryByQueryCommandStrategy", mock(GetDatatableEntryByQueryCommandStrategy.class)),
Arguments.of("datatables/test_dt_table/query?columnFilter=custom_id&valueFilter=10a62-d438-2319&resultColumns=id",
HttpMethod.GET, "getDatatableEntryByQueryCommandStrategy", mock(GetDatatableEntryByQueryCommandStrategy.class)),
Arguments.of("loans/123/interest-pauses", HttpMethod.GET, "getLoanInterestPausesByExternalIdCommandStrategy",
Arguments.of("loans/123/interest-pauses", HttpMethod.GET, "getLoanInterestPausesByLoanIdCommandStrategy",
mock(CommandStrategy.class)),
Arguments.of("loans/123/interest-pauses", HttpMethod.POST, "createLoanInterestPauseByExternalIdCommandStrategy",
Arguments.of("loans/123/interest-pauses", HttpMethod.POST, "createLoanInterestPauseByLoanIdCommandStrategy",
mock(CommandStrategy.class)),
Arguments.of("loans/123/interest-pauses/123", HttpMethod.PUT, "updateLoanInterestPauseByExternalIdCommandStrategy",
Arguments.of("loans/123/interest-pauses/456", HttpMethod.PUT, "updateLoanInterestPauseByLoanIdCommandStrategy",
mock(CommandStrategy.class)),
Arguments.of("loans/external-id/8dfad438-2319-48ce-8520-10a62801e9a1/interest-pauses", HttpMethod.GET,
"getLoanInterestPausesByExternalIdCommandStrategy", mock(CommandStrategy.class)),