All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.pincette.jes.util.Event Maven / Gradle / Ivy

There is a newer version: 3.2.1
Show newest version
package net.pincette.jes.util;

import static net.pincette.jes.util.JsonFields.BEFORE;
import static net.pincette.jes.util.JsonFields.ID;
import static net.pincette.jes.util.JsonFields.OPS;
import static net.pincette.jes.util.JsonFields.SEQ;
import static net.pincette.jes.util.JsonFields.TYPE;
import static net.pincette.jes.util.Util.isManagedObject;
import static net.pincette.json.JsonUtil.getValue;
import static net.pincette.util.Pair.pair;

import java.util.Optional;
import javax.json.JsonObject;
import javax.json.JsonValue;
import net.pincette.json.Patch;

/**
 * Utilities to work with events.
 *
 * @author Werner Donn\u00e9
 * @since 1.0
 */
public class Event {
  private Event() {}

  /**
   * Returns true if the field at jsonPointer has changed. It examines the
   * _ops field for this.
   *
   * @param event the event.
   * @param jsonPointer the path into the aggregate.
   * @return true when the event expresses a change of the given field.
   * @since 1.0
   */
  public static boolean changed(final JsonObject event, final String jsonPointer) {
    return Optional.ofNullable(event.getJsonArray(OPS))
        .map(ops -> Patch.changed(ops, jsonPointer))
        .orElse(false);
  }

  /**
   * Returns true if the field at jsonPointer has changed from the value
   * in from to the value in to. It examines the _ops field
   * for this.
   *
   * @param event the event.
   * @param jsonPointer the path into the aggregate.
   * @param from the original value.
   * @param to the new value.
   * @return true when the event expresses a change of the given field.
   * @since 1.0
   */
  public static boolean changed(
      final JsonObject event, final String jsonPointer, final JsonValue from, final JsonValue to) {
    return Optional.ofNullable(event.getJsonArray(OPS))
        .flatMap(
            ops ->
                Optional.ofNullable(event.getJsonObject(BEFORE))
                    .filter(
                        before ->
                            getValue(before, jsonPointer)
                                .filter(value -> value.equals(from))
                                .isPresent())
                    .map(before -> pair(ops, before)))
        .map(pair -> Patch.changed(pair.first, pair.second, jsonPointer, from, to))
        .orElse(false);
  }

  private static boolean hasOps(final JsonObject event) {
    return Optional.ofNullable(event.getJsonArray("_ops"))
        .filter(ops -> !ops.isEmpty())
        .isPresent();
  }

  /**
   * Checks if event has the proper event structure. This means it should be a managed
   * object with an _ops field.
   *
   * @param event the given event.
   * @return The check report.
   * @see JsonFields
   * @see Util#isManagedObject
   * @since 1.0
   */
  public static boolean isEvent(final JsonObject event) {
    return isManagedObject(event) && hasOps(event);
  }

  /**
   * Checks if the event has the next sequence number for the aggregate. Event log replicators can
   * use this to detect corruption.
   *
   * @param aggregate the aggregate instance.
   * @param event the event.
   * @return true if the event is the next one, false otherwise.
   * @since 1.0
   */
  public static boolean isNext(final JsonObject aggregate, final JsonObject event) {
    return event.getInt(SEQ) == aggregate.getInt(SEQ) + 1;
  }

  /**
   * Creates a standard message to alert event sequence errors. Events are numbered with the 
   * _seq field. When an event listener detect a "hole" in the numbering it can use this
   * message to log the corruption.
   *
   * @param aggregate the aggregate about which the error message is produced.
   * @param event the event about which the error message is produced.
   * @return The generated error message.
   * @since 1.0
   */
  public static String sequenceErrorMessage(final JsonObject aggregate, final JsonObject event) {
    return "SEQUENCE ERROR: event received for ("
        + aggregate.getString(ID)
        + ","
        + aggregate.getString(TYPE)
        + ") with sequence number "
        + event.getInt(SEQ)
        + ", while "
        + (aggregate.getInt(SEQ) + 1)
        + " was expected";
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy