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

hammock.akka.AkkaInterpreter.scala Maven / Gradle / Ivy

There is a newer version: 0.9.2
Show newest version
package hammock
package akka

import _root_.akka.http.scaladsl.HttpExt
import _root_.akka.http.scaladsl.client.RequestBuilding.RequestBuilder
import _root_.akka.http.scaladsl.model.{
  HttpMethods,
  ContentType => AkkaContentType,
  HttpRequest => AkkaRequest,
  HttpResponse => AkkaResponse,
  StatusCode => AkkaStatus,
  _
}
import _root_.akka.http.scaladsl.model.headers.RawHeader
import _root_.akka.stream.ActorMaterializer
import _root_.akka.util.ByteString
import cats._
import cats.data.Kleisli
import cats.effect.{Async, IO, Sync}
import cats.implicits._

import scala.concurrent.{ExecutionContext, Future}

class AkkaInterpreter[F[_]: Async](client: HttpExt)(
    implicit materializer: ActorMaterializer,
    executionContext: ExecutionContext)
    extends InterpTrans[F] {

  def trans(implicit S: Sync[F]): HttpF ~> F = transK andThen λ[Kleisli[F, HttpExt, ?] ~> F](_.run(client))

  def transK: HttpF ~> Kleisli[F, HttpExt, ?] =
    λ[HttpF ~> Kleisli[F, HttpExt, ?]] {
      case req @ (Options(_) | Get(_) | Head(_) | Post(_) | Put(_) | Delete(_) | Trace(_)) => doReq(req)
    }

  def doReq(req: HttpF[HttpResponse]): Kleisli[F, HttpExt, HttpResponse] = Kleisli { http =>
    for {
      akkaRequest <- transformRequest(req)
      responseFuture <- Sync[F].delay(
        http
          .singleRequest(akkaRequest)
          .flatMap(transformResponse))
      responseF <- IO.fromFuture(IO(responseFuture)).to[F]
    } yield responseF
  }

  def transformRequest(reqF: HttpF[HttpResponse]): F[AkkaRequest] =
    for {
      method      <- mapMethod(reqF)
      akkaHeaders <- reqF.req.headers.map { case (k, v) => RawHeader(k, v) }.toList.pure[F]
      req <- (reqF.req.entity match {
        case Some(Entity.StringEntity(body, contentType)) =>
          mapContentType(contentType) >>= { ct =>
            new RequestBuilder(method)(Uri(reqF.req.uri.show), HttpEntity.Strict(ct, ByteString.fromString(body)))
              .pure[F]
          }
        case Some(Entity.ByteArrayEntity(body, contentType)) =>
          mapContentType(contentType) >>= { ct =>
            new RequestBuilder(method)(Uri(reqF.req.uri.show), HttpEntity.Strict(ct, ByteString(body)))
              .pure[F]
          }
        case None => new RequestBuilder(method)(Uri(reqF.req.uri.show)).pure[F]
      }).map(_.withHeaders(akkaHeaders))
    } yield req

  def mapMethod(reqF: HttpF[HttpResponse]): F[HttpMethod] =
    (reqF match {
      case Options(_) => HttpMethods.OPTIONS
      case Get(_)     => HttpMethods.GET
      case Head(_)    => HttpMethods.HEAD
      case Post(_)    => HttpMethods.POST
      case Put(_)     => HttpMethods.PUT
      case Delete(_)  => HttpMethods.DELETE
      case Trace(_)   => HttpMethods.TRACE
    }).pure[F]

  def mapContentType(ct: ContentType)(implicit F: Sync[F]): F[AkkaContentType] =
    for {
      parsed <- AkkaContentType.parse(ct.name) match {
        case Left(errors) =>
          F.raiseError(new Exception(s"unable to parse content type ${ct.name}, $errors"))
        case Right(contentType) =>
          F.pure(contentType)
      }
    } yield parsed

  def transformResponse(akkaResp: AkkaResponse): Future[HttpResponse] = {
    val status  = mapStatus(akkaResp.status)
    val headers = akkaResp.headers.map(h => (h.name, h.value)).toMap
    akkaResp.entity.dataBytes
      .runFold(ByteString(""))(_ ++ _)
      .map(bs => Entity.StringEntity(bs.utf8String))
      .map(entity => HttpResponse(status, headers, entity))
  }

  def mapStatus(st: AkkaStatus): Status = st match {
    case StatusCodes.Continue                      => Status.Continue
    case StatusCodes.SwitchingProtocols            => Status.SwitchingProtocols
    case StatusCodes.Processing                    => Status.Processing
    case StatusCodes.OK                            => Status.OK
    case StatusCodes.Created                       => Status.Created
    case StatusCodes.Accepted                      => Status.Accepted
    case StatusCodes.NonAuthoritativeInformation   => Status.NonAuthoritativeInformation
    case StatusCodes.NoContent                     => Status.NoContent
    case StatusCodes.ResetContent                  => Status.ResetContent
    case StatusCodes.PartialContent                => Status.PartialContent
    case StatusCodes.MultiStatus                   => Status.MultiStatus
    case StatusCodes.AlreadyReported               => Status.AlreadyReported
    case StatusCodes.IMUsed                        => Status.IMUsed
    case StatusCodes.MultipleChoices               => Status.MultipleChoices
    case StatusCodes.MovedPermanently              => Status.MovedPermanently
    case StatusCodes.Found                         => Status.Found
    case StatusCodes.SeeOther                      => Status.SeeOther
    case StatusCodes.NotModified                   => Status.NotModified
    case StatusCodes.UseProxy                      => Status.UseProxy
    case StatusCodes.TemporaryRedirect             => Status.TemporaryRedirect
    case StatusCodes.PermanentRedirect             => Status.PermanentRedirect
    case StatusCodes.BadRequest                    => Status.BadRequest
    case StatusCodes.Unauthorized                  => Status.Unauthorized
    case StatusCodes.PaymentRequired               => Status.PaymentRequired
    case StatusCodes.Forbidden                     => Status.Forbidden
    case StatusCodes.NotFound                      => Status.NotFound
    case StatusCodes.MethodNotAllowed              => Status.MethodNotAllowed
    case StatusCodes.NotAcceptable                 => Status.NotAcceptable
    case StatusCodes.ProxyAuthenticationRequired   => Status.ProxyAuthenticationRequired
    case StatusCodes.RequestTimeout                => Status.RequestTimeout
    case StatusCodes.Conflict                      => Status.Conflict
    case StatusCodes.Gone                          => Status.Gone
    case StatusCodes.LengthRequired                => Status.LengthRequired
    case StatusCodes.PreconditionFailed            => Status.PreconditionFailed
    case StatusCodes.RequestEntityTooLarge         => Status.RequestEntityTooLarge
    case StatusCodes.RequestUriTooLong             => Status.RequestUriTooLong
    case StatusCodes.UnsupportedMediaType          => Status.UnsupportedMediaType
    case StatusCodes.RequestedRangeNotSatisfiable  => Status.RequestedRangeNotSatisfiable
    case StatusCodes.ExpectationFailed             => Status.ExpectationFailed
    case StatusCodes.EnhanceYourCalm               => Status.EnhanceYourCalm
    case StatusCodes.UnprocessableEntity           => Status.UnprocessableEntity
    case StatusCodes.Locked                        => Status.Locked
    case StatusCodes.FailedDependency              => Status.FailedDependency
    case StatusCodes.UnorderedCollection           => Status.UnorderedCollection
    case StatusCodes.UpgradeRequired               => Status.UpgradeRequired
    case StatusCodes.PreconditionRequired          => Status.PreconditionRequired
    case StatusCodes.TooManyRequests               => Status.TooManyRequests
    case StatusCodes.RequestHeaderFieldsTooLarge   => Status.RequestHeaderFieldsTooLarge
    case StatusCodes.RetryWith                     => Status.RetryWith
    case StatusCodes.BlockedByParentalControls     => Status.BlockedByParentalControls
    case StatusCodes.UnavailableForLegalReasons    => Status.UnavailableForLegalReasons
    case StatusCodes.InternalServerError           => Status.InternalServerError
    case StatusCodes.NotImplemented                => Status.NotImplemented
    case StatusCodes.BadGateway                    => Status.BadGateway
    case StatusCodes.ServiceUnavailable            => Status.ServiceUnavailable
    case StatusCodes.GatewayTimeout                => Status.GatewayTimeout
    case StatusCodes.HTTPVersionNotSupported       => Status.HTTPVersionNotSupported
    case StatusCodes.VariantAlsoNegotiates         => Status.VariantAlsoNegotiates
    case StatusCodes.InsufficientStorage           => Status.InsufficientStorage
    case StatusCodes.LoopDetected                  => Status.LoopDetected
    case StatusCodes.BandwidthLimitExceeded        => Status.BandwidthLimitExceeded
    case StatusCodes.NotExtended                   => Status.NotExtended
    case StatusCodes.NetworkAuthenticationRequired => Status.NetworkAuthenticationRequired
    case StatusCodes.NetworkReadTimeout            => Status.NetworkReadTimeout
    case StatusCodes.NetworkConnectTimeout         => Status.NetworkConnectTimeout
    case StatusCodes.ClientError(x)                => Status.custom(x)
    case StatusCodes.CustomStatusCode(x)           => Status.custom(x)
    case StatusCodes.Informational(x)              => Status.custom(x)
    case StatusCodes.Redirection(x)                => Status.custom(x)
    case StatusCodes.ServerError(x)                => Status.custom(x)
    case StatusCodes.Success(x)                    => Status.custom(x)
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy