package actors.auctions import com.google.inject.Provides import com.hivemq.client.mqtt.datatypes.MqttQos import com.hivemq.client.mqtt.mqtt5.Mqtt5AsyncClient import com.hivemq.client.mqtt.mqtt5.message.publish.Mqtt5Publish import models.{Auction, User} import mqttClient.MqttClientFactory import org.apache.pekko.actor.typed.Behavior import org.apache.pekko.actor.typed.scaladsl.Behaviors import play.api.{Configuration, Logging} import play.api.libs.concurrent.ActorModule import play.api.libs.json.{JsObject, Json} import javax.inject.Singleton object AuctionPublisher extends ActorModule with Logging { sealed trait Command private case object Ready extends Command case class Publish(auction: Auction, author: User) extends Command private final val topic = "auctions/created" override type Message = Command @Provides @Singleton def apply(configuration: Configuration): Behavior[Message] = Behaviors.setup { context => val client = MqttClientFactory.createClient(configuration, "-auction-publisher") client.connect().whenComplete { (_action, _throwable) => context.self ! Ready } idle(client) } private def idle(client: Mqtt5AsyncClient, waitingPublishes: Seq[Publish] = List()): Behaviors.Receive[Command] = Behaviors.receiveMessage[Command] { case p: Publish => idle(client, waitingPublishes :+ p) case Ready => for { publish <- waitingPublishes } publishAuction(client, publish.auction, publish.author) ready(client) } private def ready(client: Mqtt5AsyncClient): Behaviors.Receive[Command] = Behaviors.receiveMessage[Command] { case Publish(auction, author) => publishAuction(client, auction, author) ready(client) case Ready => logger.warn("Message Ready received after having already received it before") ready(client) } private def publishAuction(client: Mqtt5AsyncClient, auction: Auction, author: User): Unit = { val payload = Json.toBytes(auction_payload(auction, author)) val publish = Mqtt5Publish.builder() .topic(topic) .payload(payload) .qos(MqttQos.EXACTLY_ONCE) .build() logger.info(s"Publishing auction ${auction.id.getOrElse(-1)}") client.publish(publish) } private def auction_payload(auction: Auction, author: User): JsObject = { Json.obj( "gid" -> auction.gid, "author_gid" -> author.gid, "starting_price" -> auction.startingPrice ) } }