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

com.sksamuel.elastic4s.ElasticClient.scala Maven / Gradle / Ivy

There is a newer version: 8.15.4
Show newest version
package com.sksamuel.elastic4s

import com.fasterxml.jackson.module.scala.JavaTypeable
import org.slf4j.{Logger, LoggerFactory}

import java.util.Base64
import scala.concurrent.duration.{Duration, _}
import scala.language.higherKinds

/** An [[ElasticClient]] is used to execute HTTP requests against an ElasticSearch cluster. This class delegates the
  * actual HTTP calls to an instance of [[HttpClient]].
  *
  * Any third party HTTP client library can be made to work with elastic4s by creating an instance of the HttpClient
  * typeclass wrapping the underlying client library and then creating the ElasticClient with it.
  *
  * @param client
  *   the HTTP client library to use
  */
case class ElasticClient(client: HttpClient) extends AutoCloseable {

  protected val logger: Logger = LoggerFactory.getLogger(getClass.getName)

  /** Returns a String containing the request details. The string will have the HTTP method, endpoint, params and if
    * applicable the request body.
    */
  def show[T](t: T)(implicit handler: Handler[T, _]): String = Show[ElasticRequest].show(handler.build(t))

  // Executes the given request type T, and returns an effect of Response[U]
  // where U is particular to the request type.
  // For example a search request will return a Response[SearchResponse].
  def execute[T, U, F[_]](t: T)(implicit
      executor: Executor[F],
      functor: Functor[F],
      handler: Handler[T, U],
      javaTypeable: JavaTypeable[U],
      options: CommonRequestOptions
  ): F[Response[U]] = {
    val request = handler.build(t)

    val request2 = if (options.timeout.toMillis > 0) {
      request.addParameter("timeout", s"${options.timeout.toMillis}ms")
    } else {
      request
    }

    val request3 = if (options.masterNodeTimeout.toMillis > 0) {
      request2.addParameter("master_timeout", s"${options.masterNodeTimeout.toMillis}ms")
    } else {
      request2
    }

    val request4 = request3.addHeaders(options.headers)

    val request5 = authenticate(request4, options.authentication)

    val f = executor.exec(client, request5)
    functor.map(f) { resp =>
      handler.responseHandler.handle(resp) match {
        case Right(u)    => RequestSuccess(resp.statusCode, resp.entity.map(_.content), resp.headers, u)
        case Left(error) => RequestFailure(resp.statusCode, resp.entity.map(_.content), resp.headers, error)
      }
    }
  }

  private def authenticate(request: ElasticRequest, authentication: Authentication): ElasticRequest = {
    authentication match {
      case Authentication.UsernamePassword(username, password) =>
        request.addHeader(
          "Authorization",
          "Basic " + Base64.getEncoder.encodeToString(s"$username:$password".getBytes)
        )
      case Authentication.ApiKey(apiKey)                       =>
        request.addHeader(
          "Authorization",
          "ApiKey " + Base64.getEncoder.encodeToString(apiKey.getBytes)
        )
      case Authentication.NoAuth                               =>
        request
    }
  }

  def close(): Unit = client.close()
}

sealed trait Authentication

object Authentication {
  case class UsernamePassword(username: String, password: String) extends Authentication

  case class ApiKey(apiKey: String) extends Authentication

  case object NoAuth extends Authentication
}

case class CommonRequestOptions(
    timeout: Duration,
    masterNodeTimeout: Duration,
    headers: Map[String, String] = Map.empty,
    authentication: Authentication = Authentication.NoAuth
)

object CommonRequestOptions {
  implicit val defaults: CommonRequestOptions = CommonRequestOptions(
    timeout = 0.seconds,
    masterNodeTimeout = 0.seconds,
    headers = Map.empty,
    authentication = Authentication.NoAuth
  )
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy