Batch API read optimization + minor changes
This commit is contained in:
committed by
Arnold Gálovics
parent
130d9c0c3d
commit
a0749ffe19
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
@@ -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 {
|
||||
|
||||
+46
@@ -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();
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -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;
|
||||
|
||||
|
||||
+5
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -5913,6 +5913,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
||||
return this.accountNumber;
|
||||
}
|
||||
|
||||
public String getExternalId() {
|
||||
return this.externalId;
|
||||
}
|
||||
|
||||
public Client getClient() {
|
||||
return this.client;
|
||||
}
|
||||
|
||||
+4
@@ -423,4 +423,8 @@ public final class LoanSchedulePeriodData {
|
||||
public BigDecimal getTotalOverdue() {
|
||||
return defaultToZeroIfNull(this.totalOverdue);
|
||||
}
|
||||
|
||||
public BigDecimal totalOutstandingForPeriod() {
|
||||
return defaultToZeroIfNull(this.totalOutstandingForPeriod);
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -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();
|
||||
|
||||
+4
@@ -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 {
|
||||
|
||||
+56
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
+40
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user