Skip to content

Commit 485287a

Browse files
committed
Added support for headers in JSON-Batch requets
1 parent 8c66983 commit 485287a

7 files changed

Lines changed: 113 additions & 69 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Development version 2.3.0
44

55
**New Features**
6+
* Added support for headers in JSON-Batch requets, enabling JSON-Patch requests in JSON-Batch requests.
67

78

89
## Release version 2.2.0

FROST-Server.Tests/src/test/java/de/fraunhofer/iosb/ilt/statests/c02cud/JsonPatchTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ private static void cleanup() throws ServiceFailureException {
9191
* This method is run after all the tests of this class is run and clean the
9292
* database.
9393
*
94-
* @throws de.fraunhofer.iosb.ilt.sta.ServiceFailureException
94+
* @throws de.fraunhofer.iosb.ilt.frostclient.exception.ServiceFailureException
9595
*/
9696
@AfterAll
9797
public static void deleteEverything() throws ServiceFailureException {

FROST-Server.Tests/src/test/java/de/fraunhofer/iosb/ilt/statests/c04batch/BatchTests.java

Lines changed: 98 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.fasterxml.jackson.databind.ObjectMapper;
2727
import de.fraunhofer.iosb.ilt.frostclient.exception.ServiceFailureException;
2828
import de.fraunhofer.iosb.ilt.frostclient.model.Entity;
29+
import de.fraunhofer.iosb.ilt.frostclient.models.SensorThingsSensingV11;
2930
import de.fraunhofer.iosb.ilt.frostclient.models.ext.MapValue;
3031
import de.fraunhofer.iosb.ilt.frostclient.utils.CollectionsHelper;
3132
import de.fraunhofer.iosb.ilt.statests.AbstractTestClass;
@@ -42,6 +43,7 @@
4243
import java.util.HashMap;
4344
import java.util.List;
4445
import java.util.Map;
46+
import org.apache.commons.lang3.StringUtils;
4547
import org.json.JSONException;
4648
import org.junit.jupiter.api.AfterAll;
4749
import org.junit.jupiter.api.MethodOrderer;
@@ -403,43 +405,66 @@ void test05BatchRequestWithResourcePathRelativeToBatchRequest() {
403405
}
404406

405407
@Test
406-
void test06JsonBatchRequest() {
408+
void test06JsonBatchRequest() throws ServiceFailureException {
407409
LOGGER.info(" test06JsonBatchRequest");
408-
String response = postBatch(null, "{\"requests\":[{"
409-
+ "\"id\": \"0\","
410-
+ "\"method\": \"get\","
411-
+ "\"url\": \"Things(" + formatKeyValuesForUrl(THINGS.get(0))
412-
+ ")?$select=name\""
413-
+ "},{"
414-
+ "\"id\": \"1\","
415-
+ "\"atomicityGroup\": \"group1\","
416-
+ "\"method\": \"post\","
417-
+ "\"url\": \"Things\","
418-
+ "\"body\": {\"name\":\"New\",\"description\":\"Thing\"}"
419-
+ "},{"
420-
+ "\"id\": \"2\","
421-
+ "\"atomicityGroup\": \"group1\","
422-
+ "\"method\": \"patch\","
423-
+ "\"url\": \"Things(" + formatKeyValuesForUrl(THINGS.get(0)) + ")\","
424-
+ "\"body\": {\"name\":\"Json Patched\"}"
425-
+ "},{"
426-
+ "\"id\": \"3\","
427-
+ "\"method\": \"get\","
428-
+ "\"url\": \"Things(null)\""
429-
+ "}]}");
410+
String request = """
411+
{
412+
"requests":[{
413+
"id": "0",
414+
"method": "get",
415+
"url": "Things($thing0)?$select=name"
416+
},{
417+
"id": "1",
418+
"atomicityGroup": "group1",
419+
"method": "post",
420+
"url": "Things",
421+
"body": {"name":"New","description":"Thing"}
422+
},{
423+
"id": "2",
424+
"atomicityGroup": "group1",
425+
"method": "patch",
426+
"url": "Things($thing0)",
427+
"body": {"name":"Json Patched"}
428+
},{
429+
"id": "3",
430+
"atomicityGroup": "group1",
431+
"method": "patch",
432+
"headers": {
433+
"content-type": "application/json-patch+json"
434+
},
435+
"url": "Things($thing1)",
436+
"body": [
437+
{ "op": "replace", "path": "/name", "value": "Thing 1 Updated" },
438+
{ "op": "add", "path": "/properties/new", "value": "Changes" }
439+
]
440+
},{
441+
"id": "4",
442+
"method": "get",
443+
"url": "Things(null)"
444+
}]}""";
445+
request = StringUtils.replace(request, "$thing0", formatKeyValuesForUrl(THINGS.get(0)));
446+
request = StringUtils.replace(request, "$thing1", formatKeyValuesForUrl(THINGS.get(1)));
447+
String response = postBatch(null, request);
430448
String thingId = getLastestEntityIdForPath(EntityType.THING);
431449

432450
try {
433-
BatchResponseJson expected = mapper.readValue("{\"responses\":["
434-
+ "{\"id\":\"0\",\"status\":200,\"body\":{\"name\":\"Patched\"}},"
435-
+ "{\"id\":\"1\",\"status\":201,\"location\":\"" + serverSettings.getServiceUrl(version) + "/Things("
436-
+ thingId + ")\"},"
437-
+ "{\"id\":\"2\",\"status\":200},"
438-
+ "{\"id\":\"3\",\"status\":404,\"body\":{\"code\":404,\"type\":\"error\",\"message\":\"Not a valid id: Path is not valid.\"}}"
439-
+ "]}",
440-
BatchResponseJson.class);
451+
String expResponse = """
452+
{"responses":[
453+
{"id":"0","status":200,"body":{"name":"Patched"}},
454+
{"id":"1","status":201,"location":"$serviceUrl/Things($newThingId)"},
455+
{"id":"2","status":200},
456+
{"id":"3","status":200},
457+
{"id":"4","status":404,"body":{"code":404,"type":"error","message":"Not a valid id: Path is not valid."}}
458+
]}""";
459+
expResponse = StringUtils.replace(expResponse, "$serviceUrl", serverSettings.getServiceUrl(version));
460+
expResponse = StringUtils.replace(expResponse, "$newThingId", thingId);
461+
BatchResponseJson expected = mapper.readValue(expResponse, BatchResponseJson.class);
441462
BatchResponseJson actual = mapper.readValue(response, BatchResponseJson.class);
442463
assertEquals(expected, actual, "Response not as expected.");
464+
465+
Entity updatedThing1 = service.service.dao(sMdl.etThing).find(THINGS.get(1).getPrimaryKeyValues());
466+
assertEquals("Thing 1 Updated", updatedThing1.getProperty(SensorThingsSensingV11.EP_NAME));
467+
assertEquals("Changes", updatedThing1.getProperty(SensorThingsSensingV11.EP_PROPERTIES).get("new"));
443468
} catch (JsonProcessingException ex) {
444469
fail("Failed to parse response as json.");
445470
}
@@ -449,39 +474,48 @@ void test06JsonBatchRequest() {
449474
@Test
450475
void test07JsonBatchRequestWithChangeSetReferencingNewEntities() {
451476
LOGGER.info(" test07JsonBatchRequestWithChangeSetReferencingNewEntities");
452-
String post1 = "{\r\n"
453-
+ " \"name\": \"DS18B20\",\r\n"
454-
+ " \"description\": \"DS18B20 is an air temperature sensor\",\r\n"
455-
+ " \"encodingType\": \"application/pdf\",\r\n"
456-
+ " \"metadata\": \"http://datasheets.maxim-ic.com/en/ds/DS18B20.pdf\"\r\n"
457-
+ "}";
458-
String post2 = "{\r\n"
459-
+ " \"name\": \"Temperature Thing 5\",\r\n"
460-
+ " \"description\": \"The temperature of thing 5\",\r\n"
461-
+ " \"unitOfMeasurement\": {\r\n"
462-
+ " \"name\": \"degree Celsius\",\r\n"
463-
+ " \"symbol\": \"°C\",\r\n"
464-
+ " \"definition\": \"http://unitsofmeasure.org/ucum.html#para-30\"\r\n"
465-
+ " },\n"
466-
+ " \"observationType\": \"http://www.opengis.net/def/observationType/OGCOM/2.0/OM_Measurement\",\r\n"
467-
+ " \"ObservedProperty\": {\"@iot.id\": " + Utils.quoteForJson(OBSERVED_PROPS.get(0).getPrimaryKeyValues()[0]) + "},\r\n"
468-
+ " \"Sensor\": {\"@iot.id\": \"$sensor1\"}\r\n"
469-
+ "}";
470-
String response = postBatch(null, "{\"requests\":[{"
471-
+ "\"id\": \"sensor1\","
472-
+ "\"atomicityGroup\": \"group1\","
473-
+ "\"method\": \"post\","
474-
+ "\"url\": \"Sensors\","
475-
+ "\"body\":"
476-
+ post1
477-
+ "},{"
478-
+ "\"id\": \"any\","
479-
+ "\"atomicityGroup\": \"group1\","
480-
+ "\"method\": \"post\","
481-
+ "\"url\": \"Things(" + formatKeyValuesForUrl(THINGS.get(0)) + ")/Datastreams\","
482-
+ "\"body\":"
483-
+ post2
484-
+ "}]}");
477+
String post1 = """
478+
{
479+
"name": "DS18B20",
480+
"description": "DS18B20 is an air temperature sensor",
481+
"encodingType": "application/pdf",
482+
"metadata": "http://datasheets.maxim-ic.com/en/ds/DS18B20.pdf"
483+
}""";
484+
String post2 = """
485+
{\r
486+
"name": "Temperature Thing 5",
487+
"description": "The temperature of thing 5",
488+
"unitOfMeasurement": {
489+
"name": "degree Celsius",
490+
"symbol": "\u00b0C",
491+
"definition": "http://unitsofmeasure.org/ucum.html#para-30"
492+
},
493+
"observationType": "http://www.opengis.net/def/observationType/OGCOM/2.0/OM_Measurement",
494+
"ObservedProperty": {"@iot.id": $ObservedProperty0},
495+
"Sensor": {"@iot.id": "$sensor1"}
496+
}""";
497+
post2 = StringUtils.replace(post2, "$ObservedProperty0", Utils.quoteForJson(OBSERVED_PROPS.get(0).getPrimaryKeyValues()[0]));
498+
String request = """
499+
{
500+
"requests":[{
501+
"id": "sensor1",
502+
"atomicityGroup": "group1",
503+
"method": "post",
504+
"url": "Sensors",
505+
"body":$post1
506+
},{
507+
"id": "any",
508+
"atomicityGroup": "group1",
509+
"method": "post",
510+
"url": "Things($thing0)/Datastreams",
511+
"body": $post2
512+
}]
513+
}""";
514+
request = StringUtils.replace(request, "$post1", post1);
515+
request = StringUtils.replace(request, "$post2", post2);
516+
request = StringUtils.replace(request, "$thing0", formatKeyValuesForUrl(THINGS.get(0)));
517+
String response = postBatch(null, request);
518+
485519
String sensorId = getLastestEntityIdForPath(EntityType.SENSOR);
486520
String datastreamId = getLastestEntityIdForPath(EntityType.DATASTREAM);
487521

Plugins/BatchProcessing/src/main/java/de/fraunhofer/iosb/ilt/frostserver/plugin/batchprocessing/BatchProcessor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public Request processHttpRequest(Service service, Request httpRequest, boolean
7373
httpRequest.getMethod(),
7474
version,
7575
httpRequest.getPath(),
76-
httpRequest.getHttpHeaders().get(CONTENT_TYPE));
76+
httpRequest.getInnerHeaders().get(CONTENT_TYPE));
7777
boolean isCreate = RequestTypeUtils.CREATE.equals(type);
7878
UpdateMode updateMode;
7979
switch (version.urlPart) {
@@ -116,7 +116,7 @@ public Request processHttpRequest(Service service, Request httpRequest, boolean
116116
int statusCode = serviceResponse.getCode();
117117
httpResponse.setStatus(statusCode, "no text");
118118

119-
Map<String, String> headers = httpResponse.getHttpHeaders();
119+
Map<String, String> headers = httpResponse.getInnerHeaders();
120120
serviceResponse.getHeaders().entrySet().forEach(x -> headers.put(x.getKey(), x.getValue()));
121121

122122
String resultFormatted = serviceResponse.getWriter().toString();

Plugins/BatchProcessing/src/main/java/de/fraunhofer/iosb/ilt/frostserver/plugin/batchprocessing/batch/Request.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public void setContentIdValue(Id contentIdValue) {
145145
*
146146
* @return the headers of the individual batch request.
147147
*/
148-
public Map<String, String> getHttpHeaders() {
148+
public Map<String, String> getInnerHeaders() {
149149
return headersInner;
150150
}
151151

Plugins/BatchProcessing/src/main/java/de/fraunhofer/iosb/ilt/frostserver/plugin/batchprocessing/json/JsonBatch.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ public boolean parse(ServiceRequest serviceRequest) {
8989
if (req.has("body")) {
9090
request.addData(OBJECT_MAPPER.writeValueAsString(req.get("body")));
9191
}
92+
if (req.has("headers") && req.get("headers").isObject()) {
93+
JsonNode headers = req.get("headers");
94+
Map<String, String> innerHeaders = request.getInnerHeaders();
95+
Iterator<Map.Entry<String, JsonNode>> fields = headers.fields();
96+
while (fields.hasNext()) {
97+
Map.Entry<String, JsonNode> next = fields.next();
98+
innerHeaders.put(next.getKey(), next.getValue().asText());
99+
}
100+
}
92101
}
93102
}
94103
return true;

Plugins/BatchProcessing/src/main/java/de/fraunhofer/iosb/ilt/frostserver/plugin/batchprocessing/json/JsonRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public String getContent(boolean allHeaders) {
6969
try {
7070
return JsonWriter.writeObject(
7171
new ODataResponse(getContentId(), status, data.length() > 0 ? data.toString() : null,
72-
getHttpHeaders().get("Location")));
72+
getInnerHeaders().get("Location")));
7373
} catch (IOException ex) {
7474
throw new IllegalStateException("Failed to generate JSON.", ex);
7575
}

0 commit comments

Comments
 (0)