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

codacy.events.EventConsumer.scala Maven / Gradle / Ivy

There is a newer version: 12.1.2_akka_2.6_circe_0.14
Show newest version
package codacy.events

import codacy.events.internal.{EventActorSystem, EventLogger}
import com.spingo.op_rabbit._
import io.circe.Json

import scala.concurrent.{Await, Future}
import scala.concurrent.duration._
import scala.util.{Failure, Try}
import com.spingo.op_rabbit.Directives._

trait EventConsumer {
  def subscribe(): Unit
}

object EventConsumer extends RabbitPickling {
  private type JsonOrRaw = Either[(Array[Byte], Throwable), Json]

  implicit private lazy val parseWithFallback: RabbitUnmarshaller[JsonOrRaw] =
    new RabbitUnmarshaller[JsonOrRaw] {
      override def unmarshall(value: Array[Byte], contentType: Option[String], contentEncoding: Option[String]) = {
        Try(implicitly[RabbitUnmarshaller[Json]].unmarshall(value, contentType, contentEncoding)) match {
          case util.Success(p) => Right(p)
          case util.Failure(err) => Left((value, err))
        }
      }
    }

  implicit private val recoveryStrategy =
    RecoveryStrategy.abandonedQueue(abandonQueueName = queueName => s"${queueName}.abandoned")

  def defaultEventConsumer(
      initEventQueue: InitEventQueue,
      eventRouter: EventRouter,
      asyncRabbitControl: AsyncRabbitControl,
      componentName: ComponentName.Components[Future],
      actorSystem: EventActorSystem
  )(implicit eL: EventLogger): EventConsumer =
    new EventConsumer {
      import actorSystem.dispatcher

      override def subscribe(): Unit = Await.result(init, 1.minute)

      private def init = {
        val init = Future.fromTry(Try(Await.result(initEventQueue.initEventQueue, 1.minute)))
        for {
          name <- componentName.componentName
          up <- init.recoverWith { case err =>
            Future.failed(new Exception(s"error initializing queue bindings", err))
          }
        } yield {
          runSubscriptionForever(name)
          up
        }
      }

      private def runSubscriptionForever(componentName: ComponentName): Future[SubscriptionRef] = {
        asyncRabbitControl.rabbitControl
          .map { ctrl =>
            Subscription.run(ctrl) {
              // at the moment we can't guarantee that `handleEvent` is ThreadSafe let's process one event at the time
              channel(qos = 1) {
                consume(Queue.passive(componentName.value)) {
                  (body(as[JsonOrRaw]) & routingKey).apply { (jsonOrRaw, key) =>
                    ack(
                      jsonOrRaw.fold(
                        { case (raw, error) =>
                          implicitly[EventLogger].warn(s"'${new String(raw)}' is not a valid json", error)
                          Future.unit
                        },
                        json =>
                          eventRouter.handleEvent(key, json).andThen { case Failure(err) =>
                            val message =
                              s"""error handling event: ${err.getMessage}
                           |type: `$key`
                           |body: `${json.noSpaces}`""".stripMargin
                            implicitly[EventLogger].error(message, err)
                          }
                      )
                    )
                  }
                }
              }
            }
          }
          .recoverWith { case err =>
            implicitly[EventLogger].warn(err.getMessage, err)
            runSubscriptionForever(componentName)
          }
      }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy