1
package hammock
2
package resttemplate
3

4
import java.net.URI
5

6
import cats._
7
import cats.implicits._
8
import cats.data.Kleisli
9
import cats.effect._
10
import org.springframework.http._
11
import org.springframework.util.LinkedMultiValueMap
12
import org.springframework.web.client.RestTemplate
13

14
import scala.collection.JavaConverters._
15

16
object RestTemplateInterpreter {
17

18
  def apply[F[_]](implicit F: InterpTrans[F]): InterpTrans[F] = F
19

20
  implicit def instance[F[_]: Sync](
21
      implicit client: RestTemplate = new RestTemplate()
22 0
  ): InterpTrans[F] = new InterpTrans[F] {
23 0
    override def trans: HttpF ~> F = transK andThen λ[Kleisli[F, RestTemplate, *] ~> F](_.run(client))
24
  }
25

26
  def transK[F[_]: Sync]: HttpF ~> Kleisli[F, RestTemplate, *] = {
27 0
    λ[HttpF ~> Kleisli[F, RestTemplate, *]] {
28
      case reqF @ (Get(_) | Delete(_) | Head(_) | Options(_) | Trace(_) | Post(_) | Put(_) | Patch(_)) =>
29 0
        Kleisli { implicit client =>
30
          for {
31 0
            req             <- mapRequest[F](reqF)
32 0
            res             <- execute[F](req)
33 0
            hammockResponse <- mapResponse[F](res)
34
          } yield hammockResponse
35
        }
36
    }
37
  }
38

39
  def mapRequest[F[_]: Sync](reqF: HttpF[HttpResponse]): F[RequestEntity[String]] = {
40

41 1
    def httpEntity: HttpEntity[String] = new HttpEntity(
42 1
      reqF.req.entity.map(_.cata[String](_.body, _.body.map(_.toChar).mkString, Function.const(""))).orNull,
43 1
      new LinkedMultiValueMap[String, String](reqF.req.headers.mapValues(List(_).asJava).asJava)
44
    )
45

46
    def requestEntity(httpMethod: HttpMethod): RequestEntity[String] =
47 1
      new RequestEntity[String](httpEntity.getBody, httpEntity.getHeaders, httpMethod, new URI(reqF.req.uri.show))
48

49
    (reqF match {
50
      case Get(_)     => requestEntity(HttpMethod.GET)
51
      case Delete(_)  => requestEntity(HttpMethod.DELETE)
52
      case Head(_)    => requestEntity(HttpMethod.HEAD)
53
      case Options(_) => requestEntity(HttpMethod.OPTIONS)
54
      case Post(_)    => requestEntity(HttpMethod.POST)
55
      case Put(_)     => requestEntity(HttpMethod.PUT)
56
      case Trace(_)   => requestEntity(HttpMethod.TRACE)
57
      case Patch(_)   => requestEntity(HttpMethod.PATCH)
58 1
    }).pure[F]
59
  }
60

61
  def execute[F[_]: Sync](rtRequest: RequestEntity[String])(implicit client: RestTemplate): F[ResponseEntity[String]] =
62 0
    Sync[F].delay { client.exchange(rtRequest, classOf[String]) }
63

64
  def mapResponse[F[_]: Applicative](response: ResponseEntity[String]): F[HttpResponse] = {
65

66 1
    def createEntity(response: ResponseEntity[String]): Entity = response.getHeaders.getContentType match {
67 1
      case MediaType.APPLICATION_OCTET_STREAM => Entity.ByteArrayEntity(response.getBody.getBytes)
68 1
      case _                                  => Entity.StringEntity(response.getBody)
69
    }
70

71
    HttpResponse(
72
      Status.Statuses(response.getStatusCodeValue),
73
      response.getHeaders.toSingleValueMap.asScala.toMap,
74
      createEntity(response)
75 1
    ).pure[F]
76
  }
77
}

Read our documentation on viewing source code .

Loading