aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/com/drivergrp/core/rest.scala
blob: e121640e6c1246f5f3033b303f1f748dd0da4ffe (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package com.drivergrp.core

import akka.http.scaladsl.Http
import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
import akka.http.scaladsl.server.{Directive, _}
import akka.http.scaladsl.util.FastFuture._
import akka.stream.ActorMaterializer
import akka.util.Timeout
import com.drivergrp.core.execution.{ActorSystemModule, ExecutionContextModule}
import com.drivergrp.core.id.{Id, Name}
import com.drivergrp.core.logging.LoggerModule
import com.drivergrp.core.stats.StatsModule
import com.drivergrp.core.time.TimeRange
import com.drivergrp.core.time.provider.TimeModule
import spray.json.{DeserializationException, JsNumber, JsString, JsValue, RootJsonFormat}

import scala.concurrent.Future
import scala.concurrent.duration._
import scala.language.postfixOps
import scala.util.{Failure, Success, Try}
import scalaz.{Failure => _, Success => _, _}


object rest {

  trait RestService {
    this: ActorSystemModule with LoggerModule with StatsModule with TimeModule with ExecutionContextModule =>

    protected implicit val timeout = Timeout(5 seconds)

    implicit val materializer = ActorMaterializer()(actorSystem)


    def sendRequest(request: HttpRequest): Future[HttpResponse] = {

      log.audit(s"Sending to ${request.uri} request $request")

      val requestTime = time.currentTime()
      val response = Http()(actorSystem).singleRequest(request)(materializer)

      response.onComplete {
        case Success(_) =>
          val responseTime = time.currentTime()
          log.audit(s"Response from ${request.uri} to request $request is successful")
          stats.recordStats(Seq("request", request.uri.toString, "success"), TimeRange(requestTime, responseTime), 1)

        case Failure(t) =>
          val responseTime = time.currentTime()
          log.audit(s"Failed to receive response from ${request.uri} to request $request")
          log.error(s"Failed to receive response from ${request.uri} to request $request", t)
          stats.recordStats(Seq("request", request.uri.toString, "fail"), TimeRange(requestTime, responseTime), 1)
      } (executionContext)

      response
    }
  }


  object basicFormats {

    implicit def idFormat[T] = new RootJsonFormat[Id[T]] {
      def write(id: Id[T]) = JsNumber(id)

      def read(value: JsValue) = value match {
        case JsNumber(id) => Id[T](id.toLong)
        case _ => throw new DeserializationException("Id expects number")
      }
    }

    implicit def nameFormat[T] = new RootJsonFormat[Name[T]] {
      def write(name: Name[T]) = JsNumber(name)

      def read(value: JsValue): Name[T] = value match {
        case JsString(name) => Name[T](name)
        case _ => throw new DeserializationException("Name expects string")
      }
    }
  }

  trait OptionTDirectives {

    /**
      * "Unwraps" a `OptionT[Future, T]` and runs the inner route after future
      * completion with the future's value as an extraction of type `Try[T]`.
      */
    def onComplete[T](optionT: OptionT[Future, T]): Directive1[Try[Option[T]]] =
      Directive { inner  ctx 
        import ctx.executionContext
        optionT.run.fast.transformWith(t  inner(Tuple1(t))(ctx))
      }
  }
}