diff --git a/.github/workflows/reusable-build-and-publish-v3s.yml b/.github/workflows/reusable-build-and-publish-v3s.yml index dfdf47d44..0ca2b426a 100644 --- a/.github/workflows/reusable-build-and-publish-v3s.yml +++ b/.github/workflows/reusable-build-and-publish-v3s.yml @@ -55,7 +55,7 @@ jobs: # run: gradle shadowJar jacocoTestReport jacocoTestCoverageVerification shell: bash run: | - array=( here-naksha-lib-model here-naksha-lib-base here-naksha-lib-core here-naksha-lib-psql here-naksha-lib-view here-naksha-storage-http here-naksha-lib-diff here-naksha-lib-handlers here-naksha-lib-hub ) + array=( here-naksha-lib-model here-naksha-lib-base here-naksha-lib-core here-naksha-lib-psql here-naksha-lib-view here-naksha-storage-http here-naksha-lib-diff here-naksha-lib-handlers here-naksha-lib-hub here-naksha-handler-activitylog) for item in ${array[@]}; do gradle -x jsNodeTest :$item:build :$item:jacocoTestReport :$item:jacocoTestCoverageVerification done diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index c2829feab..8bbbb107b 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -16,4 +16,5 @@ dependencies { implementation("com.diffplug.spotless:spotless-plugin-gradle:6.22.0") implementation("org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin:2.0.20") implementation("org.jetbrains.kotlin.plugin.js-plain-objects:org.jetbrains.kotlin.plugin.js-plain-objects.gradle.plugin:2.0.20") + implementation("org.jetbrains.kotlin.jvm:org.jetbrains.kotlin.jvm.gradle.plugin:2.1.0") } \ No newline at end of file diff --git a/here-naksha-handler-activitylog/build.gradle.kts b/here-naksha-handler-activitylog/build.gradle.kts index cbf99c1f3..58b2e2694 100644 --- a/here-naksha-handler-activitylog/build.gradle.kts +++ b/here-naksha-handler-activitylog/build.gradle.kts @@ -1,12 +1,14 @@ plugins { id("naksha.java") id("naksha.publish") + kotlin("jvm") } description = "Naksha Activity Log Handler" dependencies { implementation(project(":here-naksha-lib-core")) implementation(project(":here-naksha-lib-psql")) implementation(project(":here-naksha-lib-handlers")) + implementation(project(":here-naksha-lib-diff")) implementation(Lib.flipkart_zjsonpatch) testImplementation(Lib.jayway_jsonpath) @@ -14,4 +16,7 @@ dependencies { testImplementation(Lib.json_assert) testImplementation(testFixtures(project(":here-naksha-lib-core"))) } -setOverallCoverage(0.4) // only increasing allowed! \ No newline at end of file +setOverallCoverage(0.4) // only increasing allowed! +kotlin { + jvmToolchain(17) +} \ No newline at end of file diff --git a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogComparator.java b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogComparator.java index 6e02afda6..c8bd3bdc7 100644 --- a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogComparator.java +++ b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogComparator.java @@ -18,14 +18,14 @@ */ package com.here.naksha.handler.activitylog; -import naksha.model.XyzFeature; -import com.here.naksha.lib.core.models.geojson.implementation.namespaces.XyzNamespace; import java.util.Comparator; +import naksha.model.XyzNs; +import naksha.model.objects.NakshaFeature; -public class ActivityLogComparator implements Comparator { +public class ActivityLogComparator implements Comparator { @Override - public int compare(XyzFeature featureA, XyzFeature featureB) { + public int compare(NakshaFeature featureA, NakshaFeature featureB) { int updatedAtComparison = Long.compare(updatedAt(featureA), updatedAt(featureB)); if (updatedAtComparison == 0) { return uuid(featureA).compareTo(uuid(featureB)) * -1; @@ -33,15 +33,15 @@ public int compare(XyzFeature featureA, XyzFeature featureB) { return updatedAtComparison * -1; } - private static String uuid(XyzFeature feature) { + private static String uuid(NakshaFeature feature) { return xyzNamespace(feature).getUuid(); } - private static long updatedAt(XyzFeature feature) { - return xyzNamespace(feature).getUpdatedAt(); + private static long updatedAt(NakshaFeature feature) { + return xyzNamespace(feature).getUpdatedAt().toLong(); } - private static XyzNamespace xyzNamespace(XyzFeature feature) { - return feature.getProperties().getXyzNamespace(); + private static XyzNs xyzNamespace(NakshaFeature feature) { + return feature.getProperties().getXyz(); } } diff --git a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogEnhancer.java b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogEnhancer.java index 2dfb007cf..884a987b0 100644 --- a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogEnhancer.java +++ b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogEnhancer.java @@ -22,11 +22,10 @@ import static com.here.naksha.handler.activitylog.ReversePatchUtil.toJsonNode; import com.fasterxml.jackson.databind.JsonNode; -import naksha.model.EXyzAction; -import naksha.model.XyzFeature; -import com.here.naksha.lib.core.models.geojson.implementation.namespaces.Original; -import com.here.naksha.lib.core.models.geojson.implementation.namespaces.XyzActivityLog; -import com.here.naksha.lib.core.models.geojson.implementation.namespaces.XyzNamespace; +import naksha.model.Action; +import naksha.model.XyzNs; +import naksha.model.objects.NakshaFeature; +import naksha.model.objects.NakshaProperties; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -38,34 +37,32 @@ public class ActivityLogEnhancer { private ActivityLogEnhancer() {} - static XyzFeature enhanceWithActivityLog( - @NotNull XyzFeature newFeature, @Nullable XyzFeature oldFeature, @NotNull String spaceId) { - XyzActivityLog activityLog = activityLog(newFeature, oldFeature, spaceId); - newFeature.getProperties().setXyzActivityLog(activityLog); + static NakshaFeature enhanceWithActivityLog( + @NotNull NakshaFeature newFeature, @Nullable NakshaFeature oldFeature, @NotNull String spaceId) { + NakshaActivityLog activityLog = activityLog(newFeature, oldFeature, spaceId); + newFeature.getProperties().put(NakshaProperties.XYZ_ACTIVITY_LOG_NS, activityLog); newFeature.setId(uuid(newFeature)); return newFeature; } - private static XyzActivityLog activityLog( - @NotNull XyzFeature newFeature, @Nullable XyzFeature oldFeature, @NotNull String spaceId) { - final XyzNamespace xyzNamespace = xyzNamespace(newFeature); - final XyzActivityLog xyzActivityLog = new XyzActivityLog(); - xyzActivityLog.setId(newFeature.getId()); - xyzActivityLog.setOriginal(original(xyzNamespace, spaceId)); - EXyzAction action = xyzNamespace.getAction(); - if (action != null) { - xyzActivityLog.setAction(action); - } - xyzActivityLog.setDiff(calculateDiff(action, newFeature, oldFeature)); - return xyzActivityLog; + private static NakshaActivityLog activityLog( + @NotNull NakshaFeature newFeature, @Nullable NakshaFeature oldFeature, @NotNull String spaceId) { + final XyzNs xyzNamespace = xyzNamespace(newFeature); + final NakshaActivityLog activityLog = new NakshaActivityLog(); + activityLog.setId(newFeature.getId()); + activityLog.setOriginal(original(xyzNamespace, spaceId)); + Action action = xyzNamespace.getAction(); + activityLog.setAction(action.toString()); + activityLog.setDiff(calculateDiff(action, newFeature, oldFeature)); + return activityLog; } - private static Original original(@Nullable XyzNamespace xyzNamespace, @Nullable String spaceId) { + private static Original original(@Nullable XyzNs xyzNamespace, @Nullable String spaceId) { Original original = new Original(); if (xyzNamespace != null) { original.setPuuid(xyzNamespace.getPuuid()); - original.setUpdatedAt(xyzNamespace.getUpdatedAt()); - original.setCreatedAt(xyzNamespace.getCreatedAt()); + original.setUpdatedAt(xyzNamespace.getUpdatedAt().toLong()); + original.setCreatedAt(xyzNamespace.getCreatedAt().toLong()); } if (spaceId != null) { original.setSpace(spaceId); @@ -74,10 +71,10 @@ private static Original original(@Nullable XyzNamespace xyzNamespace, @Nullable } private static @Nullable JsonNode calculateDiff( - @Nullable EXyzAction action, @NotNull XyzFeature newFeature, @Nullable XyzFeature oldFeature) { - if (action == null || EXyzAction.CREATE.equals(action) || EXyzAction.DELETE.equals(action)) { + @Nullable Action action, @NotNull NakshaFeature newFeature, @Nullable NakshaFeature oldFeature) { + if (action == null || Action.CREATED.equals(action) || Action.DELETED.equals(action)) { return null; - } else if (EXyzAction.UPDATE.equals(action)) { + } else if (Action.UPDATED.equals(action)) { if (oldFeature == null) { logger.warn( "Unable to calculate reversePatch for, missing predecessor for feature with uuid: {}, returning null", @@ -91,11 +88,11 @@ private static Original original(@Nullable XyzNamespace xyzNamespace, @Nullable } } - private static String uuid(XyzFeature feature) { + private static String uuid(NakshaFeature feature) { return xyzNamespace(feature).getUuid(); } - private static XyzNamespace xyzNamespace(XyzFeature feature) { - return feature.getProperties().getXyzNamespace(); + private static XyzNs xyzNamespace(NakshaFeature feature) { + return feature.getProperties().getXyz(); } } diff --git a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogHandler.java b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogHandler.java index 9f170103f..1b834f41c 100644 --- a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogHandler.java +++ b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogHandler.java @@ -24,35 +24,26 @@ import static com.here.naksha.lib.handlers.AbstractEventHandler.EventProcessingStrategy.SUCCEED_WITHOUT_PROCESSING; import static java.util.function.Function.identity; import static java.util.stream.Collectors.toMap; +import static naksha.model.util.ResultHelper.extractResponseItems; import com.here.naksha.lib.core.IEvent; import com.here.naksha.lib.core.INaksha; -import naksha.model.util.ResultHelper; -import naksha.model.NakshaContext; -import com.here.naksha.lib.core.exceptions.NoCursor; -import com.here.naksha.lib.core.models.XyzError; -import naksha.model.XyzFeature; -import com.here.naksha.lib.core.models.geojson.implementation.namespaces.XyzNamespace; import com.here.naksha.lib.core.models.naksha.EventHandler; -import com.here.naksha.lib.core.models.naksha.EventTarget; -import naksha.model.ErrorResult; -import naksha.model.POp; -import naksha.model.PRef; -import naksha.model.ReadFeatures; -import naksha.model.Request; -import com.here.naksha.lib.core.models.storage.Result; -import naksha.model.WriteCollections; -import naksha.model.IReadSession; -import com.here.naksha.lib.core.util.json.JsonSerializable; import com.here.naksha.lib.handlers.AbstractEventHandler; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; +import com.here.naksha.lib.handlers.util.RequestTypesUtil; +import java.util.*; import java.util.stream.Stream; +import naksha.base.JvmBoxingUtil; +import naksha.base.StringList; +import naksha.model.*; +import naksha.model.objects.NakshaFeature; +import naksha.model.objects.NakshaFeatureList; +import naksha.model.objects.NakshaProperties; +import naksha.model.request.*; +import naksha.model.request.query.POr; +import naksha.model.request.query.PQuery; +import naksha.model.request.query.Property; +import naksha.model.request.query.StringOp; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -60,74 +51,81 @@ public class ActivityLogHandler extends AbstractEventHandler { - private static final Comparator FEATURE_COMPARATOR = new ActivityLogComparator(); + private static final Comparator FEATURE_COMPARATOR = new ActivityLogComparator(); private final @NotNull Logger logger = LoggerFactory.getLogger(ActivityLogHandler.class); private final @NotNull ActivityLogHandlerProperties properties; // TODO: remove unused 'eventTarget' property as part of MCPODS-7103 public ActivityLogHandler( - @NotNull EventHandler handlerConfig, @NotNull INaksha hub, @NotNull EventTarget eventTarget) { + @NotNull EventHandler handlerConfig, @NotNull INaksha hub) { super(hub); - this.properties = JsonSerializable.convert(handlerConfig.getProperties(), ActivityLogHandlerProperties.class); + this.properties = Objects.requireNonNull( + JvmBoxingUtil.box(handlerConfig.getProperties(), ActivityLogHandlerProperties.class)); } @Override protected EventProcessingStrategy processingStrategyFor(IEvent event) { - final Request request = event.getRequest(); + final Request request = event.getRequest(); if (request instanceof ReadFeatures) { return PROCESS; } - if (request instanceof WriteCollections) { + if (request instanceof WriteRequest && RequestTypesUtil.isOnlyWriteCollections(request)) { return SUCCEED_WITHOUT_PROCESSING; } return NOT_IMPLEMENTED; } @Override - protected @NotNull Result process(@NotNull IEvent event) { - final ErrorResult validationError = propertiesValidationError(); + protected @NotNull Response process(@NotNull IEvent event) { + final ErrorResponse validationError = propertiesValidationError(); if (validationError != null) { return validationError; } final NakshaContext ctx = NakshaContext.currentContext(); final ReadFeatures request = transformRequest(event.getRequest()); - List activityLogFeatures = activityLogFeatures(request, ctx); - return ActivityLogSuccessResult.forFeatures(activityLogFeatures); + List activityLogFeatures = activityLogFeatures(request, ctx); + return new SuccessResponse(NakshaFeatureList.fromList(activityLogFeatures)); } - private @NotNull ReadFeatures transformRequest(Request request) { + private @NotNull ReadFeatures transformRequest(Request request) { final ReadFeatures readFeatures = (ReadFeatures) request; - readFeatures.withReturnAllVersions(true); + readFeatures.setQueryHistory(true); + readFeatures.setVersions(Integer.MAX_VALUE); ActivityLogRequestTranslationUtil.translatePropertyOperation(readFeatures); - readFeatures.setCollections(List.of(properties.getSpaceId())); + readFeatures.setCollectionIds(StringList.fromList(List.of(properties.getSpaceId()))); return readFeatures; } - private @Nullable ErrorResult propertiesValidationError() { + private @Nullable ErrorResponse propertiesValidationError() { if (nullOrEmpty(properties.getSpaceId())) { - return new ErrorResult( - XyzError.ILLEGAL_ARGUMENT, "Missing required property: " + ActivityLogHandlerProperties.SPACE_ID); + return new ErrorResponse( + NakshaError.ILLEGAL_ARGUMENT, + "Missing required property: " + ActivityLogHandlerProperties.SPACE_ID); } return null; } - private List activityLogFeatures(ReadFeatures readFeatures, NakshaContext context) { - List historyFeatures = fetchHistoryFeatures(readFeatures, context); + private List activityLogFeatures(ReadFeatures readFeatures, NakshaContext context) { + List historyFeatures = fetchHistoryFeatures(readFeatures, context); return featuresEnhancedWithActivity(historyFeatures, context); } - private List fetchHistoryFeatures(ReadFeatures readFeatures, NakshaContext context) { - try (IReadSession readSession = nakshaHub().getSpaceStorage().newReadSession(context, true)) { - try (Result result = readSession.execute(readFeatures)) { - return ResultHelper.extractResponseItems(result, XyzFeature.class); + private List fetchHistoryFeatures(ReadFeatures readFeatures, NakshaContext context) { + try (IReadSession readSession = + nakshaHub().getSpaceStorage().newReadSession(SessionOptions.from(context, true))) { + Response result = readSession.execute(readFeatures); + if (!(result instanceof SuccessResponse)) { + return Collections.emptyList(); } - } catch (NoCursor | NoSuchElementException e) { + return extractResponseItems((SuccessResponse) result, NakshaFeature.class); + } catch (NoSuchElementException e) { return Collections.emptyList(); } } - private List featuresEnhancedWithActivity(List historyFeatures, NakshaContext context) { + private List featuresEnhancedWithActivity( + List historyFeatures, NakshaContext context) { List featuresWithPredecessors = featuresWithPredecessors(historyFeatures, context); return featuresWithPredecessors.stream() .map(featureWithPredecessor -> enhanceWithActivityLog( @@ -137,20 +135,21 @@ private List featuresEnhancedWithActivity(List historyFe } private List featuresWithPredecessors( - List historyFeatures, NakshaContext context) { - List allNecessaryFeatures = collectAllNecessaryFeatures(historyFeatures, context); - Map allFeaturesByUuid = featuresByUuid(allNecessaryFeatures); + List historyFeatures, NakshaContext context) { + List allNecessaryFeatures = collectAllNecessaryFeatures(historyFeatures, context); + Map allFeaturesByUuid = featuresByUuid(allNecessaryFeatures); return historyFeatures.stream() .map(feature -> new FeatureWithPredecessor(feature, allFeaturesByUuid.get(puuid(feature)))) .toList(); } - private List collectAllNecessaryFeatures(List historyFeatures, NakshaContext context) { - List missingPredecessors = fetchMissingPredecessors(missingPuuids(historyFeatures), context); + private List collectAllNecessaryFeatures( + List historyFeatures, NakshaContext context) { + List missingPredecessors = fetchMissingPredecessors(missingPuuids(historyFeatures), context); return combine(historyFeatures, missingPredecessors); } - private List combine(List historyFeatures, List missingPredecessors) { + private List combine(List historyFeatures, List missingPredecessors) { if (missingPredecessors.isEmpty()) { return historyFeatures; } @@ -158,7 +157,7 @@ private List combine(List historyFeatures, List missingPuuids(List historyFeatures) { + private Set missingPuuids(List historyFeatures) { Set requiredPredecessorsUuids = new HashSet<>(); Set fetchedUuids = new HashSet<>(); historyFeatures.forEach(historyFeature -> { @@ -172,7 +171,7 @@ private Set missingPuuids(List historyFeatures) { return requiredPredecessorsUuids; } - private List fetchMissingPredecessors(Set missingUuids, NakshaContext context) { + private List fetchMissingPredecessors(Set missingUuids, NakshaContext context) { if (missingUuids.isEmpty()) { return Collections.emptyList(); } @@ -180,16 +179,19 @@ private List fetchMissingPredecessors(Set missingUuids, Naks } private ReadFeatures missingPredecessorsRequest(Set missingUuids) { - POp[] matchUuids = missingUuids.stream() - .map(missingUuid -> POp.eq(PRef.uuid(), missingUuid)) - .toArray(POp[]::new); - return new ReadFeatures(properties.getSpaceId()) - .withReturnAllVersions(true) - .withPropertyOp(POp.or(matchUuids)); + PQuery[] matchUuids = missingUuids.stream() + .map(missingUuid -> + new PQuery(new Property(NakshaProperties.XYZ_KEY, "uuid"), StringOp.EQUALS, missingUuid)) + .toArray(PQuery[]::new); + final ReadFeatures readFeatures = new ReadFeatures(properties.getSpaceId()); + readFeatures.setQueryHistory(true); + readFeatures.setVersions(Integer.MAX_VALUE); + readFeatures.getQuery().setProperties(new POr(matchUuids)); + return readFeatures; } @NotNull - private static Map featuresByUuid(List historyFeatures) { + private static Map featuresByUuid(List historyFeatures) { return historyFeatures.stream().collect(toMap(ActivityLogHandler::uuid, identity())); } @@ -197,17 +199,17 @@ private static boolean nullOrEmpty(String value) { return value == null || value.isBlank(); } - private static String uuid(XyzFeature feature) { + private static String uuid(NakshaFeature feature) { return xyzNamespace(feature).getUuid(); } - private static String puuid(XyzFeature feature) { + private static String puuid(NakshaFeature feature) { return xyzNamespace(feature).getPuuid(); } - private static XyzNamespace xyzNamespace(XyzFeature feature) { - return feature.getProperties().getXyzNamespace(); + private static XyzNs xyzNamespace(NakshaFeature feature) { + return feature.getProperties().getXyz(); } - private record FeatureWithPredecessor(@NotNull XyzFeature feature, @Nullable XyzFeature oldFeature) {} + private record FeatureWithPredecessor(@NotNull NakshaFeature feature, @Nullable NakshaFeature oldFeature) {} } diff --git a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogHandlerProperties.java b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogHandlerProperties.java index a9b9318a4..ece271639 100644 --- a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogHandlerProperties.java +++ b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogHandlerProperties.java @@ -21,12 +21,12 @@ import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; import naksha.model.NakshaVersion; -import naksha.geo.XyzProperties; +import naksha.model.objects.NakshaProperties; import org.jetbrains.annotations.ApiStatus.AvailableSince; import org.jetbrains.annotations.NotNull; @AvailableSince(NakshaVersion.v2_0_14) -public class ActivityLogHandlerProperties extends XyzProperties { +public class ActivityLogHandlerProperties extends NakshaProperties { @AvailableSince(NakshaVersion.v2_0_14) public static final String SPACE_ID = "spaceId"; diff --git a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogRequestTranslationUtil.java b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogRequestTranslationUtil.java index 4dc67c519..5b0a002b2 100644 --- a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogRequestTranslationUtil.java +++ b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogRequestTranslationUtil.java @@ -18,21 +18,33 @@ */ package com.here.naksha.handler.activitylog; -import static com.here.naksha.lib.core.models.geojson.implementation.namespaces.XyzActivityLog.ID; -import static naksha.geo.XyzProperties.XYZ_ACTIVITY_LOG_NS; -import static naksha.model.request.query.Property.PROPERTIES; +import static com.here.naksha.handler.activitylog.NakshaActivityLog.ID; +import static naksha.model.objects.NakshaProperties.XYZ_ACTIVITY_LOG_NS; +import static naksha.model.objects.NakshaProperties.XYZ_KEY; import com.here.naksha.lib.handlers.util.PropertyOperationUtil; import java.util.Optional; + +import naksha.base.StringList; import naksha.model.request.ReadFeatures; +import naksha.model.request.query.IPropertyQuery; +import naksha.model.request.query.PQuery; import naksha.model.request.query.Property; +import naksha.model.request.query.StringOp; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; class ActivityLogRequestTranslationUtil { - private static final String[] ACTIVITY_LOG_ID_PATH = new String[] {PROPERTIES, XYZ_ACTIVITY_LOG_NS, ID}; - static final Property PREF_ACTIVITY_LOG_ID = new Property(ACTIVITY_LOG_ID_PATH); + static final String UUID = "uuid"; + static final String PUUID = "puuid"; + static final String ACTION = "action"; + static final String CREATED_AT = "createdAt"; + static final String UPDATED_AT = "updatedAt"; + + private static final String[] ACTIVITY_LOG_ID_PATH = new String[] {XYZ_ACTIVITY_LOG_NS, ID}; + private static final String[] UUID_PATH = new String[] {XYZ_KEY, UUID}; + static final Property PROPERTY_ACTIVITY_LOG_ID = new Property(ACTIVITY_LOG_ID_PATH); + static final Property PROPERTY_UUID = new Property(UUID_PATH); private ActivityLogRequestTranslationUtil() {} @@ -43,58 +55,55 @@ private ActivityLogRequestTranslationUtil() {} *
* Translation applies to given source-target pairs: *
    - *
  • 'id' ({{@link PRef#id()}} => 'properties.@ns:com:here:xyz.uuid' ({{@link PRef#uuid()}})
  • - *
  • 'properties.@ns:com:here:xyz:log.id' ({{@link #PREF_ACTIVITY_LOG_ID}}) => 'id' ({{@link PRef#id()}})
  • + *
  • 'id' => 'properties.@ns:com:here:xyz.uuid'
  • + *
  • 'properties.@ns:com:here:xyz:log.id' => 'id'
  • *
* Translation is required because the ReadRequest that reach {{@link ActivityLogHandler}} are being delegated to HistoryHandler * * @param readFeatures ReadFeatures bearing potential POp to be translated (request will be mutated after this operation!) */ static void translatePropertyOperation(ReadFeatures readFeatures) { - POp pOp = readFeatures.getPropertyOp(); - if (pOp != null) { - if (hasChildren(pOp)) { - PropertyOperationUtil.transformPropertyInPropertyOperationTree( - pOp, ActivityLogRequestTranslationUtil::translateIfApplicable); - } else { - translateIfApplicable(pOp).ifPresent(readFeatures::setPropertyOp); - } + IPropertyQuery propertyQuery = readFeatures.getQuery().getProperties(); + if (propertyQuery != null) { + PropertyOperationUtil.transformPropertyInPropertyOperationTree( + propertyQuery, ActivityLogRequestTranslationUtil::translateIfApplicable); } } - private static Optional translateIfApplicable(POp pOp) { - if (isSingleIdEqualityQuery(pOp)) { - String featureUuid = (String) pOp.getValue(); + private static Optional translateIfApplicable(PQuery pQuery) { + if (isSingleIdEqualityQuery(pQuery)) { + String featureUuid = (String) pQuery.getValue(); return Optional.of(uuidMustMatch(featureUuid)); - } else if (isSingleActivityLogIdEqualityQuery(pOp)) { - String activityLogId = (String) pOp.getValue(); + } else if (isSingleActivityLogIdEqualityQuery(pQuery)) { + String activityLogId = (String) pQuery.getValue(); return Optional.of(idMustMatch(activityLogId)); } return Optional.empty(); } - private static boolean hasChildren(POp pOp) { - List maybeChildren = pOp.children(); - return maybeChildren != null && !maybeChildren.isEmpty(); - } - - private static boolean isSingleIdEqualityQuery(@NotNull POp pOp) { - return pOp.op().equals(POpType.EQ) && sameRefs(PRef.id(), pOp.getPropertyRef()); - } - - private static boolean isSingleActivityLogIdEqualityQuery(POp pOp) { - return pOp.op().equals(POpType.EQ) && sameRefs(PREF_ACTIVITY_LOG_ID, pOp.getPropertyRef()); + private static boolean isSingleIdEqualityQuery(@NotNull PQuery pQuery) { + final StringList path = pQuery.getProperty().getPath(); + return StringOp.EQUALS.equals(pQuery.getOp()) && path.size() == 1 + && Property.ID.equals(path.get(0)); } - private static boolean sameRefs(@NotNull PRef expected, @Nullable PRef actual) { - return actual != null && expected.getPath().equals(actual.getPath()); + private static boolean isSingleActivityLogIdEqualityQuery(PQuery pQuery) { + return StringOp.EQUALS.equals(pQuery.getOp()) && PROPERTY_ACTIVITY_LOG_ID.equals(pQuery.getProperty()); } - private static POp uuidMustMatch(String desiredUuid) { - return POp.eq(PRef.uuid(), desiredUuid); + private static PQuery uuidMustMatch(String desiredUuid) { + final PQuery pQuery = new PQuery(); + pQuery.setOp(StringOp.EQUALS); + pQuery.setValue(desiredUuid); + pQuery.setProperty(PROPERTY_UUID); + return pQuery; } - private static POp idMustMatch(String desiredId) { - return POp.eq(PRef.id(), desiredId); + private static PQuery idMustMatch(String desiredId) { + final PQuery pQuery = new PQuery(); + pQuery.setOp(StringOp.EQUALS); + pQuery.setValue(desiredId); + pQuery.setProperty(new Property(ID)); + return pQuery; } } diff --git a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogSuccessResult.java b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogSuccessResult.java deleted file mode 100644 index d4b02c0b1..000000000 --- a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ActivityLogSuccessResult.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2017-2024 HERE Europe B.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * License-Filename: LICENSE - */ -package com.here.naksha.handler.activitylog; - -import naksha.model.XyzFeature; -import com.here.naksha.lib.core.models.storage.*; -import java.util.List; - -public class ActivityLogSuccessResult extends SuccessResult { - - private ActivityLogSuccessResult(ForwardCursor cursor) { - this.cursor = cursor; - } - - static ActivityLogSuccessResult forFeatures(List features) { - XyzFeatureCodecFactory codecFactory = XyzFeatureCodecFactory.get(); - List codecs = features.stream() - .map(feature -> featureCodec(codecFactory, feature)) - .toList(); - return new ActivityLogSuccessResult(new HeapCacheCursor<>(codecFactory, codecs, null)); - } - - private static XyzFeatureCodec featureCodec(XyzFeatureCodecFactory codecFactory, XyzFeature feature) { - return codecFactory.newInstance().withOp(EExecutedOp.READ).withFeature(feature); - } -} diff --git a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ReversePatch.java b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ReversePatch.java index 133e2f6c5..d28ba7a91 100644 --- a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ReversePatch.java +++ b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ReversePatch.java @@ -23,6 +23,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import naksha.diff.RemoveOp; import naksha.diff.UpdateOp; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -90,13 +91,13 @@ Builder reverseInsert(String path) { Builder reverseRemove(RemoveOp removeOp, String path) { insert++; - ops.add(PatchOp.insert(path, removeOp.oldValue())); + ops.add(PatchOp.insert(path, removeOp.getOldValue())); return this; } Builder reverseUpdate(UpdateOp updateOp, String path) { update++; - ops.add(PatchOp.update(path, updateOp.oldValue())); + ops.add(PatchOp.update(path, updateOp.getOldValue())); return this; } } diff --git a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ReversePatchUtil.java b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ReversePatchUtil.java index d691ba7bf..b3caf325b 100644 --- a/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ReversePatchUtil.java +++ b/here-naksha-handler-activitylog/src/main/java/com/here/naksha/handler/activitylog/ReversePatchUtil.java @@ -22,18 +22,13 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import naksha.model.XyzFeature; -import naksha.geo.XyzProperties; -import naksha.model.PRef; -import naksha.diff.Difference; -import naksha.diff.InsertOp; -import naksha.diff.ListDiff; -import naksha.diff.MapDiff; -import naksha.diff.Patcher; -import naksha.diff.PrimitiveDiff; -import naksha.diff.RemoveOp; -import naksha.diff.UpdateOp; import java.util.Map; + +import naksha.diff.*; +import naksha.model.objects.NakshaFeature; +import naksha.model.objects.NakshaProperties; +import naksha.model.request.RequestQuery; +import naksha.model.request.query.Property; import org.jetbrains.annotations.Nullable; class ReversePatchUtil { @@ -44,11 +39,11 @@ class ReversePatchUtil { private static final String PATCH_PATH_DELIMITER = "/"; - private static final String ID_PATH = patchPath(ROOT_PATH, XyzFeature.ID); + private static final String ID_PATH = patchPath(ROOT_PATH, NakshaFeature.ID_KEY); private static final String XYZ_NAMESPACE_PATH = - patchPath(ROOT_PATH, XyzFeature.PROPERTIES, XyzProperties.XYZ_NAMESPACE); - private static final String XYZ_NAMESPACE_TAGS_PATH = patchPath(prependRoot(PRef.TAGS_PROP_PATH)); + patchPath(ROOT_PATH, Property.PROPERTIES, NakshaProperties.XYZ_KEY); + private static final String XYZ_NAMESPACE_TAGS_PATH = patchPath(prependRoot(RequestQuery.TAGS_PROP_PATH)); private ReversePatchUtil() {} @@ -56,8 +51,8 @@ static JsonNode toJsonNode(ReversePatch activityLogReversePatch) { return MAPPER.valueToTree(activityLogReversePatch); } - static @Nullable ReversePatch reversePatch(XyzFeature older, XyzFeature younger) { - Difference difference = Patcher.getDifference(older, younger); + static @Nullable ReversePatch reversePatch(NakshaFeature older, NakshaFeature younger) { + Difference difference = DifferenceCalculator.DifferenceCalculator_C.calculateDifference(older, younger); if (difference == null) { return null; } else { diff --git a/here-naksha-handler-activitylog/src/main/kotlin/com/here/naksha/handler/activitylog/NakshaActivityLog.kt b/here-naksha-handler-activitylog/src/main/kotlin/com/here/naksha/handler/activitylog/NakshaActivityLog.kt new file mode 100644 index 000000000..966da321b --- /dev/null +++ b/here-naksha-handler-activitylog/src/main/kotlin/com/here/naksha/handler/activitylog/NakshaActivityLog.kt @@ -0,0 +1,39 @@ +package com.here.naksha.handler.activitylog + +import com.fasterxml.jackson.databind.JsonNode +import naksha.base.AnyObject +import naksha.base.JvmBoxingUtil.box +import naksha.base.NotNullProperty +import naksha.base.NullableProperty +import naksha.model.objects.NakshaProperties + +class NakshaActivityLog : AnyObject() { + companion object { + const val ID = "id" + + private val STRING_NULL = NullableProperty(String::class) + private val ORIGINAL = NotNullProperty(Original::class) + private val DIFF_NULL = NullableProperty(JsonNode::class) + + @JvmStatic + fun getActivityLog(properties: NakshaProperties): NakshaActivityLog? = box( + properties[NakshaProperties.XYZ_ACTIVITY_LOG_NS], + NakshaActivityLog::class.java + ) + } + + /** The space ID the feature belongs to. */ + var id by STRING_NULL + + /** + * The operation that lead to the current state of the namespace. Should be a value from { + * [Action]. + */ + var action by STRING_NULL + + /** The Original tag. */ + var original by ORIGINAL + + /** The Difference tag. */ + var diff by DIFF_NULL +} \ No newline at end of file diff --git a/here-naksha-handler-activitylog/src/main/kotlin/com/here/naksha/handler/activitylog/Original.kt b/here-naksha-handler-activitylog/src/main/kotlin/com/here/naksha/handler/activitylog/Original.kt new file mode 100644 index 000000000..4e2e3ae2c --- /dev/null +++ b/here-naksha-handler-activitylog/src/main/kotlin/com/here/naksha/handler/activitylog/Original.kt @@ -0,0 +1,25 @@ +package com.here.naksha.handler.activitylog + +import naksha.base.AnyObject +import naksha.base.NullableProperty + +/** + * A subset of data stored under activity log. + */ +class Original : AnyObject() { + companion object { + private val STRING_NULL = NullableProperty(String::class) + private val LONG_NULL = NullableProperty(Long::class) + } + + /** The space ID the feature belongs to. */ + var space by STRING_NULL + /** The timestamp, when a feature was created. */ + var createdAt by LONG_NULL + /** The timestamp, when a feature was last updated. */ + var updatedAt by LONG_NULL + /** The previous uuid of the feature. */ + var puuid by STRING_NULL + /** The merge muuid of the feature. */ + var muuid by STRING_NULL +} \ No newline at end of file diff --git a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogEnhancerTest.java b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogEnhancerTest.java index 7c631b56f..6de29f9e3 100644 --- a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogEnhancerTest.java +++ b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogEnhancerTest.java @@ -1,6 +1,5 @@ package com.here.naksha.handler.activitylog; -import naksha.model.XyzFeature; import com.here.naksha.lib.core.util.json.JsonSerializable; import com.here.naksha.test.common.FileUtil; import java.io.File; @@ -8,6 +7,11 @@ import java.nio.file.Paths; import java.util.Arrays; import java.util.stream.Stream; + +import naksha.base.FromJsonOptions; +import naksha.base.JvmBoxingUtil; +import naksha.base.Platform; +import naksha.model.objects.NakshaFeature; import org.json.JSONException; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -27,10 +31,10 @@ class ActivityLogEnhancerTest { @ParameterizedTest @MethodSource("samples") - void shouldEnhanceFeatureWithPredecessor(String sampleDir, XyzFeature oldFeature, XyzFeature newFeature, String expectedFeatureJson) + void shouldEnhanceFeatureWithPredecessor(String sampleDir, NakshaFeature oldFeature, NakshaFeature newFeature, String expectedFeatureJson) throws JSONException { // When - XyzFeature enhancedFeature = ActivityLogEnhancer.enhanceWithActivityLog(newFeature, oldFeature, SPACE_ID); + NakshaFeature enhancedFeature = ActivityLogEnhancer.enhanceWithActivityLog(newFeature, oldFeature, SPACE_ID); // And String enhancedFeatureJson = JsonSerializable.serialize(enhancedFeature); @@ -57,10 +61,11 @@ private static Stream samples() { }); } - private static XyzFeature featureFromFile(String sampleDir, String fileName) { - return JsonSerializable.deserialize( - FileUtil.loadFileOrFail(sampleDir, fileName), - XyzFeature.class + private static NakshaFeature featureFromFile(String sampleDir, String fileName) { + String fileContent = FileUtil.loadFileOrFail(sampleDir, fileName); + return JvmBoxingUtil.box( + Platform.fromJSON(fileContent, FromJsonOptions.DEFAULT), + NakshaFeature.class ); } diff --git a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogHandlerTest.java b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogHandlerTest.java index 5f27f713a..bc0e391a7 100644 --- a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogHandlerTest.java +++ b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogHandlerTest.java @@ -1,15 +1,13 @@ package com.here.naksha.handler.activitylog; -import static com.here.naksha.handler.activitylog.ActivityLogRequestTranslationUtil.PREF_ACTIVITY_LOG_ID; +import static com.here.naksha.handler.activitylog.ActivityLogRequestTranslationUtil.*; import static com.here.naksha.handler.activitylog.assertions.ActivityLogSuccessResultAssertions.assertThatResult; -import static com.here.naksha.test.common.assertions.POpAssertion.assertThatOperation; +import static com.here.naksha.test.common.assertions.AssertionIPropertyQuery.assertThatOperation; import static java.util.Collections.emptyMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.AdditionalMatchers.not; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -25,38 +23,23 @@ import com.here.naksha.handler.activitylog.util.DatahubSamplesUtil.DatahubSample; import com.here.naksha.lib.core.IEvent; import com.here.naksha.lib.core.INaksha; -import com.here.naksha.lib.core.models.XyzError; -import naksha.model.EXyzAction; -import naksha.model.XyzFeature; -import naksha.geo.XyzProperties; -import com.here.naksha.lib.core.models.geojson.implementation.namespaces.XyzNamespace; +import com.here.naksha.test.common.assertions.AssertionIPropertyQuery; +import naksha.base.JvmInt64; +import naksha.model.*; import com.here.naksha.lib.core.models.naksha.EventHandler; -import com.here.naksha.lib.core.models.naksha.EventTarget; -import com.here.naksha.lib.core.models.naksha.XyzCollection; -import com.here.naksha.lib.core.models.storage.EExecutedOp; -import naksha.model.ErrorResult; -import com.here.naksha.lib.core.models.storage.ListBasedForwardCursor; -import naksha.model.OpType; -import naksha.model.POp; -import naksha.model.POpType; -import naksha.model.PRef; -import naksha.model.ReadCollections; -import naksha.model.ReadFeatures; -import naksha.model.ReadRequest; -import naksha.model.Request; -import com.here.naksha.lib.core.models.storage.Result; -import com.here.naksha.lib.core.models.storage.SuccessResult; -import com.here.naksha.lib.core.models.storage.WriteXyzCollections; -import com.here.naksha.lib.core.models.storage.WriteXyzFeatures; -import com.here.naksha.lib.core.models.storage.XyzFeatureCodec; -import com.here.naksha.lib.core.models.storage.XyzFeatureCodecFactory; -import naksha.model.IReadSession; -import naksha.model.IStorage; -import com.here.naksha.test.common.assertions.POpAssertion; + import java.util.List; import java.util.Map; import java.util.stream.Stream; +import naksha.model.objects.NakshaFeature; +import naksha.model.objects.NakshaFeatureList; +import naksha.model.objects.NakshaProperties; +import naksha.model.request.*; +import naksha.model.request.query.POr; +import naksha.model.request.query.PQuery; +import naksha.model.request.query.Property; +import naksha.model.request.query.StringOp; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -87,6 +70,7 @@ void setup() { MockitoAnnotations.openMocks(this); when(naksha.getSpaceStorage()).thenReturn(spaceStorage); handler = handlerForSpaceId(SPACE_ID); + } @ParameterizedTest @@ -96,37 +80,37 @@ void shouldFailWhenSpaceIdIsMissing(String missingSpaceId) { ActivityLogHandler handlerWithoutSpace = handlerForSpaceId(missingSpaceId); // When: handling some red features request - Result result = handlerWithoutSpace.process(eventWith(new ReadFeatures())); + Response result = handlerWithoutSpace.process(eventWith(new ReadFeatures())); // Then: error that indicates Illegal Argument is returned - assertInstanceOf(ErrorResult.class, result); - assertEquals(XyzError.ILLEGAL_ARGUMENT, ((ErrorResult) result).reason); + assertInstanceOf(ErrorResponse.class, result); + assertEquals(NakshaError.ILLEGAL_ARGUMENT, ((ErrorResponse) result).getError().getCode()); } @ParameterizedTest @MethodSource("unhandledRequests") - void shouldFailOnUnhandledRequests(Request unhandledRequest) { + void shouldFailOnUnhandledRequests(Request unhandledRequest) { // Given: event bearing some unhandler request IEvent event = eventWith(unhandledRequest); // When: handler tries to process such event - Result result = handler.processEvent(event); + Response result = handler.processEvent(event); // Then: Storage was not used at all verifyNoInteractions(spaceStorage); // And: Error result (NOT_IMPLEMENTED) was returned - assertInstanceOf(ErrorResult.class, result); - assertEquals(XyzError.NOT_IMPLEMENTED, ((ErrorResult) result).reason); + assertInstanceOf(ErrorResponse.class, result); + assertEquals(NakshaError.NOT_IMPLEMENTED, ((ErrorResponse) result).getError().getCode()); } @Test void shouldImmediatelySucceedOnWriteCollection() { // Given: event bearing some WriteCollections request - IEvent event = eventWith(new WriteXyzCollections().delete(new XyzCollection("some_collection"))); + IEvent event = eventWith(new WriteRequest().add(new Write().deleteCollectionById(null,"some_collection"))); // When: handler tries to process such event - Result result = handler.processEvent(event); + Response result = handler.processEvent(event); // Then: storage was not used at all verifyNoInteractions(spaceStorage); @@ -136,7 +120,7 @@ void shouldImmediatelySucceedOnWriteCollection() { verify(event, never()).sendUpstream(any()); // And: result is successful - assertInstanceOf(SuccessResult.class, result); + assertInstanceOf(SuccessResponse.class, result); } @Test @@ -144,19 +128,19 @@ void shouldTransformReadRequest() { // Given: Original read request String featureUuid = "featureUuid"; String featureId = "featureId"; - ReadFeatures originalReadFeatures = new ReadFeatures("not_the_space_id") - .withReturnAllVersions(false) - .withPropertyOp( - POp.or( - POp.eq(PRef.id(), featureUuid), - POp.eq(PREF_ACTIVITY_LOG_ID, featureId) + ReadFeatures originalReadFeatures = new ReadFeatures("not_the_space_id"); + originalReadFeatures.setVersions(1); + originalReadFeatures.getQuery().setProperties( + new POr( + new PQuery(new Property(NakshaFeature.ID_KEY), StringOp.EQUALS, featureUuid), + new PQuery(PROPERTY_ACTIVITY_LOG_ID, StringOp.EQUALS, featureId) ) ); // And: Configured session that will receive read request from handler IReadSession readSession = mock(IReadSession.class); - when(spaceStorage.newReadSession(any(), anyBoolean())).thenReturn(readSession); - when(readSession.execute(any())).thenReturn(new SuccessResult()); + when(spaceStorage.newReadSession(any())).thenReturn(readSession); + when(readSession.execute(any())).thenReturn(new SuccessResponse()); // When: Processing event with original request handler.processEvent(eventWith(originalReadFeatures)); @@ -167,18 +151,18 @@ void shouldTransformReadRequest() { // And: The executed request was a ReadFeatures transformed by handler ReadFeatures requestPassedToSpaceStorage = requestCaptor.getValue(); - assertEquals(List.of(SPACE_ID), requestPassedToSpaceStorage.getCollections(), + assertEquals(List.of(SPACE_ID), requestPassedToSpaceStorage.getCollectionIds(), "Transformed request should use 'spaceId' from handler's properties"); - assertTrue(requestPassedToSpaceStorage.isReturnAllVersions(), "Transformed request should return all versions of feature"); - assertThatOperation(requestPassedToSpaceStorage.getPropertyOp()) // POp for id and activityLogId should be transformed + assertEquals(Integer.MAX_VALUE,requestPassedToSpaceStorage.getVersions(), "Transformed request should return all versions of feature"); + assertThatOperation(requestPassedToSpaceStorage.getQuery().getProperties()) // POp for id and activityLogId should be transformed .hasChildrenThat( first -> first - .hasType(POpType.EQ) - .hasPRef(PRef.uuid()) + .hasType(StringOp.EQUALS) + .hasProperty(PROPERTY_UUID) .hasValue(featureUuid), second -> second - .hasType(POpType.EQ) - .hasPRef(PRef.id()) + .hasType(StringOp.EQUALS) + .hasProperty(List.of(NakshaFeature.ID_KEY)) .hasValue(featureId) ); } @@ -187,11 +171,11 @@ void shouldTransformReadRequest() { void shouldComposeActivityFeatures() throws Exception { // Given: old version of feature String featureId = "featureId"; - XyzFeature oldFeature = xyzFeature( + NakshaFeature oldFeature = nakshaFeature( featureId, "initial_uuid", null, - EXyzAction.CREATE, + Action.CREATED, Map.of( "op", "old feature", "magicNumber", 123 @@ -199,11 +183,11 @@ void shouldComposeActivityFeatures() throws Exception { ); // And: new version of feature - XyzFeature newFeature = xyzFeature( + NakshaFeature newFeature = nakshaFeature( featureId, "new_uuid", "initial_uuid", - EXyzAction.UPDATE, + Action.UPDATED, Map.of( "op", "new feature", "magicBoolean", true @@ -215,7 +199,7 @@ void shouldComposeActivityFeatures() throws Exception { spaceStorageSessionReturningHistoryFeatures(request, oldFeature, newFeature); // When: handler processes given request - Result result = handler.processEvent(eventWith(request)); + Response result = handler.processEvent(eventWith(request)); // Then: result contains activity log calculated on basis of these features assertThatResult(result) @@ -223,8 +207,8 @@ void shouldComposeActivityFeatures() throws Exception { firstFeature -> firstFeature .hasId(uuid(newFeature)) .hasActivityLogId(featureId) - .hasAction(EXyzAction.UPDATE.toString()) - .hasReversePatch(jsonNode(""" + .hasAction(Action.UPDATED.toString()) + .hasReversePatch(""" { "add": 1, "remove": 1, @@ -246,11 +230,11 @@ void shouldComposeActivityFeatures() throws Exception { } ] } - """)), + """), secondFeature -> secondFeature .hasId(uuid(oldFeature)) .hasActivityLogId(featureId) - .hasAction(EXyzAction.CREATE.toString()) + .hasAction(Action.CREATED.toString()) .hasReversePatch(null) ); } @@ -262,18 +246,18 @@ void shouldFetchAdditionalHistoryFeaturesWhenNeeded() throws Exception { // And: Space storage that will return two history features for client's request IReadSession readSession = spaceStorageSessionReturningHistoryFeatures(firstRequest, - xyzFeature("id_1", "uuid_1", "puuid_1", EXyzAction.UPDATE), - xyzFeature("id_2", "uuid_2", "puuid_2", EXyzAction.DELETE) + nakshaFeature("id_1", "uuid_1", "puuid_1", Action.UPDATED), + nakshaFeature("id_2", "uuid_2", "puuid_2", Action.DELETED) ); // And: Space storage that will return two predecessors for any other request - when(readSession.execute(not(eq(firstRequest)))).thenReturn(new SuccessHistoryResult(List.of( - xyzFeature("id_1", "puuid_1", null, EXyzAction.CREATE), - xyzFeature("id_2", "puuid_2", null, EXyzAction.CREATE) - ))); + when(readSession.execute(not(eq(firstRequest)))).thenReturn(new SuccessResponse(NakshaFeatureList.fromList(List.of( + nakshaFeature("id_1", "puuid_1", null, Action.CREATED), + nakshaFeature("id_2", "puuid_2", null, Action.CREATED) + )))); // When: Handler processes event with original client's request - Result result = handler.processEvent(eventWith(firstRequest)); + Response result = handler.processEvent(eventWith(firstRequest)); // Then: Space storage should be queried twice ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ReadFeatures.class); @@ -286,18 +270,18 @@ void shouldFetchAdditionalHistoryFeaturesWhenNeeded() throws Exception { // And: Second request passed to space should be about fetching additional predecessors ReadFeatures secondRequest = requestPassedToSpace.get(1); - assertTrue(secondRequest.isReturnAllVersions()); - assertEquals(List.of(SPACE_ID), secondRequest.getCollections()); - POpAssertion.assertThatOperation(secondRequest.getPropertyOp()) - .hasType(OpType.OR) + assertEquals(Integer.MAX_VALUE,secondRequest.getVersions()); + assertEquals(List.of(SPACE_ID), secondRequest.getCollectionIds()); + AssertionIPropertyQuery.assertThatOperation(secondRequest.getQuery().getProperties()) + .isPOr() .hasChildrenThat( first -> first - .hasPRef(PRef.uuid()) - .hasType(POpType.EQ) + .hasProperty(PROPERTY_UUID) + .hasType(StringOp.EQUALS) .hasValue("puuid_2"), second -> second - .hasPRef(PRef.uuid()) - .hasType(POpType.EQ) + .hasProperty(PROPERTY_UUID) + .hasType(StringOp.EQUALS) .hasValue("puuid_1") ); @@ -307,11 +291,11 @@ void shouldFetchAdditionalHistoryFeaturesWhenNeeded() throws Exception { first -> first .hasId("uuid_2") .hasActivityLogId("id_2") - .hasAction(EXyzAction.DELETE.toString()), + .hasAction(Action.DELETED.toString()), second -> second .hasId("uuid_1") .hasActivityLogId("id_1") - .hasAction(EXyzAction.UPDATE.toString()) + .hasAction(Action.UPDATED.toString()) ); } @@ -321,20 +305,20 @@ void shouldNotCalculateReversePatchAfterCreation() throws Exception { ReadFeatures request = new ReadFeatures(); // And: space storage that returns some feature with 'CREATE' action for given request - spaceStorageSessionReturningHistoryFeatures(request, xyzFeature( + spaceStorageSessionReturningHistoryFeatures(request, nakshaFeature( "featureId", "uuid", null, - EXyzAction.CREATE + Action.CREATED )); // When: handler processes event bearing such request - Result result = handler.processEvent(eventWith(request)); + Response result = handler.processEvent(eventWith(request)); // Then: result does not bear any reverse patch assertThatResult(result) .hasActivityFeatures(feature -> feature - .hasAction(EXyzAction.CREATE.toString()) + .hasAction(Action.CREATED.toString()) .hasId("uuid") .hasActivityLogId("featureId") .hasReversePatch(null) @@ -348,33 +332,33 @@ void shouldNotCalculateDiffAfterDeletion() throws Exception { // And: space storage that returns features with 'DELETE' and `CREATE` actions for given request spaceStorageSessionReturningHistoryFeatures(request, - xyzFeature( + nakshaFeature( "featureId", "delete_uuid", "create_uuid", - EXyzAction.DELETE + Action.DELETED ), - xyzFeature( + nakshaFeature( "featureId", "create_uuid", null, - EXyzAction.CREATE + Action.CREATED ) ); // When: handler processes event bearing such request - Result result = handler.processEvent(eventWith(request)); + Response result = handler.processEvent(eventWith(request)); // Then: there is no reverse patch for any fo these features assertThatResult(result) .hasActivityFeatures( first -> first - .hasAction(EXyzAction.DELETE.toString()) + .hasAction(Action.DELETED.toString()) .hasId("delete_uuid") .hasActivityLogId("featureId") .hasReversePatch(null), second -> second - .hasAction(EXyzAction.CREATE.toString()) + .hasAction(Action.CREATED.toString()) .hasId("create_uuid") .hasActivityLogId("featureId") .hasReversePatch(null) @@ -383,7 +367,7 @@ void shouldNotCalculateDiffAfterDeletion() throws Exception { private ActivityLogHandler handlerForSpaceId(String spaceId) { when(eventHandler.getProperties()).thenReturn(new ActivityLogHandlerProperties(spaceId)); - return new ActivityLogHandler(eventHandler, naksha, mock(EventTarget.class)); + return new ActivityLogHandler(eventHandler, naksha); } private static JsonNode jsonNode(String rawJson) { @@ -412,41 +396,43 @@ void shouldBeAlignedWithDataHubSamples() throws Exception { IEvent event = eventWith(request); // When - Result result = handlerWithSampleSpace.processEvent(event); + Response result = handlerWithSampleSpace.processEvent(event); // Then assertThatResult(result).hasActivityFeaturesIdenticalTo(datahubSample.activityFeatures()); } - private static String uuid(XyzFeature newFeature) { - return newFeature.getProperties().getXyzNamespace().getUuid(); + private static String uuid(NakshaFeature newFeature) { + return newFeature.getProperties().getXyz().getUuid(); } - private static XyzFeature xyzFeature(String id, String uuid, String puuid, EXyzAction action) { - return xyzFeature(id, uuid, puuid, action, emptyMap()); + private static NakshaFeature nakshaFeature(String id, String uuid, String puuid, Action action) { + return nakshaFeature(id, uuid, puuid, action, emptyMap()); } - private static XyzFeature xyzFeature(String id, String uuid, String puuid, EXyzAction action, Map properties) { - XyzFeature feature = new XyzFeature(id); - XyzNamespace xyzNamespace = new XyzNamespace(); - xyzNamespace.setUuid(uuid); - xyzNamespace.setPuuid(puuid); - xyzNamespace.setAction(action); - XyzProperties xyzProperties = new XyzProperties(); + private static NakshaFeature nakshaFeature(String id, String uuid, String puuid, Action action, Map properties) { + NakshaFeature feature = new NakshaFeature(id); + XyzNs xyzNamespace = new XyzNs(); + xyzNamespace.put(UUID,uuid); + xyzNamespace.put(PUUID,puuid); + xyzNamespace.put(ACTION,action); + xyzNamespace.put(CREATED_AT, new JvmInt64(System.currentTimeMillis())); + xyzNamespace.put(UPDATED_AT,new JvmInt64(System.currentTimeMillis()+100)); + NakshaProperties xyzProperties = new NakshaProperties(); xyzProperties.putAll(properties); - xyzProperties.setXyzNamespace(xyzNamespace); + xyzProperties.setXyz(xyzNamespace); feature.setProperties(xyzProperties); return feature; } - IReadSession spaceStorageSessionReturningHistoryFeatures(ReadRequest handledRequest, XyzFeature... historyFeatures) { + IReadSession spaceStorageSessionReturningHistoryFeatures(ReadRequest handledRequest, NakshaFeature... historyFeatures) { return spaceStorageSessionReturningHistoryFeatures(handledRequest, List.of(historyFeatures)); } - IReadSession spaceStorageSessionReturningHistoryFeatures(ReadRequest handledRequest, List historyFeatures) { + IReadSession spaceStorageSessionReturningHistoryFeatures(ReadRequest handledRequest, List historyFeatures) { IReadSession readSession = mock(IReadSession.class); - when(readSession.execute(handledRequest)).thenReturn(new SuccessHistoryResult(historyFeatures)); - when(spaceStorage.newReadSession(any(), anyBoolean())).thenReturn(readSession); + when(readSession.execute(handledRequest)).thenReturn(new SuccessResponse(NakshaFeatureList.fromList(historyFeatures))); + when(spaceStorage.newReadSession(any())).thenReturn(readSession); return readSession; } @@ -456,29 +442,10 @@ private IEvent eventWith(Request request) { return event; } - private static Stream> unhandledRequests() { + private static Stream unhandledRequests() { return Stream.of( - new WriteXyzFeatures("some_collection").create(new XyzFeature("some_feature")), - new ReadCollections().withIds("some_collection") + new WriteRequest().add(new Write().createFeature(null,"some_collection",new NakshaFeature("some_feature"))), + new ReadCollections("some_collection") ); } - - private static class SuccessHistoryResult extends SuccessResult { - - private SuccessHistoryResult(List historyFeatures) { - this.cursor = featureListCursor(historyFeatures); - } - - private static ListBasedForwardCursor featureListCursor(List features) { - XyzFeatureCodecFactory codecFactory = XyzFeatureCodecFactory.get(); - List codecs = features.stream() - .map(feature -> codecFactory - .newInstance() - .withOp(EExecutedOp.READ) - .withFeature(feature) - ) - .toList(); - return new ListBasedForwardCursor<>(codecFactory, codecs); - } - } } \ No newline at end of file diff --git a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogRequestTranslationUtilTest.java b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogRequestTranslationUtilTest.java index 1a6815dbf..c0cd1ca44 100644 --- a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogRequestTranslationUtilTest.java +++ b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ActivityLogRequestTranslationUtilTest.java @@ -1,31 +1,33 @@ package com.here.naksha.handler.activitylog; -import static com.here.naksha.handler.activitylog.ActivityLogRequestTranslationUtil.PREF_ACTIVITY_LOG_ID; -import static naksha.model.PRef.id; -import static naksha.model.PRef.uuid; - -import naksha.model.POp; -import naksha.model.POpType; -import naksha.model.ReadFeatures; -import com.here.naksha.test.common.assertions.POpAssertion; +import static com.here.naksha.handler.activitylog.ActivityLogRequestTranslationUtil.PROPERTY_ACTIVITY_LOG_ID; +import static com.here.naksha.handler.activitylog.ActivityLogRequestTranslationUtil.PROPERTY_UUID; + +import com.here.naksha.test.common.assertions.AssertionIPropertyQuery; +import naksha.model.objects.NakshaFeature; +import naksha.model.request.ReadFeatures; +import naksha.model.request.query.*; import org.junit.jupiter.api.Test; +import java.util.List; + class ActivityLogRequestTranslationUtilTest { @Test void shouldTranslateIdToUuid() { // Given: String expectedId = "some_id"; - POp singleIdQuery = POp.eq(id(), expectedId); - ReadFeatures readFeatures = new ReadFeatures().withPropertyOp(singleIdQuery); + PQuery singleIdQuery = new PQuery(new Property(NakshaFeature.ID_KEY), StringOp.EQUALS, expectedId); + ReadFeatures readFeatures = new ReadFeatures(); + readFeatures.getQuery().setProperties(singleIdQuery); // When: ActivityLogRequestTranslationUtil.translatePropertyOperation(readFeatures); // Then: - POpAssertion.assertThatOperation(readFeatures.getPropertyOp()) - .hasType(POpType.EQ) - .hasPRef(uuid()) + AssertionIPropertyQuery.assertThatOperation(readFeatures.getQuery().getProperties()) + .hasType(StringOp.EQUALS) + .hasProperty(PROPERTY_UUID) .hasValue(expectedId); } @@ -34,26 +36,28 @@ void shouldTranslateIdsToUuids() { // Given: String firstId = "id_1"; String secondId = "id_2"; - POp idsQuery = POp.or( - POp.eq(id(), firstId), - POp.eq(id(), secondId) + final Property idProperty = new Property(NakshaFeature.ID_KEY); + IPropertyQuery idsQuery = new POr( + new PQuery(idProperty, StringOp.EQUALS, firstId), + new PQuery(idProperty, StringOp.EQUALS, secondId) ); - ReadFeatures readFeatures = new ReadFeatures().withPropertyOp(idsQuery); + ReadFeatures readFeatures = new ReadFeatures(); + readFeatures.getQuery().setProperties(idsQuery); // When: ActivityLogRequestTranslationUtil.translatePropertyOperation(readFeatures); // Then: - POpAssertion.assertThatOperation(readFeatures.getPropertyOp()) - .hasType(POpType.OR) + AssertionIPropertyQuery.assertThatOperation(readFeatures.getQuery().getProperties()) + .isPOr() .hasChildrenThat( first -> first - .hasType(POpType.EQ) - .hasPRef(uuid()) + .hasType(StringOp.EQUALS) + .hasProperty(PROPERTY_UUID) .hasValue(firstId), second -> second - .hasType(POpType.EQ) - .hasPRef(uuid()) + .hasType(StringOp.EQUALS) + .hasProperty(PROPERTY_UUID) .hasValue(secondId) ); } @@ -62,16 +66,17 @@ void shouldTranslateIdsToUuids() { void shouldTranslateActivityLogIdToId() { // Given: String expectedId = "some_id"; - POp singleActivityLogIdQuery = POp.eq(PREF_ACTIVITY_LOG_ID, expectedId); - ReadFeatures readFeatures = new ReadFeatures().withPropertyOp(singleActivityLogIdQuery); + PQuery singleActivityLogIdQuery = new PQuery(PROPERTY_ACTIVITY_LOG_ID, StringOp.EQUALS, expectedId); + ReadFeatures readFeatures = new ReadFeatures(); + readFeatures.getQuery().setProperties(singleActivityLogIdQuery); // When: ActivityLogRequestTranslationUtil.translatePropertyOperation(readFeatures); // Then: - POpAssertion.assertThatOperation(readFeatures.getPropertyOp()) - .hasType(POpType.EQ) - .hasPRef(id()) + AssertionIPropertyQuery.assertThatOperation(readFeatures.getQuery().getProperties()) + .hasType(StringOp.EQUALS) + .hasProperty(List.of(NakshaFeature.ID_KEY)) .hasValue(expectedId); } @@ -80,26 +85,27 @@ void shouldTranslateActivityLogIdsToIds() { // Given: String firstId = "id_1"; String secondId = "id_2"; - POp activityLogIdsQuery = POp.or( - POp.eq(PREF_ACTIVITY_LOG_ID, firstId), - POp.eq(PREF_ACTIVITY_LOG_ID, secondId) + IPropertyQuery activityLogIdsQuery = new POr( + new PQuery(PROPERTY_ACTIVITY_LOG_ID, StringOp.EQUALS, firstId), + new PQuery(PROPERTY_ACTIVITY_LOG_ID, StringOp.EQUALS, secondId) ); - ReadFeatures readFeatures = new ReadFeatures().withPropertyOp(activityLogIdsQuery); + ReadFeatures readFeatures = new ReadFeatures(); + readFeatures.getQuery().setProperties(activityLogIdsQuery); // When: ActivityLogRequestTranslationUtil.translatePropertyOperation(readFeatures); // Then: - POpAssertion.assertThatOperation(readFeatures.getPropertyOp()) - .hasType(POpType.OR) + AssertionIPropertyQuery.assertThatOperation(readFeatures.getQuery().getProperties()) + .isPOr() .hasChildrenThat( first -> first - .hasType(POpType.EQ) - .hasPRef(id()) + .hasType(StringOp.EQUALS) + .hasProperty(List.of(NakshaFeature.ID_KEY)) .hasValue(firstId), second -> second - .hasType(POpType.EQ) - .hasPRef(id()) + .hasType(StringOp.EQUALS) + .hasProperty(List.of(NakshaFeature.ID_KEY)) .hasValue(secondId) ); } @@ -109,26 +115,27 @@ void shouldApplyMixedTranslations() { // Given: String id = "id"; String activityLogId = "activity_log_id"; - POp mixedQuery = POp.or( - POp.eq(id(), id), - POp.eq(PREF_ACTIVITY_LOG_ID, activityLogId) + IPropertyQuery mixedQuery = new POr( + new PQuery(new Property(NakshaFeature.ID_KEY), StringOp.EQUALS, id), + new PQuery(PROPERTY_ACTIVITY_LOG_ID, StringOp.EQUALS, activityLogId) ); - ReadFeatures readFeatures = new ReadFeatures().withPropertyOp(mixedQuery); + ReadFeatures readFeatures = new ReadFeatures(); + readFeatures.getQuery().setProperties(mixedQuery); // When: ActivityLogRequestTranslationUtil.translatePropertyOperation(readFeatures); // Then: - POpAssertion.assertThatOperation(readFeatures.getPropertyOp()) - .hasType(POpType.OR) + AssertionIPropertyQuery.assertThatOperation(readFeatures.getQuery().getProperties()) + .isPOr() .hasChildrenThat( first -> first - .hasType(POpType.EQ) - .hasPRef(uuid()) + .hasType(StringOp.EQUALS) + .hasProperty(PROPERTY_UUID) .hasValue(id), second -> second - .hasType(POpType.EQ) - .hasPRef(id()) + .hasType(StringOp.EQUALS) + .hasProperty(List.of(NakshaFeature.ID_KEY)) .hasValue(activityLogId) ); } diff --git a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ReversePatchUtilTest.java b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ReversePatchUtilTest.java index 6ef8ac444..977a08c6c 100644 --- a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ReversePatchUtilTest.java +++ b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/ReversePatchUtilTest.java @@ -6,16 +6,17 @@ import static com.here.naksha.handler.activitylog.assertions.ReversePatchAssertions.assertThat; import com.here.naksha.handler.activitylog.ReversePatch.PatchOp; -import naksha.geo.LineStringCoordinates; -import naksha.geo.PointCoordinates; -import naksha.model.XyzFeature; -import naksha.geo.XyzLineString; -import naksha.geo.XyzProperties; -import com.here.naksha.lib.core.models.geojson.implementation.namespaces.XyzNamespace; import java.util.List; import java.util.Map; import java.util.Random; import java.util.UUID; + +import naksha.geo.LineStringCoord; +import naksha.geo.PointCoord; +import naksha.geo.SpLineString; +import naksha.model.XyzNs; +import naksha.model.objects.NakshaFeature; +import naksha.model.objects.NakshaProperties; import net.bytebuddy.utility.RandomString; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -29,7 +30,7 @@ class ReversePatchUtilTest { @Test void shouldConvertDifferenceToReversePatch() { // Given: feature representing John some time ago - XyzFeature before = xyzFeature(Map.of( + NakshaFeature before = xyzFeature(Map.of( "op", "John", "age", 23, "address", Map.of( @@ -54,7 +55,7 @@ void shouldConvertDifferenceToReversePatch() { )); // And: feature representing John year later (he moved a couple of blocks away, finished studies, found a job and changed his contact details) - XyzFeature after = xyzFeature(Map.of( + NakshaFeature after = xyzFeature(Map.of( "op", "John", "age", 24, "address", Map.of( @@ -98,10 +99,10 @@ void shouldConvertDifferenceToReversePatch() { @Test void shouldIgnoreChangedId() { // Given: feature A - XyzFeature featureA = new XyzFeature("id_a"); + NakshaFeature featureA = new NakshaFeature("id_a"); // And: feature B - XyzFeature featureB = new XyzFeature("id_b"); + NakshaFeature featureB = new NakshaFeature("id_b"); // When: applying reverse patch calculation on these two ReversePatch reversePatch = ReversePatchUtil.reversePatch(featureA, featureB); @@ -113,14 +114,14 @@ void shouldIgnoreChangedId() { @Test void shouldIgnoreXyzNamespaceButNotTags() { // Given: feature with tags - XyzNamespace oldXyzNamespace = generateRandomXyzNamespace() + XyzNs oldXyzNamespace = generateRandomXyzNamespace() .addTags(List.of("one", "two", "three"), true); - XyzFeature oldFeature = featureWithXyzNamespace(oldXyzNamespace); + NakshaFeature oldFeature = featureWithXyzNamespace(oldXyzNamespace); // And: feature with different XyzNamespace (including different tags) - XyzNamespace newXyzNamespace = generateRandomXyzNamespace() + XyzNs newXyzNamespace = generateRandomXyzNamespace() .addTags(List.of("two", "three", "four", "five"), true); - XyzFeature newFeature = featureWithXyzNamespace(newXyzNamespace); + NakshaFeature newFeature = featureWithXyzNamespace(newXyzNamespace); // When: applying reverse patch calculation on these two ReversePatch reversePatch = ReversePatchUtil.reversePatch(oldFeature, newFeature); @@ -141,21 +142,21 @@ void shouldIgnoreXyzNamespaceButNotTags() { @Test void shouldCorrectlyParseLineGeometryChange(){ // Given - XyzFeature oldFeature = new XyzFeature(); - LineStringCoordinates oldCoordinates = new LineStringCoordinates(); - oldCoordinates.add(new PointCoordinates(4.0d, 5.0)); - oldCoordinates.add(new PointCoordinates(4.0d, 6.0)); - oldCoordinates.add(new PointCoordinates(4.0d, 7.0)); - oldCoordinates.add(new PointCoordinates(4.0d, 8.0)); - oldFeature.setGeometry(new XyzLineString().withCoordinates(oldCoordinates)); + NakshaFeature oldFeature = new NakshaFeature(); + LineStringCoord oldCoordinates = new LineStringCoord(); + oldCoordinates.add(new PointCoord(4.0d, 5.0)); + oldCoordinates.add(new PointCoord(4.0d, 6.0)); + oldCoordinates.add(new PointCoord(4.0d, 7.0)); + oldCoordinates.add(new PointCoord(4.0d, 8.0)); + oldFeature.setGeometry(new SpLineString().withCoordinates(oldCoordinates)); // And: - XyzFeature newFeature = new XyzFeature(); - LineStringCoordinates newCoordinates = new LineStringCoordinates(); - newCoordinates.add(new PointCoordinates(4.0d, 5.0)); - newCoordinates.add(new PointCoordinates(4.0d, 8.0)); - newCoordinates.add(new PointCoordinates(4.0d, 7.0)); - newFeature.setGeometry(new XyzLineString().withCoordinates(newCoordinates)); + NakshaFeature newFeature = new NakshaFeature(); + LineStringCoord newCoordinates = new LineStringCoord(); + newCoordinates.add(new PointCoord(4.0d, 5.0)); + newCoordinates.add(new PointCoord(4.0d, 8.0)); + newCoordinates.add(new PointCoord(4.0d, 7.0)); + newFeature.setGeometry(new SpLineString().withCoordinates(newCoordinates)); // When: applying reverse patch calculation on these two ReversePatch reversePatch = ReversePatchUtil.reversePatch(oldFeature, newFeature); @@ -167,31 +168,31 @@ void shouldCorrectlyParseLineGeometryChange(){ .hasUpdateOpsCount(1) // we did update second coordinate .hasReverseOps( new PatchOp(REPLACE, "/geometry/coordinates/1/1", 6.0), - new PatchOp(ADD, "/geometry/coordinates/3", new PointCoordinates(4.0d, 8.0d)) + new PatchOp(ADD, "/geometry/coordinates/3", new PointCoord(4.0d, 8.0d)) ); } - private XyzFeature featureWithXyzNamespace(XyzNamespace xyzNamespace) { - XyzFeature feature = new XyzFeature(); - XyzProperties properties = new XyzProperties(); - properties.setXyzNamespace(xyzNamespace); + private NakshaFeature featureWithXyzNamespace(XyzNs xyzNamespace) { + NakshaFeature feature = new NakshaFeature(); + NakshaProperties properties = new NakshaProperties(); + properties.setXyz(xyzNamespace); feature.setProperties(properties); return feature; } - private XyzNamespace generateRandomXyzNamespace() { - return new XyzNamespace() - .withAppId(RandomString.make(10)) - .withAuthor(RandomString.make(10)) - .withTxn(random.nextLong()) - .withExtend(random.nextLong()) - .withUuid(UUID.randomUUID().toString()) - .withPuuid(UUID.randomUUID().toString()); + private XyzNs generateRandomXyzNamespace() { + final XyzNs xyzNamespace = new XyzNs(); + xyzNamespace.put("appId",RandomString.make(10)); + xyzNamespace.put("author",RandomString.make(10)); + xyzNamespace.put("txn",random.nextLong()); + xyzNamespace.put("uuid",UUID.randomUUID().toString()); + xyzNamespace.put("puuid",UUID.randomUUID().toString()); + return xyzNamespace; } - private XyzFeature xyzFeature(Map props) { - XyzFeature feature = new XyzFeature(); - XyzProperties properties = new XyzProperties(); + private NakshaFeature xyzFeature(Map props) { + NakshaFeature feature = new NakshaFeature(); + NakshaProperties properties = new NakshaProperties(); properties.putAll(props); feature.setProperties(properties); return feature; diff --git a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/assertions/ActivityLogFeatureAssertions.java b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/assertions/ActivityLogFeatureAssertions.java index 9967bfb3c..4178b611f 100644 --- a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/assertions/ActivityLogFeatureAssertions.java +++ b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/assertions/ActivityLogFeatureAssertions.java @@ -1,12 +1,14 @@ package com.here.naksha.handler.activitylog.assertions; -import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; -import naksha.model.XyzFeature; -import com.here.naksha.lib.core.util.json.JsonSerializable; +import com.here.naksha.handler.activitylog.NakshaActivityLog; +import naksha.base.Platform; +import naksha.base.ToJsonOptions; +import naksha.model.objects.NakshaFeature; import org.json.JSONException; import org.junit.jupiter.api.Assertions; import org.skyscreamer.jsonassert.JSONAssert; @@ -14,13 +16,13 @@ public class ActivityLogFeatureAssertions { - private final XyzFeature subject; + private final NakshaFeature subject; - private ActivityLogFeatureAssertions(XyzFeature subject) { + private ActivityLogFeatureAssertions(NakshaFeature subject) { this.subject = subject; } - public static ActivityLogFeatureAssertions assertThatActivityLogFeature(XyzFeature xyzFeature) { + public static ActivityLogFeatureAssertions assertThatActivityLogFeature(NakshaFeature xyzFeature) { assertNotNull(xyzFeature); return new ActivityLogFeatureAssertions(xyzFeature); } @@ -31,33 +33,45 @@ public ActivityLogFeatureAssertions hasId(String id) { } public ActivityLogFeatureAssertions hasActivityLogId(String id) { - assertNotNull(subject.getProperties().getXyzActivityLog()); - Assertions.assertEquals(id, subject.getProperties().getXyzActivityLog().getId()); + final NakshaActivityLog activityLog = NakshaActivityLog.getActivityLog(subject.getProperties()); + assertNotNull(activityLog); + Assertions.assertEquals(id, activityLog.getId()); return this; } public ActivityLogFeatureAssertions hasAction(String action) { - assertNotNull(subject.getProperties().getXyzActivityLog()); - Assertions.assertEquals(action, subject.getProperties().getXyzActivityLog().getAction()); + final NakshaActivityLog activityLog = NakshaActivityLog.getActivityLog(subject.getProperties()); + assertNotNull(activityLog); + Assertions.assertEquals(action, activityLog.getAction()); return this; } - public ActivityLogFeatureAssertions hasReversePatch(JsonNode reversePatch) { - assertNotNull(subject.getProperties().getXyzActivityLog()); - Assertions.assertEquals(reversePatch, subject.getProperties().getXyzActivityLog().getDiff()); - return this; + public ActivityLogFeatureAssertions hasReversePatch(String reversePatch) { + final NakshaActivityLog activityLog = NakshaActivityLog.getActivityLog(subject.getProperties()); + assertNotNull(activityLog); + JsonNode diff = activityLog.getDiff(); + if (reversePatch == null) { + assertNull(diff); + return this; + } + try { + JSONAssert.assertEquals(reversePatch, diff.toString(), JSONCompareMode.LENIENT); + } catch (JSONException e) { + throw new RuntimeException(e); + } + return this; } - public ActivityLogFeatureAssertions isIdenticalToDatahubSampleFeature(XyzFeature datahubFeature, String message) throws JSONException { + public ActivityLogFeatureAssertions isIdenticalToDatahubSampleFeature(NakshaFeature datahubFeature, String message) throws JSONException { alignDiff(subject); - String subjectJson = JsonSerializable.serialize(subject); - String datahubFeatureJson = JsonSerializable.serialize(datahubFeature); + String subjectJson = Platform.toJSON(subject, ToJsonOptions.DEFAULT); + String datahubFeatureJson = Platform.toJSON(datahubFeature, ToJsonOptions.DEFAULT); JSONAssert.assertEquals(message, datahubFeatureJson, subjectJson, JSONCompareMode.LENIENT); return this; } - private static void alignDiff(XyzFeature xyzFeature) { - JsonNode diff = xyzFeature.getProperties().getXyzActivityLog().getDiff(); + private static void alignDiff(NakshaFeature xyzFeature) { + JsonNode diff = NakshaActivityLog.getActivityLog(xyzFeature.getProperties()).getDiff(); if(diff != null){ ((ObjectNode) diff).put("copy", 0); ((ObjectNode) diff).put("move", 0); diff --git a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/assertions/ActivityLogSuccessResultAssertions.java b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/assertions/ActivityLogSuccessResultAssertions.java index 6b5d3335f..7cdbfe8d5 100644 --- a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/assertions/ActivityLogSuccessResultAssertions.java +++ b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/assertions/ActivityLogSuccessResultAssertions.java @@ -4,32 +4,33 @@ import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; -import com.here.naksha.handler.activitylog.ActivityLogSuccessResult; -import naksha.model.XyzFeature; -import com.here.naksha.lib.core.models.storage.Result; -import naksha.model.util.ResultHelper; import java.util.List; import java.util.function.Consumer; + +import naksha.model.objects.NakshaFeature; +import naksha.model.request.Response; +import naksha.model.request.SuccessResponse; +import naksha.model.util.ResultHelper; import org.junit.jupiter.api.Assertions; public class ActivityLogSuccessResultAssertions { - private final ActivityLogSuccessResult subject; + private final SuccessResponse subject; - private ActivityLogSuccessResultAssertions(ActivityLogSuccessResult subject) { + private ActivityLogSuccessResultAssertions(SuccessResponse subject) { this.subject = subject; } - public static ActivityLogSuccessResultAssertions assertThatResult(Result result) { + public static ActivityLogSuccessResultAssertions assertThatResult(Response result) { assertNotNull(result); - assertInstanceOf(ActivityLogSuccessResult.class, result); - return new ActivityLogSuccessResultAssertions((ActivityLogSuccessResult) result); + assertInstanceOf(SuccessResponse.class, result); + return new ActivityLogSuccessResultAssertions((SuccessResponse) result); } @SafeVarargs public final ActivityLogSuccessResultAssertions hasActivityFeatures(Consumer... featuresAssertions) throws Exception { - List features = ResultHelper.extractResponseItems(subject, XyzFeature.class); + List features = ResultHelper.extractResponseItems(subject, NakshaFeature.class); Assertions.assertEquals(featuresAssertions.length, features.size()); for (int i = 0; i < featuresAssertions.length; i++) { featuresAssertions[i].accept(assertThatActivityLogFeature(features.get(i))); @@ -37,9 +38,9 @@ public final ActivityLogSuccessResultAssertions hasActivityFeatures(Consumer otherFeatures) + public final ActivityLogSuccessResultAssertions hasActivityFeaturesIdenticalTo(List otherFeatures) throws Exception { - List features = ResultHelper.extractResponseItems(subject, XyzFeature.class); + List features = ResultHelper.extractResponseItems(subject, NakshaFeature.class); Assertions.assertEquals(otherFeatures.size(), features.size()); for (int i = 0; i < features.size(); i++) { assertThatActivityLogFeature(features.get(i)) diff --git a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/util/DatahubSamplesUtil.java b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/util/DatahubSamplesUtil.java index ef7f67eac..5506ce8c8 100644 --- a/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/util/DatahubSamplesUtil.java +++ b/here-naksha-handler-activitylog/src/test/java/com/here/naksha/handler/activitylog/util/DatahubSamplesUtil.java @@ -1,16 +1,18 @@ package com.here.naksha.handler.activitylog.util; import com.here.naksha.handler.activitylog.ActivityLogComparator; -import naksha.model.EXyzAction; -import naksha.model.XyzFeature; +import com.here.naksha.handler.activitylog.NakshaActivityLog; +import com.here.naksha.handler.activitylog.Original; import naksha.model.XyzFeatureCollection; -import com.here.naksha.lib.core.models.geojson.implementation.namespaces.Original; -import com.here.naksha.lib.core.models.geojson.implementation.namespaces.XyzActivityLog; -import com.here.naksha.lib.core.models.geojson.implementation.namespaces.XyzNamespace; import com.here.naksha.lib.core.util.json.JsonSerializable; import com.here.naksha.test.common.FileUtil; +import naksha.model.XyzNs; +import naksha.model.objects.NakshaFeature; + import java.util.List; +import static naksha.model.objects.NakshaProperties.XYZ_ACTIVITY_LOG_NS; + public class DatahubSamplesUtil { public static final String SAMPLE_SPACE_ID = "SDNujm7h"; @@ -30,50 +32,50 @@ private static String loadDatahubSampleJson() { return FileUtil.loadFileOrFail(SAMPLES_DIR, SAMPLES_FILE); } - private static List historyFeatures(String sampleFeaturesJson) { - List features = activityFeatures(sampleFeaturesJson); + private static List historyFeatures(String sampleFeaturesJson) { + List features = activityFeatures(sampleFeaturesJson); features.forEach(feature -> { - String originFeatureId = feature.getProperties().getXyzActivityLog().getId(); + String originFeatureId = NakshaActivityLog.getActivityLog(feature.getProperties()).getId(); feature.setId(originFeatureId); - feature.getProperties().setXyzActivityLog(null); + feature.getProperties().remove(XYZ_ACTIVITY_LOG_NS); }); return features; } - private static List activityFeatures(String sampleFeaturesJson) { - List features = featuresFromCollectionJson(sampleFeaturesJson); + private static List activityFeatures(String sampleFeaturesJson) { + List features = featuresFromCollectionJson(sampleFeaturesJson); features.forEach(feature -> { String originId = feature.getId(); - XyzActivityLog datahubActivityLog = feature.getProperties().getXyzActivityLog(); - XyzNamespace datahubXyzNamespace = feature.getProperties().getXyzNamespace(); + NakshaActivityLog datahubActivityLog = NakshaActivityLog.getActivityLog(feature.getProperties()); + XyzNs datahubXyzNamespace = feature.getProperties().getXyz(); Original datahubOriginal = datahubActivityLog.getOriginal(); String originAction = datahubActivityLog.getAction(); String originPuuid = datahubOriginal.getPuuid(); long updatedAt = datahubOriginal.getUpdatedAt(); long createdAt = datahubOriginal.getCreatedAt(); - feature.getProperties().getXyzNamespace().setUuid(originId); + feature.getProperties().getXyz().put("uuid",originId); if (originAction.equals("SAVE")) { - originAction = "CREATE"; - datahubActivityLog.setAction("CREATE"); + originAction = "CREATED"; + datahubActivityLog.setAction("CREATED"); } if (datahubOriginal.getSpace() == null) { datahubOriginal.setSpace(SAMPLE_SPACE_ID); } - datahubXyzNamespace.setAction(EXyzAction.get(EXyzAction.class, originAction)); - datahubXyzNamespace.setPuuid(originPuuid); - datahubXyzNamespace.setUpdatedAt(updatedAt); - datahubXyzNamespace.setCreatedAt(createdAt); + datahubXyzNamespace.put("action", originAction); + datahubXyzNamespace.put("puuid",originPuuid); + datahubXyzNamespace.put("updatedAt",updatedAt); + datahubXyzNamespace.put("createdAt",createdAt); }); features.sort(new ActivityLogComparator()); return features; } - private static List featuresFromCollectionJson(String featuresCollectionJson) { + private static List featuresFromCollectionJson(String featuresCollectionJson) { XyzFeatureCollection collection = JsonSerializable.deserialize(featuresCollectionJson, XyzFeatureCollection.class); return collection.getFeatures(); } - public record DatahubSample(List historyFeatures, List activityFeatures) { + public record DatahubSample(List historyFeatures, List activityFeatures) { } } diff --git a/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/expected_enhanced_feature.json b/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/expected_enhanced_feature.json index 34bd1498a..711bc697b 100644 --- a/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/expected_enhanced_feature.json +++ b/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/expected_enhanced_feature.json @@ -93,7 +93,7 @@ "sourceId": "TMOB" }, "@ns:com:here:xyz": { - "action": "UPDATE", + "action": "UPDATED", "app_id": "ChangeSetPublisher", "author": "DEMO_APP_ID", "authorTime": 1706017408414, @@ -114,7 +114,7 @@ "version": 35 }, "@ns:com:here:xyz:log": { - "action": "UPDATE", + "action": "UPDATED", "diff": { "add": 3, "remove": 0, diff --git a/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/new_feature.json b/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/new_feature.json index 343c0f64d..f1b8be4f5 100644 --- a/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/new_feature.json +++ b/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/new_feature.json @@ -93,7 +93,7 @@ "sourceId": "TMOB" }, "@ns:com:here:xyz": { - "action": "UPDATE", + "action": "UPDATED", "app_id": "ChangeSetPublisher", "author": "DEMO_APP_ID", "authorTime": 1706017408414, diff --git a/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/old_feature.json b/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/old_feature.json index 8772d3fc0..2a3b663d7 100644 --- a/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/old_feature.json +++ b/here-naksha-handler-activitylog/src/test/resources/enhancer_samples/changed_line_string_geometry/old_feature.json @@ -108,7 +108,7 @@ "sourceId": "TMOB" }, "@ns:com:here:xyz": { - "action": "UPDATE", + "action": "UPDATED", "app_id": "ChangeSetPublisher", "author": "DEMO_APP_ID", "authorTime": 1706017408414, diff --git a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/License.java b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/License.java index ea538a8c0..62413258e 100644 --- a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/License.java +++ b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/License.java @@ -22,8 +22,6 @@ import java.util.Arrays; import java.util.List; import naksha.base.AnyObject; -import naksha.base.JvmObject; -import naksha.model.objects.NakshaFeature; public class License extends AnyObject { diff --git a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/geojson/implementation/namespaces/Original.java b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/geojson/implementation/namespaces/Original.java index 5d04f7334..1ab6e97cf 100644 --- a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/geojson/implementation/namespaces/Original.java +++ b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/geojson/implementation/namespaces/Original.java @@ -22,12 +22,14 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; -import com.here.naksha.lib.core.util.json.JsonObject; +import naksha.base.AnyObject; import org.jetbrains.annotations.Nullable; -/** The standard properties of the standard feature store in the Naksha-Hub. */ +/** The standard properties of the standard feature store in the Naksha-Hub. + * @deprecated use the class included in naksha-here-handler-activitylog instead + */ @JsonIgnoreProperties(ignoreUnknown = true) -public class Original extends JsonObject { +public class Original extends AnyObject { public static final String CREATED_AT = "createdAt"; public static final String PUUID = "puuid"; diff --git a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/geojson/implementation/namespaces/XyzActivityLog.java b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/geojson/implementation/namespaces/XyzActivityLog.java index f376c2cbd..dcd976d90 100644 --- a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/geojson/implementation/namespaces/XyzActivityLog.java +++ b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/geojson/implementation/namespaces/XyzActivityLog.java @@ -18,18 +18,22 @@ */ package com.here.naksha.lib.core.models.geojson.implementation.namespaces; +import static naksha.model.objects.NakshaProperties.XYZ_ACTIVITY_LOG_NS; + import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; -import com.here.naksha.lib.core.util.json.JsonObject; -import naksha.model.EXyzAction; +import naksha.base.AnyObject; +import naksha.base.JvmBoxingUtil; +import naksha.model.Action; +import naksha.model.objects.NakshaProperties; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @SuppressWarnings("unused") -public class XyzActivityLog extends JsonObject { +public class XyzActivityLog extends AnyObject { public static final ObjectMapper mapper = new ObjectMapper(); public static final String ID = "id"; public static final String ORIGINAL = "original"; @@ -57,7 +61,7 @@ public XyzActivityLog() { /** * The operation that lead to the current state of the namespace. Should be a value from {@link - * EXyzAction}. + * Action}. */ @JsonProperty(ACTION) private String action; @@ -78,7 +82,7 @@ public void setDiff(@Nullable JsonNode diff) { this.diff = diff; } - public void setAction(@NotNull EXyzAction action) { + public void setAction(@NotNull Action action) { this.action = action.toString(); } @@ -87,18 +91,18 @@ public void setAction(@NotNull EXyzAction action) { return this; } - public @NotNull XyzActivityLog withAction(@NotNull EXyzAction action) { + public @NotNull XyzActivityLog withAction(@NotNull Action action) { setAction(action); return this; } public boolean isDeleted() { - return EXyzAction.DELETE.equals(getAction()); + return Action.DELETED.toString().equals(getAction()); } public void setDeleted(boolean deleted) { if (deleted) { - setAction(EXyzAction.DELETE); + setAction(Action.DELETED); } } @@ -122,4 +126,8 @@ public void setId(@Nullable String id) { public void setOriginal(@NotNull Original original) { this.original = original; } + + public static @Nullable XyzActivityLog getXyzActivityLog(@NotNull NakshaProperties properties) { + return JvmBoxingUtil.box(properties.get(XYZ_ACTIVITY_LOG_NS), XyzActivityLog.class); + } } diff --git a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/indexing/Constraint.java b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/indexing/Constraint.java index e7813d323..38483e8dc 100644 --- a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/indexing/Constraint.java +++ b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/models/indexing/Constraint.java @@ -21,7 +21,6 @@ import naksha.base.AnyObject; import naksha.base.JvmListProxy; import naksha.base.JvmMapProxy; -import naksha.model.objects.NakshaFeature; /** Base class of all possible constraints that can be combined. */ public class Constraint extends AnyObject { diff --git a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/json/JsonObject.java b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/json/JsonObject.java index 3a05d263a..bcfd268b4 100644 --- a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/json/JsonObject.java +++ b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/json/JsonObject.java @@ -37,7 +37,6 @@ import java.util.Set; import java.util.function.BiFunction; import java.util.function.Function; -import naksha.diff.Patcher; import naksha.model.NakshaVersion; import naksha.model.StringHelper; import org.jetbrains.annotations.ApiStatus.AvailableSince; @@ -49,7 +48,7 @@ * properties. In other words, this is a hash-map that can be extended with native POJO members. In * that case all Java fields annotated with {@link JsonProperty}, being {@code public} or {@code * protected} and not annotated with {@link JsonIgnore} are made available as hash-map - * key-value pairs. This is necessary for the {@link Patcher} to work. Note that the map will + * key-value pairs. This is necessary for the old {@code Patcher} to work. Note that the map will * reflect the properties by their annotated name, if annotated differently from the name in the * source code. * @@ -59,6 +58,7 @@ * using the {@link #additionalProperties() additional properties map}! * * @since 2.0.0 + * @deprecated From Naksha V3 onward, please use {@link naksha.base.AnyObject} as the base for any JSON class. */ @AvailableSince(NakshaVersion.v2_0_0) @JsonFormat(shape = JsonFormat.Shape.OBJECT) diff --git a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/modify/FeatureModificationEntry.java b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/modify/FeatureModificationEntry.javabak similarity index 100% rename from here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/modify/FeatureModificationEntry.java rename to here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/modify/FeatureModificationEntry.javabak diff --git a/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/modify/FeatureModificationList.java b/here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/modify/FeatureModificationList.javabak similarity index 100% rename from here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/modify/FeatureModificationList.java rename to here-naksha-lib-core/src/main/java/com/here/naksha/lib/core/util/modify/FeatureModificationList.javabak diff --git a/here-naksha-lib-core/src/main/java/naksha/diff/ConflictResolution.java b/here-naksha-lib-core/src/main/java/naksha/diff/ConflictResolution.java deleted file mode 100644 index a51cf2994..000000000 --- a/here-naksha-lib-core/src/main/java/naksha/diff/ConflictResolution.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2017-2024 HERE Europe B.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * License-Filename: LICENSE - */ -package naksha.diff; - -import com.fasterxml.jackson.annotation.JsonCreator; -import org.jetbrains.annotations.Nullable; - -/** The way conflicts are to be resolved by the modification entry. */ -public enum ConflictResolution { - /** - * Whenever the current head state is different as the base state, abort the transaction and - * report an error. - */ - ERROR, - - /** - * Whenever the current head state is different as the base state, keep the current head and - * continue the transaction without error. - */ - RETAIN, - - /** - * Always replace the current head state with the given version, no matter if any other change - * conflicts. In this case, the puuid will refer to the state being replaced. - */ - REPLACE; - - /** - * Whenever the current head state is different as the base state, try a three-way-merge - * and conflicting properties or parts of the feature will be retained (stay unmodified). - */ - // MERGE_OR_RETAIN, - - /** - * Whenever the current head state is different as the base state, try a implements Difference { - - int originalLength; - int newLength; - - public ListDiff(final int totalLength) { - super(totalLength); - } -} diff --git a/here-naksha-lib-core/src/main/java/naksha/diff/MapDiff.java b/here-naksha-lib-core/src/main/java/naksha/diff/MapDiff.java deleted file mode 100644 index aaa61da38..000000000 --- a/here-naksha-lib-core/src/main/java/naksha/diff/MapDiff.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2017-2024 HERE Europe B.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * License-Filename: LICENSE - */ -package naksha.diff; - -import java.util.HashMap; - -/** Represents a difference between maps. */ -public class MapDiff extends HashMap implements Difference {} diff --git a/here-naksha-lib-core/src/main/java/naksha/diff/MergeConflictException.java b/here-naksha-lib-core/src/main/java/naksha/diff/MergeConflictException.java deleted file mode 100644 index 77918b0ed..000000000 --- a/here-naksha-lib-core/src/main/java/naksha/diff/MergeConflictException.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2017-2024 HERE Europe B.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * License-Filename: LICENSE - */ -package naksha.diff; - -import org.jetbrains.annotations.NotNull; - -/** - * An exception thrown if applying a patch fails, the creation of a difference fails or any other merge error occurs. - */ -public class MergeConflictException extends Exception { - - MergeConflictException(@NotNull String msg) { - super(msg); - } -} diff --git a/here-naksha-lib-core/src/main/java/naksha/diff/Patcher.java b/here-naksha-lib-core/src/main/java/naksha/diff/Patcher.java deleted file mode 100644 index dd08849ac..000000000 --- a/here-naksha-lib-core/src/main/java/naksha/diff/Patcher.java +++ /dev/null @@ -1,610 +0,0 @@ -/* - * Copyright (C) 2017-2024 HERE Europe B.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * License-Filename: LICENSE - */ -package naksha.diff; - -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * The class provides methods to extract differences from {@link Map} and {@link List} instances, - * merge differences and patch objects with such differences. All objects not being either a {@link - * Map} or {@link List} are treated as primitive values. - */ -@SuppressWarnings({"rawtypes", "UnusedReturnValue"}) -public class Patcher { - - /** - * Returns the difference of the two entities or null, if both entities are equal. - * - * @param sourceState first object with the source state to be compared against second target - * state. - * @param targetState the target state against which to compare the source state. - * @return either null if both objects are equal or the difference that contains what was changed - * in the target state compared to the source state. - */ - public static @Nullable Difference getDifference( - final @Nullable Object sourceState, final @Nullable Object targetState) { - return getDifference(sourceState, targetState, null); - } - - /** - * Returns the difference of the two states or null, if both entities are equal. This method will - * return {@link InsertOp} if the source state is null, {@link RemoveOp} if the target state is - * null, {@link MapDiff} if both states are {@link Map maps} that differ, {@link ListDiff} if both - * states are {@link List lists} that differ and {@link UpdateOp} if the two states are different, - * but none of them is null and not both of them are {@link Map} or {@link List}. - * - * @param sourceState first object with the source state to be compared against second target - * state. - * @param targetState the target state against which to compare the source state. - * @param ignoreKey a method to test for keys to ignore while creating the difference. - * @return the difference between the two states or null, if both states are equal. - */ - @SuppressWarnings("WeakerAccess") - public static @Nullable Difference getDifference( - final @Nullable Object sourceState, - final @Nullable Object targetState, - final @Nullable IgnoreKey ignoreKey) { - if (sourceState == targetState) { - return null; - } - - if (sourceState == null) { - return new InsertOp(targetState); - } - - if (targetState == null) { - return new RemoveOp(sourceState); - } - - if (sourceState instanceof Map && targetState instanceof Map) { - return getMapDifference((Map) sourceState, (Map) targetState, ignoreKey); - } - - if (sourceState instanceof List && targetState instanceof List) { - return getListDifference((List) sourceState, (List) targetState, ignoreKey); - } - - if ((sourceState instanceof Number) && (targetState instanceof Number)) { - Number targetNumber = (Number) targetState; - Number sourceNumber = (Number) sourceState; - if ((sourceState instanceof Float) - || (sourceState instanceof Double) - || (targetState instanceof Float) - || (targetState instanceof Double)) { - if (sourceNumber.doubleValue() == targetNumber.doubleValue()) { - return null; - } - } else if (sourceNumber.longValue() == targetNumber.longValue()) { - return null; - } - } - - // Treat source and target as scalar values. - if (sourceState.equals(targetState)) { - return null; - } - - // The source state was updated to the target state. - return new UpdateOp(sourceState, targetState); - } - - /** - * Returns the difference between two maps. - * - * @param sourceState first object to be compared against object B. - * @param targetState the object against which to compare object A. - * @param ignoreKey a method to test for keys to ignore while creating the difference. - * @return either null if both objects are equal or Difference instance that shows the difference - * between object A and object B. - * @throws NullPointerException if the sourceState or targetState is null. - */ - private static MapDiff getMapDifference( - final Map sourceState, final Map targetState, final @Nullable IgnoreKey ignoreKey) { - final MapDiff diff = new MapDiff(); - - final Set keysA = sourceState.keySet(); - final Set keysB = targetState.keySet(); - - for (final @NotNull Object key : keysA) { - if (ignoreKey != null && ignoreKey.ignore(key, sourceState, targetState)) { - continue; - } - - if (!targetState.containsKey(key)) { - diff.put(key, new RemoveOp(sourceState.get(key))); - } else { - Difference tDiff = getDifference(sourceState.get(key), targetState.get(key), ignoreKey); - if (tDiff != null) { - diff.put(key, tDiff); - } - } - } - - for (final @NotNull Object key : keysB) { - if (sourceState.containsKey(key) || ignoreKey != null && ignoreKey.ignore(key, sourceState, targetState)) { - continue; - } - - diff.put(key, new InsertOp(targetState.get(key))); - } - - if (diff.size() == 0) { - return null; - } - - return diff; - } - - /** - * Returns the difference of the two list objects. - * - * @param sourceList first list to be compared against list B. - * @param targetList the list against which to compare list A. - * @param ignoreKey a method to test for keys to ignore while creating the difference. - * @return either null if both lists are equal or Difference instance that shows the difference - * between list A and list B. - * @throws NullPointerException if the sourceList or targetList is null. - */ - private static ListDiff getListDifference( - final List sourceList, final List targetList, final @Nullable IgnoreKey ignoreKey) { - int sourceLength = sourceList.size(), - targetLength = targetList.size(), - minLen = Math.min(sourceLength, targetLength), - maxLen = Math.max(sourceLength, targetLength), - i; - final ListDiff listDiff = new ListDiff(maxLen); - listDiff.originalLength = sourceLength; - listDiff.newLength = targetLength; - - boolean isModified = false; - - // The items that we will find in both lists. - for (i = 0; i < minLen; i++) { - final Difference diff = getDifference(sourceList.get(i), targetList.get(i), ignoreKey); - if (diff != null) { - isModified = true; - } - listDiff.add(diff); - } - - // If the source (original) list was longer than the target one. - if (sourceLength > targetLength) { - isModified = true; - for (; i < maxLen; i++) { - listDiff.add(new RemoveOp(sourceList.get(i))); - } - } - // If the target (new) list is longer than the source (original) one. - else if (targetLength > sourceLength) { - isModified = true; - for (; i < maxLen; i++) { - listDiff.add(new InsertOp(targetList.get(i))); - } - } - - // If nothing changed, so we have an array full of null, there is no change. - if (!isModified) { - return null; - } - - return listDiff; - } - - /** - * Merge two differences, both differences must be of the same type. - * - * @param diffA the first difference. - * @param diffB the second difference. - * @param cr The conflict resolution strategy. - * @return a merged difference. - * @throws MergeConflictException if a conflict that is not automatically solvable occurred. - */ - public static @NotNull Difference mergeDifferences( - final @NotNull T diffA, final @NotNull T diffB, final @NotNull ConflictResolution cr) - throws MergeConflictException { - if (diffA.getClass() != diffB.getClass()) { - throw new MergeConflictException("Conflict while merging " + diffA + " with " + diffB); - } - - if (diffA instanceof ListDiff) { - return mergeListDifferences((ListDiff) diffA, (ListDiff) diffB, cr); - } - - if (diffA instanceof MapDiff) { - return mergeMapDifferences((MapDiff) diffA, (MapDiff) diffB, cr); - } - - if (diffA instanceof UpdateOp || diffA instanceof InsertOp) { - Object valueA; - Object valueB; - - if (diffA instanceof UpdateOp) { - valueA = ((UpdateOp) diffA).newValue(); - valueB = ((UpdateOp) diffB).newValue(); - } else { - valueA = ((InsertOp) diffA).newValue(); - valueB = ((InsertOp) diffB).newValue(); - } - - if (valueA != valueB) { - if (valueA == null - || valueB == null - || valueA.getClass() != valueB.getClass() - || !valueA.equals(valueB)) { - switch (cr) { - case ERROR: - throw new MergeConflictException("Conflict while merging " + diffA + " with " + diffB); - case RETAIN: - return diffA; - case REPLACE: - return diffB; - default: - throw new IllegalArgumentException(); - } - } - } - } - - return diffA; - } - - /** - * Internal method to merge two objects differences. - * - * @param diffA an object difference. - * @param diffB another object difference. - * @param cr The conflict resolution strategy. - * @return the merged object difference. - * @throws MergeConflictException if a conflict that is not automatically solvable occurred. - * @throws MergeConflictException if any error related to JSON occurred. - */ - private static MapDiff mergeMapDifferences(MapDiff diffA, MapDiff diffB, ConflictResolution cr) - throws MergeConflictException { - final MapDiff mergedDiff = new MapDiff(); - - Set diffAKeys = diffA.keySet(); - for (Object key : diffAKeys) { - mergedDiff.put(key, mergeDifferences(diffA.get(key), diffB.get(key), cr)); - } - - Set diffBKeys = diffB.keySet(); - for (Object key : diffBKeys) { - if (!mergedDiff.containsKey(key)) { - mergedDiff.put(key, diffB.get(key)); - } - } - - return mergedDiff; - } - - /** - * Internal method to merge two list differences. - * - * @param diffA a list difference. - * @param diffB another list difference. - * @param cr The conflict resolution strategy. - * @return the merged list difference. - * @throws MergeConflictException if a conflict that is not automatically solvable occurred. - * @throws NullPointerException if either diffA or diffB are null. - */ - private static ListDiff mergeListDifferences(ListDiff diffA, ListDiff diffB, ConflictResolution cr) - throws MergeConflictException { - final int aLength = diffA.size(); - final int bLength = diffB.size(); - final int MAX = Math.max(aLength, bLength); - final int MIN = Math.min(aLength, bLength); - final ListDiff mergeDiff = new ListDiff(MAX); - mergeDiff.originalLength = diffA.originalLength; - - // Ensure that aDiff is longer than bDiff. - final ListDiff aDiff; - final ListDiff bDiff; - if (bLength > aLength) { - aDiff = diffB; - bDiff = diffA; - } else { - aDiff = diffA; - bDiff = diffB; - } - - try { - boolean removeFound = false; - boolean insertFound = false; - for (int i = 0; i < aDiff.size(); i++) { - Difference a = aDiff.get(i); - // As b might be shorter than a, we treat any missing element in b as an "unchanged" - final Difference b = i < MIN ? bDiff.get(i) : null; - - // If in any item is found that is remove or insert, we expect that all further items are as - // well remove or insert - if ((a instanceof RemoveOp) || (b instanceof RemoveOp)) { - if (insertFound) { - throw new MergeConflictException( - "Conflict while merging " + a + " with " + b + ", collision on index " + i + "."); - } - removeFound = true; - } else if ((a instanceof InsertOp) || (b instanceof InsertOp)) { - if (removeFound) { - throw new MergeConflictException( - "Conflict while merging " + a + " with " + b + ", collision on index " + i + "."); - } - insertFound = true; - } - - // If we found remove, we expect only removes - if (removeFound) { - if (!(a instanceof RemoveOp)) { - throw new MergeConflictException( - "Conflict while merging " + a + " with " + b + ", collision on index " + i + "."); - } - if (i < MIN && !(b instanceof RemoveOp)) { - throw new MergeConflictException( - "Conflict while merging " + a + " with " + b + ", collision on index " + i + "."); - } - mergeDiff.add(a); - continue; - } - - // If we found insert, we expect only inserts - if (insertFound) { - if (!(a instanceof InsertOp)) { - throw new MergeConflictException( - "Conflict while merging " + a + " with " + b + ", collision on index " + i + "."); - } - mergeDiff.newLength++; - mergeDiff.add(a); - - if (i < MIN) { - if (!(b instanceof InsertOp)) { - throw new MergeConflictException( - "Conflict while merging " + a + " with " + b + ", collision on index " + i + "."); - } - mergeDiff.newLength++; - mergeDiff.add(b); - } - continue; - } - - // If the value was not changed in a - if (a == null) { - // Any update done in b is acceptable - mergeDiff.newLength++; - mergeDiff.add(b); - continue; - } - - // If the value was not changed in b - if (b == null) { - // Any update done in a is acceptable - mergeDiff.newLength++; - mergeDiff.add(a); - continue; - } - - // If in the longer list an item was update, we expect that it was as well updated in the - // other list - if (a instanceof UpdateOp) { - final Object newA = ((UpdateOp) a).newValue(); - if (!(b instanceof UpdateOp)) { - throw new MergeConflictException( - "Conflict while merging " + a + " with " + b + ", collision on index " + i + "."); - } - - final Object newB = ((UpdateOp) b).newValue(); - if (!Objects.equals(newA, newB)) { - throw new MergeConflictException( - "Conflict while merging " + a + " with " + b + ", collision on index " + i + "."); - } - mergeDiff.add(a); - continue; - } - - // If a is an map difference, we expect b to be one either and that we can merge them - if (a instanceof MapDiff) { - if (!(b instanceof MapDiff)) { - throw new MergeConflictException( - "Conflict while merging " + a + " with " + b + ", collision on index " + i + "."); - } - mergeDiff.add(mergeMapDifferences((MapDiff) a, (MapDiff) b, cr)); - continue; - } - - // If a is an list difference, we expect b to be one either and that we can merge them - if (a instanceof ListDiff) { - if (!(b instanceof ListDiff)) { - throw new MergeConflictException( - "Conflict while merging " + a + " with " + b + ", collision on index " + i + "."); - } - mergeDiff.add(mergeListDifferences((ListDiff) a, (ListDiff) b, cr)); - continue; - } - - // We must not reach this point - throw new MergeConflictException( - "Conflict while merging " + a + " with " + b + ", collision on index " + i + "."); - } - } catch (MergeConflictException e) { - throw e; - } catch (Exception e) { - throw new MergeConflictException("Unexpected exception while merging:\n" + ExceptionUtils.getStackTrace(e)); - } - return mergeDiff; - } - - /** - * Patches the map with the provided difference object. - * - * @param object The object to be patched. - * @param diff The difference object; if {@code null}, then the unmodified object is returned. - */ - public static @NotNull T patch(@NotNull T object, final @Nullable Difference diff) { - if (diff == null) { - return object; - } - - if (object instanceof Map) { - Map map = (Map) object; - if (!(diff instanceof MapDiff)) { - throw new IllegalArgumentException( - "Patch failed, the object is a Map, but the difference is no DiffMap"); - } - MapDiff mapDiff = (MapDiff) diff; - //noinspection unchecked - patchMap(map, mapDiff); - return object; - } - - if (object instanceof List) { - List list = (List) object; - if (!(diff instanceof ListDiff)) { - throw new IllegalArgumentException( - "Patch failed, the object is a List, but the difference is no DiffList"); - } - //noinspection unchecked - patchList(list, (ListDiff) diff); - return object; - } - - throw new IllegalArgumentException("The given object is no Map or List"); - } - - /** - * Patches a JSON object with the provided difference object. - * - * @param targetMap the map to be patched. - * @param mapDiff the difference to be applied to the provided object. - * @throws NullPointerException if the provided targetMap is null. - * @throws IllegalArgumentException if the provided map is no valid Map or MutableMap instance. - */ - private static void patchMap(final Map targetMap, final MapDiff mapDiff) { - if (mapDiff == null) { - return; - } - - final Set keys = mapDiff.keySet(); - for (final Object key : keys) { - final Object diff = mapDiff.get(key); - if (diff instanceof InsertOp) { - targetMap.put(key, ((InsertOp) diff).newValue()); - } else if (diff instanceof RemoveOp) { - targetMap.remove(key); - } else if (diff instanceof UpdateOp) { - targetMap.put(key, ((UpdateOp) diff).newValue()); - } else if (diff instanceof ListDiff) { - //noinspection unchecked - patchList((List) targetMap.get(key), (ListDiff) diff); - } else if (diff instanceof MapDiff) { - //noinspection unchecked - patchMap((Map) targetMap.get(key), (MapDiff) diff); - } else { - throw new IllegalStateException("The given map contains at key '" + key + "' an invalid element"); - } - } - } - - /** - * Patches a list with the provided difference object. - * - * @param list the array to become merged. - * @param listDiff the difference to be merged into the provided list. - * @throws NullPointerException if the provided list is null. - * @throws IllegalArgumentException if the provided list is no valid List or MutableList instance. - */ - private static void patchList(final List list, final ListDiff listDiff) - throws NullPointerException, IllegalArgumentException { - if (listDiff == null) { - return; - } - try { - for (int i = listDiff.size() - 1; i >= 0; i--) { - Difference diff = listDiff.get(i); - if (diff == null) { - continue; - } - - if (diff instanceof UpdateOp) { - list.set(i, ((UpdateOp) diff).newValue()); - } else if (diff instanceof RemoveOp) { - list.remove(i); - } else if (diff instanceof InsertOp) { - list.add(((InsertOp) diff).newValue()); - } else if (diff instanceof ListDiff) { - //noinspection unchecked - patchList((List) list.get(i), (ListDiff) diff); - } else if (diff instanceof MapDiff) { - //noinspection unchecked - patchMap((Map) list.get(i), (MapDiff) diff); - } else { - throw new IllegalStateException("The given list contains at index #" + i + " an invalid element"); - } - } - } catch (IllegalStateException e) { - throw e; - } catch (Exception e) { - throw new IllegalStateException("Unexpected exception in visitor", e); - } - } - - /** - * Converts a partial update object into a difference object. - * - * @param sourceObject the source object. - * @param partialUpdate the partial update object. - * @param ignoreKey a method to test for keys to ignore while creating the difference. - * @return the created difference object; {@code null} if no difference found. - */ - public static @Nullable Difference calculateDifferenceOfPartialUpdate( - final @NotNull Map sourceObject, - final @NotNull Map partialUpdate, - final @Nullable IgnoreKey ignoreKey, - final boolean recursive) { - final MapDiff diff = new MapDiff(); - final Set keys = partialUpdate.keySet(); - for (final Object key : keys) { - if (ignoreKey != null && ignoreKey.ignore(key, sourceObject, partialUpdate)) { - continue; - } - - final Object partialUpdateVal = partialUpdate.get(key); - final Object sourceObjectVal = sourceObject.get(key); - if (partialUpdateVal == null) { - if (sourceObject.containsKey(key)) { - diff.put(key, new RemoveOp(sourceObjectVal)); - } - } else if (sourceObjectVal == null) { - diff.put(key, new InsertOp(partialUpdateVal)); - } else if (recursive && sourceObjectVal instanceof Map && partialUpdateVal instanceof Map) { - final Difference childDiff = calculateDifferenceOfPartialUpdate( - (Map) sourceObjectVal, (Map) partialUpdateVal, ignoreKey, true); - if (childDiff != null) { - diff.put(key, childDiff); - } - } else if (!sourceObjectVal.equals(partialUpdateVal)) { - diff.put(key, new UpdateOp(sourceObjectVal, partialUpdateVal)); - } - } - return diff.size() == 0 ? null : diff; - } -} diff --git a/here-naksha-lib-core/src/main/java/naksha/diff/PatcherUtils.java b/here-naksha-lib-core/src/main/java/naksha/diff/PatcherUtils.java deleted file mode 100644 index 7b4dfed89..000000000 --- a/here-naksha-lib-core/src/main/java/naksha/diff/PatcherUtils.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2017-2024 HERE Europe B.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * License-Filename: LICENSE - */ -package naksha.diff; - -import java.util.Iterator; -import java.util.Map.Entry; - -public class PatcherUtils { - public static Difference removeAllRemoveOp(Difference difference) { - if (difference instanceof RemoveOp) { - return null; - } else if (difference instanceof ListDiff) { - final ListDiff listdiff = (ListDiff) difference; - final Iterator iterator = listdiff.iterator(); - while (iterator.hasNext()) { - Difference next = iterator.next(); - if (next == null) continue; - next = removeAllRemoveOp(next); - if (next == null) iterator.remove(); - } - return listdiff; - } else if (difference instanceof MapDiff) { - final MapDiff mapdiff = (MapDiff) difference; - final Iterator> iterator = - mapdiff.entrySet().iterator(); - while (iterator.hasNext()) { - Entry next = iterator.next(); - next.setValue(removeAllRemoveOp(next.getValue())); - if (next.getValue() == null) iterator.remove(); - } - return mapdiff; - } - return difference; - } -} diff --git a/here-naksha-lib-core/src/main/java/naksha/diff/PrimitiveDiff.java b/here-naksha-lib-core/src/main/java/naksha/diff/PrimitiveDiff.java deleted file mode 100644 index 37425f620..000000000 --- a/here-naksha-lib-core/src/main/java/naksha/diff/PrimitiveDiff.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2017-2024 HERE Europe B.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * License-Filename: LICENSE - */ -package naksha.diff; - -import java.util.Objects; - -/** A common interface for primitive changes. */ -public abstract class PrimitiveDiff implements Difference { - - private final Object newValue; - private final Object oldValue; - - public PrimitiveDiff(Object oldValue, Object newValue) { - this.oldValue = oldValue; - this.newValue = newValue; - } - - /** - * Returns the old value. - * - * @return the old value. - */ - public Object oldValue() { - return oldValue; - } - - /** - * Returns the new value. - * - * @return the new value. - */ - public Object newValue() { - return newValue; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - PrimitiveDiff that = (PrimitiveDiff) o; - return Objects.equals(newValue, that.newValue) && Objects.equals(oldValue, that.oldValue); - } - - @Override - public int hashCode() { - return Objects.hash(newValue, oldValue); - } -} diff --git a/here-naksha-lib-core/src/main/java/naksha/diff/RemoveOp.java b/here-naksha-lib-core/src/main/java/naksha/diff/RemoveOp.java deleted file mode 100644 index 3571a8042..000000000 --- a/here-naksha-lib-core/src/main/java/naksha/diff/RemoveOp.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2017-2024 HERE Europe B.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * License-Filename: LICENSE - */ -package naksha.diff; - -/** A remove. */ -public class RemoveOp extends PrimitiveDiff { - - public RemoveOp(final Object value) { - super(value, null); - } -} diff --git a/here-naksha-lib-core/src/main/java/naksha/diff/UpdateOp.java b/here-naksha-lib-core/src/main/java/naksha/diff/UpdateOp.java deleted file mode 100644 index f599325b0..000000000 --- a/here-naksha-lib-core/src/main/java/naksha/diff/UpdateOp.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2017-2024 HERE Europe B.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * License-Filename: LICENSE - */ -package naksha.diff; - -/** An update. */ -public class UpdateOp extends PrimitiveDiff { - - public UpdateOp(Object oldValue, Object newValue) { - super(oldValue, newValue); - } -} diff --git a/here-naksha-lib-core/src/main/java/naksha/model/EXyzAction.java b/here-naksha-lib-core/src/main/java/naksha/model/EXyzAction.java index 283b15127..56ff40d9f 100644 --- a/here-naksha-lib-core/src/main/java/naksha/model/EXyzAction.java +++ b/here-naksha-lib-core/src/main/java/naksha/model/EXyzAction.java @@ -21,7 +21,9 @@ import com.here.naksha.lib.core.models.geojson.implementation.namespaces.XyzNamespace; import com.here.naksha.lib.core.util.json.JsonEnum; -/** The actions that are supported by Naksha. */ +/** The actions that are supported by Naksha. + * @deprecated use {@link Action} instead + */ public class EXyzAction extends JsonEnum { /** diff --git a/here-naksha-lib-core/src/test/java/com/here/naksha/lib/core/util/diff/PatcherTest.java b/here-naksha-lib-core/src/test/java/com/here/naksha/lib/core/util/diff/PatcherTest.java deleted file mode 100644 index 6be8e64aa..000000000 --- a/here-naksha-lib-core/src/test/java/com/here/naksha/lib/core/util/diff/PatcherTest.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright (C) 2017-2023 HERE Europe B.V. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * SPDX-License-Identifier: Apache-2.0 - * License-Filename: LICENSE - */ -package com.here.naksha.lib.core.util.diff; - -import naksha.diff.*; -import naksha.model.EXyzAction; -import com.here.naksha.lib.core.models.geojson.implementation.namespaces.XyzNamespace; -import com.here.naksha.lib.core.util.IoHelp; -import com.here.naksha.lib.core.util.json.JsonObject; -import com.here.naksha.lib.core.util.json.JsonSerializable; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Stream; - -import naksha.model.objects.NakshaFeature; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.json.JSONException; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.skyscreamer.jsonassert.JSONAssert; -import org.skyscreamer.jsonassert.JSONCompareMode; - -import java.util.Map; - -import static naksha.diff.PatcherUtils.removeAllRemoveOp; -import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.params.provider.Arguments.arguments; - -@SuppressWarnings({"rawtypes", "ConstantConditions"}) -class PatcherTest { - - @Test - void basic() { - final NakshaFeature f1 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_1.json"), NakshaFeature.class); - assertNotNull(f1); - - final NakshaFeature f2 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_2.json"), NakshaFeature.class); - assertNotNull(f2); - - final Difference diff = Patcher.getDifference(f1, f2); - assertNotNull(diff); - - final NakshaFeature f1_patched_to_f2 = Patcher.patch(f1, diff); - assertNotNull(f1_patched_to_f2); - - final Difference newDiff = Patcher.getDifference(f1_patched_to_f2, f2); - assertNull(newDiff); - } - - @Test - void testCompareBasicNestedJson() throws JSONException { - final JsonObject f3 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_3.json"), JsonObject.class); - assertNotNull(f3); - final JsonObject f4 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_4.json"), JsonObject.class); - assertNotNull(f4); - - final Difference diff34 = Patcher.getDifference(f3, f4); - assertNotNull(diff34); - assert (diff34 instanceof MapDiff); - - // Assert outermost layer - final MapDiff mapDiff34 = (MapDiff) diff34; - // TODO if possible to serialize Difference, simply compare the serialized Difference object with test file content - assertInstanceOf(InsertOp.class, mapDiff34.get("isAdded")); - assertInstanceOf(UpdateOp.class, mapDiff34.get("willBeUpdated")); - assertInstanceOf(RemoveOp.class, mapDiff34.get("firstToBeDeleted")); - assertInstanceOf(MapDiff.class, mapDiff34.get("map")); - assertInstanceOf(ListDiff.class, mapDiff34.get("array")); - assertInstanceOf(RemoveOp.class, mapDiff34.get("speedLimit")); - - // Assert nested layer - final MapDiff nestedMapDiff34 = (MapDiff) mapDiff34.get("map"); - // "mapID" is retained, does not appear in nestedMapDiff34 - assertInstanceOf(InsertOp.class, nestedMapDiff34.get("isAdded")); - assertInstanceOf(UpdateOp.class, nestedMapDiff34.get("willBeUpdated")); - assertInstanceOf(RemoveOp.class, nestedMapDiff34.get("willBeDeleted")); - - // Assert nested array - final ListDiff nestedArrayDiff34 = (ListDiff) mapDiff34.get("array"); - assertInstanceOf(UpdateOp.class, nestedArrayDiff34.get(1)); - assertInstanceOf(MapDiff.class, nestedArrayDiff34.get(2)); - // "retainedElement" is retained, does not appear in nestedMapDiff34 - // InsertOp case for array (ListDiff) is addressed in the test testCompareSameArrayDifferentOrder() - assertInstanceOf(RemoveOp.class, nestedArrayDiff34.get(3)); - - // Some extra nested JSON object in array assertions - assertInstanceOf(InsertOp.class, ((MapDiff) nestedArrayDiff34.get(2)).get("isAddedProperty")); - assertInstanceOf(UpdateOp.class, ((MapDiff) nestedArrayDiff34.get(2)).get("nestedShouldBeUpdated")); - assertInstanceOf(RemoveOp.class, ((MapDiff) nestedArrayDiff34.get(2)).get("willBeDeletedProperty")); - - // Modify the whole difference to get rid of all RemoveOp - Difference newDiff34 = removeAllRemoveOp(mapDiff34); - final JsonObject patchedf3 = Patcher.patch(f3,newDiff34); - - assertNotNull(patchedf3); - - final JsonObject expectedPatchedf3 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_3_patched_to_4_no_remove.json"), JsonObject.class); - assertNotNull(expectedPatchedf3); - - // Check that the patched feature 3 has the correct content as 4 but no JSON properties deleted - JSONAssert.assertEquals(expectedPatchedf3.serialize(), patchedf3.serialize(), JSONCompareMode.STRICT); - final Difference newDiff = Patcher.getDifference(patchedf3, expectedPatchedf3); - assertNull(newDiff); - } - - @Test - void testCompareSameArrayDifferentOrder() throws JSONException { - final JsonObject f3 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_3.json"), JsonObject.class); - assertNotNull(f3); - final JsonObject f5 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_5.json"), JsonObject.class); - assertNotNull(f5); - final Difference diff35 = Patcher.getDifference(f3, f5); - assertNotNull(diff35); - assert (diff35 instanceof MapDiff); - final MapDiff mapDiff35 = (MapDiff) diff35; - assertEquals(2,mapDiff35.size()); - assertTrue(mapDiff35.get("array") instanceof ListDiff); - assertTrue(mapDiff35.get("speedLimit") instanceof RemoveOp); - final ListDiff nestedArrayDiff35 = (ListDiff) mapDiff35.get("array"); - // The patcher compares array element by element in order, - // so the nested JSON in feature 3 is compared against the string in feature 5 - // and the string in feature 3 is against the nested JSON in feature 5 - assertTrue(nestedArrayDiff35.get(2) instanceof UpdateOp); - assertTrue(nestedArrayDiff35.get(3) instanceof UpdateOp); - assertTrue(nestedArrayDiff35.get(4) instanceof InsertOp); - - // Check that the patched feature 3 has the same content as 5 - final JsonObject patchedf3Tof5 = Patcher.patch(f3, diff35); - JSONAssert.assertEquals(patchedf3Tof5.serialize(),f3.serialize(), JSONCompareMode.STRICT); - final Difference newDiff = Patcher.getDifference(patchedf3Tof5, f5); - assertNull(newDiff); - } - - @Test - void testPatchingOnlyShuffledArrayProvided() throws JSONException { - final JsonObject f3 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_3.json"), JsonObject.class); - assertNotNull(f3); - // feature 6 only contains the same array in feature 3, but with the order of the elements changed - final JsonObject f6 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_6.json"), JsonObject.class); - assertNotNull(f6); - - final Difference diff36 = Patcher.getDifference(f3, f6); - assertNotNull(diff36); - // Simulate REST API behaviour, ignore all RemoveOp type of Difference - final Difference diff36NoRemove = removeAllRemoveOp(diff36); - final JsonObject patchedf3Tof6 = Patcher.patch(f3, diff36NoRemove); - - final JsonObject expectedPatchedf3 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_3_patched_with_6_no_remove_op.json"), JsonObject.class); - assertNotNull(expectedPatchedf3); - - JSONAssert.assertEquals(expectedPatchedf3.serialize(),patchedf3Tof6.serialize(), JSONCompareMode.STRICT); - final Difference newDiff36 = Patcher.getDifference(patchedf3Tof6, expectedPatchedf3); - assertNull(newDiff36); - } - - private static boolean ignoreAll(@NotNull Object key, @Nullable Map source, @Nullable Map target) { - return true; - } - - @Test - void testIgnoreAll() { - final NakshaFeature f1 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_1.json"), NakshaFeature.class); - assertNotNull(f1); - - final NakshaFeature f2 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_2.json"), NakshaFeature.class); - assertNotNull(f2); - - final Difference diff = Patcher.getDifference(f1, f2, PatcherTest::ignoreAll); - assertNull(diff); - } - - private static boolean ignoreXyzProps(@NotNull Object key, @Nullable Map source, @Nullable Map target) { - if (source instanceof XyzNamespace || target instanceof XyzNamespace) { - return "txn".equals(key) - || "txn_next".equals(key) - || "txn_uuid".equals(key) - || "uuid".equals(key) - || "puuid".equals(key) - || "version".equals(key) - || "rt_ts".equals(key) - || "createdAt".equals(key) - || "updatedAt".equals(key); - } - return false; - } - - @Test - void testXyzNamespace() { - final NakshaFeature f1 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_1.json"), NakshaFeature.class); - assertNotNull(f1); - - final NakshaFeature f2 = - JsonSerializable.deserialize(IoHelp.readResource("patcher/feature_2.json"), NakshaFeature.class); - assertNotNull(f2); - - final Difference rawDiff = Patcher.getDifference(f1, f2, PatcherTest::ignoreXyzProps); - - final MapDiff feature = assertInstanceOf(MapDiff.class, rawDiff); - assertEquals(1, feature.size()); - final MapDiff properties = assertInstanceOf(MapDiff.class, feature.get("properties")); - assertEquals(1, properties.size()); - final MapDiff xyzNs = assertInstanceOf(MapDiff.class, properties.get("@ns:com:here:xyz")); - assertEquals(8, xyzNs.size()); - final UpdateOp action = assertInstanceOf(UpdateOp.class, xyzNs.get("action")); - assertEquals(EXyzAction.CREATE.toString(), action.oldValue()); - assertEquals(EXyzAction.UPDATE.toString(), action.newValue()); - final ListDiff tags = assertInstanceOf(ListDiff.class, xyzNs.get("tags")); - assertEquals(23, tags.size()); - for (int i = 0; i < 22; i++) { - assertNull(tags.get(i)); - } - final InsertOp inserted = assertInstanceOf(InsertOp.class, tags.get(22)); - assertEquals("utm_dummy_update", inserted.newValue()); - assertNull(inserted.oldValue()); - } - - @ParameterizedTest - @MethodSource("listDiffSamples") - void testListDiff(List before, List after, ListDiff expectedResult){ - // When: - Difference difference = Patcher.getDifference(before, after); - - // Then: - Assertions.assertEquals(expectedResult, difference); - } - - private static Stream listDiffSamples(){ - return Stream.of( - arguments( - List.of("one", "two"), - List.of("one", "three"), - listDiff( - null, - new UpdateOp("two","three") - ) - ), - arguments( - List.of("one", "two", "three"), - List.of("three", "four"), - listDiff( - new UpdateOp("one", "three"), - new UpdateOp("two", "four"), - new RemoveOp("three") - ) - ), - arguments( - List.of("one", "two"), - List.of("three", "four", "five"), - listDiff( - new UpdateOp("one", "three"), - new UpdateOp("two", "four"), - new InsertOp("five") - ) - ), - arguments( - List.of(), - List.of("one", "two", "three"), - listDiff( - new InsertOp("one"), - new InsertOp("two"), - new InsertOp("three") - ) - ), - arguments( - List.of("one", "two", "three"), - List.of(), - listDiff( - new RemoveOp("one"), - new RemoveOp("two"), - new RemoveOp("three") - ) - ) - ); - } - - private static ListDiff listDiff(Difference... diffs){ - ListDiff listDiff = new ListDiff(diffs.length); - listDiff.addAll(Arrays.asList(diffs)); - return listDiff; - } -} diff --git a/here-naksha-lib-core/src/testFixtures/java/com/here/naksha/test/common/assertions/AssertionIPropertyQuery.java b/here-naksha-lib-core/src/testFixtures/java/com/here/naksha/test/common/assertions/AssertionIPropertyQuery.java new file mode 100644 index 000000000..4ae162adb --- /dev/null +++ b/here-naksha-lib-core/src/testFixtures/java/com/here/naksha/test/common/assertions/AssertionIPropertyQuery.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2017-2024 HERE Europe B.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ +package com.here.naksha.test.common.assertions; + +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.function.Consumer; +import naksha.model.request.query.*; + +public class AssertionIPropertyQuery { + private final IPropertyQuery subject; + + public AssertionIPropertyQuery(IPropertyQuery subject) { + this.subject = subject; + } + + public IPropertyQuery getQuery() { + return this.subject; + } + + public static AssertionIPropertyQuery assertThatOperation(IPropertyQuery subject) { + assertNotNull(subject); + return new AssertionIPropertyQuery(subject); + } + + public AssertionIPropertyQuery hasType(AnyOp expectedOpType) { + assertInstanceOf(PQuery.class, subject); + assertEquals(expectedOpType, ((PQuery) subject).getOp()); + return this; + } + + public AssertionIPropertyQuery isPOr() { + assertInstanceOf(POr.class, subject); + return this; + } + + public AssertionIPropertyQuery hasProperty(Property expected) { + return hasProperty(expected.getPath()); + } + + public AssertionIPropertyQuery hasProperty(List expected) { + assertNotNull(expected); + assertInstanceOf(PQuery.class, subject); + assertEquals(expected, ((PQuery) subject).getProperty().getPath()); + return this; + } + + public AssertionIPropertyQuery hasPRefWithPath(String[] path) { + assertInstanceOf(PQuery.class, subject); + assertArrayEquals(path, ((PQuery) subject).getProperty().getPath().toArray()); + return this; + } + + public AssertionIPropertyQuery hasValue(Number value) { + assertInstanceOf(PQuery.class, subject); + assertEquals(value, ((PQuery) subject).getValue()); + return this; + } + + public AssertionIPropertyQuery hasValue(String value) { + assertInstanceOf(PQuery.class, subject); + assertEquals(value, ((PQuery) subject).getValue()); + return this; + } + + @SafeVarargs + public final AssertionIPropertyQuery hasChildrenThat(Consumer... childrenAssertions) { + assertInstanceOf(List.class, subject, "Expected multiple operations"); + List subjects = (List) subject; + assertEquals(subjects.size(), childrenAssertions.length, "Expecting single assertion per property query"); + for (int i = 0; i < subjects.size(); i++) { + AssertionIPropertyQuery childAssertion = new AssertionIPropertyQuery(subjects.get(i)); + childrenAssertions[i].accept(childAssertion); + } + return this; + } +} diff --git a/here-naksha-lib-diff/src/commonMain/kotlin/naksha/diff/DifferenceCalculator.kt b/here-naksha-lib-diff/src/commonMain/kotlin/naksha/diff/DifferenceCalculator.kt index e0290a162..efe4f62bd 100644 --- a/here-naksha-lib-diff/src/commonMain/kotlin/naksha/diff/DifferenceCalculator.kt +++ b/here-naksha-lib-diff/src/commonMain/kotlin/naksha/diff/DifferenceCalculator.kt @@ -1,5 +1,7 @@ package naksha.diff +import kotlin.jvm.JvmOverloads + class DifferenceCalculator private constructor() { companion object DifferenceCalculator_C { @@ -17,6 +19,7 @@ class DifferenceCalculator private constructor() { * @return the difference between the two states or null, if both states are equal. * @since 3.0.0 */ + @JvmOverloads fun calculateDifference( source: Any?, target: Any?, diff --git a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/XyzNs.kt b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/XyzNs.kt index 6b8a28591..825470f51 100644 --- a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/XyzNs.kt +++ b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/XyzNs.kt @@ -6,8 +6,6 @@ import naksha.base.* import naksha.model.TagNormalizer.TagNormalizer_C.normalizeTag import kotlin.DeprecationLevel.WARNING import kotlin.js.JsExport -import kotlin.js.JsStatic -import kotlin.jvm.JvmStatic /** * The XYZ namespace stored in [properties.@ns:com:here:xyz][NakshaProperties.XYZ] of the [NakshaFeature]. @@ -16,6 +14,8 @@ import kotlin.jvm.JvmStatic class XyzNs : AnyObject() { companion object XyzNsCompanion { + const val TAGS_KEY = "tags" + private val ACTION = NotNullEnum(Action::class) { _, _ -> Action.CREATED } private val STRING = NotNullProperty(String::class) { _, name -> throw IllegalStateException("The field $name must have a value") diff --git a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/objects/NakshaFeature.kt b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/objects/NakshaFeature.kt index 95cce815b..484635d6e 100644 --- a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/objects/NakshaFeature.kt +++ b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/objects/NakshaFeature.kt @@ -32,6 +32,10 @@ open class NakshaFeature() : AnyObject() { * The feature-type of this feature itself. */ const val FEATURE_TYPE = "Feature" + /** + * The JSON keys + */ + const val ID_KEY = "id" const val PROPERTIES_KEY = "properties" const val TITLE_KEY = "title" const val DESCRIPTION_KEY = "description" diff --git a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/objects/NakshaProperties.kt b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/objects/NakshaProperties.kt index b9da36ea4..f956dec88 100644 --- a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/objects/NakshaProperties.kt +++ b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/objects/NakshaProperties.kt @@ -20,6 +20,11 @@ open class NakshaProperties : AnyObject() { const val XYZ_KEY = "@ns:com:here:xyz" const val DELTA_KEY = "@ns:com:here:mom:delta" const val META_KEY = "@ns:com:here:mom:meta" + /** + * Properties used by the deprecated Activity-Log service, just here to allow downward + * compatibility. + */ + const val XYZ_ACTIVITY_LOG_NS = "@ns:com:here:xyz:log" private val XYZ = NotNullProperty(XyzNs::class, name = XYZ_KEY) private val DELTA_PROXY_NULL = NullableProperty(MomDeltaNs::class, name = DELTA_KEY) diff --git a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/request/PropertyFilter.kt b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/request/PropertyFilter.kt index b4cbce34a..12ccd2da3 100644 --- a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/request/PropertyFilter.kt +++ b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/request/PropertyFilter.kt @@ -7,10 +7,6 @@ import naksha.model.request.query.* class PropertyFilter(val req: ReadFeatures) : ResultFilter { - companion object { - const val PROPERTIES = "properties" - } - /** * Check if the feature matches the query * @param resultTuple the tuple containing the feature @@ -33,7 +29,7 @@ class PropertyFilter(val req: ReadFeatures) : ResultFilter { is POr -> return pQuery.any { resolvePropsQuery(it, decoder) } is PNot -> return !resolvePropsQuery(pQuery.query, decoder) is PQuery -> { - val propFromFeature = decoder.get(PROPERTIES,*pQuery.property.path.filterNotNull().toTypedArray()) + val propFromFeature = decoder.get(Property.PROPERTIES,*pQuery.property.path.filterNotNull().toTypedArray()) val op = pQuery.op return resolveEachOp(op,propFromFeature,pQuery.value) } diff --git a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/request/RequestQuery.kt b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/request/RequestQuery.kt index bd4179930..0527ebe80 100644 --- a/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/request/RequestQuery.kt +++ b/here-naksha-lib-model/src/commonMain/kotlin/naksha/model/request/RequestQuery.kt @@ -4,8 +4,12 @@ package naksha.model.request import naksha.base.* import naksha.geo.HereTile +import naksha.model.XyzNs +import naksha.model.objects.NakshaFeature +import naksha.model.objects.NakshaProperties import naksha.model.request.query.* import kotlin.js.JsExport +import kotlin.jvm.JvmField /** * A set of conditions to be executed against the storage, logically AND combined. @@ -24,6 +28,9 @@ import kotlin.js.JsExport open class RequestQuery : AnyObject() { companion object RequestQuery_C { + @JvmField + val TAGS_PROP_PATH = arrayOf(NakshaFeature.PROPERTIES_KEY, NakshaProperties.XYZ_KEY, XyzNs.TAGS_KEY) + private val INT_LIST = NotNullProperty(IntList::class) private val SPATIAL_QUERY_NULL = NullableProperty(ISpatialQuery::class) private val TAG_QUERY_NULL = NullableProperty(ITagQuery::class) diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index 15069823a..72b44dcbe 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -279,6 +279,13 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +kotlin-web-helpers@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/kotlin-web-helpers/-/kotlin-web-helpers-2.0.0.tgz#b112096b273c1e733e0b86560998235c09a19286" + integrity sha512-xkVGl60Ygn/zuLkDPx+oHj7jeLR7hCvoNF99nhwXMn8a3ApB4lLiC9pk4ol4NHPjyoCbvQctBqvzUcp8pkqyWw== + dependencies: + format-util "^1.0.5" + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -301,10 +308,10 @@ minimatch@^5.0.1, minimatch@^5.1.6: dependencies: brace-expansion "^2.0.1" -mocha@10.7.0: - version "10.7.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.0.tgz#9e5cbed8fa9b37537a25bd1f7fb4f6fc45458b9a" - integrity sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA== +mocha@10.7.3: + version "10.7.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752" + integrity sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A== dependencies: ansi-colors "^4.1.3" browser-stdout "^1.3.1"