Skip to content

Commit

Permalink
support WoT based JSON skeleton creation for creating new features
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Jaeckle <thomas.jaeckle@bosch.io>
  • Loading branch information
thjaeckle committed Aug 23, 2022
1 parent 9f41bd7 commit 63cdb53
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 22 deletions.
Expand Up @@ -44,6 +44,18 @@ private ImmutableFeatureDefinition(final Collection<DefinitionIdentifier> identi
identifierList = Collections.unmodifiableList(new ArrayList<>(identifiers));
}

/**
* Creates a new instance of {@code ImmutableFeatureDefinition} based on the passed {@code definitionIdentifiers}.
*
* @param definitionIdentifiers the Identifiers of the FeatureDefinition to be returned.
* @return the instance.
* @throws NullPointerException if {@code definitionIdentifiers} is {@code null}.
* @since 3.0.0
*/
public static ImmutableFeatureDefinition of(final Collection<DefinitionIdentifier> definitionIdentifiers) {
return new ImmutableFeatureDefinition(checkNotNull(definitionIdentifiers, "definitionIdentifiers"));
}

/**
* Parses the specified JsonArray and returns an instance of {@code ImmutableFeatureDefinition}.
*
Expand Down
Expand Up @@ -171,6 +171,18 @@ public static DefinitionIdentifier newFeatureDefinitionIdentifier(
return ImmutableFeatureDefinitionIdentifier.ofParsed(featureIdentifierAsCharSequence);
}

/**
* Creates a new instance of {@code ImmutableFeatureDefinition} based on the passed {@code definitionIdentifiers}.
*
* @param definitionIdentifiers the Identifiers of the FeatureDefinition to be returned.
* @return the instance.
* @throws NullPointerException if {@code definitionIdentifiers} is {@code null}.
* @since 3.0.0
*/
public static FeatureDefinition newFeatureDefinition(final Collection<DefinitionIdentifier> definitionIdentifiers) {
return ImmutableFeatureDefinition.of(definitionIdentifiers);
}

/**
* Parses the specified JsonArray and returns an immutable instance of {@code FeatureDefinition} which is
* initialised with the values of the given JSON array.
Expand Down
Expand Up @@ -109,7 +109,8 @@ protected Result<ThingEvent<?>> doApply(final Context<ThingId> context,
final Thing finalNewThing = newThing;
newThing = wotThingDescriptionProvider.provideThingSkeletonForCreation(
command.getEntityId(),
newThing.getDefinition().orElse(null), commandHeaders
newThing.getDefinition().orElse(null),
commandHeaders
)
.map(wotBasedThingSkeleton ->
JsonFactory.mergeJsonValues(finalNewThing.toJson(), wotBasedThingSkeleton.toJson())
Expand Down
Expand Up @@ -12,39 +12,56 @@
*/
package org.eclipse.ditto.things.service.persistence.actors.strategies.commands;

import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory;
import org.eclipse.ditto.json.JsonFactory;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonValue;
import org.eclipse.ditto.things.model.DefinitionIdentifier;
import org.eclipse.ditto.things.model.Feature;
import org.eclipse.ditto.things.model.FeatureDefinition;
import org.eclipse.ditto.things.model.Thing;
import org.eclipse.ditto.things.model.ThingId;
import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory;
import org.eclipse.ditto.things.model.ThingsModelFactory;
import org.eclipse.ditto.things.model.signals.commands.ThingCommandSizeValidator;
import org.eclipse.ditto.things.model.signals.commands.modify.ModifyFeature;
import org.eclipse.ditto.things.model.signals.commands.modify.ModifyFeatureResponse;
import org.eclipse.ditto.things.model.signals.events.FeatureCreated;
import org.eclipse.ditto.things.model.signals.events.FeatureModified;
import org.eclipse.ditto.things.model.signals.events.ThingEvent;
import org.eclipse.ditto.wot.integration.provider.WotThingDescriptionProvider;

import akka.actor.ActorSystem;

/**
* This strategy handles the {@link org.eclipse.ditto.things.model.signals.commands.modify.ModifyFeature} command.
*/
@Immutable
final class ModifyFeatureStrategy extends AbstractThingCommandStrategy<ModifyFeature> {

private final WotThingDescriptionProvider wotThingDescriptionProvider;

/**
* Constructs a new {@code ModifyFeatureStrategy} object.
*
* @param actorSystem the actor system to use for loading the WoT extension.
*/
ModifyFeatureStrategy() {
ModifyFeatureStrategy(final ActorSystem actorSystem) {
super(ModifyFeature.class);
wotThingDescriptionProvider = WotThingDescriptionProvider.get(actorSystem);
}

@Override
Expand Down Expand Up @@ -103,7 +120,42 @@ private Result<ThingEvent<?>> getCreateResult(final Context<ThingId> context, fi
final ModifyFeature command, @Nullable final Thing thing, @Nullable final Metadata metadata) {

final DittoHeaders dittoHeaders = command.getDittoHeaders();
final Feature feature = command.getFeature();

Feature feature = command.getFeature();
final Feature finalNewFeature = feature;
feature = wotThingDescriptionProvider.provideFeatureSkeletonForCreation(
finalNewFeature.getId(),
finalNewFeature.getDefinition().orElse(null),
dittoHeaders
)
.map(wotBasedFeatureSkeleton -> {
final Optional<FeatureDefinition> mergedDefinition =
wotBasedFeatureSkeleton.getDefinition()
.map(def -> {
final Set<DefinitionIdentifier> identifiers = Stream.concat(
wotBasedFeatureSkeleton.getDefinition()
.map(FeatureDefinition::stream)
.orElse(Stream.empty()),
finalNewFeature.getDefinition()
.map(FeatureDefinition::stream)
.orElse(Stream.empty())
).collect(Collectors.toCollection(LinkedHashSet::new));
return ThingsModelFactory.newFeatureDefinition(identifiers);
})
.or(finalNewFeature::getDefinition);

return mergedDefinition.map(definitionIdentifiers -> JsonFactory.mergeJsonValues(
finalNewFeature.setDefinition(definitionIdentifiers).toJson(),
wotBasedFeatureSkeleton.toJson()
)).orElseGet(() -> JsonFactory.mergeJsonValues(finalNewFeature.toJson(),
wotBasedFeatureSkeleton.toJson())
);
})
.filter(JsonValue::isObject)
.map(JsonValue::asObject)
.map(ThingsModelFactory::newFeatureBuilder)
.map(b -> b.useId(finalNewFeature.getId()).build())
.orElse(finalNewFeature);

final ThingEvent<?> event =
FeatureCreated.of(command.getEntityId(), feature, nextRevision, getEventTimestamp(), dittoHeaders,
Expand Down
Expand Up @@ -12,39 +12,56 @@
*/
package org.eclipse.ditto.things.service.persistence.actors.strategies.commands;

import java.util.LinkedHashSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.base.model.entity.metadata.Metadata;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.base.model.headers.WithDittoHeaders;
import org.eclipse.ditto.base.model.headers.entitytag.EntityTag;
import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory;
import org.eclipse.ditto.json.JsonFactory;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.json.JsonValue;
import org.eclipse.ditto.things.model.DefinitionIdentifier;
import org.eclipse.ditto.things.model.FeatureDefinition;
import org.eclipse.ditto.things.model.Features;
import org.eclipse.ditto.things.model.Thing;
import org.eclipse.ditto.things.model.ThingId;
import org.eclipse.ditto.internal.utils.persistentactors.results.Result;
import org.eclipse.ditto.internal.utils.persistentactors.results.ResultFactory;
import org.eclipse.ditto.things.model.ThingsModelFactory;
import org.eclipse.ditto.things.model.signals.commands.ThingCommandSizeValidator;
import org.eclipse.ditto.things.model.signals.commands.modify.ModifyFeatures;
import org.eclipse.ditto.things.model.signals.commands.modify.ModifyFeaturesResponse;
import org.eclipse.ditto.things.model.signals.events.FeaturesCreated;
import org.eclipse.ditto.things.model.signals.events.FeaturesModified;
import org.eclipse.ditto.things.model.signals.events.ThingEvent;
import org.eclipse.ditto.wot.integration.provider.WotThingDescriptionProvider;

import akka.actor.ActorSystem;

/**
* This strategy handles the {@link org.eclipse.ditto.things.model.signals.commands.modify.ModifyFeatures} command.
*/
@Immutable
final class ModifyFeaturesStrategy extends AbstractThingCommandStrategy<ModifyFeatures> {

private final WotThingDescriptionProvider wotThingDescriptionProvider;

/**
* Constructs a new {@code ModifyFeaturesStrategy} object.
*
* @param actorSystem the actor system to use for loading the WoT extension.
*/
ModifyFeaturesStrategy() {
ModifyFeaturesStrategy(final ActorSystem actorSystem) {
super(ModifyFeatures.class);
wotThingDescriptionProvider = WotThingDescriptionProvider.get(actorSystem);
}

@Override
Expand Down Expand Up @@ -94,9 +111,48 @@ private Result<ThingEvent<?>> getModifyResult(final Context<ThingId> context, fi
private Result<ThingEvent<?>> getCreateResult(final Context<ThingId> context, final long nextRevision,
final ModifyFeatures command, @Nullable final Thing thing, @Nullable final Metadata metadata) {

final Features features = command.getFeatures();
final DittoHeaders dittoHeaders = command.getDittoHeaders();

final Features features = ThingsModelFactory.newFeatures(command.getFeatures()
.stream()
.map(feature -> wotThingDescriptionProvider.provideFeatureSkeletonForCreation(
feature.getId(),
feature.getDefinition().orElse(null),
dittoHeaders
)
.map(wotBasedFeatureSkeleton -> {
final Optional<FeatureDefinition> mergedDefinition =
wotBasedFeatureSkeleton.getDefinition()
.map(def -> {
final Set<DefinitionIdentifier> identifiers = Stream.concat(
wotBasedFeatureSkeleton.getDefinition()
.map(FeatureDefinition::stream)
.orElse(Stream.empty()),
feature.getDefinition()
.map(FeatureDefinition::stream)
.orElse(Stream.empty())
).collect(Collectors.toCollection(LinkedHashSet::new));
return ThingsModelFactory.newFeatureDefinition(identifiers);
})
.or(feature::getDefinition);

return mergedDefinition.map(definitionIdentifiers -> JsonFactory.mergeJsonValues(
feature.setDefinition(definitionIdentifiers).toJson(),
wotBasedFeatureSkeleton.toJson()
)).orElseGet(() -> JsonFactory.mergeJsonValues(feature.toJson(),
wotBasedFeatureSkeleton.toJson())
);

})
.filter(JsonValue::isObject)
.map(JsonValue::asObject)
.map(ThingsModelFactory::newFeatureBuilder)
.map(b -> b.useId(feature.getId()).build())
.orElse(feature)
)
.toList()
);

final ThingEvent<?> event =
FeaturesCreated.of(command.getEntityId(), features, nextRevision, getEventTimestamp(),
dittoHeaders, metadata);
Expand Down
Expand Up @@ -111,8 +111,8 @@ private void addDefinitionStrategies() {
}

private void addFeaturesStrategies(final ActorSystem system) {
addStrategy(new ModifyFeaturesStrategy());
addStrategy(new ModifyFeatureStrategy());
addStrategy(new ModifyFeaturesStrategy(system));
addStrategy(new ModifyFeatureStrategy(system));
addStrategy(new RetrieveFeaturesStrategy());
addStrategy(new RetrieveFeatureStrategy(system));
addStrategy(new DeleteFeaturesStrategy());
Expand Down
Expand Up @@ -13,23 +13,28 @@
package org.eclipse.ditto.things.service.persistence.actors.strategies.commands;

import static org.eclipse.ditto.things.model.TestConstants.Thing.THING_V2;
import static org.mutabilitydetector.unittesting.AllowedReason.provided;
import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;

import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.internal.utils.persistentactors.commands.CommandStrategy;
import org.eclipse.ditto.things.model.Feature;
import org.eclipse.ditto.things.model.TestConstants;
import org.eclipse.ditto.things.model.ThingId;
import org.eclipse.ditto.internal.utils.persistentactors.commands.CommandStrategy;
import org.eclipse.ditto.things.model.signals.commands.modify.ModifyFeature;
import org.eclipse.ditto.things.model.signals.events.FeatureCreated;
import org.eclipse.ditto.things.model.signals.events.FeatureModified;
import org.eclipse.ditto.things.service.persistence.actors.ETagTestUtils;
import org.eclipse.ditto.wot.integration.provider.WotThingDescriptionProvider;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

import com.typesafe.config.ConfigFactory;

import akka.actor.ActorSystem;

/**
* Unit test for {@link ModifyFeatureStrategy}.
*/
Expand All @@ -46,12 +51,14 @@ public static void initTestFixture() {

@Before
public void setUp() {
underTest = new ModifyFeatureStrategy();
final ActorSystem system = ActorSystem.create("test", ConfigFactory.load("test"));
underTest = new ModifyFeatureStrategy(system);
}

@Test
public void assertImmutability() {
assertInstancesOf(ModifyFeatureStrategy.class, areImmutable());
assertInstancesOf(ModifyFeatureStrategy.class, areImmutable(),
provided(WotThingDescriptionProvider.class).areAlsoImmutable());
}

@Test
Expand Down
Expand Up @@ -14,29 +14,34 @@

import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.eclipse.ditto.things.model.TestConstants.Thing.THING_V2;
import static org.mutabilitydetector.unittesting.AllowedReason.provided;
import static org.mutabilitydetector.unittesting.MutabilityAssert.assertInstancesOf;
import static org.mutabilitydetector.unittesting.MutabilityMatchers.areImmutable;

import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.base.model.headers.DittoHeaders;
import org.eclipse.ditto.internal.utils.persistentactors.commands.CommandStrategy;
import org.eclipse.ditto.json.JsonObject;
import org.eclipse.ditto.things.model.Feature;
import org.eclipse.ditto.things.model.Features;
import org.eclipse.ditto.things.model.TestConstants;
import org.eclipse.ditto.things.model.Thing;
import org.eclipse.ditto.things.model.ThingId;
import org.eclipse.ditto.things.model.ThingTooLargeException;
import org.eclipse.ditto.things.model.ThingsModelFactory;
import org.eclipse.ditto.internal.utils.persistentactors.commands.CommandStrategy;
import org.eclipse.ditto.things.model.signals.commands.modify.CreateThing;
import org.eclipse.ditto.things.model.signals.commands.modify.ModifyFeatures;
import org.eclipse.ditto.things.model.signals.events.FeaturesCreated;
import org.eclipse.ditto.things.model.signals.events.FeaturesModified;
import org.eclipse.ditto.things.service.persistence.actors.ETagTestUtils;
import org.eclipse.ditto.wot.integration.provider.WotThingDescriptionProvider;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

import com.typesafe.config.ConfigFactory;

import akka.actor.ActorSystem;

/**
* Unit test for {@link ModifyFeaturesStrategy}.
*/
Expand All @@ -57,12 +62,14 @@ public static void initTestFixture() {

@Before
public void setUp() {
underTest = new ModifyFeaturesStrategy();
final ActorSystem system = ActorSystem.create("test", ConfigFactory.load("test"));
underTest = new ModifyFeaturesStrategy(system);
}

@Test
public void assertImmutability() {
assertInstancesOf(ModifyFeaturesStrategy.class, areImmutable());
assertInstancesOf(ModifyFeaturesStrategy.class, areImmutable(),
provided(WotThingDescriptionProvider.class).areAlsoImmutable());
}

@Test
Expand Down

0 comments on commit 63cdb53

Please sign in to comment.