Search code examples
kotlingoogle-cloud-platformgoogle-cloud-firestoreprotocol-buffersktor

Parsing protobuf payload with Firestore Eventarc notifications and Google Cloud Run + Kotlin/Ktor


I have a very simple Kotlin/Ktor docker image that does this:

import com.google.events.cloud.firestore.v1.DocumentEventData

fun Application.configureRouting() {
  val logger = LoggerFactory.getLogger(javaClass)

  routing {
    post("/debugfirestore") {
      val message = call.receive<ByteArray>()
      val data = DocumentEventData.parseFrom(message)
      logger.info("documentEventData: $data")
      return@post
    }
  }
}

fun Application.configureSerialization() {
  install(ContentNegotiation) {
    json()

    @OptIn(ExperimentalSerializationApi::class)
    protobuf()
  }
}

I also have Eventarc and Firestore configured like this:

% gcloud eventarc triggers update my-firestore-update \
   --location=nam5 \
   --service-account=... \
   --destination-run-service=my-prototype \
   --destination-run-region=us-central1 \
   --destination-run-path="/debugfirestore" \
   --event-filters="database=(default)" \
   --event-filters-path-pattern="entity=test-collection/**" \
   --event-filters="type=google.cloud.datastore.entity.v1.written"

I'm running my app on Google Cloud Run. I can successfully trigger and receive an event but I'm unable to parse the protobuf payload (I think it's protobuf?).

com.google.protobuf.InvalidProtocolBufferException: Protocol message had invalid UTF-8.
    at com.google.protobuf.InvalidProtocolBufferException.invalidUtf8(InvalidProtocolBufferException.java:149)
    at com.google.protobuf.Utf8$UnsafeProcessor.decodeUtf8(Utf8.java:1365)
    at com.google.protobuf.Utf8.decodeUtf8(Utf8.java:318)
    at com.google.protobuf.CodedInputStream$ArrayDecoder.readStringRequireUtf8(CodedInputStream.java:788)
    at com.google.events.cloud.firestore.v1.Document$Builder.mergeFrom(Document.java:854)
    at com.google.events.cloud.firestore.v1.Document$Builder.mergeFrom(Document.java:655)
    at com.google.protobuf.CodedInputStream$ArrayDecoder.readMessage(CodedInputStream.java:844)
    at com.google.events.cloud.firestore.v1.DocumentEventData$Builder.mergeFrom(DocumentEventData.java:615)
    at com.google.events.cloud.firestore.v1.DocumentEventData$1.parsePartialFrom(DocumentEventData.java:1290)
    at com.google.events.cloud.firestore.v1.DocumentEventData$1.parsePartialFrom(DocumentEventData.java:1282)
    at com.google.protobuf.AbstractParser.parsePartialFrom(AbstractParser.java:135)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:168)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:180)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:185)
    at com.google.protobuf.AbstractParser.parseFrom(AbstractParser.java:25)
    at com.google.events.cloud.firestore.v1.DocumentEventData.parseFrom(DocumentEventData.java:360)

The data that I'm writing to Firestore is simple JSON object without any crazy characters (AFAICT). Why is it complaining about UTF8? I'm guessing there's a problem with the way that I'm trying to parse the request payload OR the response isn't in the format (ByteArray) that I think it is. Where am I going wrong?

edit: I also tried this:

    post("/debugfirestore") {
      logger.info("Content-Type = ${call.request.contentType()}")
      val data = call.receive<DocumentEventData>()
      logger.info("documentEventData: $data")
      call.respond("$data")
      return@post
    }

I thought this might work since I have the protobuf() plugin but it fails saying:

Caused by: kotlinx.serialization.SerializationException: Serializer for class 'DocumentEventData' is not found.

Further down I see logs saying:

Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.


Solution

  • The issue is that I needed to use EntityEventData instead of DocumentEventData.

    Thanks to DazWilkin for the answer.

    Side note, using the kotlinx-serialization way doesn't seem to work for me here because, I think, neither EntityEventData nor DocumentEventData are @Serializable.