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

com.permutive.pubsub.consumer.http.PubsubHttpConsumer.scala Maven / Gradle / Ivy

package com.permutive.pubsub.consumer.http

import java.util.Base64

import cats.effect.{Concurrent, Timer}
import cats.syntax.all._
import com.permutive.pubsub.consumer.ConsumerRecord
import com.permutive.pubsub.consumer.Model.{ProjectId, Subscription}
import com.permutive.pubsub.consumer.decoder.MessageDecoder
import com.permutive.pubsub.consumer.http.internal.PubsubSubscriber
import fs2.Stream
import io.chrisdavenport.log4cats.Logger
import org.http4s.client.Client

object PubsubHttpConsumer {

  /**
    * Subscribe with manual acknowledgement
    *
    * @param projectId          google cloud project id
    * @param subscription       name of the subscription
    * @param serviceAccountPath path to the Google Service account file (json)
    * @param errorHandler       upon failure to decode, an exception is thrown. Allows acknowledging the message.
    */
  final def subscribe[F[_]: Concurrent: Timer: Logger, A: MessageDecoder](
    projectId: ProjectId,
    subscription: Subscription,
    serviceAccountPath: String,
    config: PubsubHttpConsumerConfig[F],
    httpClient: Client[F],
    errorHandler: (PubsubMessage, Throwable, F[Unit], F[Unit]) => F[Unit]
  ): Stream[F, ConsumerRecord[F, A]] =
    PubsubSubscriber
      .subscribe(projectId, subscription, serviceAccountPath, config, httpClient)
      .flatMap { record =>
        MessageDecoder[A].decode(Base64.getDecoder.decode(record.value.data.getBytes)) match {
          case Left(e)  => Stream.eval_(errorHandler(record.value, e, record.ack, record.nack))
          case Right(v) => Stream.emit(record.toConsumerRecord(v))
        }
      }

  /**
    * Subscribe with automatic acknowledgement
    *
    * @param projectId          google cloud project id
    * @param subscription       name of the subscription
    * @param serviceAccountPath path to the Google Service account file (json)
    * @param errorHandler       upon failure to decode, an exception is thrown. Allows acknowledging the message.
    */
  final def subscribeAndAck[F[_]: Concurrent: Timer: Logger, A: MessageDecoder](
    projectId: ProjectId,
    subscription: Subscription,
    serviceAccountPath: String,
    config: PubsubHttpConsumerConfig[F],
    httpClient: Client[F],
    errorHandler: (PubsubMessage, Throwable, F[Unit], F[Unit]) => F[Unit]
  ): Stream[F, A] =
    PubsubSubscriber
      .subscribe(projectId, subscription, serviceAccountPath, config, httpClient)
      .flatMap { record =>
        MessageDecoder[A].decode(Base64.getDecoder.decode(record.value.data.getBytes)) match {
          case Left(e)  => Stream.eval_(errorHandler(record.value, e, record.ack, record.nack))
          case Right(v) => Stream.eval(record.ack >> v.pure)
        }
      }

  /**
    * Subscribe to the raw stream, receiving the the message as retrieved from PubSub
    */
  final def subscribeRaw[F[_]: Concurrent: Timer: Logger](
    projectId: ProjectId,
    subscription: Subscription,
    serviceAccountPath: String,
    config: PubsubHttpConsumerConfig[F],
    httpClient: Client[F]
  ): Stream[F, ConsumerRecord[F, PubsubMessage]] =
    PubsubSubscriber
      .subscribe(projectId, subscription, serviceAccountPath, config, httpClient)
      .map(msg => msg.toConsumerRecord(msg.value))
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy