Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mcpods 6515 update apis #97

Merged
merged 37 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
b5d4369
WIP api implementation
gunplar Nov 16, 2023
df5e303
implement update multiple features, one test
gunplar Nov 17, 2023
155981e
wip
gunplar Nov 17, 2023
e93382c
update one feature by id
gunplar Nov 20, 2023
b352f80
add test
gunplar Nov 20, 2023
5435e53
add test
gunplar Nov 21, 2023
e41044f
add test
gunplar Nov 21, 2023
ebaa3b8
Merge remote-tracking branch 'origin/Naksha_maintenance' into MCPODS-…
gunplar Nov 21, 2023
1fc7552
align tests, autoformat
gunplar Nov 21, 2023
f812bf6
allow returned feature collection to specify updated
gunplar Nov 22, 2023
be103be
revert previous change, extract features twice in transformWriteResul…
gunplar Nov 22, 2023
482105f
Merge remote-tracking branch 'origin/Naksha_maintenance' into MCPODS-…
gunplar Nov 22, 2023
e205117
adjust result helper to not rely on cursor reposition
gunplar Nov 22, 2023
07b3ab9
switch to test helper class
gunplar Nov 22, 2023
d1499c1
add deleted features readout ability
gunplar Nov 23, 2023
61d8c57
decouple update feature test resource from create
gunplar Nov 23, 2023
e6c8c62
add test
gunplar Nov 23, 2023
a212dcb
add test
gunplar Nov 23, 2023
62ed72e
add test
gunplar Nov 23, 2023
5130de4
Merge remote-tracking branch 'origin/Naksha_maintenance' into MCPODS-…
gunplar Nov 24, 2023
cf1eb05
explicit imports
gunplar Nov 24, 2023
b51b3c7
autoformat
gunplar Nov 24, 2023
00f13e2
renaming correctly to upsert
gunplar Nov 27, 2023
31811ab
fix cherry pick error
gunplar Nov 27, 2023
7c91dd3
autoformat
gunplar Nov 27, 2023
6736ad3
decouple upsert and update tests completely from create
gunplar Nov 27, 2023
77234e0
improve test assertion
gunplar Nov 27, 2023
648f0ed
change mock and test to return 404 for updating non existent features
gunplar Nov 28, 2023
76d58f6
Merge remote-tracking branch 'origin/Naksha_maintenance' into MCPODS-…
gunplar Nov 28, 2023
602e699
fix conflict with maintenance branch
gunplar Nov 28, 2023
7fc499d
Merge remote-tracking branch 'origin/Naksha_maintenance' into MCPODS-…
gunplar Nov 28, 2023
8d605e3
update openapi yaml
gunplar Nov 28, 2023
eff4732
fix mock
gunplar Nov 28, 2023
719a5bb
clean up
gunplar Nov 28, 2023
1e4c287
Merge remote-tracking branch 'origin/Naksha_maintenance' into MCPODS-…
gunplar Nov 28, 2023
ec19663
reformat
gunplar Nov 28, 2023
1a726b7
adjust description in openapi yaml
gunplar Nov 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
*/
package com.here.naksha.app.service.http.apis;

import static com.here.naksha.app.service.http.tasks.WriteFeatureApiTask.WriteFeatureApiReqType.CREATE_FEATURES;
import static com.here.naksha.app.service.http.tasks.WriteFeatureApiTask.WriteFeatureApiReqType.*;

import com.here.naksha.app.service.http.NakshaHttpVerticle;
import com.here.naksha.app.service.http.tasks.WriteFeatureApiTask;
Expand All @@ -41,6 +41,8 @@ public WriteFeatureApi(final @NotNull NakshaHttpVerticle verticle) {
@Override
public void addOperations(final @NotNull RouterBuilder rb) {
rb.operation("postFeatures").handler(this::createFeatures);
rb.operation("putFeatures").handler(this::upsertFeatures);
rb.operation("putFeature").handler(this::updateFeature);
}

@Override
Expand All @@ -50,6 +52,14 @@ private void createFeatures(final @NotNull RoutingContext routingContext) {
startWriteFeatureApiTask(CREATE_FEATURES, routingContext);
}

private void upsertFeatures(final @NotNull RoutingContext routingContext) {
startWriteFeatureApiTask(UPSERT_FEATURES, routingContext);
}

private void updateFeature(final @NotNull RoutingContext routingContext) {
startWriteFeatureApiTask(UPDATE_BY_ID, routingContext);
}

private void startWriteFeatureApiTask(WriteFeatureApiReqType reqType, RoutingContext routingContext) {
new WriteFeatureApiTask<>(
reqType, verticle, naksha(), routingContext, verticle.createNakshaContext(routingContext))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package com.here.naksha.app.service.http.tasks;

import static com.here.naksha.lib.core.util.storage.ResultHelper.readFeaturesFromResult;
import static com.here.naksha.lib.core.util.storage.ResultHelper.readFeaturesGroupedByOp;
import static java.util.Collections.emptyList;

import com.here.naksha.app.service.http.HttpResponseType;
Expand All @@ -32,14 +33,12 @@
import com.here.naksha.lib.core.models.geojson.implementation.XyzFeature;
import com.here.naksha.lib.core.models.geojson.implementation.XyzFeatureCollection;
import com.here.naksha.lib.core.models.payload.XyzResponse;
import com.here.naksha.lib.core.models.storage.ErrorResult;
import com.here.naksha.lib.core.models.storage.ReadFeatures;
import com.here.naksha.lib.core.models.storage.Result;
import com.here.naksha.lib.core.models.storage.WriteFeatures;
import com.here.naksha.lib.core.models.storage.*;
import com.here.naksha.lib.core.storage.IReadSession;
import com.here.naksha.lib.core.storage.IWriteSession;
import io.vertx.ext.web.RoutingContext;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -162,11 +161,17 @@ protected AbstractApiTask(
return verticle.sendErrorResponse(routingContext, er.reason, er.message);
} else {
try {
List<R> features = readFeaturesFromResult(wrResult, type);
final Map<EExecutedOp, List<R>> featureMap = readFeaturesGroupedByOp(wrResult, type);
final List<R> insertedFeatures = featureMap.get(EExecutedOp.CREATED);
final List<R> updatedFeatures = featureMap.get(EExecutedOp.UPDATED);
final List<R> deletedFeatures = featureMap.get(EExecutedOp.DELETED);
return verticle.sendXyzResponse(
routingContext,
HttpResponseType.FEATURE_COLLECTION,
new XyzFeatureCollection().withInsertedFeatures(features));
new XyzFeatureCollection()
.withInsertedFeatures(insertedFeatures)
.withUpdatedFeatures(updatedFeatures)
.withDeletedFeatures(deletedFeatures));
} catch (NoCursor | NoSuchElementException emptyException) {
return verticle.sendErrorResponse(
routingContext, XyzError.EXCEPTION, "Unexpected empty result from ResultCursor");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
package com.here.naksha.app.service.http.tasks;

import static com.here.naksha.app.service.http.apis.ApiParams.ADD_TAGS;
import static com.here.naksha.app.service.http.apis.ApiParams.FEATURE_ID;
import static com.here.naksha.app.service.http.apis.ApiParams.PREFIX_ID;
import static com.here.naksha.app.service.http.apis.ApiParams.REMOVE_TAGS;
import static com.here.naksha.app.service.http.apis.ApiParams.SPACE_ID;
import static com.here.naksha.app.service.http.apis.ApiParams.pathParam;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.here.naksha.app.service.http.NakshaHttpVerticle;
import com.here.naksha.app.service.models.FeatureCollectionRequest;
import com.here.naksha.lib.core.INaksha;
Expand All @@ -37,6 +39,7 @@
import com.here.naksha.lib.core.util.json.Json;
import com.here.naksha.lib.core.util.storage.RequestHelper;
import com.here.naksha.lib.core.view.ViewDeserialize;
import com.here.naksha.lib.core.view.ViewDeserialize.User;
import io.vertx.ext.web.RoutingContext;
import java.util.List;
import org.jetbrains.annotations.NotNull;
Expand All @@ -50,7 +53,9 @@ public class WriteFeatureApiTask<T extends XyzResponse> extends AbstractApiTask<

public enum WriteFeatureApiReqType {
CREATE_FEATURES,
MODIFY_FEATURES
UPSERT_FEATURES,
UPDATE_BY_ID,
DELETE_FEATURES
}

public WriteFeatureApiTask(
Expand Down Expand Up @@ -79,12 +84,12 @@ protected void init() {}
logger.info("Received Http request {}", this.reqType);
// Custom execute logic to process input API request based on reqType
try {
switch (this.reqType) {
case CREATE_FEATURES:
return executeCreateFeatures();
default:
return executeUnsupported();
}
return switch (this.reqType) {
case CREATE_FEATURES -> executeCreateFeatures();
case UPSERT_FEATURES -> executeUpsertFeatures();
case UPDATE_BY_ID -> executeUpdateFeature();
default -> executeUnsupported();
};
} catch (Exception ex) {
// unexpected exception
logger.error("Exception processing Http request. ", ex);
Expand All @@ -95,13 +100,7 @@ protected void init() {}

private @NotNull XyzResponse executeCreateFeatures() throws Exception {
// Deserialize input request
final FeatureCollectionRequest collectionRequest;
try (final Json json = Json.get()) {
final String bodyJson = routingContext.body().asString();
collectionRequest = json.reader(ViewDeserialize.User.class)
.forType(FeatureCollectionRequest.class)
.readValue(bodyJson);
}
final FeatureCollectionRequest collectionRequest = featuresFromRequestBody();
final List<XyzFeature> features = (List<XyzFeature>) collectionRequest.getFeatures();
if (features.isEmpty()) {
return verticle.sendErrorResponse(routingContext, XyzError.ILLEGAL_ARGUMENT, "Can't create empty features");
Expand All @@ -123,17 +122,121 @@ protected void init() {}
}

// as applicable, modify features based on parameters supplied
if (prefixId != null || addTags != null || removeTags != null) {
for (final XyzFeature feature : features) {
feature.setIdPrefix(prefixId);
feature.getProperties().getXyzNamespace().addTags(addTags, true).removeTags(removeTags, true);
}

for (final XyzFeature feature : features) {
feature.setIdPrefix(prefixId);
addTagsToFeature(feature, addTags);
removeTagsFromFeature(feature, removeTags);
}

final WriteXyzFeatures wrRequest = RequestHelper.createFeaturesRequest(spaceId, features);

// Forward request to NH Space Storage writer instance
final Result wrResult = executeWriteRequestFromSpaceStorage(wrRequest);
// transform WriteResult to Http FeatureCollection response
return transformWriteResultToXyzCollectionResponse(wrResult, XyzFeature.class);
}

private @NotNull XyzResponse executeUpsertFeatures() throws Exception {
// Deserialize input request
final FeatureCollectionRequest collectionRequest = featuresFromRequestBody();
final List<XyzFeature> features = (List<XyzFeature>) collectionRequest.getFeatures();
if (features.isEmpty()) {
return verticle.sendErrorResponse(routingContext, XyzError.ILLEGAL_ARGUMENT, "Can't update empty features");
}

// Parse API parameters
final String spaceId = pathParam(routingContext, SPACE_ID);
final QueryParameterList queryParams = (routingContext.request().query() != null)
? new QueryParameterList(routingContext.request().query())
: null;
final List<String> addTags = (queryParams != null) ? queryParams.collectAllOf(ADD_TAGS, String.class) : null;
final List<String> removeTags =
(queryParams != null) ? queryParams.collectAllOf(REMOVE_TAGS, String.class) : null;

// Validate parameters
if (spaceId == null || spaceId.isEmpty()) {
return verticle.sendErrorResponse(routingContext, XyzError.ILLEGAL_ARGUMENT, "Missing spaceId parameter");
}

// as applicable, modify features based on parameters supplied
for (final XyzFeature feature : features) {
addTagsToFeature(feature, addTags);
removeTagsFromFeature(feature, removeTags);
}
final WriteXyzFeatures wrRequest = RequestHelper.upsertFeaturesRequest(spaceId, features);

// Forward request to NH Space Storage writer instance
final Result wrResult = executeWriteRequestFromSpaceStorage(wrRequest);
// transform WriteResult to Http FeatureCollection response
return transformWriteResultToXyzCollectionResponse(wrResult, XyzFeature.class);
}

private @NotNull XyzResponse executeUpdateFeature() throws Exception {
// Deserialize input request
XyzFeature feature;
try (final Json json = Json.get()) {
final String bodyJson = routingContext.body().asString();
feature = json.reader(User.class).forType(XyzFeature.class).readValue(bodyJson);
}

// Parse API parameters
final String spaceId = pathParam(routingContext, SPACE_ID);
final String featureId = pathParam(routingContext, FEATURE_ID);

final QueryParameterList queryParams = (routingContext.request().query() != null)
? new QueryParameterList(routingContext.request().query())
: null;
final List<String> addTags = (queryParams != null) ? queryParams.collectAllOf(ADD_TAGS, String.class) : null;
final List<String> removeTags =
(queryParams != null) ? queryParams.collectAllOf(REMOVE_TAGS, String.class) : null;

// Validate parameters
if (spaceId == null || spaceId.isEmpty()) {
return verticle.sendErrorResponse(routingContext, XyzError.ILLEGAL_ARGUMENT, "Missing spaceId parameter");
}

if (featureId == null || featureId.isEmpty()) {
return verticle.sendErrorResponse(routingContext, XyzError.ILLEGAL_ARGUMENT, "Missing featureId parameter");
}

if (!featureId.equals(feature.getId())) {
return verticle.sendErrorResponse(
routingContext,
XyzError.ILLEGAL_ARGUMENT,
"URI path parameter featureId is not the same as id in feature request body.");
}

// as applicable, modify features based on parameters supplied
addTagsToFeature(feature, addTags);
removeTagsFromFeature(feature, removeTags);

final WriteXyzFeatures wrRequest = RequestHelper.updateFeatureRequest(spaceId, feature);

// Forward request to NH Space Storage writer instance
final Result wrResult = executeWriteRequestFromSpaceStorage(wrRequest);
// transform WriteResult to Http FeatureCollection response
return transformWriteResultToXyzFeatureResponse(wrResult, XyzFeature.class);
}

private @NotNull FeatureCollectionRequest featuresFromRequestBody() throws JsonProcessingException {
try (final Json json = Json.get()) {
final String bodyJson = routingContext.body().asString();
return json.reader(ViewDeserialize.User.class)
.forType(FeatureCollectionRequest.class)
.readValue(bodyJson);
}
}

private void addTagsToFeature(XyzFeature feature, List<String> addTags) {
if (addTags != null) {
feature.getProperties().getXyzNamespace().addTags(addTags, true);
}
}

private void removeTagsFromFeature(XyzFeature feature, List<String> removeTags) {
if (removeTags != null) {
feature.getProperties().getXyzNamespace().removeTags(removeTags, true);
}
}
}
Loading
Loading