Skip to content

Commit 939b8ef

Browse files
committed
fixup! Use full header type in response header instances
1 parent 7dfae41 commit 939b8ef

File tree

2 files changed

+14
-12
lines changed

2 files changed

+14
-12
lines changed

servant-server/test/Servant/ServerSpec.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ import Servant.API
5454
((:<|>) (..), (:>), AuthProtect, BasicAuth,
5555
BasicAuthData (BasicAuthData), Capture, Capture', CaptureAll,
5656
Delete, EmptyAPI, Fragment, Get, HasStatus (StatusOf), Header,
57-
Headers, HttpVersion, IsSecure (..), JSON, Lenient,
57+
Header', Headers, HttpVersion, IsSecure (..), JSON, Lenient,
5858
NoContent (..), NoContentVerb, NoFraming, OctetStream, Patch,
5959
PlainText, Post, Put, QueryFlag, QueryParam, QueryParams, Raw, RawM,
6060
RemoteHost, ReqBody, SourceIO, StdMethod (..), Stream, Strict,
61-
UVerb, Union, Verb, WithStatus (..), addHeader)
61+
UVerb, Union, Verb, WithStatus (..), Optional, addHeader)
6262
import Servant.Server
6363
(Context ((:.), EmptyContext), Handler, Server, ServerT, Tagged (..),
6464
emptyServer, err401, err403, err404, hoistServer, respond, serve,
@@ -746,7 +746,7 @@ type UVerbResponseHeadersApi =
746746
Capture "ok" Bool :> UVerb 'GET '[JSON] UVerbHeaderResponse
747747

748748
uverbResponseHeadersServer :: Server UVerbResponseHeadersApi
749-
uverbResponseHeadersServer True = respond . WithStatus @200 . addHeader @"H1" (5 :: Int) $ ("foo" :: String)
749+
uverbResponseHeadersServer True = respond . WithStatus @200 . addHeader @"H1" @_ @_ @_ @('[Optional, Strict]) (5 :: Int) $ ("foo" :: String)
750750
uverbResponseHeadersServer False = respond . WithStatus @404 $ ("bar" :: String)
751751

752752
uverbResponseHeadersSpec :: Spec

servant/src/Servant/API/ResponseHeaders.hs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ import Prelude ()
5353
import Prelude.Compat
5454
import Servant.API.Header
5555
(Header, Header')
56+
import Servant.API.Modifiers
57+
(Optional, Strict)
5658
import Servant.API.UVerb.Union
5759
import qualified Data.SOP.BasicFunctors as SOP
5860
import qualified Data.SOP.NS as SOP
@@ -158,35 +160,35 @@ instance (KnownSymbol h, GetHeadersFromHList rest, ToHttpApiData v)
158160
-- * Adding headers
159161

160162
-- We need all these fundeps to save type inference
161-
class AddHeader (mods :: [*]) h v orig new
162-
| mods h v orig -> new, new -> mods, new -> h, new -> v, new -> orig where
163+
class AddHeader h v orig new (mods :: [*])
164+
| h v orig mods -> new, new -> h, new -> v, new -> orig, new -> mods where
163165
addOptionalHeader :: ResponseHeader h v -> orig -> new -- ^ N.B.: The same header can't be added multiple times
164166

165167
-- In this instance, we add a Header on top of something that is already decorated with some headers
166168
instance {-# OVERLAPPING #-} ( KnownSymbol h, ToHttpApiData v )
167-
=> AddHeader mods h v (Headers (fst ': rest) a) (Headers (Header' mods h v ': fst ': rest) a) where
169+
=> AddHeader h v (Headers (fst ': rest) a) (Headers (Header' mods h v ': fst ': rest) a) mods where
168170
addOptionalHeader hdr (Headers resp heads) = Headers resp (HCons hdr heads)
169171

170172
-- In this instance, 'a' parameter is decorated with a Header.
171173
instance {-# OVERLAPPABLE #-} ( KnownSymbol h, ToHttpApiData v , new ~ Headers '[Header' mods h v] a)
172-
=> AddHeader mods h v a new where
174+
=> AddHeader h v a new mods where
173175
addOptionalHeader hdr resp = Headers resp (HCons hdr HNil)
174176

175177
-- Instances to decorate all responses in a 'Union' with headers. The functional
176178
-- dependencies force us to consider singleton lists as the base case in the
177179
-- recursion (it is impossible to determine h and v otherwise from old / new
178180
-- responses if the list is empty).
179-
instance (AddHeader mods h v old new) => AddHeader mods h v (Union '[old]) (Union '[new]) where
181+
instance (AddHeader h v old new mods) => AddHeader h v (Union '[old]) (Union '[new]) mods where
180182
addOptionalHeader hdr resp =
181183
SOP.Z $ SOP.I $ addOptionalHeader hdr $ SOP.unI $ SOP.unZ $ resp
182184

183185
instance
184-
( AddHeader mods h v old new, AddHeader mods h v (Union oldrest) (Union newrest)
186+
( AddHeader h v old new mods, AddHeader h v (Union oldrest) (Union newrest) mods
185187
-- This ensures that the remainder of the response list is _not_ empty
186188
-- It is necessary to prevent the two instances for union types from
187189
-- overlapping.
188190
, oldrest ~ (a ': as), newrest ~ (b ': bs))
189-
=> AddHeader mods h v (Union (old ': (a ': as))) (Union (new ': (b ': bs))) where
191+
=> AddHeader h v (Union (old ': (a ': as))) (Union (new ': (b ': bs))) mods where
190192
addOptionalHeader hdr resp = case resp of
191193
SOP.Z (SOP.I rHead) -> SOP.Z $ SOP.I $ addOptionalHeader hdr rHead
192194
SOP.S rOthers -> SOP.S $ addOptionalHeader hdr rOthers
@@ -211,15 +213,15 @@ instance
211213
-- Note that while in your handlers type annotations are not required, since
212214
-- the type can be inferred from the API type, in other cases you may find
213215
-- yourself needing to add annotations.
214-
addHeader :: AddHeader mods h v orig new => v -> orig -> new
216+
addHeader :: AddHeader h v orig new mods => v -> orig -> new
215217
addHeader = addOptionalHeader . Header
216218

217219
-- | Deliberately do not add a header to a value.
218220
--
219221
-- >>> let example1 = noHeader "hi" :: Headers '[Header "someheader" Int] String
220222
-- >>> getHeaders example1
221223
-- []
222-
noHeader :: AddHeader mods h v orig new => orig -> new
224+
noHeader :: AddHeader h v orig new mods => orig -> new
223225
noHeader = addOptionalHeader MissingHeader
224226

225227
class HasResponseHeader h a headers where

0 commit comments

Comments
 (0)