git.iko.soy/typeable/servant
on master help sign in
Support UVerb in servant-auth-server
030d852Gaël Deest
servant-auth/servant-auth-server/servant-auth-server.cabal+1
@@ -129,6 +129,7 @@ test-suite spec
129 , lens-aeson >= 1.0.2 && < 1.2
129 , lens-aeson >= 1.0.2 && < 1.2
130 , warp >= 3.2.25 && < 3.4
130 , warp >= 3.2.25 && < 3.4
131 , wreq >= 0.5.2.1 && < 0.6
131 , wreq >= 0.5.2.1 && < 0.6
132+ , text >= 1.2.3.0 && < 2.1
132 other-modules:
133 other-modules:
133 Servant.Auth.ServerSpec
134 Servant.Auth.ServerSpec
134 default-language: Haskell2010
135 default-language: Haskell2010
servant-auth/servant-auth-server/src/Servant/Auth/Server/Internal/AddSetCookie.hs+14−1
@@ -11,6 +11,7 @@ import Data.Tagged (Tagged (..))
11import qualified Network.HTTP.Types as HTTP
11import qualified Network.HTTP.Types as HTTP
12import Network.Wai (mapResponseHeaders)
12import Network.Wai (mapResponseHeaders)
13import Servant
13import Servant
14+import Servant.API.UVerb.Union
14import Servant.API.Generic
15import Servant.API.Generic
15import Servant.Server.Generic
16import Servant.Server.Generic
16import Web.Cookie
17import Web.Cookie
@@ -33,12 +34,24 @@ type family AddSetCookieApiVerb a where
33 AddSetCookieApiVerb (Headers ls a) = Headers (Header "Set-Cookie" SetCookie ': ls) a
34 AddSetCookieApiVerb (Headers ls a) = Headers (Header "Set-Cookie" SetCookie ': ls) a
34 AddSetCookieApiVerb a = Headers '[Header "Set-Cookie" SetCookie] a
35 AddSetCookieApiVerb a = Headers '[Header "Set-Cookie" SetCookie] a
35
36
37+#if MIN_VERSION_servant_server(0,18,1)
38+type family MapAddSetCookieApiVerb (as :: [*]) where
39+ MapAddSetCookieApiVerb '[] = '[]
40+ MapAddSetCookieApiVerb (a ': as) = (AddSetCookieApiVerb a ': MapAddSetCookieApiVerb as)
41+#endif
42+
36type family AddSetCookieApi a :: *
43type family AddSetCookieApi a :: *
37type instance AddSetCookieApi (a :> b) = a :> AddSetCookieApi b
44type instance AddSetCookieApi (a :> b) = a :> AddSetCookieApi b
38type instance AddSetCookieApi (a :<|> b) = AddSetCookieApi a :<|> AddSetCookieApi b
45type instance AddSetCookieApi (a :<|> b) = AddSetCookieApi a :<|> AddSetCookieApi b
46+#if MIN_VERSION_servant_server(0,19,0)
39type instance AddSetCookieApi (NamedRoutes api) = AddSetCookieApi (ToServantApi api)
47type instance AddSetCookieApi (NamedRoutes api) = AddSetCookieApi (ToServantApi api)
48+#endif
40type instance AddSetCookieApi (Verb method stat ctyps a)
49type instance AddSetCookieApi (Verb method stat ctyps a)
41 = Verb method stat ctyps (AddSetCookieApiVerb a)
50 = Verb method stat ctyps (AddSetCookieApiVerb a)
51+#if MIN_VERSION_servant_server(0,18,1)
52+type instance AddSetCookieApi (UVerb method ctyps as)
53+ = UVerb method ctyps (MapAddSetCookieApiVerb as)
54+#endif
42type instance AddSetCookieApi Raw = Raw
55type instance AddSetCookieApi Raw = Raw
43#if MIN_VERSION_servant_server(0,15,0)
56#if MIN_VERSION_servant_server(0,15,0)
44type instance AddSetCookieApi (Stream method stat framing ctyps a)
57type instance AddSetCookieApi (Stream method stat framing ctyps a)
@@ -57,7 +70,7 @@ instance {-# OVERLAPS #-} AddSetCookies ('S n) oldb newb
57 => AddSetCookies ('S n) (a -> oldb) (a -> newb) where
70 => AddSetCookies ('S n) (a -> oldb) (a -> newb) where
58 addSetCookies cookies oldfn = addSetCookies cookies . oldfn
71 addSetCookies cookies oldfn = addSetCookies cookies . oldfn
59
72
60−instance AddSetCookies 'Z orig orig where
73+instance (orig1 ~ orig2) => AddSetCookies 'Z orig1 orig2 where
61 addSetCookies _ = id
74 addSetCookies _ = id
62
75
63instance {-# OVERLAPPABLE #-}
76instance {-# OVERLAPPABLE #-}
servant-auth/servant-auth-server/test/Servant/Auth/ServerSpec.hs+4
@@ -1,4 +1,5 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TypeApplications #-}
2module Servant.Auth.ServerSpec (spec) where
3module Servant.Auth.ServerSpec (spec) where
3
4
4#if !MIN_VERSION_servant_server(0,16,0)
5#if !MIN_VERSION_servant_server(0,16,0)
@@ -25,6 +26,7 @@ import Data.Aeson (FromJSON, ToJSON, Value,
25import Data.Aeson.Lens (_JSON)
26import Data.Aeson.Lens (_JSON)
26import qualified Data.ByteString as BS
27import qualified Data.ByteString as BS
27import qualified Data.ByteString.Lazy as BSL
28import qualified Data.ByteString.Lazy as BSL
29+import Data.Text (Text)
28import Data.CaseInsensitive (mk)
30import Data.CaseInsensitive (mk)
29import Data.Foldable (find)
31import Data.Foldable (find)
30import Data.Monoid
32import Data.Monoid
@@ -407,6 +409,7 @@ type API auths
407 ( Get '[JSON] Int
409 ( Get '[JSON] Int
408 :<|> ReqBody '[JSON] Int :> Post '[JSON] Int
410 :<|> ReqBody '[JSON] Int :> Post '[JSON] Int
409 :<|> NamedRoutes DummyRoutes
411 :<|> NamedRoutes DummyRoutes
412+ :<|> UVerb 'GET '[JSON] '[WithStatus 200 Int, WithStatus 500 Text]
410 :<|> "header" :> Get '[JSON] (Headers '[Header "Blah" Int] Int)
413 :<|> "header" :> Get '[JSON] (Headers '[Header "Blah" Int] Int)
411#if MIN_VERSION_servant_server(0,15,0)
414#if MIN_VERSION_servant_server(0,15,0)
412 :<|> "stream" :> StreamGet NoFraming OctetStream (SourceIO BS.ByteString)
415 :<|> "stream" :> StreamGet NoFraming OctetStream (SourceIO BS.ByteString)
@@ -483,6 +486,7 @@ server ccfg =
483 Authenticated usr -> getInt usr
486 Authenticated usr -> getInt usr
484 :<|> postInt usr
487 :<|> postInt usr
485 :<|> DummyRoutes { dummyInt = getInt usr }
488 :<|> DummyRoutes { dummyInt = getInt usr }
489+ :<|> respond (WithStatus @200 (42 :: Int))
486 :<|> getHeaderInt
490 :<|> getHeaderInt
487#if MIN_VERSION_servant_server(0,15,0)
491#if MIN_VERSION_servant_server(0,15,0)
488 :<|> return (S.source ["bytestring"])
492 :<|> return (S.source ["bytestring"])
servant/src/Servant/API/ResponseHeaders.hs+19
@@ -8,6 +8,7 @@
8{-# LANGUAGE PolyKinds #-}
8{-# LANGUAGE PolyKinds #-}
9{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE TypeFamilyDependencies #-}
11{-# LANGUAGE TypeOperators #-}
12{-# LANGUAGE TypeOperators #-}
12{-# LANGUAGE UndecidableInstances #-}
13{-# LANGUAGE UndecidableInstances #-}
13{-# OPTIONS_HADDOCK not-home #-}
14{-# OPTIONS_HADDOCK not-home #-}
@@ -51,6 +52,9 @@ import Prelude ()
51import Prelude.Compat
52import Prelude.Compat
52import Servant.API.Header
53import Servant.API.Header
53 (Header)
54 (Header)
55+import Servant.API.UVerb.Union
56+import qualified Data.SOP.BasicFunctors as SOP
57+import qualified Data.SOP.NS as SOP
54
58
55-- | Response Header objects. You should never need to construct one directly.
59-- | Response Header objects. You should never need to construct one directly.
56-- Instead, use 'addOptionalHeader'.
60-- Instead, use 'addOptionalHeader'.
@@ -170,6 +174,21 @@ instance {-# OVERLAPPABLE #-} ( KnownSymbol h, ToHttpApiData v , new ~ Headers '
170 => AddHeader h v a new where
174 => AddHeader h v a new where
171 addOptionalHeader hdr resp = Headers resp (HCons hdr HNil)
175 addOptionalHeader hdr resp = Headers resp (HCons hdr HNil)
172
176
177+-- Instances to decorate all responses in a 'Union' with headers. The functional
178+-- dependencies force us to consider singleton lists as the base case in the
179+-- recursion (it is impossible to determine h and v otherwise from old / new
180+-- responses if the list is empty).
181+instance (AddHeader h v old new) => AddHeader h v (Union '[old]) (Union '[new]) where
182+ addOptionalHeader hdr resp =
183+ SOP.Z $ SOP.I $ addOptionalHeader hdr $ SOP.unI $ SOP.unZ $ resp
184+
185+instance
186+ (AddHeader h v old new, AddHeader h v (Union oldrest) (Union newrest), oldrest ~ (a ': as), newrest ~ (b ': bs))
187+ => AddHeader h v (Union (old ': (a ': as))) (Union (new ': (b ': bs))) where
188+ addOptionalHeader hdr resp = case resp of
189+ SOP.Z (SOP.I rHead) -> SOP.Z $ SOP.I $ addOptionalHeader hdr rHead
190+ SOP.S rOthers -> SOP.S $ addOptionalHeader hdr rOthers
191+
173-- | @addHeader@ adds a header to a response. Note that it changes the type of
192-- | @addHeader@ adds a header to a response. Note that it changes the type of
174-- the value in the following ways:
193-- the value in the following ways:
175--
194--
servant/src/Servant/API/UVerb.hs+3
@@ -38,6 +38,7 @@ import GHC.TypeLits (Nat)
38import Network.HTTP.Types (Status, StdMethod)
38import Network.HTTP.Types (Status, StdMethod)
39import Servant.API.ContentTypes (JSON, PlainText, FormUrlEncoded, OctetStream, NoContent, MimeRender(mimeRender), MimeUnrender(mimeUnrender))
39import Servant.API.ContentTypes (JSON, PlainText, FormUrlEncoded, OctetStream, NoContent, MimeRender(mimeRender), MimeUnrender(mimeUnrender))
40import Servant.API.Status (KnownStatus, statusVal)
40import Servant.API.Status (KnownStatus, statusVal)
41+import Servant.API.ResponseHeaders (Headers)
41import Servant.API.UVerb.Union
42import Servant.API.UVerb.Union
42
43
43class KnownStatus (StatusOf a) => HasStatus (a :: *) where
44class KnownStatus (StatusOf a) => HasStatus (a :: *) where
@@ -86,6 +87,8 @@ newtype WithStatus (k :: Nat) a = WithStatus a
86instance KnownStatus n => HasStatus (WithStatus n a) where
87instance KnownStatus n => HasStatus (WithStatus n a) where
87 type StatusOf (WithStatus n a) = n
88 type StatusOf (WithStatus n a) = n
88
89
90+instance HasStatus a => HasStatus (Headers ls a) where
91+ type StatusOf (Headers ls a) = StatusOf a
89
92
90-- | A variant of 'Verb' that can have any of a number of response values and status codes.
93-- | A variant of 'Verb' that can have any of a number of response values and status codes.
91--
94--