You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ar...@apache.org on 2022/09/11 14:56:18 UTC
[fineract] branch develop updated: Batch API read optimization + minor changes
This is an automated email from the ASF dual-hosted git repository.
arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new a0749ffe1 Batch API read optimization + minor changes
a0749ffe1 is described below
commit a0749ffe19293cb4719e02873fc2020910b66306
Author: Arnold Galovics <ga...@gmail.com>
AuthorDate: Fri Sep 9 20:20:10 2022 +0200
Batch API read optimization + minor changes
---
.../fineract/batch/api/BatchApiResource.java | 25 ++++++++++
.../service/CodeValueReadPlatformServiceImpl.java | 2 +
.../InvalidInstanceTypeMethodExceptionMapper.java | 46 ++++++++++++++++++
.../filter/FineractInstanceModeApiFilter.java | 5 +-
.../InvalidInstanceTypeMethodException.java | 5 ++
.../portfolio/loanaccount/domain/Loan.java | 4 ++
.../loanschedule/data/LoanSchedulePeriodData.java | 4 ++
.../PaymentTypeReadPlatformServiceImpl.java | 2 +
.../service/PaymentTypeWriteServiceImpl.java | 4 ++
...validInstanceTypeMethodExceptionMapperTest.java | 56 ++++++++++++++++++++++
.../filter/FineractInstanceModeApiFilterTest.java | 40 ++++++++++++++++
11 files changed, 192 insertions(+), 1 deletion(-)
diff --git a/fineract-provider/src/main/java/org/apache/fineract/batch/api/BatchApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/batch/api/BatchApiResource.java
index f49003e41..663f70bd0 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/batch/api/BatchApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/batch/api/BatchApiResource.java
@@ -29,8 +29,10 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
+import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@@ -43,7 +45,9 @@ import org.apache.fineract.batch.domain.BatchRequest;
import org.apache.fineract.batch.domain.BatchResponse;
import org.apache.fineract.batch.serialization.BatchRequestJsonHelper;
import org.apache.fineract.batch.service.BatchApiService;
+import org.apache.fineract.infrastructure.core.config.FineractProperties;
import org.apache.fineract.infrastructure.core.serialization.ToApiJsonSerializer;
+import org.apache.fineract.infrastructure.security.exception.InvalidInstanceTypeMethodException;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@@ -78,6 +82,7 @@ public class BatchApiResource {
private final ToApiJsonSerializer<BatchResponse> toApiJsonSerializer;
private final BatchApiService service;
private final BatchRequestJsonHelper batchRequestJsonHelper;
+ private final FineractProperties fineractProperties;
/**
* Rest assured POST method to get {@link BatchRequest} and returns back the consolidated {@link BatchResponse}
@@ -106,6 +111,8 @@ public class BatchApiResource {
// Converts request array into BatchRequest List
final List<BatchRequest> requestList = this.batchRequestJsonHelper.extractList(jsonRequestString);
+ validateRequestMethodsAllowedOnInstanceType(requestList);
+
// Gets back the consolidated BatchResponse from BatchApiservice
List<BatchResponse> result = new ArrayList<>();
@@ -120,4 +127,22 @@ public class BatchApiResource {
return this.toApiJsonSerializer.serialize(result);
}
+
+ /**
+ * Validates to make sure the request methods are allowed on currently running instance mode (type).
+ *
+ * @param requestList
+ * the list of {@link BatchRequest}s
+ */
+ private void validateRequestMethodsAllowedOnInstanceType(final List<BatchRequest> requestList) {
+ // Throw exception if instance is read only and any of the batch requests are trying to write/update data.
+ if (fineractProperties.getMode().isReadOnlyMode()) {
+ final Optional<BatchRequest> nonGetRequest = requestList.stream()
+ .filter(batchRequest -> !HttpMethod.GET.equals(batchRequest.getMethod())).findFirst();
+ if (nonGetRequest.isPresent()) {
+ throw new InvalidInstanceTypeMethodException(nonGetRequest.get().getMethod());
+ }
+ }
+ }
+
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformServiceImpl.java
index 4decf34b7..7821aa062 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/codes/service/CodeValueReadPlatformServiceImpl.java
@@ -65,6 +65,7 @@ public class CodeValueReadPlatformServiceImpl implements CodeValueReadPlatformSe
}
@Override
+ @Cacheable(value = "code_values", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#code+'cv')")
public Collection<CodeValueData> retrieveCodeValuesByCode(final String code) {
this.context.authenticatedUser();
@@ -88,6 +89,7 @@ public class CodeValueReadPlatformServiceImpl implements CodeValueReadPlatformSe
}
@Override
+ @Cacheable(value = "code_values", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat(#codeValueId+'cv_by_id')")
public CodeValueData retrieveCodeValue(final Long codeValueId) {
try {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/InvalidInstanceTypeMethodExceptionMapper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/InvalidInstanceTypeMethodExceptionMapper.java
new file mode 100644
index 000000000..77831d231
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/core/exceptionmapper/InvalidInstanceTypeMethodExceptionMapper.java
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.core.exceptionmapper;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import javax.ws.rs.ext.ExceptionMapper;
+import javax.ws.rs.ext.Provider;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.infrastructure.core.data.ApiGlobalErrorResponse;
+import org.apache.fineract.infrastructure.security.exception.InvalidInstanceTypeMethodException;
+import org.springframework.stereotype.Component;
+
+/**
+ * An {@link ExceptionMapper} to map {@link InvalidInstanceTypeMethodException} thrown by platform into an HTTP API
+ * friendly format.
+ */
+@Provider
+@Component
+@Slf4j
+public class InvalidInstanceTypeMethodExceptionMapper implements ExceptionMapper<InvalidInstanceTypeMethodException> {
+
+ @Override
+ public Response toResponse(final InvalidInstanceTypeMethodException exception) {
+ log.warn("Exception: {}, Message: {}", exception.getClass().getName(), exception.getMessage());
+ ApiGlobalErrorResponse errorResponse = ApiGlobalErrorResponse.invalidInstanceTypeMethod(exception.getMethod());
+ return Response.status(Status.METHOD_NOT_ALLOWED).entity(errorResponse).type(MediaType.APPLICATION_JSON).build();
+ }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/instancemode/filter/FineractInstanceModeApiFilter.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/instancemode/filter/FineractInstanceModeApiFilter.java
index 4266de6b5..6b366d3f6 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/instancemode/filter/FineractInstanceModeApiFilter.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/instancemode/filter/FineractInstanceModeApiFilter.java
@@ -43,7 +43,10 @@ public class FineractInstanceModeApiFilter extends OncePerRequestFilter {
private static final List<ExceptionListItem> EXCEPTION_LIST = List.of(
item(FineractProperties.FineractModeProperties::isBatchManagerEnabled, pi -> pi.startsWith("/jobs")),
- item(p -> true, pi -> pi.startsWith("/instance-mode")));
+ item(p -> true, pi -> pi.startsWith("/instance-mode")),
+ // Batches with all GET requests need to be allowed in read-only instances, hence this check will be moved
+ // under the Api Resource.
+ item(p -> true, pi -> pi.startsWith("/batches")));
private final FineractProperties fineractProperties;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/exception/InvalidInstanceTypeMethodException.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/exception/InvalidInstanceTypeMethodException.java
index a69a0813b..85d44f8df 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/exception/InvalidInstanceTypeMethodException.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/security/exception/InvalidInstanceTypeMethodException.java
@@ -18,6 +18,7 @@
*/
package org.apache.fineract.infrastructure.security.exception;
+import lombok.Getter;
import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainRuleException;
/**
@@ -25,9 +26,13 @@ import org.apache.fineract.infrastructure.core.exception.AbstractPlatformDomainR
*
*
*/
+@Getter
public class InvalidInstanceTypeMethodException extends AbstractPlatformDomainRuleException {
+ private final String method;
+
public InvalidInstanceTypeMethodException(final String method) {
super("error.msg.invalid.method.for.instance.type", "Method Not Allowed " + method + " for the instance type");
+ this.method = method;
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index dfd42a124..3acf93cc1 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -5913,6 +5913,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
return this.accountNumber;
}
+ public String getExternalId() {
+ return this.externalId;
+ }
+
public Client getClient() {
return this.client;
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
index 0de3e8719..b03f44735 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/LoanSchedulePeriodData.java
@@ -423,4 +423,8 @@ public final class LoanSchedulePeriodData {
public BigDecimal getTotalOverdue() {
return defaultToZeroIfNull(this.totalOverdue);
}
+
+ public BigDecimal totalOutstandingForPeriod() {
+ return defaultToZeroIfNull(this.totalOutstandingForPeriod);
+ }
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformServiceImpl.java
index 50ee62e35..65fd525b2 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformServiceImpl.java
@@ -24,6 +24,7 @@ import java.util.Collection;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.Cacheable;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
@@ -41,6 +42,7 @@ public class PaymentTypeReadPlatformServiceImpl implements PaymentTypeReadPlatfo
}
@Override
+ @Cacheable(value = "payment_types", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
public Collection<PaymentTypeData> retrieveAllPaymentTypes() {
// TODO Auto-generated method stub
this.context.authenticatedUser();
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeWriteServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeWriteServiceImpl.java
index 03955093d..0fcf4f26c 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeWriteServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeWriteServiceImpl.java
@@ -29,6 +29,7 @@ import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepository;
import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.annotation.CacheEvict;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.orm.jpa.JpaSystemException;
import org.springframework.stereotype.Service;
@@ -50,6 +51,7 @@ public class PaymentTypeWriteServiceImpl implements PaymentTypeWriteService {
}
@Override
+ @CacheEvict(value = "payment_types", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
public CommandProcessingResult createPaymentType(JsonCommand command) {
this.fromApiJsonDeserializer.validateForCreate(command.json());
String name = command.stringValueOfParameterNamed(PaymentTypeApiResourceConstants.NAME);
@@ -63,6 +65,7 @@ public class PaymentTypeWriteServiceImpl implements PaymentTypeWriteService {
}
@Override
+ @CacheEvict(value = "payment_types", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
public CommandProcessingResult updatePaymentType(Long paymentTypeId, JsonCommand command) {
this.fromApiJsonDeserializer.validateForUpdate(command.json());
@@ -77,6 +80,7 @@ public class PaymentTypeWriteServiceImpl implements PaymentTypeWriteService {
}
@Override
+ @CacheEvict(value = "payment_types", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
public CommandProcessingResult deletePaymentType(Long paymentTypeId) {
final PaymentType paymentType = this.repositoryWrapper.findOneWithNotFoundDetection(paymentTypeId);
try {
diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/core/exceptionmapper/InvalidInstanceTypeMethodExceptionMapperTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/core/exceptionmapper/InvalidInstanceTypeMethodExceptionMapperTest.java
new file mode 100644
index 000000000..f52ee35d4
--- /dev/null
+++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/core/exceptionmapper/InvalidInstanceTypeMethodExceptionMapperTest.java
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.core.exceptionmapper;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import javax.ws.rs.HttpMethod;
+import javax.ws.rs.core.Response;
+import org.apache.fineract.infrastructure.core.data.ApiGlobalErrorResponse;
+import org.apache.fineract.infrastructure.security.exception.InvalidInstanceTypeMethodException;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests for {@link InvalidInstanceTypeMethodExceptionMapper}.
+ */
+public class InvalidInstanceTypeMethodExceptionMapperTest {
+
+ /**
+ * Tests the {@link InvalidInstanceTypeMethodExceptionMapper#toResponse(InvalidInstanceTypeMethodException)} method
+ * for successful scenario.
+ */
+ @Test
+ public void testToResponse() {
+ final InvalidInstanceTypeMethodExceptionMapper exceptionMapper = new InvalidInstanceTypeMethodExceptionMapper();
+ final InvalidInstanceTypeMethodException exception = new InvalidInstanceTypeMethodException(HttpMethod.POST);
+
+ final Response response = exceptionMapper.toResponse(exception);
+
+ assertNotNull(response);
+ assertEquals(Response.Status.METHOD_NOT_ALLOWED.getStatusCode(), response.getStatus());
+ assertNotNull(response.getEntity());
+ final ApiGlobalErrorResponse errorResponse = (ApiGlobalErrorResponse) response.getEntity();
+ assertEquals(String.valueOf(Response.Status.METHOD_NOT_ALLOWED.getReasonPhrase()), errorResponse.getHttpStatusCode());
+ assertEquals("Invalid instance type called in api request for the method POST", errorResponse.getDeveloperMessage());
+ assertEquals("error.msg.invalid.instance.type", errorResponse.getUserMessageGlobalisationCode());
+ assertEquals("Invalid method POST used with request to this instance type.", errorResponse.getDefaultUserMessage());
+ }
+
+}
diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/instancemode/filter/FineractInstanceModeApiFilterTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/instancemode/filter/FineractInstanceModeApiFilterTest.java
index 86d14d8f7..a071dc5c8 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/instancemode/filter/FineractInstanceModeApiFilterTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/instancemode/filter/FineractInstanceModeApiFilterTest.java
@@ -244,4 +244,44 @@ class FineractInstanceModeApiFilterTest {
// then
verify(filterChain).doFilter(request, response);
}
+
+ @Test
+ void testDoFilterInternal_ShouldLetBatchesApisThrough_WhenFineractIsInReadMode() throws ServletException, IOException {
+ // given
+ FineractProperties.FineractModeProperties modeProperties = InstanceModeMock.createModeProps(true, false, false, false);
+ given(fineractProperties.getMode()).willReturn(modeProperties);
+ given(request.getMethod()).willReturn(HttpMethod.POST.name());
+ given(request.getPathInfo()).willReturn("/batches");
+ // when
+ underTest.doFilterInternal(request, response, filterChain);
+ // then
+ verify(filterChain).doFilter(request, response);
+ }
+
+ @Test
+ void testDoFilterInternal_ShouldLetBatchesApisThrough_WhenFineractIsInWriteMode() throws ServletException, IOException {
+ // given
+ FineractProperties.FineractModeProperties modeProperties = InstanceModeMock.createModeProps(false, true, false, false);
+ given(fineractProperties.getMode()).willReturn(modeProperties);
+ given(request.getMethod()).willReturn(HttpMethod.POST.name());
+ given(request.getPathInfo()).willReturn("/batches");
+ // when
+ underTest.doFilterInternal(request, response, filterChain);
+ // then
+ verify(filterChain).doFilter(request, response);
+ }
+
+ @Test
+ void testDoFilterInternal_ShouldLetBatchesApisThrough_WhenFineractIsInBatchMode() throws ServletException, IOException {
+ // given
+ FineractProperties.FineractModeProperties modeProperties = InstanceModeMock.createModeProps(false, false, true, true);
+ given(fineractProperties.getMode()).willReturn(modeProperties);
+ given(request.getMethod()).willReturn(HttpMethod.POST.name());
+ given(request.getPathInfo()).willReturn("/batches");
+ // when
+ underTest.doFilterInternal(request, response, filterChain);
+ // then
+ verify(filterChain).doFilter(request, response);
+ }
+
}