sprayspray-routing

How to collapse of Spray Directives


How can one collapse these directives as much as possible?

I was able to collapse three directives into mainHeader but how can include authenticate and responseWithMediaType as well?

val mainHeaders = cors & headerValueByName("Access_Token") & headerValueByName("Tenant_Name")

override def getLayer = path("api" / Segment / "model" / "layers") { modeledSystemName =>
    mainHeaders { (accessToken, tenantName) =>
      authenticate(validate(accessToken)) { authInfo =>
        respondWithMediaType(`application/json`) {
          get {
            onComplete(service.getlayers(modeledSystemName, tenantName)) {
              case Success(layers) => complete(layers)
              //case Success(None) => complete(NotFound, "Release version not found")
              case Failure(ex) => complete(InternalServerError, s"An error occurred: ${ex.getMessage}")
            }
          }
        }
      }
    }
  }

Solution

  • Spray routes are super compossible so you can do whatever you want. For example I would write this route like this:

    val authWithHeader = headerValueByName("Access_Token")
      .flatMap(accessToken => authenticate(validate(accessToken)))
    
    val authenticatedJsonRoute = cors &
      authWithHeader &
      headerValueByName("Tenant_Name") &
      respondWithMediaType(MediaTypes.`application/json`)
    
    override val getLayer =
      (get & path("api" / Segment / "model" / "layers")) { modeledSystemName =>
        authenticatedJsonRoute { (authInfo, tenantName) =>
          onComplete(service.getlayers(modeledSystemName, tenantName)) {
            case Success(layers) => complete(layers)
            //case Success(None) => complete(NotFound, "Release version not found")
            case Failure(ex) => complete(InternalServerError, s"An error occurred: ${ex.getMessage}")
          }
        }
      }
    

    Let me explain this code. First, I created the directive authWithHeader that reads header value and then do authentication with this value. It is super simple and all magic done by flatMap.

    Then, I created authenticatedJsonRoute which is just a composition of other simple directives. Composition magic is done with & operator (I pronounce it as "and then" in this context) and those directives will be executed in order.

    Lastly, I used my composed directive, authenticatedJsonRoute, in a complete route getLayer.