
com.github.rahulsom.grooves.queries.internal.Utils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of grooves-api Show documentation
Show all versions of grooves-api Show documentation
APIs that help in computation of Grooves based Snapshots
The newest version!
package com.github.rahulsom.grooves.queries.internal;
import com.github.rahulsom.grooves.api.GroovesException;
import com.github.rahulsom.grooves.api.events.BaseEvent;
import com.github.rahulsom.grooves.api.events.DeprecatedBy;
import com.github.rahulsom.grooves.api.events.Deprecates;
import com.github.rahulsom.grooves.api.events.RevertEvent;
import com.github.rahulsom.grooves.api.snapshots.TemporalSnapshot;
import com.github.rahulsom.grooves.api.snapshots.VersionedSnapshot;
import com.github.rahulsom.grooves.api.snapshots.internal.BaseSnapshot;
import io.reactivex.Flowable;
import org.jetbrains.annotations.NotNull;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import static io.reactivex.Flowable.*;
import static java.util.stream.Collectors.toList;
/**
* Utility objects and methods to help with Queries.
*
* @author Rahul Somasunderam
*/
public class Utils {
private static final Collector JOIN_EVENT_IDS =
Collectors.joining(", ");
private static final Collector JOIN_EVENTS =
Collectors.joining(",\n ", "[\n ", "\n]");
private Utils() {
}
/**
* Returns a snapshot or redirects to its deprecator.
*
* @param redirect Whether a redirect is desirable
* @param events The sequence of events
* @param it The snapshot
* @param redirectedSnapshot A computation for the redirected snapshot
* @param The type of Aggregate
* @param The type of EventT's id
* @param The type of the event
* @param The type of SnapshotT's id
* @param The type of the snapshot
*
* @return An observable of a snapshot.
*/
@NotNull
public static <
AggregateT,
EventIdT,
EventT extends BaseEvent,
SnapshotIdT,
SnapshotT extends BaseSnapshot
> Flowable returnOrRedirect(
boolean redirect,
@NotNull List events,
@NotNull SnapshotT it,
@NotNull Supplier> redirectedSnapshot) {
final EventT lastEvent =
events.isEmpty() ? null : events.get(events.size() - 1);
final boolean redirectToDeprecator =
lastEvent != null
&& lastEvent instanceof DeprecatedBy
&& redirect;
return fromPublisher(it.getDeprecatedByObservable())
.flatMap(deprecatedBy -> redirectToDeprecator ? redirectedSnapshot.get() : just(it))
.defaultIfEmpty(it);
}
/**
* Computes forward only events. This could mean cancelling out events with their reverts
* within a list, or sometimes, invoking the supplier of fallback events to get events.
*
* @param events The sequence of events
* @param executor The executor to use for processing events
* @param fallbackSnapshotAndEvents The fallback supplier
* @param The type of Aggregate
* @param The type of EventT's id
* @param The type of Event
* @param The type of SnapshotT's id
* @param The type of Snapshot
* @param The type of Query
* @param The type of the query executor
*
* @return an observable of forward only events
*/
@NotNull
public static <
AggregateT,
EventIdT,
EventT extends BaseEvent,
SnapshotIdT,
SnapshotT extends BaseSnapshot,
QueryT extends BaseQuery,
ExecutorT extends Executor
> Flowable getForwardOnlyEvents(
@NotNull List events,
@NotNull ExecutorT executor,
@NotNull Supplier>>> fallbackSnapshotAndEvents) {
return executor.applyReverts(fromIterable(events))
.toList()
.map(Flowable::just)
.onErrorReturn(throwable -> executor
.applyReverts(
fallbackSnapshotAndEvents.get()
.flatMap(it -> fromIterable(it.getSecond()))
)
.toList().toFlowable()
)
.toFlowable()
.flatMap(it -> it)
.flatMap(Flowable::fromIterable);
}
/**
* Turns a list of events into a readable log style string.
*
* @param events The list of events
* @param The type of events
*
* @return A String representation of events
*/
@NotNull
public static String stringify(
@NotNull List events) {
return events.stream()
.map(EventT::toString)
.collect(JOIN_EVENTS);
}
/**
* Turns a list of events into a readable list of ids.
*
* @param events The list of events
* @param The type of events
*
* @return A String representation of events
*/
@NotNull
public static String ids(
@NotNull List events) {
return events.stream()
.map(i -> String.valueOf(i.getId()))
.collect(JOIN_EVENT_IDS);
}
/**
* Sets the last event of a snapshot. Detects the kind of snapshot and sets required
* properties appropriately
*
* @param snapshot The snapshot
* @param event The last event
*/
public static void setLastEvent(@NotNull BaseSnapshot snapshot, @NotNull BaseEvent event) {
if (snapshot instanceof VersionedSnapshot) {
((VersionedSnapshot) snapshot).setLastEventPosition(event.getPosition());
}
if (snapshot instanceof TemporalSnapshot) {
((TemporalSnapshot) snapshot).setLastEventTimestamp(event.getTimestamp());
}
}
/**
* Computes applicable events.
*
* @param The aggregate over which the query executes
* @param The type of the EventT's id field
* @param The type of the Event
* @param The type of the SnapshotT's id field
* @param The type of the Snapshot
* @param forwardOnlyEvents Known forward only events
* @param executor An instance of Executor
* @param snapshotAndEventsSince Events to use if forwardOnlyEvents is empty
*
* @return events that can be applied.
*/
public static <
AggregateT,
EventIdT,
EventT extends BaseEvent,
SnapshotIdT,
SnapshotT extends BaseSnapshot
> Flowable getApplicableEvents(
@NotNull Flowable forwardOnlyEvents,
@NotNull Executor executor,
@NotNull Supplier>>> snapshotAndEventsSince) {
return forwardOnlyEvents
.filter(e -> e instanceof Deprecates)
.toList()
.toFlowable()
.flatMap(list -> list.isEmpty() ?
forwardOnlyEvents :
snapshotAndEventsSince.get().flatMap(p ->
getForwardOnlyEvents(p.getSecond(), executor, () ->
error(new GroovesException(
"Couldn't apply deprecates events")))
));
}
private static final Logger logger = LoggerFactory.getLogger(Utils.class);
/**
* Computes a Flowable of a Pair of Snapshot and List of Events.
*
* @param reuseEarlierSnapshot Whether earlier snapshots can be reused for this
* computation. It is generally a good idea to set this to
* true unless there are known reverts that demand this be
* set to false.
* @param lastUsableSnapshot Supplies the last usable snapshot
* @param uncomputedEvents Gets the list of uncomputed events for a snapshot
* @param nonReusableSnapshotAndEvents Supplies a snapshot without reuse
* @param emptySnapshot Supplies an empty snapshot
* @param The aggregate over which the query executes
* @param The type of the EventT's id field
* @param The type of the Event
* @param The type of the SnapshotT's id field
* @param The type of the Snapshot
*
* @return A flowable with one pair of snapshot and list of events.
*/
@NotNull public static <
AggregateT,
EventIdT,
EventT extends BaseEvent,
SnapshotIdT,
SnapshotT extends BaseSnapshot
> Flowable>> getSnapshotsWithReuse(
boolean reuseEarlierSnapshot,
@NotNull Supplier> lastUsableSnapshot,
@NotNull Function> uncomputedEvents,
@NotNull Supplier>>> nonReusableSnapshotAndEvents,
@NotNull Supplier emptySnapshot
) {
if (reuseEarlierSnapshot) {
return lastUsableSnapshot.get().flatMap(lastSnapshot ->
fromPublisher(uncomputedEvents.apply(lastSnapshot))
.toList()
.toFlowable()
.flatMap(events -> {
if (events.stream().anyMatch(it -> it instanceof RevertEvent)) {
List reverts = events.stream()
.filter(it -> it instanceof RevertEvent)
.collect(toList());
logger.info(" Uncomputed reverts exist: {}",
stringify(reverts));
return nonReusableSnapshotAndEvents.get();
} else {
logger.debug(" Events since last snapshot: {}",
stringify(events));
return just(new Pair<>(lastSnapshot, events));
}
}));
} else {
SnapshotT lastSnapshot = emptySnapshot.get();
final Flowable> uncomputedEventsF =
fromPublisher(uncomputedEvents.apply(lastSnapshot))
.toList()
.toFlowable();
return uncomputedEventsF
.doOnNext(ue -> logger.debug(" Events since origin: {}", stringify(ue)))
.map(ue -> new Pair<>(lastSnapshot, ue));
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy