This is an automated email from the ASF dual-hosted git repository.

He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git


The following commit(s) were added to refs/heads/main by this push:
     new 9f2b4ac67b feat: add with-methods to typed query EventEnvelope (#3646)
9f2b4ac67b is described below

commit 9f2b4ac67b5654214d27d2a69ffa9f4380616193
Author: PJ Fanning <[email protected]>
AuthorDate: Fri Oct 23 04:35:29 2026 +0100

    feat: add with-methods to typed query EventEnvelope (#3646)
    
    Motivation:
    
    The typed persistence query `EventEnvelope` is a final class with a long 
constructor and no copy support. Changing one field (for example the event 
payload or the tags when transforming envelopes) means rebuilding the whole 
envelope by hand.
    
    Modification:
    
    Add `withPersistenceId(persistenceId, entityType, slice)`, `withEvent`, 
`withEventOption`, `withTags` and `withMetadata` (`withEventOption` and 
`withTags` each have Scala and Java overloads) to 
`org.apache.pekko.persistence.query.typed.EventEnvelope`, backed by a private 
`copy` helper. `withPersistenceId` takes `entityType` and `slice` explicitly, 
because both are derived from the persistence id.
    
    Result:
    
    Envelopes can be changed with additive, binary compatible methods.
    
    Tests:
    
    - Not run locally - left to CI
    - New case in `EventEnvelopeSpec`
    
    References:
    Ported from (Apache-2.0 since 2026-10-23):
    - https://github.com/akka/akka-core/pull/32067
    
    Notes for reviewers:
    - Only additive methods on a final class (plus a private helper), so no 
MiMa excludes are needed.
    - `withTags` has Scala `Set` and Java `java.util.Set` overloads; 
`withEventOption` has Scala `Option` and Java `java.util.Optional` overloads.
    - `EventEnvelope` is `@ApiMayChange`.
---
 .../persistence/query/typed/EventEnvelope.scala    | 70 ++++++++++++++++++++++
 .../query/typed/EventEnvelopeSpec.scala            | 35 +++++++++++
 2 files changed, 105 insertions(+)

diff --git 
a/persistence-query/src/main/scala/org/apache/pekko/persistence/query/typed/EventEnvelope.scala
 
b/persistence-query/src/main/scala/org/apache/pekko/persistence/query/typed/EventEnvelope.scala
index be42dc8161..d6d0d0313c 100644
--- 
a/persistence-query/src/main/scala/org/apache/pekko/persistence/query/typed/EventEnvelope.scala
+++ 
b/persistence-query/src/main/scala/org/apache/pekko/persistence/query/typed/EventEnvelope.scala
@@ -234,6 +234,76 @@ final class EventEnvelope[Event](
     tags.asJava
   }
 
+  /**
+   * `entityType` and `slice` should be derived from the `persistenceId`, but 
must be explicitly defined
+   * when changing the `persistenceId` of the envelope.
+   * The `slice` should be calculated with 
[[pekko.persistence.Persistence.sliceForPersistenceId]] for
+   * the given `persistenceId`.
+   * The `entityType` should be extracted from the `persistenceId` with
+   * `org.apache.pekko.persistence.typed.PersistenceId.extractEntityType`.
+   */
+  def withPersistenceId(persistenceId: String, entityType: String, slice: 
Int): EventEnvelope[Event] =
+    copy(persistenceId = persistenceId, entityType = entityType, slice = slice)
+
+  def withEvent(event: Event): EventEnvelope[Event] =
+    copy(eventOption = Option(event))
+
+  /**
+   * Scala API
+   */
+  def withEventOption(eventOption: Option[Event]): EventEnvelope[Event] =
+    copy(eventOption = eventOption)
+
+  /**
+   * Java API
+   */
+  def withEventOption(eventOption: Optional[Event]): EventEnvelope[Event] = {
+    import scala.jdk.OptionConverters._
+    copy(eventOption = eventOption.toScala)
+  }
+
+  /**
+   * Scala API
+   */
+  def withTags(tags: Set[String]): EventEnvelope[Event] =
+    copy(tags = tags)
+
+  /**
+   * Java API
+   */
+  def withTags(tags: JSet[String]): EventEnvelope[Event] = {
+    import scala.jdk.CollectionConverters._
+    copy(tags = tags.asScala.toSet)
+  }
+
+  def withMetadata(metadata: Any): EventEnvelope[Event] =
+    copy(eventMetadata = Option(metadata))
+
+  private def copy(
+      offset: Offset = offset,
+      persistenceId: String = persistenceId,
+      sequenceNr: Long = sequenceNr,
+      eventOption: Option[Event] = eventOption,
+      timestamp: Long = timestamp,
+      eventMetadata: Option[Any] = eventMetadata,
+      entityType: String = entityType,
+      slice: Int = slice,
+      filtered: Boolean = filtered,
+      source: String = source,
+      tags: Set[String] = tags): EventEnvelope[Event] =
+    new EventEnvelope(
+      offset,
+      persistenceId,
+      sequenceNr,
+      eventOption,
+      timestamp,
+      eventMetadata,
+      entityType,
+      slice,
+      filtered,
+      source,
+      tags)
+
   override def hashCode(): Int = {
     var result = HashCode.SEED
     result = HashCode.hash(result, offset)
diff --git 
a/persistence-query/src/test/scala/org/apache/pekko/persistence/query/typed/EventEnvelopeSpec.scala
 
b/persistence-query/src/test/scala/org/apache/pekko/persistence/query/typed/EventEnvelopeSpec.scala
index ff482463d6..62a64863d3 100644
--- 
a/persistence-query/src/test/scala/org/apache/pekko/persistence/query/typed/EventEnvelopeSpec.scala
+++ 
b/persistence-query/src/test/scala/org/apache/pekko/persistence/query/typed/EventEnvelopeSpec.scala
@@ -49,5 +49,40 @@ class EventEnvelopeSpec extends PekkoSpec {
       intercept[IllegalStateException](env.event).getMessage should 
include("not loaded")
       intercept[IllegalStateException](env.getEvent()).getMessage should 
include("not loaded")
     }
+
+    "create modified copies with the with-methods" in {
+      val env = envelope(filtered = false)
+
+      val withPid = env.withPersistenceId("pid-2", "entity2", 17)
+      withPid.persistenceId should ===("pid-2")
+      withPid.entityType should ===("entity2")
+      withPid.slice should ===(17)
+      withPid.sequenceNr should ===(env.sequenceNr)
+
+      val withEvent = env.withEvent("evt")
+      withEvent.eventOption should ===(Some("evt"))
+      withEvent.event should ===("evt")
+      withEvent.persistenceId should ===(env.persistenceId)
+      env.eventOption should ===(None)
+
+      env.withEvent("evt").withEventOption(None).eventOption should ===(None)
+      env.withEventOption(Some("evt2")).eventOption should ===(Some("evt2"))
+      env.withEventOption(java.util.Optional.of("evt3")).eventOption should 
===(Some("evt3"))
+      
env.withEvent("evt").withEventOption(java.util.Optional.empty[String]()).eventOption
 should ===(None)
+
+      val withTags = env.withTags(Set("a", "b"))
+      withTags.tags should ===(Set("a", "b"))
+      env.tags should ===(Set.empty[String])
+
+      val javaTags = new java.util.HashSet[String]()
+      javaTags.add("c")
+      val withJavaTags = env.withTags(javaTags)
+      withJavaTags.tags should ===(Set("c"))
+      withJavaTags.getTags() should ===(javaTags)
+
+      val withMeta = env.withMetadata("meta")
+      withMeta.eventMetadata should ===(Some("meta"))
+      withMeta.getEventMetaData().get should ===("meta")
+    }
   }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to