This commit introduces a new type-level combinator, `WithRoutingHeader`.
It modifies the behaviour of the following sub-API, such that all endpoint
of said API return an additional routing header in their response.
A routing header is a header that specifies which endpoint the
incoming request was routed to.
Endpoint are designated by their path, in which `Capture'` and
`CaptureAll` combinators are replaced by a capture hint.
This header can be used by downstream middlewares to gather
information about individual endpoints, since in most cases
a routing header uniquely identifies a single endpoint.
Example:
```haskell
type MyApi =
WithRoutingHeader :> "by-id" :> Capture "id" Int :> Get '[JSON] Foo
-- GET /by-id/1234 will return a response with the following header:
-- ("Servant-Routed-Path", "/by-id/<id::Int>")
```
To achieve this, two refactorings were necessary:
* Introduce a type `RouterEnv env` to encapsulate the `env` type
(as in `Router env a`), which contains a tuple-encoded list of url
pieces parsed from the incoming request.
This type makes it possible to pass more information throughout the
routing process, and the computation of the `Delayed env c` associated
with each request.
* Introduce a new kind of router, which only modifies the RouterEnv, and
doesn't affect the routing process otherwise.
`EnvRouter (RouterEnv env -> RouterEnv env) (Router' env a)`
This new router is used when encountering the `WithRoutingHeader`
combinator in an API, to notify the endpoints of the sub-API that they
must produce a routing header (this behaviour is disabled by default).
* bumped cabal-version field
Cabal supports two types of licenses, native and SPDX, which can be seen here hackage.haskell.org/package/Cabal-3.6.2.0/docs/Distribution-Types-PackageDescription.html#v:licenseRaw
Several packages use BSD-3-Clause as a license, in conjonction with cabal-version: >=1.10 which cabal parses as Right (UnknownLicense "BSD-3").
If I change teh cabal-version to cabal-version: 2.2 , cabal correctly identifdies the license License (ELicense (ELicenseId BSD_3_Clause)).
* changed license from cabal to spdx format
aka BSD3 -> BSD-3-Clause: next cabal may deprecate the old format
We define `ServerT (NamedRoutes api) m` as `api (AsServerT m)`, so that
the server of an record-defined API is a record of handlers.
The implementation piggy backs on the instance for “vanilla” servant
types with `(:<|>)`, using the `GServantProduct` for converting backd
and forth between the record / vanilla servers.
The main difficulty is that GHC needs to know that this operation is
legit, which can be expressed as the fact that:
```
GToServant (Rep (ServerT (NamedRoutes api))) m ~
ServerT (GToServant (Rep (api AsApi))) m
```
plus a few additional constraints.
This is easy enough for `route`, as we know that `m ~ Handler`. But in
the case of `hoistServerWithContext`, the two involved monads are
unknown ; in other words, this constraint needs to hold `forall m.`
Switching `-XQuantifiedConstraints` on is not sufficient, as our
constraints involve type families (`Rep` and `ServerT`). Our trick is to
use an intermediary typeclass, `GServer`, as a provider of evidence (in
the form of a `Dict`) that our constraints are indeed satisfied for a
particular monad.
The only instance of `GServer` is defined along with it, so it is
practically invisible to users.
Currently there is no way for Servant users to customize formatting of
error messages that arise when combinators can't parse URL or request
body, apart from reimplementing those combinators for themselves or
using middlewares.
This commit adds a possibility to specify custom error formatters
through Context.
Fixes#685