Skip to content

Commit c426a8f

Browse files
committed
added post processors to write api
Signed-off-by: Jakub Amanowicz <[email protected]>
1 parent ba20b9e commit c426a8f

File tree

8 files changed

+99
-78
lines changed

8 files changed

+99
-78
lines changed

here-naksha-app-service/src/main/java/com/here/naksha/app/service/http/tasks/AbstractApiTask.java

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import static com.here.naksha.common.http.apis.ApiParamsConst.DEF_ADMIN_FEATURE_LIMIT;
2424
import static com.here.naksha.lib.core.util.storage.ResultHelper.readFeatureFromResult;
2525
import static com.here.naksha.lib.core.util.storage.ResultHelper.readFeaturesFromResult;
26-
import static com.here.naksha.lib.core.util.storage.ResultHelper.readFeaturesGroupedByOp;
2726
import static java.util.Collections.emptyList;
2827

2928
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -42,15 +41,18 @@
4241
import com.here.naksha.lib.core.models.storage.ContextXyzFeatureResult;
4342
import com.here.naksha.lib.core.models.storage.EExecutedOp;
4443
import com.here.naksha.lib.core.models.storage.ErrorResult;
44+
import com.here.naksha.lib.core.models.storage.ForwardCursor;
4545
import com.here.naksha.lib.core.models.storage.ReadFeatures;
4646
import com.here.naksha.lib.core.models.storage.Result;
4747
import com.here.naksha.lib.core.models.storage.WriteFeatures;
48+
import com.here.naksha.lib.core.models.storage.XyzFeatureCodec;
4849
import com.here.naksha.lib.core.storage.IReadSession;
4950
import com.here.naksha.lib.core.storage.IWriteSession;
5051
import com.here.naksha.lib.core.util.json.Json;
5152
import com.here.naksha.lib.core.view.ViewDeserialize;
5253
import io.vertx.ext.web.RoutingContext;
5354
import java.util.ArrayList;
55+
import java.util.HashMap;
5456
import java.util.List;
5557
import java.util.Map;
5658
import java.util.NoSuchElementException;
@@ -236,13 +238,17 @@ private static String getIterateHandleAsString(
236238
}
237239

238240
protected <R extends XyzFeature> @NotNull XyzResponse transformWriteResultToXyzCollectionResponse(
239-
final @Nullable Result wrResult, final @NotNull Class<R> type, final boolean isDeleteOperation) {
241+
final @Nullable Result wrResult,
242+
final @NotNull Class<R> type,
243+
final boolean isDeleteOperation,
244+
final @Nullable FeaturePostProcessor<R> featurePostProcessor) {
240245
final XyzResponse validatedErrorResponse = validateErrorResult(wrResult);
241246
if (validatedErrorResponse != null) {
242247
return validatedErrorResponse;
243248
} else {
244249
try {
245-
final Map<EExecutedOp, List<R>> featureMap = readFeaturesGroupedByOp(wrResult, type);
250+
final Map<EExecutedOp, List<R>> featureMap =
251+
postProcessedFeaturesByOp(wrResult, type, featurePostProcessor);
246252
final List<R> insertedFeatures = featureMap.get(EExecutedOp.CREATED);
247253
final List<R> updatedFeatures = featureMap.get(EExecutedOp.UPDATED);
248254
final List<R> deletedFeatures = featureMap.get(EExecutedOp.DELETED);
@@ -271,6 +277,45 @@ private static String getIterateHandleAsString(
271277
}
272278
}
273279

280+
/**
281+
* Helper method to fetch features from given Result and return a map of multiple lists grouped by {@link EExecutedOp} of features with
282+
* type T. Returned lists are limited with respect to supplied `limit` parameter.
283+
*
284+
* @param result the Result which is to be read
285+
* @param featureType the type of feature to be extracted from result
286+
* @param <R> type of feature
287+
* @return a map grouping the lists of features extracted from ReadResult
288+
*/
289+
private static <R extends XyzFeature> Map<EExecutedOp, List<R>> postProcessedFeaturesByOp(
290+
Result result, Class<R> featureType, FeaturePostProcessor<R> postProcessor) throws NoCursor {
291+
try (ForwardCursor<XyzFeature, XyzFeatureCodec> resultCursor = result.getXyzFeatureCursor()) {
292+
final List<R> insertedFeatures = new ArrayList<>();
293+
final List<R> updatedFeatures = new ArrayList<>();
294+
final List<R> deletedFeatures = new ArrayList<>();
295+
while (resultCursor.hasNext()) {
296+
if (!resultCursor.next()) {
297+
throw new RuntimeException("Unexpected invalid result");
298+
}
299+
R feature = featureType.cast(resultCursor.getFeature());
300+
if (postProcessor != null) {
301+
feature = postProcessor.postProcess(feature);
302+
}
303+
if (resultCursor.getOp().equals(EExecutedOp.CREATED)) {
304+
insertedFeatures.add(feature);
305+
} else if (resultCursor.getOp().equals(EExecutedOp.UPDATED)) {
306+
updatedFeatures.add(feature);
307+
} else if (resultCursor.getOp().equals(EExecutedOp.DELETED)) {
308+
deletedFeatures.add(feature);
309+
}
310+
}
311+
final Map<EExecutedOp, List<R>> features = new HashMap<>();
312+
features.put(EExecutedOp.CREATED, insertedFeatures);
313+
features.put(EExecutedOp.UPDATED, updatedFeatures);
314+
features.put(EExecutedOp.DELETED, deletedFeatures);
315+
return features;
316+
}
317+
}
318+
274319
protected Result executeReadRequestFromSpaceStorage(ReadFeatures readRequest) {
275320
try (final IReadSession reader = naksha().getSpaceStorage().newReadSession(context(), false)) {
276321
return reader.execute(readRequest);

here-naksha-app-service/src/main/java/com/here/naksha/app/service/http/tasks/ReadFeatureApiTask.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,7 @@ public ReadFeatureApiTask(
117117
* Initializes this task.
118118
*/
119119
@Override
120-
protected void init() {
121-
}
120+
protected void init() {}
122121

123122
/**
124123
* Execute this task.
@@ -482,7 +481,8 @@ protected void init() {
482481

483482
// Forward request to NH Space Storage reader instance
484483
final Result result = executeReadRequestFromSpaceStorage(rdRequest);
485-
final FeaturePostProcessor<XyzFeature> postProcessor = postProcessor(propPaths); // TODO CASL-1479: consider adding clip support
484+
final FeaturePostProcessor<XyzFeature> postProcessor =
485+
postProcessor(propPaths); // TODO CASL-1479: consider adding clip support
486486
// transform Result to Http FeatureCollection response, restricted by given feature limit
487487
return transformReadResultToXyzCollectionResponse(result, XyzFeature.class, 0, limit, null, postProcessor);
488488
}

here-naksha-app-service/src/main/java/com/here/naksha/app/service/http/tasks/WriteFeatureApiTask.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static com.here.naksha.app.service.http.apis.ApiParams.extractParamAsStringList;
2222
import static com.here.naksha.app.service.http.apis.ApiParams.queryParamsFromRequest;
2323
import static com.here.naksha.app.service.http.apis.ApiParams.validateFeatureId;
24+
import static com.here.naksha.app.service.http.tasks.processor.Mom10PostProcessor.MOM_10_POST_PROCESSOR;
2425
import static com.here.naksha.app.service.http.tasks.processor.Mom10PreProcessor.MOM_10_PRE_PROCESSOR;
2526
import static com.here.naksha.app.service.http.tasks.processor.SequentialPreProcessor.combine;
2627
import static com.here.naksha.common.http.apis.ApiParamsConst.ADD_TAGS;
@@ -170,7 +171,8 @@ protected void init() {}
170171
// Forward request to NH Space Storage writer instance
171172
try (Result wrResult = executeWriteRequestFromSpaceStorage(wrRequest)) {
172173
// transform WriteResult to Http FeatureCollection response
173-
return transformWriteResultToXyzCollectionResponse(wrResult, XyzFeature.class, false);
174+
return transformWriteResultToXyzCollectionResponse(
175+
wrResult, XyzFeature.class, false, MOM_10_POST_PROCESSOR);
174176
}
175177
}
176178

@@ -216,7 +218,7 @@ protected void init() {}
216218
// Forward request to NH Space Storage writer instance
217219
try (Result wrResult = executeWriteRequestFromSpaceStorage(wrRequest)) {
218220
// transform WriteResult to Http FeatureCollection response
219-
return transformWriteResultToXyzCollectionResponse(wrResult, XyzFeature.class, true);
221+
return transformWriteResultToXyzCollectionResponse(wrResult, XyzFeature.class, true, MOM_10_POST_PROCESSOR);
220222
}
221223
}
222224

@@ -387,7 +389,8 @@ private XyzResponse patchAndPreProcess(
387389
if (responseType.equals(HttpResponseType.FEATURE)) {
388390
return transformWriteResultToXyzFeatureResponse(wrResult, XyzFeature.class);
389391
}
390-
return transformWriteResultToXyzCollectionResponse(wrResult, XyzFeature.class, false);
392+
return transformWriteResultToXyzCollectionResponse(
393+
wrResult, XyzFeature.class, false, MOM_10_POST_PROCESSOR);
391394
}
392395
}
393396
}

here-naksha-app-service/src/test/java/com/here/naksha/app/common/assertions/ResponseAssertions.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,18 @@
2424
import static org.junit.jupiter.api.Assertions.assertNotNull;
2525
import static org.junit.jupiter.api.Assertions.assertNull;
2626
import static org.junit.jupiter.api.Assertions.assertTrue;
27+
import static org.junit.jupiter.api.Assertions.fail;
2728

2829
import com.here.naksha.app.common.TestUtil;
2930
import com.here.naksha.app.service.models.FeatureCollectionRequest;
3031
import com.here.naksha.lib.core.models.geojson.implementation.XyzFeature;
3132
import com.here.naksha.lib.core.models.geojson.implementation.XyzFeatureCollection;
3233
import com.here.naksha.lib.core.models.geojson.implementation.XyzReference;
34+
import com.here.naksha.lib.core.util.json.JsonObject;
3335
import java.net.http.HttpResponse;
36+
import java.util.Arrays;
3437
import java.util.List;
38+
import java.util.Map;
3539
import java.util.Optional;
3640
import org.jetbrains.annotations.NotNull;
3741
import org.jetbrains.annotations.Nullable;
@@ -108,6 +112,31 @@ public ResponseAssertions hasInsertedCountMatchingWithFeaturesInRequest(final @N
108112
return hasMatchingInsertedCount(collectionRequest.getFeatures().size());
109113
}
110114

115+
public ResponseAssertions hasBodyWithout(String[]... paths){
116+
JsonObject jsonBody = parseJson(subject.body(), JsonObject.class);
117+
if(jsonBody.containsKey("features")){
118+
List<Map<String, Object>> features = (List<Map<String, Object>>) jsonBody.get("features");
119+
for(Map<String, Object> feature : features){
120+
failIfFeatureJsonContainsEntry(feature, paths);
121+
}
122+
} else {
123+
failIfFeatureJsonContainsEntry(jsonBody, paths);
124+
}
125+
return this;
126+
}
127+
128+
private void failIfFeatureJsonContainsEntry(Map<String, Object> rawFeature, String[]... paths){
129+
for(String[] path : paths){
130+
Map<String, Object> cur = rawFeature;
131+
for(int i = 0; cur != null && i < path.length - 1; i++){
132+
cur = (Map<String, Object>) cur.get(path[i]);
133+
}
134+
if(cur != null && cur.containsKey(path[path.length - 1])){
135+
fail("Body should not contain entry for path: " + Arrays.toString(path));
136+
}
137+
}
138+
}
139+
111140
public ResponseAssertions hasMatchingInsertedCount(int cnt) throws JSONException {
112141
JSONAssert.assertEquals("{inserted:[" + cnt + "]}", subject.body(),
113142
new ArraySizeComparator(JSONCompareMode.LENIENT));

here-naksha-app-service/src/test/java/com/here/naksha/app/service/Mom10Test.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
import com.here.naksha.app.common.ApiTest;
88
import com.here.naksha.app.common.NakshaTestWebClient;
9+
import com.here.naksha.lib.core.models.geojson.implementation.XyzFeature;
10+
import com.here.naksha.lib.core.models.geojson.implementation.XyzProperties;
911
import java.io.IOException;
1012
import java.net.URISyntaxException;
1113
import java.net.http.HttpResponse;
@@ -17,6 +19,8 @@ class Mom10Test extends ApiTest {
1719

1820
private static final NakshaTestWebClient nakshaClient = new NakshaTestWebClient();
1921
private static final String SPACE_ID = "mom_10_test_space";
22+
private static final String[] DELTA_NS_PATH = new String[]{XyzFeature.PROPERTIES, XyzProperties.HERE_DELTA_NS};
23+
private static final String[] META_NS_PATH = new String[]{XyzFeature.PROPERTIES, XyzProperties.HERE_META_NS};
2024

2125
@BeforeAll
2226
static void setup() {
@@ -37,6 +41,7 @@ void shouldCreateMom10Feature() throws URISyntaxException, IOException, Interrup
3741
assertThat(response)
3842
.hasStatus(200)
3943
.hasJsonBody(featuresJson)
44+
.hasBodyWithout(DELTA_NS_PATH, META_NS_PATH)
4045
.hasStreamIdHeader(streamId);
4146
}
4247

@@ -58,6 +63,7 @@ void shouldReadCreatedMom10Feature() throws URISyntaxException, IOException, Int
5863
assertThat(getResp)
5964
.hasStatus(200)
6065
.hasJsonBody(createFeatureJson)
66+
.hasBodyWithout(DELTA_NS_PATH, META_NS_PATH)
6167
.hasStreamIdHeader(streamId);
6268
}
6369

@@ -76,7 +82,8 @@ void shouldPopulateOldNamespaces() throws URISyntaxException, IOException, Inter
7682
assertThat(createResp).hasStatus(200);
7783

7884
// And: We do little hacking - patching the feature with different `modelVersion` so that MOM 10 transformation don't happen
79-
HttpResponse<String> patchResp = getNakshaClient().patch("hub/spaces/" + SPACE_ID + "/features/" + featureId, patchFeatureJson, streamId);
85+
HttpResponse<String> patchResp = getNakshaClient().patch("hub/spaces/" + SPACE_ID + "/features/" + featureId, patchFeatureJson,
86+
streamId);
8087
assertThat(patchResp).hasStatus(200);
8188

8289
// And: We query for the patched feature that was transformed during the write phase (it was MOM 10) but will not during read phase (it was not MOM 10)
@@ -104,7 +111,8 @@ void shouldDropOldNamespaces() throws URISyntaxException, IOException, Interrupt
104111
assertThat(createResp).hasStatus(200);
105112

106113
// And: We do little hacking - patching the feature with different `modelVersion` (MOM 10) so that MOM 10 transformation will happen
107-
HttpResponse<String> patchResp = getNakshaClient().patch("hub/spaces/" + SPACE_ID + "/features/" + featureId, patchFeatureJson, streamId);
114+
HttpResponse<String> patchResp = getNakshaClient().patch("hub/spaces/" + SPACE_ID + "/features/" + featureId, patchFeatureJson,
115+
streamId);
108116
assertThat(patchResp).hasStatus(200);
109117

110118
// And: We query for the patched feature that was not transformed during the write phase (it was pre MOM 10) but will during read phase (it was MOM 10)
@@ -114,6 +122,7 @@ void shouldDropOldNamespaces() throws URISyntaxException, IOException, Interrupt
114122
assertThat(getResp)
115123
.hasStatus(200)
116124
.hasJsonBody(expectedGetResp)
125+
.hasBodyWithout(DELTA_NS_PATH, META_NS_PATH)
117126
.hasStreamIdHeader(streamId);
118127
}
119128
}

here-naksha-app-service/src/test/resources/unit_test_data/Mom10/testDropNamespaces/patch.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"type": "Feature",
55
"properties": {
66
"meta": {
7-
"modelVersion": "1.0.0"
7+
"modelVersion": "10.0.0"
88
}
99
}
1010
}

here-naksha-app-service/src/test/resources/unit_test_data/Mom10/testDropNamespaces/response.json

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,6 @@
1919
]
2020
},
2121
"properties": {
22-
"@ns:com:here:mom:delta": {
23-
"changeState": "CREATED",
24-
"reviewState": "PENDING",
25-
"changeCounter": 0,
26-
"potentialValue": 0,
27-
"priorityCategory": 0
28-
},
29-
"@ns:com:here:mom:meta": {
30-
"modelVersion": "10.0.0",
31-
"createdTS": "2024-04-24T10:16:15.787710027Z"
32-
},
3322
"@ns:com:here:xyz": {
3423
"action": "UPDATE",
3524
"app_id": "naksha",
@@ -47,7 +36,7 @@
4736
"meta": {
4837
"createdTS": "2024-04-24T10:16:15.787710027Z",
4938
"unmappable": 123,
50-
"modelVersion": "1.0.0",
39+
"modelVersion": "10.0.0",
5140
"moderationInfo": {
5241
"changeState": "CREATED",
5342
"reviewState": "PENDING"

here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/storage/ResultHelper.java

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,11 @@
2222

2323
import com.here.naksha.lib.core.exceptions.NoCursor;
2424
import com.here.naksha.lib.core.models.geojson.implementation.XyzFeature;
25-
import com.here.naksha.lib.core.models.storage.EExecutedOp;
2625
import com.here.naksha.lib.core.models.storage.ForwardCursor;
2726
import com.here.naksha.lib.core.models.storage.Result;
2827
import com.here.naksha.lib.core.models.storage.XyzFeatureCodec;
2928
import java.util.ArrayList;
30-
import java.util.HashMap;
3129
import java.util.List;
32-
import java.util.Map;
3330
import java.util.NoSuchElementException;
3431
import org.jetbrains.annotations.NotNull;
3532
import org.jetbrains.annotations.Nullable;
@@ -121,55 +118,4 @@ public static List<String> readIdsFromResult(final @NotNull Result result) {
121118
return emptyList();
122119
}
123120
}
124-
125-
/**
126-
* Helper method to fetch features from given Result and return a map of multiple lists grouped by {@link EExecutedOp} of features with
127-
* type T. Returned lists are limited with respect to supplied `limit` parameter.
128-
*
129-
* @param result the Result which is to be read
130-
* @param featureType the type of feature to be extracted from result
131-
* @param limit the max number of features to be extracted
132-
* @param <R> type of feature
133-
* @return a map grouping the lists of features extracted from ReadResult
134-
*/
135-
public static <R extends XyzFeature> Map<EExecutedOp, List<R>> readFeaturesGroupedByOp(
136-
Result result, Class<R> featureType, long limit) throws NoCursor {
137-
try (ForwardCursor<XyzFeature, XyzFeatureCodec> resultCursor = result.getXyzFeatureCursor()) {
138-
final List<R> insertedFeatures = new ArrayList<>();
139-
final List<R> updatedFeatures = new ArrayList<>();
140-
final List<R> deletedFeatures = new ArrayList<>();
141-
int cnt = 0;
142-
while (resultCursor.hasNext() && cnt++ < limit) {
143-
if (!resultCursor.next()) {
144-
throw new RuntimeException("Unexpected invalid result");
145-
}
146-
if (resultCursor.getOp().equals(EExecutedOp.CREATED)) {
147-
insertedFeatures.add(featureType.cast(resultCursor.getFeature()));
148-
} else if (resultCursor.getOp().equals(EExecutedOp.UPDATED)) {
149-
updatedFeatures.add(featureType.cast(resultCursor.getFeature()));
150-
} else if (resultCursor.getOp().equals(EExecutedOp.DELETED)) {
151-
deletedFeatures.add(featureType.cast(resultCursor.getFeature()));
152-
}
153-
}
154-
final Map<EExecutedOp, List<R>> features = new HashMap<>();
155-
features.put(EExecutedOp.CREATED, insertedFeatures);
156-
features.put(EExecutedOp.UPDATED, updatedFeatures);
157-
features.put(EExecutedOp.DELETED, deletedFeatures);
158-
return features;
159-
}
160-
}
161-
162-
/**
163-
* Helper method to fetch features from given Result and return a map of multiple lists grouped by {@link EExecutedOp} of features with
164-
* type T. Returned list is not limited - to set the upper bound, use sibling method with limit argument.
165-
*
166-
* @param result the Result which is to be read
167-
* @param featureType the type of feature to be extracted from result
168-
* @param <R> type of feature
169-
* @return a map grouping the lists of features extracted from ReadResult
170-
*/
171-
public static <R extends XyzFeature> Map<EExecutedOp, List<R>> readFeaturesGroupedByOp(
172-
Result result, Class<R> featureType) throws NoCursor, NoSuchElementException {
173-
return readFeaturesGroupedByOp(result, featureType, Long.MAX_VALUE);
174-
}
175121
}

0 commit comments

Comments
 (0)