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

org.elasticmq.rest.stats.StatisticsDirectives.scala Maven / Gradle / Ivy

There is a newer version: 1.6.11
Show newest version
package org.elasticmq.rest.stats

import org.apache.pekko.actor.ActorRef
import org.apache.pekko.http.scaladsl.model.StatusCodes.NotFound
import org.apache.pekko.http.scaladsl.model._
import org.apache.pekko.http.scaladsl.model.headers.`Access-Control-Allow-Origin`
import org.apache.pekko.http.scaladsl.server.Route
import org.elasticmq.metrics.QueueMetricsOps
import org.elasticmq.rest.sqs.QueueAttributesOps
import org.elasticmq.rest.sqs.directives.ElasticMQDirectives
import org.elasticmq.util.NowProvider
import org.elasticmq.{QueueData, QueueStatistics}

import java.io.File
import scala.concurrent.Future
import scala.io.Source
import scala.util.{Failure, Success, Try}
import scala.concurrent.duration.FiniteDuration

final case class QueuesResponse(
    name: String,
    statistics: QueueStatisticsResponse
)

final case class QueueResponse(
    name: String,
    attributes: Attributes
)

final case class QueueStatisticsResponse(
    approximateNumberOfVisibleMessages: Long,
    approximateNumberOfInvisibleMessages: Long,
    approximateNumberOfMessagesDelayed: Long
)

trait StatisticsDirectives extends StatisticsJsonFormat {
  this: ElasticMQDirectives with QueueAttributesOps =>

  sealed trait UILocation {
    def isResourceBased: Boolean = this match {
      case ResourceBased => true
      case FileBased     => false
    }
  }
  case object ResourceBased extends UILocation
  case object FileBased extends UILocation

  lazy val uiLocation: UILocation =
    if (new File("index.html").exists) FileBased
    else if (Try(Source.fromResource("index.html")).toOption.isDefined) ResourceBased
    else throw new RuntimeException("Could not find UI files")

  implicit val duration: FiniteDuration = timeout.duration

  def statistics(implicit np: NowProvider) = {
    pathPrefix("statistics" / "queues") {
      concat(
        pathEndOrSingleSlash {
          respondWithHeader(`Access-Control-Allow-Origin`.*) {
            complete(gatherAllQueuesWithStats)
          }
        },
        path(Segment) { queueName =>
          respondWithHeader(`Access-Control-Allow-Origin`.*) {
            gatherSpecificQueueWithAttributesRoute(queueName)
          }
        }
      )
    } ~
      serveUI
  }

  def gatherAllQueuesWithStats(implicit np: NowProvider): Future[Iterable[QueuesResponse]] = {

    QueueMetricsOps
      .getQueuesStatistics(queueManagerActor, np)
      .map { x => x.map { case (name, stats) => mapToRestQueuesResponse(name, stats) } }
  }

  def gatherSpecificQueueWithAttributesRoute(queueName: String): Route = {
    queueActorAndDataFromQueueName(queueName) { (queueActor, queueData) =>
      onComplete(getQueryResponseWithAttributesFuture(queueName, queueActor, queueData)) {
        case Success(value) => complete(value)
        case Failure(ex) =>
          logger.error(s"Error while loading statistics for queue ${queueName}", ex)
          complete(NotFound, s"Can't load data for queue ${queueName}")
      }
    }
  }

  def serveUI = {
    get {
      entity(as[HttpRequest]) { requestData =>
        requestData.uri.path.toString match {
          case "/" =>
            if (uiLocation.isResourceBased) getFromResource("index.html") else getFromFile("index.html")
          case "" =>
            if (uiLocation.isResourceBased) getFromResource("index.html") else getFromFile("index.html")
          case path =>
            // We have to drop leading slash in order to successfully find files
            if (uiLocation.isResourceBased) getFromResource(path.drop(1)) else getFromFile(path.drop(1))
        }
      }
    }
  }

  private def getQueryResponseWithAttributesFuture(
      name: String,
      queueActor: ActorRef,
      queueData: QueueData
  ): Future[QueueResponse] = {
    getAllQueueAttributes(queueActor, queueData)
      .map(list => QueueResponse(name = name, list.toMap))
  }

  private def mapToRestQueuesResponse(queueName: String, statistics: QueueStatistics): QueuesResponse = {
    QueuesResponse(
      queueName,
      QueueStatisticsResponse(
        approximateNumberOfInvisibleMessages = statistics.approximateNumberOfInvisibleMessages,
        approximateNumberOfMessagesDelayed = statistics.approximateNumberOfMessagesDelayed,
        approximateNumberOfVisibleMessages = statistics.approximateNumberOfVisibleMessages
      )
    )
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy