Skip to content

Commit e021e2d

Browse files
authored
fix: cloud events - convert kafka prefix (#2055)
* fix: cloud events - convert kafka prefix * using a conversion map instead
1 parent 3c38b2c commit e021e2d

File tree

15 files changed

+69
-35
lines changed

15 files changed

+69
-35
lines changed

sdk/java-sdk-protobuf-testkit/src/main/scala/kalix/javasdk/testkit/impl/EventingTestKitImpl.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ private[testkit] class OutgoingMessagesImpl(
376376

377377
override def expectOneTyped[T](clazz: Class[T], timeout: time.Duration): TestKitMessage[T] = {
378378
val msg = expectMsgInternal(destinationProbe, timeout, Some(clazz))
379-
val metadata = new MetadataImpl(msg.getMessage.getMetadata.entries)
379+
val metadata = MetadataImpl.of(msg.getMessage.getMetadata.entries)
380380
val scalaPb = ScalaPbAny(typeUrlFor(metadata), msg.getMessage.payload)
381381

382382
val decodedMsg = if (typeUrlFor(metadata).startsWith(JsonSupport.KALIX_JSON)) {
@@ -392,7 +392,7 @@ private[testkit] class OutgoingMessagesImpl(
392392
}
393393

394394
private def anyFromMessage(m: kalix.testkit.protocol.eventing_test_backend.Message): TestKitMessage[_] = {
395-
val metadata = new MetadataImpl(m.metadata.getOrElse(Metadata.defaultInstance).entries)
395+
val metadata = MetadataImpl.of(m.metadata.getOrElse(Metadata.defaultInstance).entries)
396396
val anyMsg = if (typeUrlFor(metadata).startsWith(JsonSupport.KALIX_JSON)) {
397397
m.payload.toStringUtf8
398398
} else {
@@ -503,7 +503,7 @@ private[testkit] class TopicImpl(
503503

504504
override def expectOneTyped[T](clazz: Class[T], timeout: time.Duration): TestKitMessage[T] = {
505505
val msg = expectMsgInternal(destinationProbe, timeout, Some(clazz))
506-
val metadata = new MetadataImpl(msg.getMessage.getMetadata.entries)
506+
val metadata = MetadataImpl.of(msg.getMessage.getMetadata.entries)
507507
val scalaPb = ScalaPbAny(typeUrlFor(metadata), msg.getMessage.payload)
508508

509509
val decodedMsg = if (typeUrlFor(metadata).startsWith(JsonSupport.KALIX_JSON)) {
@@ -519,7 +519,7 @@ private[testkit] class TopicImpl(
519519
}
520520

521521
private def anyFromMessage(m: kalix.testkit.protocol.eventing_test_backend.Message): TestKitMessage[_] = {
522-
val metadata = new MetadataImpl(m.metadata.getOrElse(Metadata.defaultInstance).entries)
522+
val metadata = MetadataImpl.of(m.metadata.getOrElse(Metadata.defaultInstance).entries)
523523
val anyMsg = if (typeUrlFor(metadata).startsWith(JsonSupport.KALIX_JSON)) {
524524
m.payload.toStringUtf8
525525
} else {
@@ -579,7 +579,7 @@ private[testkit] case class TestKitMessageImpl[P](payload: P, metadata: SdkMetad
579579

580580
private[testkit] object TestKitMessageImpl {
581581
def ofProtocolMessage(m: kalix.testkit.protocol.eventing_test_backend.Message): TestKitMessage[ByteString] = {
582-
val metadata = new MetadataImpl(m.metadata.getOrElse(Metadata()).entries)
582+
val metadata = MetadataImpl.of(m.metadata.getOrElse(Metadata()).entries)
583583
TestKitMessageImpl[ByteString](m.payload, metadata).asInstanceOf[TestKitMessage[ByteString]]
584584
}
585585

sdk/java-sdk-protobuf/src/main/scala/kalix/javasdk/impl/MetadataImpl.scala

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import java.util.Optional
4343
import scala.compat.java8.OptionConverters._
4444
import scala.jdk.CollectionConverters._
4545

46-
private[kalix] class MetadataImpl(val entries: Seq[MetadataEntry]) extends Metadata with CloudEvent {
46+
private[kalix] class MetadataImpl private (val entries: Seq[MetadataEntry]) extends Metadata with CloudEvent {
4747

4848
override def has(key: String): Boolean = entries.exists(_.key.equalsIgnoreCase(key))
4949

@@ -89,28 +89,28 @@ private[kalix] class MetadataImpl(val entries: Seq[MetadataEntry]) extends Metad
8989
override def set(key: String, value: String): MetadataImpl = {
9090
Objects.requireNonNull(key, "Key must not be null")
9191
Objects.requireNonNull(value, "Value must not be null")
92-
new MetadataImpl(removeKey(key) :+ MetadataEntry(key, MetadataEntry.Value.StringValue(value)))
92+
MetadataImpl.of(removeKey(key) :+ MetadataEntry(key, MetadataEntry.Value.StringValue(value)))
9393
}
9494

9595
override def setBinary(key: String, value: ByteBuffer): MetadataImpl = {
9696
Objects.requireNonNull(key, "Key must not be null")
9797
Objects.requireNonNull(value, "Value must not be null")
98-
new MetadataImpl(removeKey(key) :+ MetadataEntry(key, MetadataEntry.Value.BytesValue(ByteString.copyFrom(value))))
98+
MetadataImpl.of(removeKey(key) :+ MetadataEntry(key, MetadataEntry.Value.BytesValue(ByteString.copyFrom(value))))
9999
}
100100

101101
override def add(key: String, value: String): MetadataImpl = {
102102
Objects.requireNonNull(key, "Key must not be null")
103103
Objects.requireNonNull(value, "Value must not be null")
104-
new MetadataImpl(entries :+ MetadataEntry(key, MetadataEntry.Value.StringValue(value)))
104+
MetadataImpl.of(entries :+ MetadataEntry(key, MetadataEntry.Value.StringValue(value)))
105105
}
106106

107107
override def addBinary(key: String, value: ByteBuffer): MetadataImpl = {
108108
Objects.requireNonNull(key, "Key must not be null")
109109
Objects.requireNonNull(value, "Value must not be null")
110-
new MetadataImpl(entries :+ MetadataEntry(key, MetadataEntry.Value.BytesValue(ByteString.copyFrom(value))))
110+
MetadataImpl.of(entries :+ MetadataEntry(key, MetadataEntry.Value.BytesValue(ByteString.copyFrom(value))))
111111
}
112112

113-
override def remove(key: String): MetadataImpl = new MetadataImpl(removeKey(key))
113+
override def remove(key: String): MetadataImpl = MetadataImpl.of(removeKey(key))
114114

115115
override def clear(): MetadataImpl = MetadataImpl.Empty
116116

@@ -137,7 +137,7 @@ private[kalix] class MetadataImpl(val entries: Seq[MetadataEntry]) extends Metad
137137
} else this
138138

139139
override def asCloudEvent(id: String, source: URI, `type`: String): MetadataImpl =
140-
new MetadataImpl(
140+
MetadataImpl.of(
141141
entries.filterNot(e => MetadataImpl.CeRequired(e.key)) ++
142142
Seq(
143143
MetadataEntry(MetadataImpl.CeSpecversion, MetadataEntry.Value.StringValue(MetadataImpl.CeSpecversionValue)),
@@ -274,8 +274,19 @@ object MetadataImpl {
274274
val CeSubject = "ce-subject"
275275
val CeTime = "ce-time"
276276
val CeRequired: Set[String] = Set(CeSpecversion, CeId, CeSource, CeType)
277+
private val AllCeAttributes = CeRequired ++ Set(CeDataschema, CeDatacontenttype, CeSubject, CeTime)
277278

278-
val Empty = new MetadataImpl(Vector.empty)
279+
/**
280+
* Maps alternative prefixed keys to our default key format, ie: ce-.
281+
*
282+
* For the moment, only the Kafka prefix is in use, ie: ce_, but others might be needed in future.
283+
*/
284+
private val alternativeKeyFormats = AllCeAttributes.map { attr =>
285+
val key = attr.replaceFirst("^ce-", "ce_")
286+
(key, attr)
287+
}.toMap
288+
289+
val Empty = MetadataImpl.of(Vector.empty)
279290

280291
val JwtClaimPrefix = "_kalix-jwt-claim-"
281292

@@ -291,4 +302,17 @@ object MetadataImpl {
291302
throw new RuntimeException(s"Unknown metadata implementation: ${other.getClass}, cannot send")
292303
}
293304

305+
def of(entries: Seq[MetadataEntry]): MetadataImpl = {
306+
val transformedEntries =
307+
entries.map { entry =>
308+
// is incoming ce key in one of the alternative formats?
309+
// if so, convert key to our internal default key format
310+
alternativeKeyFormats.get(entry.key) match {
311+
case Some(defaultKey) => MetadataEntry(defaultKey, entry.value)
312+
case _ => entry
313+
}
314+
}
315+
316+
new MetadataImpl(transformedEntries)
317+
}
294318
}

sdk/java-sdk-protobuf/src/main/scala/kalix/javasdk/impl/action/ActionsImpl.scala

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ private[javasdk] final class ActionsImpl(
253253
.handleStreamedIn(
254254
call.name,
255255
messages.map { message =>
256-
val metadata = new MetadataImpl(message.metadata.map(_.entries.toVector).getOrElse(Nil))
256+
val metadata = MetadataImpl.of(message.metadata.map(_.entries.toVector).getOrElse(Nil))
257257
val decodedPayload = service.messageCodec.decodeMessage(
258258
message.payload.getOrElse(throw new IllegalArgumentException("No command payload")))
259259
MessageEnvelope.of(decodedPayload, metadata)
@@ -333,7 +333,7 @@ private[javasdk] final class ActionsImpl(
333333
.handleStreamed(
334334
call.name,
335335
messages.map { message =>
336-
val metadata = new MetadataImpl(message.metadata.map(_.entries.toVector).getOrElse(Nil))
336+
val metadata = MetadataImpl.of(message.metadata.map(_.entries.toVector).getOrElse(Nil))
337337
val decodedPayload = service.messageCodec.decodeMessage(
338338
message.payload.getOrElse(throw new IllegalArgumentException("No command payload")))
339339
MessageEnvelope.of(decodedPayload, metadata)
@@ -364,7 +364,7 @@ private[javasdk] final class ActionsImpl(
364364
messageCodec: MessageCodec,
365365
spanContext: Option[SpanContext],
366366
serviceName: String): ActionContext = {
367-
val metadata = new MetadataImpl(in.metadata.map(_.entries.toVector).getOrElse(Nil))
367+
val metadata = MetadataImpl.of(in.metadata.map(_.entries.toVector).getOrElse(Nil))
368368
val updatedMetadata = spanContext.map(metadataWithTracing(metadata, _)).getOrElse(metadata)
369369
new ActionContextImpl(updatedMetadata, messageCodec, system, serviceName, telemetries)
370370
}
@@ -381,7 +381,7 @@ private[javasdk] final class ActionsImpl(
381381
log.trace(
382382
"Updated metadata with trace context: [{}]",
383383
l.toList.filter(m => m.key == TRACE_PARENT_KEY || m.key == TRACE_STATE_KEY))
384-
new MetadataImpl(l.toSeq)
384+
MetadataImpl.of(l.toSeq)
385385
}
386386

387387
}
@@ -411,7 +411,7 @@ class ActionContextImpl(
411411

412412
override def componentCallMetadata: MetadataImpl = {
413413
if (metadata.has(Telemetry.TRACE_PARENT_KEY)) {
414-
new MetadataImpl(
414+
MetadataImpl.of(
415415
List(
416416
MetadataEntry(
417417
Telemetry.TRACE_PARENT_KEY,

sdk/java-sdk-protobuf/src/main/scala/kalix/javasdk/impl/eventsourcedentity/EventSourcedEntitiesImpl.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ final class EventSourcedEntitiesImpl(
186186
val cmd =
187187
service.messageCodec.decodeMessage(
188188
command.payload.getOrElse(throw ProtocolException(command, "No command payload")))
189-
val metadata = new MetadataImpl(command.metadata.map(_.entries.toVector).getOrElse(Nil))
189+
val metadata = MetadataImpl.of(command.metadata.map(_.entries.toVector).getOrElse(Nil))
190190
val context =
191191
new CommandContextImpl(thisEntityId, sequence, command.name, command.id, metadata)
192192

sdk/java-sdk-protobuf/src/main/scala/kalix/javasdk/impl/replicatedentity/ReplicatedEntitiesImpl.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ object ReplicatedEntitiesImpl {
238238

239239
override val commandName: String = command.name
240240

241-
override val metadata: Metadata = new MetadataImpl(command.metadata.map(_.entries.toVector).getOrElse(Nil))
241+
override val metadata: Metadata = MetadataImpl.of(command.metadata.map(_.entries.toVector).getOrElse(Nil))
242242

243243
}
244244
}

sdk/java-sdk-protobuf/src/main/scala/kalix/javasdk/impl/telemetry/Telemetry.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ private final class TraceInstrumentation(
185185
*/
186186
override def buildSpan(service: Service, command: Command): Option[Span] = {
187187
if (logger.isTraceEnabled) logger.trace("Building span for command [{}].", command)
188-
val metadata = new MetadataImpl(command.metadata.map(_.entries).getOrElse(Nil))
188+
val metadata = MetadataImpl.of(command.metadata.map(_.entries).getOrElse(Nil))
189189
if (metadata.get(TRACE_PARENT_KEY).isPresent) {
190190
if (logger.isTraceEnabled) logger.trace("`traceparent` found")
191191

@@ -212,7 +212,7 @@ private final class TraceInstrumentation(
212212
override def buildSpan(service: Service, command: ActionCommand): Option[Span] = {
213213
if (logger.isTraceEnabled) logger.trace("Building span for action command [{}].", command)
214214

215-
val metadata = new MetadataImpl(command.metadata.map(_.entries).getOrElse(Nil))
215+
val metadata = MetadataImpl.of(command.metadata.map(_.entries).getOrElse(Nil))
216216
if (metadata.get(TRACE_PARENT_KEY).isPresent) {
217217
if (logger.isTraceEnabled) logger.trace("`traceparent` found")
218218

sdk/java-sdk-protobuf/src/main/scala/kalix/javasdk/impl/valueentity/ValueEntitiesImpl.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ final class ValueEntitiesImpl(
157157
throw ProtocolException(command, "No command payload for Value entity")
158158

159159
case InCommand(command) =>
160-
val metadata = new MetadataImpl(command.metadata.map(_.entries.toVector).getOrElse(Nil))
160+
val metadata = MetadataImpl.of(command.metadata.map(_.entries.toVector).getOrElse(Nil))
161161

162162
if (log.isTraceEnabled) log.trace("Metadata entries [{}].", metadata.entries)
163163
val span = instrumentations(service.serviceName).buildSpan(service, command)

sdk/java-sdk-protobuf/src/main/scala/kalix/javasdk/impl/view/ViewsImpl.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ final class ViewsImpl(system: ActorSystem, _services: Map[String, ViewService],
104104

105105
val commandName = receiveEvent.commandName
106106
val msg = service.messageCodec.decodeMessage(receiveEvent.payload.get)
107-
val metadata = new MetadataImpl(receiveEvent.metadata.map(_.entries.toVector).getOrElse(Nil))
107+
val metadata = MetadataImpl.of(receiveEvent.metadata.map(_.entries.toVector).getOrElse(Nil))
108108
val context = new UpdateContextImpl(service.viewId, commandName, metadata)
109109

110110
val effect =

sdk/java-sdk-protobuf/src/main/scala/kalix/javasdk/impl/workflow/WorkflowImpl.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ final class WorkflowImpl(system: ActorSystem, val services: Map[String, Workflow
279279
Future.failed(ProtocolException(command, "No command payload for Workflow"))
280280

281281
case InCommand(command) =>
282-
val metadata = new MetadataImpl(command.metadata.map(_.entries.toVector).getOrElse(Nil))
282+
val metadata = MetadataImpl.of(command.metadata.map(_.entries.toVector).getOrElse(Nil))
283283

284284
val context = new CommandContextImpl(workflowId, command.name, command.id, metadata, system)
285285
val timerScheduler = new TimerSchedulerImpl(service.messageCodec, system, context.componentCallMetadata)

sdk/java-sdk-protobuf/src/test/scala/kalix/javasdk/impl/MetadataImplSpec.scala

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class MetadataImplSpec extends AnyWordSpec with Matchers with OptionValues {
4040
metadata("_kalix-jwt-claim-sub" -> "some-subject").jwtClaims.subject().toScala.value shouldBe "some-subject"
4141
}
4242

43-
"support getting the expiriation JWT claim" in {
43+
"support getting the expiration JWT claim" in {
4444
metadata("_kalix-jwt-claim-exp" -> "12345").jwtClaims.expirationTime().toScala.value shouldBe Instant
4545
.ofEpochSecond(12345)
4646
}
@@ -159,10 +159,20 @@ class MetadataImplSpec extends AnyWordSpec with Matchers with OptionValues {
159159
val mdRedirect = md.withStatusCode(Redirect.MOVED_PERMANENTLY)
160160
mdRedirect.get("_kalix-http-code").toScala.value shouldBe "301"
161161
}
162+
163+
"support creationg with CloudEvents prefixed with ce_" in {
164+
val md = metadata("ce_id" -> "id", "ce_source" -> "source", "ce_specversion" -> "1.0", "ce_type" -> "foo")
165+
md.isCloudEvent shouldBe true
166+
val ce = md.asCloudEvent()
167+
ce.id() shouldBe "id"
168+
ce.source().toString shouldBe "source"
169+
ce.specversion() shouldBe "1.0"
170+
ce.`type`() shouldBe "foo"
171+
}
162172
}
163173

164174
private def metadata(entries: (String, String)*): Metadata = {
165-
new MetadataImpl(entries.map { case (key, value) =>
175+
MetadataImpl.of(entries.map { case (key, value) =>
166176
MetadataEntry(key, MetadataEntry.Value.StringValue(value))
167177
})
168178
}

0 commit comments

Comments
 (0)