0

I am working on a codebase where calling my Spray API need to synchronously call a service that returns a Try which Spray need to format and return over HTTP.

My initial attempt looked like this :

// Assume myService has a run method that returns a Try[Unit]
lazy val myService = new Service() 

val routes = 
  path("api") {
    get {
      tryToComplete {
        myService.run()
      } 
    }
  } ~ 
  path("api" / LongNumber) { (num: Long) =>
    get {
      tryToComplete {
        myService.run(num)
      } 
    }
  }

def tryToComplete(result: => Try[Unit]): routing.Route = result match {
  case Failure(t) => complete(StatusCodes.BadRequest, t.getMessage)
  case Success(_) => complete("success")
}

However this caused myService.run() to be called when the application started. I am not sure why this method was called as there was no HTTP call made.

So I have two questions :

  1. Why is my service being called as part of initialising the routes?
  2. What is the cleanest way to handle this case? Imagine that there are a few other end points following a similar pattern. So I need to be able to handle this consistently.
ahjmorton
  • 965
  • 1
  • 5
  • 18

2 Answers2

0

Even though you have result parameter as call-by-name, it'll immediately get evaluated as you're doing

result match {

For it not to get evaluated, it has to be within the complete, ie your code should look something like (haven't tried to compile this):

def tryToComplete(result: => Try[Unit]): routing.Route = complete {
result match {
  case Failure(t) => StatusCodes.BadRequest, t.getMessage
  case Success(_) => "success"
}
}
Ajay Padala
  • 2,371
  • 16
  • 13
0

The way I solved this was do do the following :

lazy val myService = new Service() 

val routes = 
  path("api") {
    get {
      complete {
        handleTry {
          myService.run()
        }
      } 
    }
  } ~ 
  path("api" / LongNumber) { (num: Long) =>
    get {
      complete {
         handleTry {
          myService.run(num)
        } 
      }
    }
  }

private def handleTry(operation: Try[_]):HttpResponse = operation match {
  case Failure(t) => 
    HttpResponse(status = StatusCodes.BadRequest, entity = t.getMessage)
  case Success(_) => 
    HttpResponse(status = StatusCodes.OK, entity = successMessage)
}
ahjmorton
  • 965
  • 1
  • 5
  • 18