From 126379a1ae6f8b6ef33e8f593abd3759f7c095fb Mon Sep 17 00:00:00 2001 From: Clemens Portele Date: Wed, 10 Jan 2024 20:52:57 +0530 Subject: [PATCH] fix mutation requests (#256) --- .../domain/FeatureTokenDecoderGeoJson.java | 51 ++++++++++++------- .../FeatureTokenDecoderGeoJsonSpec2.groovy | 18 +++---- .../features/sql/domain/SchemaMappingSql.java | 46 ++++++++--------- .../sql/app/MutationSchemaDeriverSpec.groovy | 2 +- .../sql/app/MutationSchemaFixtures.groovy | 6 +-- .../features/domain/FeatureTokenDecoder.java | 3 +- .../domain/FeatureTokenFixtures.groovy | 46 +++++++++++------ 7 files changed, 100 insertions(+), 72 deletions(-) diff --git a/xtraplatform-features-json/src/main/java/de/ii/xtraplatform/features/json/domain/FeatureTokenDecoderGeoJson.java b/xtraplatform-features-json/src/main/java/de/ii/xtraplatform/features/json/domain/FeatureTokenDecoderGeoJson.java index 57fbb9a53..7cfe7e501 100644 --- a/xtraplatform-features-json/src/main/java/de/ii/xtraplatform/features/json/domain/FeatureTokenDecoderGeoJson.java +++ b/xtraplatform-features-json/src/main/java/de/ii/xtraplatform/features/json/domain/FeatureTokenDecoderGeoJson.java @@ -17,6 +17,7 @@ import de.ii.xtraplatform.features.domain.FeatureTokenDecoder; import de.ii.xtraplatform.features.domain.SchemaBase.Type; import de.ii.xtraplatform.features.domain.SchemaMapping; +import de.ii.xtraplatform.features.domain.SchemaMappingBase; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -37,6 +38,7 @@ public class FeatureTokenDecoderGeoJson private final JsonParser parser; private final ByteArrayFeeder feeder; private final Optional nullValue; + private String type; private boolean started; private int depth = -1; @@ -53,11 +55,16 @@ public class FeatureTokenDecoderGeoJson FeatureSchema, SchemaMapping, ModifiableContext> downstream; - public FeatureTokenDecoderGeoJson() { - this(Optional.empty()); + // for unit tests + FeatureTokenDecoderGeoJson(String type) { + this(Optional.empty(), type); } public FeatureTokenDecoderGeoJson(Optional nullValue) { + this(nullValue, null); + } + + private FeatureTokenDecoderGeoJson(Optional nullValue, String type) { try { this.parser = JSON_FACTORY.createNonBlockingByteArrayParser(); } catch (IOException e) { @@ -65,12 +72,18 @@ public FeatureTokenDecoderGeoJson(Optional nullValue) { } this.feeder = (ByteArrayFeeder) parser.getNonBlockingInputFeeder(); this.nullValue = nullValue; + this.type = type; } @Override protected void init() { this.context = createContext(); this.downstream = new FeatureTokenBuffer<>(getDownstream(), context); + + if (Objects.isNull(type) && Objects.nonNull(context.mapping())) { + SchemaMappingBase mapping = context.mapping(); + type = mapping.getTargetSchema().getSourcePath().orElse(null); + } } @Override @@ -137,12 +150,12 @@ public boolean advanceParser() { switch (currentName) { case "properties": inProperties = true; - featureDepth = depth; - context.pathTracker().track(0); + // featureDepth = depth; + context.pathTracker().track(type, 0); break; case "geometry": context.setInGeometry(true); - context.pathTracker().track(currentName, 0); + context.pathTracker().track(currentName, 1); geoPath = context.path(); break; default: @@ -152,7 +165,7 @@ public boolean advanceParser() { break; } // nested array_object start - } else if (!context.pathTracker().asList().isEmpty() && started) { + } else if (context.pathTracker().asList().size() > 1 && started) { downstream.onObjectStart(context); // feature in collection start } else if (depth == featureDepth - 1 && inFeature) { @@ -226,11 +239,11 @@ public boolean advanceParser() { if (context.inGeometry()) { checkBufferForDimension(); - context.pathTracker().track(depth - featureDepth - 1); + context.pathTracker().track(depth - featureDepth); } depth -= 1; if (inProperties) { - context.pathTracker().track(depth - featureDepth - 1); + context.pathTracker().track(depth - featureDepth); } lastNameIsArrayDepth -= 1; // end nested geo array @@ -238,7 +251,7 @@ public boolean advanceParser() { checkBufferForDimension(); endArray++; depth -= 1; - context.pathTracker().track(depth - featureDepth); + context.pathTracker().track(depth - featureDepth + 1); } break; @@ -257,7 +270,7 @@ public boolean advanceParser() { // end geo if (context.inGeometry()) { - context.pathTracker().track(depth - featureDepth - 1); + context.pathTracker().track(depth - featureDepth); } depth -= 1; @@ -288,7 +301,7 @@ public boolean advanceParser() { downstream.bufferStop(false); } if (inProperties) { - context.pathTracker().track(depth - featureDepth - 1); + context.pathTracker().track(depth - featureDepth); } break; @@ -323,9 +336,9 @@ public boolean advanceParser() { && (Objects.equals(currentName, "geometry") || Objects.equals(currentName, "properties"))) { if (Objects.equals(currentName, "properties")) { - context.pathTracker().track(0); + context.pathTracker().track(type, 0); } else { - context.pathTracker().track(currentName, 0); + context.pathTracker().track(currentName, 1); } context.setValue(nullValue.get()); downstream.onValue(context); @@ -350,12 +363,12 @@ public boolean advanceParser() { break; } - context.pathTracker().track(currentName, 0); + context.pathTracker().track(currentName, 1); context.setValue(parser.getValueAsString()); downstream.onValue(context); - context.pathTracker().track(0); + context.pathTracker().track(1); break; } // feature id or props or geo value @@ -397,13 +410,13 @@ public boolean advanceParser() { // feature id if (Objects.equals(currentName, "id")) { - context.pathTracker().track(0); + context.pathTracker().track(1); } // why reset depth? else if (!context.inGeometry()) { - context.pathTracker().track(depth - featureDepth - 1); - } else if (context.inGeometry()) { context.pathTracker().track(depth - featureDepth); + } else if (context.inGeometry()) { + context.pathTracker().track(depth - featureDepth + 1); } } break; @@ -432,6 +445,8 @@ private void startIfNecessary(boolean isCollection) { if (!started) { started = true; inFeature = true; + context.pathTracker().track(type, 0); + if (isCollection) { featureDepth = 1; } else { diff --git a/xtraplatform-features-json/src/test/groovy/de/ii/xtraplatform/features/json/app/FeatureTokenDecoderGeoJsonSpec2.groovy b/xtraplatform-features-json/src/test/groovy/de/ii/xtraplatform/features/json/app/FeatureTokenDecoderGeoJsonSpec2.groovy index c3a112c19..3dc23f599 100644 --- a/xtraplatform-features-json/src/test/groovy/de/ii/xtraplatform/features/json/app/FeatureTokenDecoderGeoJsonSpec2.groovy +++ b/xtraplatform-features-json/src/test/groovy/de/ii/xtraplatform/features/json/app/FeatureTokenDecoderGeoJsonSpec2.groovy @@ -45,7 +45,7 @@ class FeatureTokenDecoderGeoJsonSpec2 extends Specification { } def setup() { - decoder = new FeatureTokenDecoderGeoJson() + decoder = new FeatureTokenDecoderGeoJson("biotop") } public T runStream(Reactive.Stream stream) { @@ -78,7 +78,7 @@ class FeatureTokenDecoderGeoJsonSpec2 extends Specification { List tokens = runStream(stream) then: - tokens == FeatureTokenFixtures.SINGLE_FEATURE + tokens == FeatureTokenFixtures.SINGLE_FEATURE_SOURCE } @@ -95,7 +95,7 @@ class FeatureTokenDecoderGeoJsonSpec2 extends Specification { List tokens = runStream(stream) then: - tokens == FeatureTokenFixtures.SINGLE_FEATURE_POINT + tokens == FeatureTokenFixtures.SINGLE_FEATURE_POINT_SOURCE } @@ -112,7 +112,7 @@ class FeatureTokenDecoderGeoJsonSpec2 extends Specification { List tokens = runStream(stream) then: - tokens == FeatureTokenFixtures.SINGLE_FEATURE_MULTI_POINT + tokens == FeatureTokenFixtures.SINGLE_FEATURE_MULTI_POINT_SOURCE } @@ -129,7 +129,7 @@ class FeatureTokenDecoderGeoJsonSpec2 extends Specification { List tokens = runStream(stream) then: - tokens == FeatureTokenFixtures.SINGLE_FEATURE_MULTI_POLYGON + tokens == FeatureTokenFixtures.SINGLE_FEATURE_MULTI_POLYGON_SOURCE } @@ -146,7 +146,7 @@ class FeatureTokenDecoderGeoJsonSpec2 extends Specification { List tokens = runStream(stream) then: - tokens == FeatureTokenFixtures.SINGLE_FEATURE_NESTED_OBJECT + tokens == FeatureTokenFixtures.SINGLE_FEATURE_NESTED_OBJECT_SOURCE } @@ -163,7 +163,7 @@ class FeatureTokenDecoderGeoJsonSpec2 extends Specification { List tokens = runStream(stream) then: - tokens == FeatureTokenFixtures.SINGLE_FEATURE_VALUE_ARRAY + tokens == FeatureTokenFixtures.SINGLE_FEATURE_VALUE_ARRAY_SOURCE } @@ -180,7 +180,7 @@ class FeatureTokenDecoderGeoJsonSpec2 extends Specification { List tokens = runStream(stream) then: - tokens == FeatureTokenFixtures.SINGLE_FEATURE_NESTED_OBJECT_ARRAYS + tokens == FeatureTokenFixtures.SINGLE_FEATURE_NESTED_OBJECT_ARRAYS_SOURCE } @@ -197,7 +197,7 @@ class FeatureTokenDecoderGeoJsonSpec2 extends Specification { List tokens = runStream(stream) then: - tokens == FeatureTokenFixtures.COLLECTION + tokens == FeatureTokenFixtures.COLLECTION_SOURCE } } diff --git a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SchemaMappingSql.java b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SchemaMappingSql.java index c944831fb..b14592def 100644 --- a/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SchemaMappingSql.java +++ b/xtraplatform-features-sql/src/main/java/de/ii/xtraplatform/features/sql/domain/SchemaMappingSql.java @@ -7,42 +7,40 @@ */ package de.ii.xtraplatform.features.sql.domain; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; import de.ii.xtraplatform.features.domain.SchemaMappingBase; -import de.ii.xtraplatform.features.domain.SchemaToMappingVisitor; import de.ii.xtraplatform.geometries.domain.SimpleFeatureGeometry; -import java.util.AbstractMap; import java.util.List; -import java.util.Map; -import java.util.function.BiFunction; +import java.util.stream.Collectors; import org.immutables.value.Value; @Value.Immutable @Value.Style(deepImmutablesDetection = true, builder = "new", attributeBuilderDetection = true) public interface SchemaMappingSql extends SchemaMappingBase { - BiFunction getSourcePathTransformer(); + @Override + default SchemaSql schemaWithGeometryType(SchemaSql schema, SimpleFeatureGeometry geometryType) { + return new ImmutableSchemaSql.Builder().from(schema).geometryType(geometryType).build(); + } - @Value.Derived - @Value.Auxiliary - default Map, List> getSchemasByTargetPath() { - return getTargetSchema() - .accept(new SchemaToMappingVisitor<>(getSourcePathTransformer())) - .asMap() - .entrySet() - .stream() - // TODO: removal of first path element only makes sense for geojson, so change in parser - .map( - entry -> - new AbstractMap.SimpleImmutableEntry<>( - entry.getKey().subList(1, entry.getKey().size()), - Lists.newArrayList(entry.getValue()))) - .collect(ImmutableMap.toImmutableMap(Map.Entry::getKey, Map.Entry::getValue)); + @Override + default List cleanPath(List path) { + if (path.stream().anyMatch(elem -> elem.contains("{"))) { + return path.stream().map(this::cleanPath).collect(Collectors.toList()); + } + + return path; } + // TODO: static cleanup method in PathParser @Override - default SchemaSql schemaWithGeometryType(SchemaSql schema, SimpleFeatureGeometry geometryType) { - return new ImmutableSchemaSql.Builder().from(schema).geometryType(geometryType).build(); + default String cleanPath(String path) { + if (path.contains("{")) { + int i = path.indexOf("{"); + if (path.startsWith("filter", i + 1)) { + return path.substring(0, i + 2) + cleanPath(path.substring(i + 2)); + } + return path.substring(0, path.indexOf("{")); + } + return path; } } diff --git a/xtraplatform-features-sql/src/test/groovy/de/ii/xtraplatform/features/sql/app/MutationSchemaDeriverSpec.groovy b/xtraplatform-features-sql/src/test/groovy/de/ii/xtraplatform/features/sql/app/MutationSchemaDeriverSpec.groovy index 0dffe6ed9..cd768baae 100644 --- a/xtraplatform-features-sql/src/test/groovy/de/ii/xtraplatform/features/sql/app/MutationSchemaDeriverSpec.groovy +++ b/xtraplatform-features-sql/src/test/groovy/de/ii/xtraplatform/features/sql/app/MutationSchemaDeriverSpec.groovy @@ -27,7 +27,7 @@ class MutationSchemaDeriverSpec extends Specification { def cql = new CqlImpl() pathParser = new PathParserSql(syntax, cql) - pathParser2 = new SqlPathParser(defaults, cql, filterEncoder) + pathParser2 = new SqlPathParser(defaults, cql, Set.of()) schemaBuilderSql = new MutationSchemaDeriver(pathParser, pathParser2) } diff --git a/xtraplatform-features-sql/src/test/groovy/de/ii/xtraplatform/features/sql/app/MutationSchemaFixtures.groovy b/xtraplatform-features-sql/src/test/groovy/de/ii/xtraplatform/features/sql/app/MutationSchemaFixtures.groovy index 8fea5f8ce..b7b8feea1 100644 --- a/xtraplatform-features-sql/src/test/groovy/de/ii/xtraplatform/features/sql/app/MutationSchemaFixtures.groovy +++ b/xtraplatform-features-sql/src/test/groovy/de/ii/xtraplatform/features/sql/app/MutationSchemaFixtures.groovy @@ -25,7 +25,7 @@ class MutationSchemaFixtures { .name("externalprovider_externalprovidername") .type(Type.OBJECT_ARRAY) .parentPath(["externalprovider"]) - .addRelation(ImmutableSqlRelation.builder() + .addRelation(new ImmutableSqlRelation.Builder() .cardinality(SqlRelation.CARDINALITY.ONE_2_N) .sourceContainer("externalprovider") .sourceField("id") @@ -52,7 +52,7 @@ class MutationSchemaFixtures { .name("explorationsite_task") .type(Type.OBJECT_ARRAY) .parentPath(["explorationsite"]) - .addRelation(ImmutableSqlRelation.builder() + .addRelation(new ImmutableSqlRelation.Builder() .cardinality(SqlRelation.CARDINALITY.ONE_2_N) .sourceContainer("explorationsite") .sourceField("id") @@ -64,7 +64,7 @@ class MutationSchemaFixtures { .sourcePath("task") .type(Type.OBJECT) .parentPath(["explorationsite"]) - .addRelation(ImmutableSqlRelation.builder() + .addRelation(new ImmutableSqlRelation.Builder() .cardinality(SqlRelation.CARDINALITY.ONE_2_ONE) .sourceContainer("explorationsite_task") .sourceField("task_fk") diff --git a/xtraplatform-features/src/main/java/de/ii/xtraplatform/features/domain/FeatureTokenDecoder.java b/xtraplatform-features/src/main/java/de/ii/xtraplatform/features/domain/FeatureTokenDecoder.java index 376530c5f..b66047307 100644 --- a/xtraplatform-features/src/main/java/de/ii/xtraplatform/features/domain/FeatureTokenDecoder.java +++ b/xtraplatform-features/src/main/java/de/ii/xtraplatform/features/domain/FeatureTokenDecoder.java @@ -107,7 +107,8 @@ public final W createContext() { return (W) ModifiableGenericContext.create() .setMappings(ImmutableMap.of()) - .setQuery(ImmutableFeatureQuery.builder().type("default").build()); + .setQuery(ImmutableFeatureQuery.builder().type("default").build()) + .setType("default"); } protected final FeatureEventHandler getDownstream() { diff --git a/xtraplatform-features/src/testFixtures/groovy/de/ii/xtraplatform/features/domain/FeatureTokenFixtures.groovy b/xtraplatform-features/src/testFixtures/groovy/de/ii/xtraplatform/features/domain/FeatureTokenFixtures.groovy index 74d0378a3..598e6300d 100644 --- a/xtraplatform-features/src/testFixtures/groovy/de/ii/xtraplatform/features/domain/FeatureTokenFixtures.groovy +++ b/xtraplatform-features/src/testFixtures/groovy/de/ii/xtraplatform/features/domain/FeatureTokenFixtures.groovy @@ -11,6 +11,9 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.google.common.io.Resources import de.ii.xtraplatform.geometries.domain.SimpleFeatureGeometry +import java.util.stream.Collectors +import java.util.stream.Stream + import static de.ii.xtraplatform.features.domain.SchemaBase.Type class FeatureTokenFixtures { @@ -22,6 +25,18 @@ class FeatureTokenFixtures { return YAML.readValue(Resources.toByteArray(resource), FeatureTokens.class); } + public static List withType(String type, List tokens) { + return tokens.stream().flatMap { + if (it instanceof List) { + return Stream.of([type] + ((List) it)) + } + if (it == FeatureTokenType.FEATURE) { + return [it, [type]].stream() + } + return Stream.of(it) + }.collect(Collectors.toList()); + } + public static final List SINGLE_FEATURE = [ FeatureTokenType.INPUT, true, @@ -38,22 +53,7 @@ class FeatureTokenFixtures { FeatureTokenType.INPUT_END ] - public static final List SINGLE_FEATURE_SQL = [ - FeatureTokenType.INPUT, - true, - FeatureTokenType.FEATURE, - ["biotop"], - FeatureTokenType.VALUE, - ["biotop", "id"], - "24", - Type.STRING, - FeatureTokenType.VALUE, - ["biotop", "kennung"], - "611320001-1", - Type.STRING, - FeatureTokenType.FEATURE_END, - FeatureTokenType.INPUT_END - ] + public static final List SINGLE_FEATURE_SOURCE = withType("biotop", SINGLE_FEATURE) public static final List SINGLE_FEATURE_POINT = [ FeatureTokenType.INPUT, @@ -89,6 +89,8 @@ class FeatureTokenFixtures { FeatureTokenType.INPUT_END ] + public static final List SINGLE_FEATURE_POINT_SOURCE = withType("biotop", SINGLE_FEATURE_POINT) + public static final List SINGLE_FEATURE_MULTI_POINT = [ FeatureTokenType.INPUT, true, @@ -131,6 +133,8 @@ class FeatureTokenFixtures { FeatureTokenType.INPUT_END ] + public static final List SINGLE_FEATURE_MULTI_POINT_SOURCE = withType("biotop", SINGLE_FEATURE_MULTI_POINT) + public static final List SINGLE_FEATURE_MULTI_POLYGON = [ FeatureTokenType.INPUT, true, @@ -225,6 +229,8 @@ class FeatureTokenFixtures { FeatureTokenType.INPUT_END ] + public static final List SINGLE_FEATURE_MULTI_POLYGON_SOURCE = withType("biotop", SINGLE_FEATURE_MULTI_POLYGON) + public static final List SINGLE_FEATURE_POLYGON_INNER = [ FeatureTokenType.INPUT, true, @@ -293,6 +299,8 @@ class FeatureTokenFixtures { FeatureTokenType.INPUT_END ] + public static final List SINGLE_FEATURE_NESTED_OBJECT_SOURCE = withType("biotop", SINGLE_FEATURE_NESTED_OBJECT) + public static final List SINGLE_FEATURE_NESTED_OBJECT_EMPTY = [ FeatureTokenType.INPUT, true, @@ -361,6 +369,8 @@ class FeatureTokenFixtures { FeatureTokenType.INPUT_END ] + public static final List SINGLE_FEATURE_VALUE_ARRAY_SOURCE = withType("biotop", SINGLE_FEATURE_VALUE_ARRAY) + public static final List SINGLE_FEATURE_VALUE_ARRAY_EMPTY = [ FeatureTokenType.INPUT, true, @@ -561,6 +571,8 @@ class FeatureTokenFixtures { FeatureTokenType.INPUT_END ] + public static final List SINGLE_FEATURE_NESTED_OBJECT_ARRAYS_SOURCE = withType("biotop", SINGLE_FEATURE_NESTED_OBJECT_ARRAYS) + public static final List OBJECT_WITHOUT_SOURCE_PATH = [ FeatureTokenType.INPUT, true, @@ -786,6 +798,8 @@ class FeatureTokenFixtures { FeatureTokenType.INPUT_END ] + public static final List COLLECTION_SOURCE = withType("biotop", COLLECTION) + public static final List STUFF = [ FeatureTokenType.VALUE, ["oid"],