gitfy
gitfy

Reputation: 13

How to handle an Akka HTTP route recursively

I have a scenario where the route contains alternating segments of key/value pairs. The number of pairs is undefined, what is the effective way to collect those pairs. For example, the route may look like

/key1/value1/key2/value2/key3/value3

and I would like get a linkedmap with

(key1, value1) -> (key2, value2) -> (key3, value3)

I know that we can get a list of path segments and convert them as above. But what I am looking for is something that navigates the path down like

class EntitiesSpec extends WordSpec with Matchers with ScalatestRouteTest with Directives {

  def lastSegment:Directive1[String] = path(Segment)
  def intermediate: Directive1[String] = <processing the intermediate>

  val navigate = (intermediate & navigate) ~ lastSegment
  val route = navigate { (param1:String) =>
    complete(param1)
  }

  "this should call the route" should {
    "invoke a route /hello" in {
      Get("/hello/world/hello1/world1/hello3/world3") ~> route ~> check {
        println(responseAs[String])
      }
    }
  }
}

Another good one would be, for example, if you have a path like

/12/1/5/6/8

How to define a directive that can add the numbers recursively? Any help is appreciated.

Upvotes: 1

Views: 99

Answers (1)

Don't Use Path for Key/Value Pairs

The methodology described in the question is an anti-pattern. Key/value pairing can be accomplished much easier in the query string. By using the parameterMap Directive it becomes trivial to convert the key/value pairs into a list of String tuples:

//matches a uri of the form: /?key1=value1&key2=value2
val queryStringRoute = 
  parameterMap { params =>
    val linkedPairList : List[(String, String)] = params.toList
  }

Don't Use Recursion on Directives

A Recursive Directive is also ill-advised. A Route is declared as a simple Function1:

type Route = (RequestContext) ⇒ Future[RouteResult]

Because the return type is a Future then any recursion would have to be an operation on the original Future. Therefore, the original calling stack cannot be ignored and the recursion will never be tail optimized. With a long enough Path it is possible to cause a stackoverflow of the recursive function.

If You Have to Use the Path

When you have no other choice but to use the Path then you can just do some String parsing:

val pathToKeyValuePairs : Uri.Path => List[(String, String)] =
  (_ : Uri.Path)
    .toString
    .split('/')
    .grouped(2)
    .map(arr => arr(0) -> arr(1))
    .toList

This parser can now be used inside of a Route:

val unadvisedRoute = 
  extractRequest { request =>
    val linkedPairList : List[(String, String)] = 
      pathToKeyValuePairs(request.uri.path)
  }

Upvotes: 2

Related Questions