From 6348140716d3b7f3903483d1a383f8e9857ae04b Mon Sep 17 00:00:00 2001 From: Adam Saghy Date: Thu, 9 Jan 2025 20:13:17 +0100 Subject: [PATCH] FINERACT-2081: Fix "resolve dependent variable" logic of Batch request resolver --- .../batch/service/ResolutionHelper.java | 43 ++++--- .../batch/service/ResolutionHelperTest.java | 107 ++++++++++++++++++ 2 files changed, 127 insertions(+), 23 deletions(-) create mode 100644 fineract-core/src/test/java/org/apache/fineract/batch/service/ResolutionHelperTest.java diff --git a/fineract-core/src/main/java/org/apache/fineract/batch/service/ResolutionHelper.java b/fineract-core/src/main/java/org/apache/fineract/batch/service/ResolutionHelper.java index f27e0afbfa3..332041706e9 100644 --- a/fineract-core/src/main/java/org/apache/fineract/batch/service/ResolutionHelper.java +++ b/fineract-core/src/main/java/org/apache/fineract/batch/service/ResolutionHelper.java @@ -22,6 +22,7 @@ import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.ReadContext; import java.util.ArrayList; @@ -133,7 +134,7 @@ public BatchRequest resolveRequest(final BatchRequest request, final BatchRespon // parameter for (Map.Entry element : jsonRequestBody.entrySet()) { final String key = element.getKey(); - final JsonElement value = resolveDependentVariables(element, responseCtx); + final JsonElement value = resolveDependentVariables(element.getValue(), responseCtx); jsonResultBody.add(key, value); } // Set the body after dependency resolution @@ -162,20 +163,19 @@ public BatchRequest resolveRequest(final BatchRequest request, final BatchRespon return request; } - private JsonElement resolveDependentVariables(final Map.Entry entryElement, final ReadContext responseCtx) { + private JsonElement resolveDependentVariables(final JsonElement jsonElement, final ReadContext responseCtx) { JsonElement value; - final JsonElement element = entryElement.getValue(); - if (element.isJsonObject()) { - final JsonObject jsObject = element.getAsJsonObject(); + if (jsonElement.isJsonObject()) { + final JsonObject jsObject = jsonElement.getAsJsonObject(); value = processJsonObject(jsObject, responseCtx); - } else if (element.isJsonArray()) { - final JsonArray jsElementArray = element.getAsJsonArray(); + } else if (jsonElement.isJsonArray()) { + final JsonArray jsElementArray = jsonElement.getAsJsonArray(); value = processJsonArray(jsElementArray, responseCtx); - } else if (element.isJsonNull()) { + } else if (jsonElement.isJsonNull()) { // No further processing of null values - value = element; + value = jsonElement; } else { - value = resolveDependentVariable(element, responseCtx); + value = processJsonPrimitive(jsonElement, responseCtx); } return value; } @@ -183,9 +183,7 @@ private JsonElement resolveDependentVariables(final Map.Entry element : jsObject.entrySet()) { - final String key = element.getKey(); - final JsonElement value = resolveDependentVariable(element.getValue(), responseCtx); - valueObj.add(key, value); + valueObj.add(element.getKey(), resolveDependentVariables(element.getValue(), responseCtx)); } return valueObj; } @@ -193,21 +191,20 @@ private JsonElement processJsonObject(final JsonObject jsObject, final ReadConte private JsonArray processJsonArray(final JsonArray elementArray, final ReadContext responseCtx) { JsonArray valueArr = new JsonArray(); for (JsonElement element : elementArray) { - if (element.isJsonObject()) { - final JsonObject jsObject = element.getAsJsonObject(); - valueArr.add(processJsonObject(jsObject, responseCtx)); - } + valueArr.add(resolveDependentVariables(element, responseCtx)); } return valueArr; } - private JsonElement resolveDependentVariable(final JsonElement element, final ReadContext responseCtx) { + private JsonElement processJsonPrimitive(final JsonElement element, final ReadContext responseCtx) { JsonElement value = element; - String paramVal = element.getAsString(); - if (paramVal.contains("$.")) { - // Get the value of the parameter from parent response - final String resParamValue = responseCtx.read(paramVal).toString(); - value = this.fromJsonHelper.parse(resParamValue); + if (element instanceof JsonPrimitive) { + String paramVal = element.getAsString(); + if (paramVal.contains("$.")) { + // Get the value of the parameter from parent response + final String resParamValue = responseCtx.read(paramVal).toString(); + value = this.fromJsonHelper.parse(resParamValue); + } } return value; } diff --git a/fineract-core/src/test/java/org/apache/fineract/batch/service/ResolutionHelperTest.java b/fineract-core/src/test/java/org/apache/fineract/batch/service/ResolutionHelperTest.java new file mode 100644 index 00000000000..1a735b9d6f2 --- /dev/null +++ b/fineract-core/src/test/java/org/apache/fineract/batch/service/ResolutionHelperTest.java @@ -0,0 +1,107 @@ +/** + * 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.service; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.jayway.jsonpath.ReadContext; +import java.util.ArrayList; +import java.util.List; +import org.apache.fineract.batch.domain.BatchRequest; +import org.apache.fineract.batch.domain.BatchResponse; +import org.apache.fineract.batch.exception.BatchReferenceInvalidException; +import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; +import org.junit.jupiter.api.Test; + +public class ResolutionHelperTest { + + private final FromJsonHelper fromJsonHelper = new FromJsonHelper(); + private final ResolutionHelper resolutionHelper = new ResolutionHelper(fromJsonHelper); + + @Test + void testBuildNodesTreeWithValidRequests() { + List requests = new ArrayList<>(); + BatchRequest firstRequest = new BatchRequest(); + firstRequest.setRequestId(1L); + firstRequest.setBody("{\"key1\": \"value1\", \"key2\": 12, \"key3\": null, \"key4\": false}"); + firstRequest.setRelativeUrl("/resource/id"); + requests.add(firstRequest); + BatchRequest secondRequest = new BatchRequest(); + secondRequest.setRequestId(2L); + secondRequest.setReference(1L); + secondRequest.setRelativeUrl("/resource/id"); + secondRequest.setBody("{\"key1\": { \"subKey\": false }, \"key2\": [1,2,3]}"); + requests.add(secondRequest); + BatchRequest thirdRequest = new BatchRequest(); + thirdRequest.setRequestId(3L); + thirdRequest.setReference(2L); + requests.add(thirdRequest); + List nodes = resolutionHelper.buildNodesTree(requests); + assertEquals(1, nodes.size()); + assertEquals(1, nodes.get(0).getRequest().getRequestId()); + assertEquals("{\"key1\": \"value1\", \"key2\": 12, \"key3\": null, \"key4\": false}", nodes.get(0).getRequest().getBody()); + assertEquals(1, nodes.get(0).getChildNodes().size()); + assertEquals(2, nodes.get(0).getChildNodes().get(0).getRequest().getRequestId()); + assertEquals("{\"key1\": { \"subKey\": false }, \"key2\": [1,2,3]}", nodes.get(0).getChildNodes().get(0).getRequest().getBody()); + } + + @Test + void testBuildNodesTreeWithInvalidReference() { + List requests = new ArrayList<>(); + BatchRequest invalidRequest = new BatchRequest(); + invalidRequest.setRequestId(2L); + // Not existing reference + invalidRequest.setReference(1L); + requests.add(invalidRequest); + assertThrows(BatchReferenceInvalidException.class, () -> resolutionHelper.buildNodesTree(requests)); + } + + @Test + void testResolveRequestWithValidDependenciesResolveParameterFromResponse() { + BatchRequest batchRequest = new BatchRequest(); + batchRequest.setBody("{\"key1\": \"$.value1\"}"); + batchRequest.setRelativeUrl("/resource/$.id?key=value"); + BatchResponse parentResponse = new BatchResponse(); + parentResponse.setBody("{\"value1\": \"resolvedValue\", \"id\": \"123\"}"); + ReadContext readContext = mock(ReadContext.class); + when(readContext.read("$.value1")).thenReturn("resolvedValue"); + when(readContext.read("$.id")).thenReturn("123"); + BatchRequest resolvedRequest = resolutionHelper.resolveRequest(batchRequest, parentResponse); + assertNotNull(resolvedRequest); + assertEquals("{\"key1\":\"resolvedValue\"}", resolvedRequest.getBody()); + assertEquals("/resource/123?key=value", resolvedRequest.getRelativeUrl()); + } + + @Test + void testResolveRequestWithNoDependencies() { + BatchRequest batchRequest = new BatchRequest(); + batchRequest.setBody("{\"key1\": \"value1\",\"key2\": { \"subKey\": false }, \"key3\": [1,2,3], \"key4\": null}"); + batchRequest.setRelativeUrl("/resource/id"); + BatchResponse parentResponse = new BatchResponse(); + parentResponse.setBody("{\"value2\": \"not used\"}"); + BatchRequest resolvedRequest = resolutionHelper.resolveRequest(batchRequest, parentResponse); + assertNotNull(resolvedRequest); + assertEquals("{\"key1\":\"value1\",\"key2\":{\"subKey\":false},\"key3\":[1,2,3],\"key4\":null}", resolvedRequest.getBody()); + assertEquals("/resource/id", resolvedRequest.getRelativeUrl()); + } +}