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

pl.touk.nussknacker.processCounts.influxdb.SimpleInfluxClient.scala Maven / Gradle / Ivy

There is a newer version: 1.18.0
Show newest version
package pl.touk.nussknacker.processCounts.influxdb

import io.circe.Decoder
import pl.touk.nussknacker.engine.api.CirceUtil._
import pl.touk.nussknacker.engine.sttp.SttpJson
import sttp.client3._
import sttp.client3.circe._
import sttp.monad.syntax.MonadErrorOps
import sttp.monad.MonadError

import scala.language.{higherKinds, implicitConversions}

class InfluxException(cause: Throwable) extends Exception(cause)

final case class InvalidInfluxResponse(message: String, cause: Throwable) extends InfluxException(cause) {
  override def getMessage: String = s"Influx query failed with message '$message'"
}

final case class InfluxHttpError(influxUrl: String, body: String, cause: Throwable) extends InfluxException(cause) {
  override def getMessage: String = s"Connection to influx failed with message '$body'"
}

//we use simplistic InfluxClient, as we only need queries
class SimpleInfluxClient[F[_]](config: InfluxConfig)(implicit backend: SttpBackend[F, Any]) {
  implicit val monadError: MonadError[F] = backend.responseMonad

  private implicit class RequestExtensions[U[_], T, -R](val request: RequestT[U, T, R]) {

    def withAuthentication(): RequestT[U, T, R] = (for {
      user     <- config.user
      password <- config.password
    } yield request.auth.basic(user, password)).getOrElse(request)

  }

  def query(query: String): F[List[InfluxSeries]] = {
    basicRequest
      .get(config.uri.addParams("db" -> config.database, "q" -> query))
      .withAuthentication()
      .headers(config.additionalHeaders)
      .response(asJson[InfluxResponse])
      .send(backend)
      .flatMap(SttpJson.failureToError[F, InfluxResponse])
      .handleError {
        case ex: DeserializationException[_] => monadError.error(InvalidInfluxResponse(ex.getMessage, ex))
        case ex: HttpError[_]                => monadError.error(InfluxHttpError(config.influxUrl, s"${ex.body}", ex))
      }
      // we assume only one query
      .map(_.results.head.series)
  }

}

final case class InfluxResponse(results: List[InfluxResult] = Nil)

object InfluxResponse {
  import io.circe.generic.extras.semiauto._
  implicit val decoder: Decoder[InfluxResponse] = deriveConfiguredDecoder
}

final case class InfluxResult(series: List[InfluxSeries] = Nil)

object InfluxResult {
  import io.circe.generic.extras.semiauto._
  implicit val decoder: Decoder[InfluxResult] = deriveConfiguredDecoder
}

object InfluxSeries {

  import io.circe.generic.extras.semiauto._

  private implicit val numberOrStringDecoder: Decoder[Any] =
    Decoder.decodeBigDecimal.asInstanceOf[Decoder[Any]] or Decoder.decodeString.asInstanceOf[Decoder[Any]] or Decoder
      .const[Any]("")

  implicit val decoder: Decoder[InfluxSeries] = deriveConfiguredDecoder[InfluxSeries]

}

final case class InfluxSeries(
    name: String,
    tags: Option[Map[String, String]],
    columns: List[String],
    values: List[List[Any]] = Nil
) {
  val toMap: List[Map[String, Any]] = values.map(value => columns.zip(value).toMap)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy