FINERACT-2333: Batch API - Support date format: from array to string

This commit is contained in:
Adam Saghy
2025-07-28 19:06:48 +02:00
parent 52be53778d
commit ab8bb930ec
2 changed files with 110 additions and 11 deletions
@@ -25,6 +25,8 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive; import com.google.gson.JsonPrimitive;
import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.ReadContext; import com.jayway.jsonpath.ReadContext;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -134,7 +136,7 @@ public class ResolutionHelper {
// parameter // parameter
for (Map.Entry<String, JsonElement> element : jsonRequestBody.entrySet()) { for (Map.Entry<String, JsonElement> element : jsonRequestBody.entrySet()) {
final String key = element.getKey(); final String key = element.getKey();
final JsonElement value = resolveDependentVariables(element.getValue(), responseCtx); final JsonElement value = resolveDependentVariables(element.getValue(), requestBody, responseCtx);
jsonResultBody.add(key, value); jsonResultBody.add(key, value);
} }
// Set the body after dependency resolution // Set the body after dependency resolution
@@ -163,44 +165,51 @@ public class ResolutionHelper {
return request; return request;
} }
private JsonElement resolveDependentVariables(final JsonElement jsonElement, final ReadContext responseCtx) { private JsonElement resolveDependentVariables(final JsonElement jsonElement, final String requestBody, final ReadContext responseCtx) {
JsonElement value; JsonElement value;
if (jsonElement.isJsonObject()) { if (jsonElement.isJsonObject()) {
final JsonObject jsObject = jsonElement.getAsJsonObject(); final JsonObject jsObject = jsonElement.getAsJsonObject();
value = processJsonObject(jsObject, responseCtx); value = processJsonObject(jsObject, requestBody, responseCtx);
} else if (jsonElement.isJsonArray()) { } else if (jsonElement.isJsonArray()) {
final JsonArray jsElementArray = jsonElement.getAsJsonArray(); final JsonArray jsElementArray = jsonElement.getAsJsonArray();
value = processJsonArray(jsElementArray, responseCtx); value = processJsonArray(jsElementArray, requestBody, responseCtx);
} else if (jsonElement.isJsonNull()) { } else if (jsonElement.isJsonNull()) {
// No further processing of null values // No further processing of null values
value = jsonElement; value = jsonElement;
} else { } else {
value = processJsonPrimitive(jsonElement, responseCtx); value = processJsonPrimitive(jsonElement, requestBody, responseCtx);
} }
return value; return value;
} }
private JsonElement processJsonObject(final JsonObject jsObject, final ReadContext responseCtx) { private JsonElement processJsonObject(final JsonObject jsObject, final String requestBody, final ReadContext responseCtx) {
JsonObject valueObj = new JsonObject(); JsonObject valueObj = new JsonObject();
for (Map.Entry<String, JsonElement> element : jsObject.entrySet()) { for (Map.Entry<String, JsonElement> element : jsObject.entrySet()) {
valueObj.add(element.getKey(), resolveDependentVariables(element.getValue(), responseCtx)); valueObj.add(element.getKey(), resolveDependentVariables(element.getValue(), requestBody, responseCtx));
} }
return valueObj; return valueObj;
} }
private JsonArray processJsonArray(final JsonArray elementArray, final ReadContext responseCtx) { private JsonArray processJsonArray(final JsonArray elementArray, final String requestBody, final ReadContext responseCtx) {
JsonArray valueArr = new JsonArray(); JsonArray valueArr = new JsonArray();
for (JsonElement element : elementArray) { for (JsonElement element : elementArray) {
valueArr.add(resolveDependentVariables(element, responseCtx)); valueArr.add(resolveDependentVariables(element, requestBody, responseCtx));
} }
return valueArr; return valueArr;
} }
private JsonElement processJsonPrimitive(final JsonElement element, final ReadContext responseCtx) { private JsonElement processJsonPrimitive(final JsonElement element, final String requestBody, final ReadContext responseCtx) {
JsonElement value = element; JsonElement value = element;
if (element instanceof JsonPrimitive) { if (element instanceof JsonPrimitive) {
String paramVal = element.getAsString(); String paramVal = element.getAsString();
if (paramVal.contains("$.")) { if (paramVal.contains("$[ARRAYDATE]")) {
String resolvableParamVal = paramVal.replace("$[ARRAYDATE]", "$");
final String resParamValue = responseCtx.read(resolvableParamVal).toString();
JsonArray date = (JsonArray) this.fromJsonHelper.parse(resParamValue);
String dateFormat = JsonPath.read(requestBody, "$.dateFormat");
return new JsonPrimitive(DateTimeFormatter.ofPattern(dateFormat)
.format(LocalDate.of(date.get(0).getAsInt(), date.get(1).getAsInt(), date.get(2).getAsInt())));
} else if (paramVal.contains("$.")) {
// Get the value of the parameter from parent response // Get the value of the parameter from parent response
final String resParamValue = responseCtx.read(paramVal).toString(); final String resParamValue = responseCtx.read(paramVal).toString();
value = this.fromJsonHelper.parse(resParamValue); value = this.fromJsonHelper.parse(resParamValue);
@@ -21,6 +21,7 @@ package org.apache.fineract.batch.service;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@@ -104,4 +105,93 @@ public class ResolutionHelperTest {
assertEquals("{\"key1\":\"value1\",\"key2\":{\"subKey\":false},\"key3\":[1,2,3],\"key4\":null}", resolvedRequest.getBody()); assertEquals("{\"key1\":\"value1\",\"key2\":{\"subKey\":false},\"key3\":[1,2,3],\"key4\":null}", resolvedRequest.getBody());
assertEquals("/resource/id", resolvedRequest.getRelativeUrl()); assertEquals("/resource/id", resolvedRequest.getRelativeUrl());
} }
@Test
void testResolveRequestWithArrayDateParameter() {
// Test resolving a JSON primitive with array date format
BatchRequest batchRequest = new BatchRequest();
batchRequest
.setBody("{\"dateFormat\":\"dd MMMM yyyy\",\"startDate\":\"$[ARRAYDATE].dates[0]\",\"endDate\":\"$[ARRAYDATE].dates[1]\"}");
batchRequest.setRelativeUrl("/resource/endpoint");
BatchResponse parentResponse = new BatchResponse();
parentResponse.setBody("{\"dates\":[[2023,5,15],[2023,6,15]]}");
// Mock the response context
ReadContext readContext = mock(ReadContext.class);
when(readContext.read("$.dates[0]")).thenReturn(new int[] { 2023, 5, 15 });
when(readContext.read("$.dates[1]")).thenReturn(new int[] { 2023, 6, 15 });
BatchRequest resolvedRequest = resolutionHelper.resolveRequest(batchRequest, parentResponse);
assertNotNull(resolvedRequest);
// Check for possible date formats
String body = resolvedRequest.getBody();
assertTrue(body.contains("\"startDate\":\"15 May 2023\"") || body.contains("\"startDate\":\"15 May, 2023\"")
|| body.contains("\"startDate\":\"15 May. 2023\"") || body.contains("\"startDate\":\"May 15, 2023\""));
assertTrue(body.contains("\"endDate\":\"15 June 2023\"") || body.contains("\"endDate\":\"15 June, 2023\"")
|| body.contains("\"endDate\":\"15 Jun. 2023\"") || body.contains("\"endDate\":\"June 15, 2023\""));
}
@Test
void testResolveRequestWithNestedJsonPrimitives() {
// Test resolving nested JSON primitives
BatchRequest batchRequest = new BatchRequest();
batchRequest.setBody("{\"nested\":{\"key1\":\"$.value1\",\"key2\":123,\"key3\":true,\"key4\":null}}");
batchRequest.setRelativeUrl("/resource/endpoint");
BatchResponse parentResponse = new BatchResponse();
parentResponse.setBody("{\"value1\":\"resolvedValue\"}");
// Mock the response context
ReadContext readContext = mock(ReadContext.class);
when(readContext.read("$.value1")).thenReturn("resolvedValue");
BatchRequest resolvedRequest = resolutionHelper.resolveRequest(batchRequest, parentResponse);
assertNotNull(resolvedRequest);
assertEquals("{\"nested\":{\"key1\":\"resolvedValue\",\"key2\":123,\"key3\":true,\"key4\":null}}", resolvedRequest.getBody());
}
@Test
void testResolveRequestWithPrimitiveTypes() {
// Test resolving different primitive types
BatchRequest batchRequest = new BatchRequest();
batchRequest.setBody("{\"string\":\"text\",\"number\":123.45,\"boolean\":true,\"nullValue\":null}");
batchRequest.setRelativeUrl("/resource/endpoint");
BatchResponse parentResponse = new BatchResponse();
parentResponse.setBody("{}"); // Empty response as we're not using any references
BatchRequest resolvedRequest = resolutionHelper.resolveRequest(batchRequest, parentResponse);
assertNotNull(resolvedRequest);
// The JSON might be reordered, so we need to check for existence of each key-value pair
String body = resolvedRequest.getBody();
assertTrue(body.contains("\"string\":\"text\""));
assertTrue(body.contains("\"number\":123.45"));
assertTrue(body.contains("\"boolean\":true"));
assertTrue(body.contains("\"nullValue\":null"));
}
@Test
void testResolveRequestWithComplexReferencePath() {
// Test resolving with complex reference paths
BatchRequest batchRequest = new BatchRequest();
batchRequest.setBody("{\"user\": {\"name\": \"$.userData.name\",\"age\": \"$.userData.age\"}}");
batchRequest.setRelativeUrl("/users/$.userData.id");
BatchResponse parentResponse = new BatchResponse();
parentResponse.setBody("{\"userData\":{\"id\": 42,\"name\": \"John\",\"age\": 30}}");
// Mock the response context
ReadContext readContext = mock(ReadContext.class);
when(readContext.read("$.userData.name")).thenReturn("John");
when(readContext.read("$.userData.age")).thenReturn(30);
when(readContext.read("$.userData.id")).thenReturn(42);
BatchRequest resolvedRequest = resolutionHelper.resolveRequest(batchRequest, parentResponse);
assertNotNull(resolvedRequest);
assertEquals("{\"user\":{\"name\":\"John\",\"age\":30}}", resolvedRequest.getBody());
assertEquals("/users/42", resolvedRequest.getRelativeUrl());
}
} }