From 5fa2295bd0923916d9553717f2936d422fd04fef Mon Sep 17 00:00:00 2001 From: Peter Bagrij Date: Thu, 28 Mar 2024 14:11:52 +0100 Subject: [PATCH] FINERACT-2057: JSON deserialization backward compatibility --- .../jobs/filter/LoanCOBFilterHelper.java | 12 +- .../jobs/filter/LoanCOBFilterHelperTest.java | 128 ++++++++++++++++++ 2 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBFilterHelperTest.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBFilterHelper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBFilterHelper.java index 6225d7f9c96..92b282208c7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBFilterHelper.java +++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBFilterHelper.java @@ -21,6 +21,7 @@ import static org.apache.fineract.batch.command.CommandStrategyUtils.isRelativeUrlVersioned; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.json.JsonReadFeature; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -54,6 +55,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; +import org.springframework.beans.factory.InitializingBean; import org.springframework.context.annotation.Conditional; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Component; @@ -61,7 +63,7 @@ @RequiredArgsConstructor @Component @Conditional(LoanCOBEnabledCondition.class) -public class LoanCOBFilterHelper { +public class LoanCOBFilterHelper implements InitializingBean { private final GLIMAccountInfoRepository glimAccountInfoRepository; private final LoanAccountLockService loanAccountLockService; @@ -72,7 +74,7 @@ public class LoanCOBFilterHelper { private final RetrieveLoanIdService retrieveLoanIdService; private final LoanRescheduleRequestRepository loanRescheduleRequestRepository; - private final ObjectMapper objectMapper; + private final ObjectMapper objectMapper = new ObjectMapper(); private static final List HTTP_METHODS = List.of(HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE); @@ -259,4 +261,10 @@ private List getLoanIdList(String pathInfo) { public void executeInlineCob(List loanIds) { inlineLoanCOBExecutorService.execute(loanIds, JOB_NAME); } + + @Override + public void afterPropertiesSet() throws Exception { + objectMapper.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true); + } + } diff --git a/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBFilterHelperTest.java b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBFilterHelperTest.java new file mode 100644 index 00000000000..edadf4dcb49 --- /dev/null +++ b/fineract-provider/src/test/java/org/apache/fineract/infrastructure/jobs/filter/LoanCOBFilterHelperTest.java @@ -0,0 +1,128 @@ +/** + * 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.jobs.filter; + +import jakarta.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.List; +import org.apache.fineract.cob.loan.RetrieveLoanIdService; +import org.apache.fineract.cob.service.InlineLoanCOBExecutorServiceImpl; +import org.apache.fineract.cob.service.LoanAccountLockService; +import org.apache.fineract.infrastructure.core.config.FineractProperties; +import org.apache.fineract.infrastructure.core.http.BodyCachingHttpServletRequestWrapper; +import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; +import org.apache.fineract.portfolio.loanaccount.domain.GLIMAccountInfoRepository; +import org.apache.fineract.portfolio.loanaccount.domain.LoanRepository; +import org.apache.fineract.portfolio.loanaccount.rescheduleloan.domain.LoanRescheduleRequestRepository; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class LoanCOBFilterHelperTest { + + @Mock + private GLIMAccountInfoRepository glimAccountInfoRepository; + @Mock + private LoanAccountLockService loanAccountLockService; + @Mock + private PlatformSecurityContext context; + @Mock + private InlineLoanCOBExecutorServiceImpl inlineLoanCOBExecutorService; + @Mock + private LoanRepository loanRepository; + @Mock + private FineractProperties fineractProperties; + @Mock + private RetrieveLoanIdService retrieveLoanIdService; + + @Mock + private LoanRescheduleRequestRepository loanRescheduleRequestRepository; + + @InjectMocks + private LoanCOBFilterHelper helper; + + @BeforeEach + public void initLoanCOBFilterHelper() throws Exception { + helper.afterPropertiesSet(); + } + + @Test + public void testCOBFilterUnescapedChars() throws IOException { + String json = """ + [ + { + "requestId": 1, + "relativeUrl": "clients", + "method": "POST", + "headers": [ + { + "name": "Idempotency-Key", + "value": "{{temp_idempotencyKey1}}" + }, + { + "name": "Content-Type", + "value": "application/json" + }, + { + "name": "Fineract-Platform-TenantId", + "value": "{{tenantId}}" + }, + { + "name": "Authorization", + "value": "Basic bWlmb3M6cGFzc3dvcmQ=" + } + ], + "body":"{ + \\"officeId\\": 1, + \\"legalFormId\\": 2, + \\"isStaff\\": false, + \\"active\\": true, + \\"fullname\\": \\"Current Company 1\\", + \\"clientNonPersonDetails\\": { + \\"constitutionId\\": 1, + \\"incorpValidityTillDate\\": \\"\\", + \\"incorpNumber\\": \\"\\", + \\"mainBusinessLineId\\": \\"\\", + \\"remarks\\": \\"\\" + }, + \\"activationDate\\": \\"01 January 2022\\", + \\"familyMembers\\": [], + \\"dateFormat\\": \\"dd MMMM yyyy\\", + \\"locale\\": \\"en\\" + }"} + ] + """; + + HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); + Mockito.when(httpServletRequest.getPathInfo()).thenReturn("/v1/batches/endpoint"); + BodyCachingHttpServletRequestWrapper.CachedBodyServletInputStream inputStream = new BodyCachingHttpServletRequestWrapper.CachedBodyServletInputStream( + json.getBytes(Charset.forName("UTF-8"))); + Mockito.when(httpServletRequest.getInputStream()).thenReturn(inputStream); + List loanIds = helper.calculateRelevantLoanIds(httpServletRequest); + Assertions.assertEquals(0, loanIds.size()); + } + +}