Skip to content

Commit f99c6f6

Browse files
authored
Casl 1434 mom10 app service (#536)
* pre and post processors Signed-off-by: Jakub Amanowicz <[email protected]> * CASL-1434 included Mom10 post processing for reads Signed-off-by: Jakub Amanowicz <[email protected]> * CASL-1434 included preprocessors in writing flow Signed-off-by: Jakub Amanowicz <[email protected]> * MOM 10 rest tests Signed-off-by: Jakub Amanowicz <[email protected]> * CASL-1434 patch file Signed-off-by: Jakub Amanowicz <[email protected]> * CASL-1434 review fixes Signed-off-by: Jakub Amanowicz <[email protected]> * added post processors to write api Signed-off-by: Jakub Amanowicz <[email protected]> * CASL-1434 added test for meta priority over old namespaces Signed-off-by: Jakub Amanowicz <[email protected]> * CASL-1434 added missing postprocessors Signed-off-by: Jakub Amanowicz <[email protected]> * CASL-1434 added missing postprocessor Signed-off-by: Jakub Amanowicz <[email protected]> --------- Signed-off-by: Jakub Amanowicz <[email protected]>
1 parent cd0d477 commit f99c6f6

File tree

39 files changed

+1549
-405
lines changed

39 files changed

+1549
-405
lines changed

build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ project(":here-naksha-lib-handlers") {
459459
implementation(project(":here-naksha-lib-psql"))
460460
implementation(project(":here-naksha-lib-view"))
461461
implementation(project(":here-naksha-storage-http"))
462+
implementation(project(":here-naksha-lib-mom10"))
462463

463464
implementation(commons_lang3)
464465
implementation(commons_dbutils)
@@ -517,6 +518,7 @@ project(":here-naksha-app-service") {
517518
//implementation(project(":here-naksha-lib-extension"))
518519
implementation(project(":here-naksha-lib-hub"))
519520
implementation(project(":here-naksha-common-http"))
521+
implementation(project(":here-naksha-lib-mom10"))
520522

521523
implementation(log4j_slf4j)
522524
implementation(log4j_api)
@@ -547,7 +549,6 @@ project(":here-naksha-lib-mom10"){
547549
description = "Naksha MOM 10 library"
548550
dependencies {
549551
implementation(project(":here-naksha-lib-core"))
550-
implementation(project(":here-naksha-lib-handlers"))
551552

552553
testImplementation(mockito)
553554
}

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

Lines changed: 82 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -23,36 +23,41 @@
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;
3029
import com.here.naksha.app.service.http.HttpResponseType;
3130
import com.here.naksha.app.service.http.NakshaHttpVerticle;
31+
import com.here.naksha.app.service.http.tasks.processor.FeaturePostProcessor;
3232
import com.here.naksha.app.service.models.IterateHandle;
3333
import com.here.naksha.lib.core.AbstractTask;
3434
import com.here.naksha.lib.core.INaksha;
3535
import com.here.naksha.lib.core.NakshaContext;
3636
import com.here.naksha.lib.core.exceptions.NoCursor;
37-
import com.here.naksha.lib.core.lambdas.F1;
3837
import com.here.naksha.lib.core.models.XyzError;
3938
import com.here.naksha.lib.core.models.geojson.implementation.XyzFeature;
4039
import com.here.naksha.lib.core.models.geojson.implementation.XyzFeatureCollection;
41-
import com.here.naksha.lib.core.models.geojson.implementation.XyzGeometry;
4240
import com.here.naksha.lib.core.models.payload.XyzResponse;
43-
import com.here.naksha.lib.core.models.storage.*;
41+
import com.here.naksha.lib.core.models.storage.ContextXyzFeatureResult;
42+
import com.here.naksha.lib.core.models.storage.EExecutedOp;
43+
import com.here.naksha.lib.core.models.storage.ErrorResult;
44+
import com.here.naksha.lib.core.models.storage.ForwardCursor;
45+
import com.here.naksha.lib.core.models.storage.ReadFeatures;
46+
import com.here.naksha.lib.core.models.storage.Result;
47+
import com.here.naksha.lib.core.models.storage.WriteFeatures;
48+
import com.here.naksha.lib.core.models.storage.XyzFeatureCodec;
4449
import com.here.naksha.lib.core.storage.IReadSession;
4550
import com.here.naksha.lib.core.storage.IWriteSession;
46-
import com.here.naksha.lib.core.util.PropertyPathUtil;
4751
import com.here.naksha.lib.core.util.json.Json;
48-
import com.here.naksha.lib.core.util.json.JsonSerializable;
4952
import com.here.naksha.lib.core.view.ViewDeserialize;
5053
import io.vertx.ext.web.RoutingContext;
51-
import java.util.*;
54+
import java.util.ArrayList;
55+
import java.util.HashMap;
56+
import java.util.List;
57+
import java.util.Map;
58+
import java.util.NoSuchElementException;
5259
import org.jetbrains.annotations.NotNull;
5360
import org.jetbrains.annotations.Nullable;
54-
import org.locationtech.jts.geom.Geometry;
55-
import org.locationtech.jts.geom.util.GeometryFixer;
5661
import org.slf4j.Logger;
5762
import org.slf4j.LoggerFactory;
5863

@@ -98,8 +103,10 @@ protected AbstractApiTask(
98103
}
99104

100105
protected <R extends XyzFeature> @NotNull XyzResponse transformReadResultToXyzFeatureResponse(
101-
final @NotNull Result rdResult, final @NotNull Class<R> type, @Nullable F1<R, R> preResponseProcessing) {
102-
return transformResultToXyzFeatureResponse(rdResult, type, NOT_FOUND_ON_NO_ELEMENTS, preResponseProcessing);
106+
final @NotNull Result rdResult,
107+
final @NotNull Class<R> type,
108+
@Nullable FeaturePostProcessor<R> postProcessor) {
109+
return transformResultToXyzFeatureResponse(rdResult, type, NOT_FOUND_ON_NO_ELEMENTS, postProcessor);
103110
}
104111

105112
protected <R extends XyzFeature> @NotNull XyzResponse transformWriteResultToXyzFeatureResponse(
@@ -108,8 +115,10 @@ protected AbstractApiTask(
108115
}
109116

110117
protected <R extends XyzFeature> @NotNull XyzResponse transformWriteResultToXyzFeatureResponse(
111-
final @Nullable Result wrResult, final @NotNull Class<R> type, @Nullable F1<R, R> preResponseProcessing) {
112-
return transformResultToXyzFeatureResponse(wrResult, type, FAIL_ON_NO_ELEMENTS, preResponseProcessing);
118+
final @Nullable Result wrResult,
119+
final @NotNull Class<R> type,
120+
@Nullable FeaturePostProcessor<R> postProcessor) {
121+
return transformResultToXyzFeatureResponse(wrResult, type, FAIL_ON_NO_ELEMENTS, postProcessor);
113122
}
114123

115124
protected <R extends XyzFeature> @NotNull XyzResponse transformDeleteResultToXyzFeatureResponse(
@@ -118,8 +127,10 @@ protected AbstractApiTask(
118127
}
119128

120129
protected <R extends XyzFeature> @NotNull XyzResponse transformDeleteResultToXyzFeatureResponse(
121-
final @Nullable Result wrResult, final @NotNull Class<R> type, @Nullable F1<R, R> postProcessing) {
122-
return transformResultToXyzFeatureResponse(wrResult, type, NOT_FOUND_ON_NO_ELEMENTS, postProcessing);
130+
final @Nullable Result wrResult,
131+
final @NotNull Class<R> type,
132+
@Nullable FeaturePostProcessor<R> postProcessor) {
133+
return transformResultToXyzFeatureResponse(wrResult, type, NOT_FOUND_ON_NO_ELEMENTS, postProcessor);
123134
}
124135

125136
protected XyzResponse handleNoElements(NoElementsStrategy noElementsStrategy) {
@@ -130,16 +141,16 @@ protected XyzResponse handleNoElements(NoElementsStrategy noElementsStrategy) {
130141
final @Nullable Result result,
131142
final @NotNull Class<R> type,
132143
final @NotNull NoElementsStrategy noElementsStrategy,
133-
final @Nullable F1<R, R> preResponseProcessing) {
144+
final @Nullable FeaturePostProcessor<R> postProcessor) {
134145
final XyzResponse validatedErrorResponse = validateErrorResult(result);
135146
if (validatedErrorResponse != null) {
136147
return validatedErrorResponse;
137148
} else {
138149
try {
139150
final R feature = readFeatureFromResult(result, type);
140151
R processedFeature = feature;
141-
if (feature != null && preResponseProcessing != null) {
142-
processedFeature = preResponseProcessing.call(feature);
152+
if (feature != null && postProcessor != null) {
153+
processedFeature = postProcessor.postProcess(feature);
143154
}
144155
if (processedFeature == null) {
145156
return verticle.sendErrorResponse(
@@ -161,9 +172,9 @@ protected XyzResponse handleNoElements(NoElementsStrategy noElementsStrategy) {
161172
protected <R extends XyzFeature> @NotNull XyzResponse transformReadResultToXyzCollectionResponse(
162173
final @Nullable Result rdResult,
163174
final @NotNull Class<R> type,
164-
final @Nullable F1<R, R> preResponseProcessing) {
175+
final @Nullable FeaturePostProcessor<R> featurePostProcessor) {
165176
return transformReadResultToXyzCollectionResponse(
166-
rdResult, type, 0, DEF_ADMIN_FEATURE_LIMIT, null, preResponseProcessing);
177+
rdResult, type, 0, DEF_ADMIN_FEATURE_LIMIT, null, featurePostProcessor);
167178
}
168179

169180
protected <R extends XyzFeature> @NotNull XyzResponse transformReadResultToXyzCollectionResponse(
@@ -182,18 +193,18 @@ protected XyzResponse handleNoElements(NoElementsStrategy noElementsStrategy) {
182193
final long offset,
183194
final long maxLimit,
184195
final @Nullable IterateHandle handle,
185-
final @Nullable F1<R, R> preResponseProcessing) {
196+
final @Nullable FeaturePostProcessor<R> featurePostProcessor) {
186197
final XyzResponse validatedErrorResponse = validateErrorResultEmptyCollection(rdResult);
187198
if (validatedErrorResponse != null) {
188199
return validatedErrorResponse;
189200
} else {
190201
try {
191202
final List<R> features = readFeaturesFromResult(rdResult, type, offset, maxLimit);
192203
List<R> processedFeatures = features;
193-
if (preResponseProcessing != null) {
204+
if (featurePostProcessor != null) {
194205
processedFeatures = new ArrayList<>();
195206
for (R feature : features) {
196-
final R processedFeature = preResponseProcessing.call(feature);
207+
final R processedFeature = featurePostProcessor.postProcess(feature);
197208
if (processedFeature != null) {
198209
processedFeatures.add(processedFeature);
199210
}
@@ -218,20 +229,26 @@ protected XyzResponse handleNoElements(NoElementsStrategy noElementsStrategy) {
218229
private static String getIterateHandleAsString(
219230
long featuresFound, long crtOffset, long maxLimit, final @Nullable IterateHandle handle) {
220231
// nothing to populate if handle is not provided OR if we don't have more features to iterate
221-
if (handle == null || featuresFound < maxLimit) return null;
232+
if (handle == null || featuresFound < maxLimit) {
233+
return null;
234+
}
222235
handle.setOffset(crtOffset + featuresFound); // set offset for next iteration
223236
handle.setLimit(maxLimit);
224237
return handle.base64EncodedSerializedJson();
225238
}
226239

227240
protected <R extends XyzFeature> @NotNull XyzResponse transformWriteResultToXyzCollectionResponse(
228-
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) {
229245
final XyzResponse validatedErrorResponse = validateErrorResult(wrResult);
230246
if (validatedErrorResponse != null) {
231247
return validatedErrorResponse;
232248
} else {
233249
try {
234-
final Map<EExecutedOp, List<R>> featureMap = readFeaturesGroupedByOp(wrResult, type);
250+
final Map<EExecutedOp, List<R>> featureMap =
251+
postProcessedFeaturesByOp(wrResult, type, featurePostProcessor);
235252
final List<R> insertedFeatures = featureMap.get(EExecutedOp.CREATED);
236253
final List<R> updatedFeatures = featureMap.get(EExecutedOp.UPDATED);
237254
final List<R> deletedFeatures = featureMap.get(EExecutedOp.DELETED);
@@ -260,6 +277,45 @@ private static String getIterateHandleAsString(
260277
}
261278
}
262279

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+
263319
protected Result executeReadRequestFromSpaceStorage(ReadFeatures readRequest) {
264320
try (final IReadSession reader = naksha().getSpaceStorage().newReadSession(context(), false)) {
265321
return reader.execute(readRequest);
@@ -309,38 +365,4 @@ XyzFeatureCollection emptyFeatureCollection() {
309365
return json.reader(ViewDeserialize.User.class).forType(type).readValue(bodyJson);
310366
}
311367
}
312-
313-
protected <F extends XyzFeature> @Nullable F1<F, F> standardReadFeaturesPreResponseProcessing(
314-
final @Nullable Set<String> propPaths, final boolean clip, final Geometry clipGeo) {
315-
if (propPaths == null && !clip) return null;
316-
return f -> {
317-
F newF = f;
318-
// Apply prop selection if enabled
319-
if (propPaths != null) newF = applyPropertySelection(newF, propPaths);
320-
// Apply geometry clipping if enabled
321-
if (clip) applyGeometryClipping(newF, clipGeo);
322-
return newF;
323-
};
324-
}
325-
326-
@SuppressWarnings("unchecked")
327-
private <F extends XyzFeature> @NotNull F applyPropertySelection(
328-
final @NotNull F f, final @NotNull Set<String> propPaths) {
329-
final Map<String, Object> tgtMap = PropertyPathUtil.extractPropertyMapFromFeature(f, propPaths);
330-
return (F) JsonSerializable.fromMap(tgtMap, f.getClass());
331-
}
332-
333-
private <F extends XyzFeature> void applyGeometryClipping(final @NotNull F f, final Geometry clipGeo) {
334-
// clip Feature geometry (if present) to a given clipGeo geometry
335-
final XyzGeometry xyzGeo = f.getGeometry();
336-
if (xyzGeo != null) {
337-
// NOTE - in JTS when we say:
338-
// GeometryFixer.fix(geom).intersection(bbox)
339-
// it is the best available way of clipping geometry, equivalent to PostGIS approach of:
340-
// ST_Intersection(ST_MakeValid(geo, 'method=structure'), bbox)
341-
final Geometry clippedGeo =
342-
GeometryFixer.fix(xyzGeo.getJTSGeometry()).intersection(clipGeo);
343-
f.setGeometry(XyzGeometry.convertJTSGeometry(clippedGeo));
344-
}
345-
}
346368
}

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

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
*/
1919
package com.here.naksha.app.service.http.tasks;
2020

21-
import static com.here.naksha.app.service.http.ops.MaskingUtil.maskProperties;
2221
import static com.here.naksha.common.http.apis.ApiParamsConst.HANDLER_ID;
2322
import static com.here.naksha.lib.core.NakshaAdminCollection.EVENT_HANDLERS;
2423

2524
import com.fasterxml.jackson.core.JsonProcessingException;
2625
import com.here.naksha.app.service.http.NakshaHttpVerticle;
2726
import com.here.naksha.app.service.http.apis.ApiParams;
27+
import com.here.naksha.app.service.http.tasks.processor.FeaturePostProcessor;
28+
import com.here.naksha.app.service.http.tasks.processor.MaskingPostProcessor;
2829
import com.here.naksha.lib.core.INaksha;
2930
import com.here.naksha.lib.core.NakshaContext;
3031
import com.here.naksha.lib.core.exceptions.XyzErrorException;
@@ -47,6 +48,7 @@
4748
public class EventHandlerApiTask<T extends XyzResponse> extends AbstractApiTask<XyzResponse> {
4849

4950
private static final Logger logger = LoggerFactory.getLogger(EventHandlerApiTask.class);
51+
private static final FeaturePostProcessor<EventHandler> HANDLER_MASKING = new MaskingPostProcessor<>();
5052

5153
private final @NotNull EventHandlerApiReqType reqType;
5254

@@ -107,8 +109,7 @@ protected void init() {}
107109
// Submit request to NH Space Storage
108110
try (Result rdResult = executeReadRequestFromSpaceStorage(request)) {
109111
// transform ReadResult to Http FeatureCollection response
110-
return transformReadResultToXyzCollectionResponse(
111-
rdResult, EventHandler.class, this::handlerWithMaskedSensitiveProperties);
112+
return transformReadResultToXyzCollectionResponse(rdResult, EventHandler.class, HANDLER_MASKING);
112113
}
113114
}
114115

@@ -136,33 +137,25 @@ protected void init() {}
136137
final String handlerId = ApiParams.extractMandatoryPathParam(routingContext, HANDLER_ID);
137138
final WriteXyzFeatures wrRequest = RequestHelper.deleteFeatureByIdRequest(EVENT_HANDLERS, handlerId);
138139
try (Result wrResult = executeWriteRequestFromSpaceStorage(wrRequest)) {
139-
return transformDeleteResultToXyzFeatureResponse(
140-
wrResult, EventHandler.class, this::handlerWithMaskedSensitiveProperties);
140+
return transformDeleteResultToXyzFeatureResponse(wrResult, EventHandler.class, HANDLER_MASKING);
141141
}
142142
}
143143

144144
@NotNull
145145
private XyzResponse transformResponseFor(ReadFeatures rdRequest) {
146146
try (Result rdResult = executeReadRequestFromSpaceStorage(rdRequest)) {
147-
return transformReadResultToXyzFeatureResponse(
148-
rdResult, EventHandler.class, this::handlerWithMaskedSensitiveProperties);
147+
return transformReadResultToXyzFeatureResponse(rdResult, EventHandler.class, HANDLER_MASKING);
149148
}
150149
}
151150

152151
@NotNull
153152
private XyzResponse transformResponseFor(WriteXyzFeatures updateHandlerReq) {
154153
// persist new handler in Admin DB (if doesn't exist already)
155154
try (Result updateHandlerResult = executeWriteRequestFromSpaceStorage(updateHandlerReq)) {
156-
return transformWriteResultToXyzFeatureResponse(
157-
updateHandlerResult, EventHandler.class, this::handlerWithMaskedSensitiveProperties);
155+
return transformWriteResultToXyzFeatureResponse(updateHandlerResult, EventHandler.class, HANDLER_MASKING);
158156
}
159157
}
160158

161-
private EventHandler handlerWithMaskedSensitiveProperties(EventHandler handler) {
162-
maskProperties(handler);
163-
return handler;
164-
}
165-
166159
private @NotNull EventHandler handlerFromRequestBody() throws JsonProcessingException {
167160
try (final Json json = Json.get()) {
168161
final String bodyJson = routingContext.body().asString();

0 commit comments

Comments
 (0)