Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ability to support different request/response types for a route based on the api version #592

Open
alilewin opened this issue Nov 7, 2024 · 1 comment

Comments

@alilewin
Copy link

alilewin commented Nov 7, 2024

Is your feature request related to a problem? Please describe.
I have a set of endpoints implemented using Finatra, and I'd like to add support for different Request/Response types depending on the API version. The issue I'm having is that the version will be passed through the header, so the actual route defined in the controller will always be the same. I know an alternative could be to version in the path, ex have v1/my/endpoint and v2/my/endpoint defined separately where I could easily have different Request/Response types defined, but the requirements I need to follow include passing the version through the header (passing it as a query param is also an option if that's easier to do in Finatra). I have been able to find a way to support returning multiple Response types depending on the version, but can't seem to get this working for the Request.

Describe the solution you'd like
One suggestion would be to support some kind of header based routing where we can define multiple routes in the controller whose paths might be the same, but expect different values for a specified header param. Ex:

get(route = "/my/endpoint", headerMappingOpt = Map("API-Version" -> "2024-09-01")) { req = CustomRequestV1 => ... }

get(route = "/my/endpoint", headerMappingOpt = Map("API-Version" -> "2024-10-01")) { req = CustomRequestV2 => ... }

There are probably multiple ways to go about this, so any solution is fine by me. Additionally if there is already a workaround to support this that I may have missed, please let me know.

Describe alternatives you've considered
I've already tried creating a base CustomRequest trait, having the two separate requests extend from that, and using that in the controller; ex:

trait CustomRequestBase
case class CustomRequestV1( ...)
case class CustomRequestV2(...)

get(route = "/my/endpoint") { req = CustomRequestBase => ...

}

but ran into some issues getting this to work.

Additional context
As mentioned above, if there is already a way I can implement my endpoints to support different Request types that I may have missed, please advise. Thank you!

@ronbrz
Copy link

ronbrz commented Nov 23, 2024

Maybe an idea is to define a case class that only captures the version string, and then further parse the request based on that.

To illustrate, we can define a couple classes like so:

sealed trait HelloRequest
case class HelloV1(@QueryParam name: String) extends HelloRequest
case class HelloV2(@QueryParam id: Long) extends HelloRequest

case class Hello(@QueryParam v: String, req: Request) {
  def versioned(messageBodyReader: DefaultMessageBodyReader): HelloRequest = {
    if (v == "1") {
      messageBodyReader.parse[HelloV1](req)
    } else {
      messageBodyReader.parse[HelloV2](req)
    }
  }
}

and the controller here:

class HelloWorldController @Inject() (messageBodyReader: DefaultMessageBodyReader) extends Controller {
  get("/hi") { request: Hello =>
    val req = request.versioned(messageBodyReader)
    req match {
      case HelloV1(name) => "Hello " + name
      case HelloV2(id) => s"Hello Employee #$id"
    }
  }
}

The endpoint initially parses the request into the Hello case class, which only cares about the v version param, and passes along the request. We can then use the DefaultMessageBodyReader to further parse the request into HelloV1 or HelloV2 depending on the version number.

Hopefully this addresses your use-case! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants