Add HasServer instance for Redirect

This commit is contained in:
Nicolas BACQUEY 2022-03-29 17:52:03 +02:00
parent bfa854f20e
commit 56f639b581
3 changed files with 42 additions and 6 deletions

View file

@ -74,7 +74,7 @@ import Servant.API
CaptureAll, Description, EmptyAPI, Fragment, CaptureAll, Description, EmptyAPI, Fragment,
FramingRender (..), FramingUnrender (..), FromSourceIO (..), FramingRender (..), FramingUnrender (..), FromSourceIO (..),
Header', If, IsSecure (..), NoContentVerb, QueryFlag, Header', If, IsSecure (..), NoContentVerb, QueryFlag,
QueryParam', QueryParams, Raw, ReflectMethod (reflectMethod), QueryParam', QueryParams, Raw, Redirect, ReflectMethod (reflectMethod),
RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO, RemoteHost, ReqBody', SBool (..), SBoolI (..), SourceIO,
Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb, Stream, StreamBody', Summary, ToSourceIO (..), Vault, Verb,
WithNamedContext, WithRoutingHeader, NamedRoutes) WithNamedContext, WithRoutingHeader, NamedRoutes)
@ -89,7 +89,7 @@ import Servant.API.Modifiers
import Servant.API.ResponseHeaders import Servant.API.ResponseHeaders
(GetHeaders, Headers, getHeaders, getResponse) (GetHeaders, Headers, getHeaders, getResponse)
import Servant.API.Status import Servant.API.Status
(statusFromNat) (Redirection, statusFromNat)
import qualified Servant.Types.SourceT as S import qualified Servant.Types.SourceT as S
import Servant.API.TypeErrors import Servant.API.TypeErrors
import Web.HttpApiData import Web.HttpApiData
@ -102,6 +102,7 @@ import Servant.Server.Internal.Delayed
import Servant.Server.Internal.DelayedIO import Servant.Server.Internal.DelayedIO
import Servant.Server.Internal.ErrorFormatter import Servant.Server.Internal.ErrorFormatter
import Servant.Server.Internal.Handler import Servant.Server.Internal.Handler
import Servant.Server.Internal.Redirect
import Servant.Server.Internal.Router import Servant.Server.Internal.Router
import Servant.Server.Internal.RouteResult import Servant.Server.Internal.RouteResult
import Servant.Server.Internal.RouterEnv import Servant.Server.Internal.RouterEnv
@ -260,6 +261,23 @@ instance ( HasServer api context
route _ context d = route _ context d =
EnvRouter enableRoutingHeaders $ route (Proxy :: Proxy api) context d EnvRouter enableRoutingHeaders $ route (Proxy :: Proxy api) context d
-- | TODO: Documentation
instance ( HasServer api context
, HasContextEntry (MkContextWithErrorFormatter context) ErrorFormatters
, KnownSymbol location
, AllStatusesInClass Redirection api
)
=> HasServer (Redirect location :> api) context where
type ServerT (Redirect location :> api) m = ServerT api m
hoistServerWithContext _ pc nt s = hoistServerWithContext (Proxy :: Proxy api) pc nt s
route _ context d =
EnvRouter
(withLocationHeader $ symbolVal $ Proxy @location)
(route (Proxy :: Proxy api) context d)
allowedMethodHead :: Method -> Request -> Bool allowedMethodHead :: Method -> Request -> Bool
allowedMethodHead method request = method == methodGet && requestMethod request == methodHead allowedMethodHead method request = method == methodGet && requestMethod request == methodHead

View file

@ -22,7 +22,7 @@ import Network.Wai
import Servant.Server.Internal.DelayedIO import Servant.Server.Internal.DelayedIO
import Servant.Server.Internal.Handler import Servant.Server.Internal.Handler
import Servant.Server.Internal.RouterEnv import Servant.Server.Internal.RouterEnv
(RouterEnv (..), hRoutedPathHeader, routedPathRepr) (RouterEnv (..), hLocationHeader, hRoutedPathHeader, routedPathRepr)
import Servant.Server.Internal.RouteResult import Servant.Server.Internal.RouteResult
import Servant.Server.Internal.ServerError import Servant.Server.Internal.ServerError
@ -266,11 +266,17 @@ runAction action env req respond k = runResourceT $
e <- runHandler a e <- runHandler a
case e of case e of
Left err -> return . Route . withRoutingHeaders $ responseServerError err Left err -> return . Route . withRoutingHeaders $ responseServerError err
Right x -> return $! withRoutingHeaders <$> k x Right x -> return $! withHeaders <$> k x
withRoutingHeaders :: Response -> Response withRoutingHeaders :: Response -> Response
withRoutingHeaders = if shouldReturnRoutedPath env withRoutingHeaders = if shouldReturnRoutedPath env
then mapResponseHeaders ((hRoutedPathHeader, cs $ routedPathRepr env) :) then mapResponseHeaders ((hRoutedPathHeader, cs $ routedPathRepr env) :)
else id else id
withLocationHeader :: Response -> Response
withLocationHeader = case locationHeader env of
Nothing -> id
Just location -> mapResponseHeaders ((hLocationHeader, cs location) :)
withHeaders :: Response -> Response
withHeaders = withLocationHeader . withRoutingHeaders
{- Note [Existential Record Update] {- Note [Existential Record Update]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View file

@ -23,14 +23,20 @@ import Network.HTTP.Types.Header
(HeaderName) (HeaderName)
data RouterEnv env = RouterEnv data RouterEnv env = RouterEnv
{ routedPath :: [PathPiece] { locationHeader :: Maybe String
, routedPath :: [PathPiece]
, shouldReturnRoutedPath :: Bool , shouldReturnRoutedPath :: Bool
, routerEnv :: env , routerEnv :: env
} }
deriving Functor deriving Functor
emptyEnv :: a -> RouterEnv a emptyEnv :: a -> RouterEnv a
emptyEnv v = RouterEnv [] False v emptyEnv v = RouterEnv
{ locationHeader = Nothing
, routedPath = []
, shouldReturnRoutedPath = False
, routerEnv = v
}
enableRoutingHeaders :: RouterEnv env -> RouterEnv env enableRoutingHeaders :: RouterEnv env -> RouterEnv env
enableRoutingHeaders env = env { shouldReturnRoutedPath = True } enableRoutingHeaders env = env { shouldReturnRoutedPath = True }
@ -61,5 +67,11 @@ toCaptureTag hint = captureName hint <> "::" <> (T.pack . show) (captureType hin
toCaptureTags :: [CaptureHint] -> Text toCaptureTags :: [CaptureHint] -> Text
toCaptureTags hints = "<" <> T.intercalate "|" (map toCaptureTag hints) <> ">" toCaptureTags hints = "<" <> T.intercalate "|" (map toCaptureTag hints) <> ">"
withLocationHeader :: String -> RouterEnv a -> RouterEnv a
withLocationHeader loc env = env { locationHeader = Just loc}
hRoutedPathHeader :: HeaderName hRoutedPathHeader :: HeaderName
hRoutedPathHeader = "Servant-Routed-Path" hRoutedPathHeader = "Servant-Routed-Path"
hLocationHeader :: HeaderName
hLocationHeader = "Location"