diff --git a/ext.gradle b/ext.gradle index b897e14c5f9..44c1a90ae60 100644 --- a/ext.gradle +++ b/ext.gradle @@ -25,7 +25,7 @@ * as we want to manage the versions in a single source. */ -def final SPINE_VERSION = '0.9.65-SNAPSHOT' +def final SPINE_VERSION = '0.9.66-SNAPSHOT' ext { // The version of the modules in this project. diff --git a/server/src/main/java/io/spine/server/event/EEntity.java b/server/src/main/java/io/spine/server/event/EEntity.java index 0a84ea22122..ff285dd2145 100644 --- a/server/src/main/java/io/spine/server/event/EEntity.java +++ b/server/src/main/java/io/spine/server/event/EEntity.java @@ -23,6 +23,7 @@ import com.google.protobuf.Timestamp; import io.spine.annotation.Internal; import io.spine.core.Event; +import io.spine.core.EventContext; import io.spine.core.EventEnvelope; import io.spine.core.EventId; import io.spine.core.Events; @@ -33,6 +34,8 @@ import javax.annotation.Nullable; import java.util.Comparator; +import static io.spine.util.Exceptions.newIllegalStateException; + /** * Stores an event. * @@ -84,7 +87,8 @@ public int compare(EEntity e1, EEntity e2) { EEntity(Event event) { this(event.getId()); - updateState(event); + final Event compactedEvent = compact(event); + updateState(compactedEvent); } /** @@ -124,4 +128,49 @@ public String getType() { } return typeName.value(); } + + /** + * Obtains the compacted version of the event. + * + *

A compacted version doesn't contain: + *

+ * + * @param event the event to compact + * @return the compacted event + */ + private static Event compact(Event event) { + final EventContext context = event.getContext(); + final EventContext.Builder resultContext = context.toBuilder() + .clearEnrichment(); + final EventContext.OriginCase originCase = resultContext.getOriginCase(); + switch (originCase) { + case EVENT_CONTEXT: + resultContext.setEventContext(context.getEventContext() + .toBuilder() + .clearOrigin() + .clearEnrichment()); + break; + case REJECTION_CONTEXT: + resultContext.setRejectionContext(context.getRejectionContext() + .toBuilder() + .clearEnrichment()); + break; + case COMMAND_CONTEXT: + // Does nothing. + break; + case ORIGIN_NOT_SET: + // Does nothing because there is no origin for this event. + break; + default: + throw newIllegalStateException("Unsupported origin case encountered: %s", + originCase); + } + return event.toBuilder() + .setContext(resultContext) + .build(); + } } diff --git a/server/src/test/java/io/spine/server/event/EventStoreShould.java b/server/src/test/java/io/spine/server/event/EventStoreShould.java index 708dcdab688..031a4d43d58 100644 --- a/server/src/test/java/io/spine/server/event/EventStoreShould.java +++ b/server/src/test/java/io/spine/server/event/EventStoreShould.java @@ -22,20 +22,26 @@ import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.MoreExecutors; +import com.google.protobuf.Any; import com.google.protobuf.Duration; import com.google.protobuf.Timestamp; import io.grpc.stub.StreamObserver; import io.spine.core.ActorContext; import io.spine.core.CommandContext; +import io.spine.core.Enrichment; +import io.spine.core.Enrichment.Container; import io.spine.core.Event; import io.spine.core.EventContext; +import io.spine.core.RejectionContext; import io.spine.core.TenantId; +import io.spine.grpc.MemoizingObserver; import io.spine.server.BoundedContext; import io.spine.server.command.TestEventFactory; import io.spine.test.event.ProjectCreated; import io.spine.test.event.TaskAdded; import io.spine.testdata.Sample; import io.spine.time.Durations2; +import io.spine.time.Time; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; @@ -45,14 +51,19 @@ import java.util.concurrent.atomic.AtomicBoolean; import static com.google.common.collect.Sets.newConcurrentHashSet; +import static com.google.protobuf.Any.pack; import static com.google.protobuf.util.Timestamps.add; import static com.google.protobuf.util.Timestamps.subtract; +import static io.spine.grpc.StreamObservers.memoizingObserver; +import static io.spine.protobuf.TypeConverter.toMessage; import static io.spine.test.Verify.assertContainsAll; import static io.spine.test.Verify.assertSize; import static io.spine.time.Time.getCurrentTime; import static io.spine.type.TypeName.of; +import static io.spine.validate.Validate.isDefault; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** @@ -210,21 +221,102 @@ public void fail_to_store_events_of_different_tenants_in_a_single_operation() { .setActorContext(secondTenantActor) .build(); final EventContext firstTenantContext = EventContext.newBuilder() - .setCommandContext(firstTenantCommand) - .build(); + .setCommandContext(firstTenantCommand) + .build(); final EventContext secondTenantContext = EventContext.newBuilder() - .setCommandContext(secondTenantCommand) - .build(); + .setCommandContext(secondTenantCommand) + .build(); final Event firstTenantEvent = Event.newBuilder() .setContext(firstTenantContext) .build(); final Event secondTenantEvent = Event.newBuilder() - .setContext(secondTenantContext) - .build(); + .setContext(secondTenantContext) + .build(); final Collection event = ImmutableSet.of(firstTenantEvent, secondTenantEvent); eventStore.appendAll(event); } + @Test + public void not_store_enrichment_for_EventContext() { + final Event event = projectCreated(Time.getCurrentTime()); + final Event enriched = event.toBuilder() + .setContext(event.getContext() + .toBuilder() + .setEnrichment(newEnrichment())) + .build(); + eventStore.append(enriched); + final MemoizingObserver observer = memoizingObserver(); + eventStore.read(EventStreamQuery.getDefaultInstance(), observer); + final EventContext context = observer.responses() + .get(0) + .getContext(); + assertTrue(isDefault(context.getEnrichment())); + } + + @Test + public void not_store_enrichment_for_origin_of_RejectionContext_type() { + final RejectionContext originContext = RejectionContext.newBuilder() + .setEnrichment(newEnrichment()) + .build(); + final Event event = projectCreated(Time.getCurrentTime()); + final Event enriched = event.toBuilder() + .setContext(event.getContext() + .toBuilder() + .setRejectionContext(originContext)) + .build(); + eventStore.append(enriched); + final MemoizingObserver observer = memoizingObserver(); + eventStore.read(EventStreamQuery.getDefaultInstance(), observer); + final RejectionContext loadedOriginContext = observer.responses() + .get(0) + .getContext() + .getRejectionContext(); + assertTrue(isDefault(loadedOriginContext.getEnrichment())); + } + + @Test + public void not_store_enrichment_for_origin_of_EventContext_type() { + final EventContext.Builder originContext = EventContext.newBuilder() + .setEnrichment(newEnrichment()); + final Event event = projectCreated(Time.getCurrentTime()); + final Event enriched = event.toBuilder() + .setContext(event.getContext() + .toBuilder() + .setEventContext(originContext)) + .build(); + eventStore.append(enriched); + final MemoizingObserver observer = memoizingObserver(); + eventStore.read(EventStreamQuery.getDefaultInstance(), observer); + final EventContext loadedOriginContext = observer.responses() + .get(0) + .getContext() + .getEventContext(); + assertTrue(isDefault(loadedOriginContext.getEnrichment())); + } + + @Test + public void not_store_nested_origins_for_EventContext_origin() { + final EventContext.Builder context = EventContext.newBuilder() + .setEnrichment(newEnrichment()); + final EventContext originContext = EventContext.newBuilder() + .setEventContext(context) + .build(); + final Event event = projectCreated(Time.getCurrentTime()); + final Event enriched = event.toBuilder() + .setContext(event.getContext() + .toBuilder() + .setEventContext(originContext)) + .build(); + eventStore.append(enriched); + final MemoizingObserver observer = memoizingObserver(); + eventStore.read(EventStreamQuery.getDefaultInstance(), observer); + final EventContext loadedOriginContext = observer.responses() + .get(0) + .getContext() + .getEventContext(); + assertTrue(isDefault(loadedOriginContext.getEventContext())); + } + /* * Test environment *********************/ @@ -245,6 +337,16 @@ private static void assertDone(AtomicBoolean done) { } } + private static Enrichment newEnrichment() { + final String key = "enrichment key"; + final Any value = pack(toMessage("enrichment value")); + return Enrichment.newBuilder() + .setContainer(Container.newBuilder() + .putItems(key, value) + .build()) + .build(); + } + private static class ResponseObserver implements StreamObserver { private final Collection resultStorage; diff --git a/server/src/test/java/io/spine/server/event/given/EventReactorMethodTestEnv.java b/server/src/test/java/io/spine/server/event/given/EventReactorMethodTestEnv.java deleted file mode 100644 index ab36d14b1c0..00000000000 --- a/server/src/test/java/io/spine/server/event/given/EventReactorMethodTestEnv.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2017, TeamDev Ltd. All rights reserved. - * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -package io.spine.server.event.given; - -import com.google.protobuf.Int32Value; -import com.google.protobuf.StringValue; -import com.google.protobuf.UInt32Value; -import io.spine.core.React; -import io.spine.server.aggregate.Aggregate; -import io.spine.server.aggregate.Apply; -import io.spine.server.command.Assign; -import io.spine.validate.StringValueVBuilder; - -/** - * @author Alexander Yevsyukov - */ -public class EventReactorMethodTestEnv { - - public static class ReactingAggregate - extends Aggregate { - - public ReactingAggregate(Long id) { - super(id); - } - - @Assign - StringValue handle(Int32Value value) { - final String str = String.valueOf(value.getValue()); - return toMessage(str); - } - - private static StringValue toMessage(String str) { - return StringValue.newBuilder() - .setValue(str) - .build(); - } - - @Apply - private void event(StringValue value) { - final String currentState = getState().getValue(); - getBuilder().setValue(currentState + System.lineSeparator() + value.getValue()); - } - - @React - StringValue on(UInt32Value value) { - final String str = String.valueOf(value.getValue()); - return toMessage(str); - } - } -}