codacy.events.EventConsumer.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of events-rabbitmq_2.13 Show documentation
Show all versions of events-rabbitmq_2.13 Show documentation
A library to send events on rabbit-mq
The 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
def subscribeOnce(): Future[SubscriptionRef]
}
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
}
}
override def subscribeOnce(): Future[SubscriptionRef] = {
val init = initEventQueue.initEventQueue
for {
name <- componentName.componentName
_ <- init.recoverWith { case err =>
Future.failed(new Exception(s"error initializing queue bindings", err))
}
subscriptionRef <- runSubscriptionOnce(name)
} yield subscriptionRef
}
private def runSubscriptionOnce(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)
}
)
)
}
}
}
}
}
}
private def runSubscriptionForever(componentName: ComponentName): Future[SubscriptionRef] = {
runSubscriptionOnce(componentName)
.recoverWith { case err =>
implicitly[EventLogger].warn(err.getMessage, err)
runSubscriptionForever(componentName)
}
}
}
}