git.iko.soy/typeable/servant
on master help sign in
Make NoContent still take an arg.

For consistency with other combinators, and to make using headers easier.

commit783a849c6741de37b5b09755a96f7ba5a843d8b1 authorJulian K. Arni <jkarni@gmail.com> dateThu 7 Jan 2016, 14:30 (11 years ago) parent574e9c4
6 files changed+69−75
servant-client/src/Servant/Client.hs+7−7 servant-client/test/Servant/ClientSpec.hs+6−6 servant-server/test/Servant/ServerSpec.hs+12−12 servant/src/Servant/API.hs+2−2 servant/src/Servant/API/ContentTypes.hs+26−32 servant/src/Servant/API/Verbs.hs+16−16
servant-client/src/Servant/Client.hs+7−7
@@ -129,10 +129,10 @@ instance OVERLAPPABLE_
129129 where method = reflectMethod (Proxy :: Proxy method)
130130
131131instance OVERLAPPING_
132− (ReflectMethod method) => HasClient (Verb method status cts ()) where
133− type Client (Verb method status cts ()) = ExceptT ServantError IO ()
132+ (ReflectMethod method) => HasClient (Verb method status cts NoContent) where
133+ type Client (Verb method status cts NoContent) = ExceptT ServantError IO NoContent
134134 clientWithRoute Proxy req baseurl manager =
135− void $ performRequestNoBody method req baseurl manager
135+ performRequestNoBody method req baseurl manager >> return NoContent
136136 where method = reflectMethod (Proxy :: Proxy method)
137137
138138instance OVERLAPPING_
@@ -150,13 +150,13 @@ instance OVERLAPPING_
150150
151151instance OVERLAPPING_
152152 ( BuildHeadersTo ls, ReflectMethod method
153− ) => HasClient (Verb method status cts (Headers ls ())) where
154− type Client (Verb method status cts (Headers ls ()))
155− = ExceptT ServantError IO (Headers ls ())
153+ ) => HasClient (Verb method status cts (Headers ls NoContent)) where
154+ type Client (Verb method status cts (Headers ls NoContent))
155+ = ExceptT ServantError IO (Headers ls NoContent)
156156 clientWithRoute Proxy req baseurl manager = do
157157 let method = reflectMethod (Proxy :: Proxy method)
158158 hdrs <- performRequestNoBody method req baseurl manager
159− return $ Headers { getResponse = ()
159+ return $ Headers { getResponse = NoContent
160160 , getHeadersHList = buildHeadersTo hdrs
161161 }
162162
servant-client/test/Servant/ClientSpec.hs+6−6
@@ -90,7 +90,7 @@ type TestHeaders = '[Header "X-Example1" Int, Header "X-Example2" String]
9090
9191type Api =
9292 "get" :> Get '[JSON] Person
93− :<|> "deleteEmpty" :> Delete '[JSON] ()
93+ :<|> "deleteEmpty" :> DeleteNoContent '[JSON] NoContent
9494 :<|> "capture" :> Capture "name" String :> Get '[JSON,FormUrlEncoded] Person
9595 :<|> "body" :> ReqBody '[FormUrlEncoded,JSON] Person :> Post '[JSON] Person
9696 :<|> "param" :> QueryParam "name" String :> Get '[FormUrlEncoded,JSON] Person
@@ -105,14 +105,14 @@ type Api =
105105 ReqBody '[JSON] [(String, [Rational])] :>
106106 Get '[JSON] (String, Maybe Int, Bool, [(String, [Rational])])
107107 :<|> "headers" :> Get '[JSON] (Headers TestHeaders Bool)
108− :<|> "deleteContentType" :> Delete '[JSON] ()
108+ :<|> "deleteContentType" :> DeleteNoContent '[JSON] NoContent
109109api :: Proxy Api
110110api = Proxy
111111
112112server :: Application
113113server = serve api (
114114 return alice
115− :<|> return ()
115+ :<|> return NoContent
116116 :<|> (\ name -> return $ Person name 0)
117117 :<|> return
118118 :<|> (\ name -> case name of
@@ -125,7 +125,7 @@ server = serve api (
125125 :<|> (\ _request respond -> respond $ responseLBS badRequest400 [] "rawFailure")
126126 :<|> (\ a b c d -> return (a, b, c, d))
127127 :<|> (return $ addHeader 1729 $ addHeader "eg2" True)
128− :<|> return ()
128+ :<|> return NoContent
129129 )
130130
131131
@@ -157,11 +157,11 @@ sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
157157 describe "Servant.API.Delete" $ do
158158 it "allows empty content type" $ \(_, baseUrl) -> do
159159 let getDeleteEmpty = getNth (Proxy :: Proxy 1) $ client api baseUrl manager
160− (left show <$> runExceptT getDeleteEmpty) `shouldReturn` Right ()
160+ (left show <$> runExceptT getDeleteEmpty) `shouldReturn` Right NoContent
161161
162162 it "allows content type" $ \(_, baseUrl) -> do
163163 let getDeleteContentType = getLast $ client api baseUrl manager
164− (left show <$> runExceptT getDeleteContentType) `shouldReturn` Right ()
164+ (left show <$> runExceptT getDeleteContentType) `shouldReturn` Right NoContent
165165
166166 it "Servant.API.Capture" $ \(_, baseUrl) -> do
167167 let getCapture = getNth (Proxy :: Proxy 2) $ client api baseUrl manager
servant-server/test/Servant/ServerSpec.hs+12−12
@@ -38,8 +38,8 @@ import Servant.API ((:<|>) (..), (:>), Capture, Delete,
3838 HttpVersion, IsSecure (..), JSON,
3939 Patch, PlainText, Post, Put,
4040 QueryFlag, QueryParam, QueryParams,
41− Raw, RemoteHost, ReqBody,
42− addHeader)
41+ Raw, RemoteHost, ReqBody, GetNoContent,
42+ PostNoContent, addHeader, NoContent(..))
4343import Servant.Server (Server, serve, ServantErr(..), err404)
4444import Test.Hspec (Spec, describe, it, shouldBe)
4545import Test.Hspec.Wai (get, liftIO, matchHeaders,
@@ -130,9 +130,9 @@ captureSpec = do
130130
131131
132132type GetApi = Get '[JSON] Person
133− :<|> "empty" :> Get '[JSON] ()
134− :<|> "emptyWithHeaders" :> Get '[JSON] (Headers '[Header "H" Int] ())
135− :<|> "post" :> Post '[JSON] ()
133+ :<|> "empty" :> GetNoContent '[JSON] NoContent
134+ :<|> "emptyWithHeaders" :> GetNoContent '[JSON] (Headers '[Header "H" Int] NoContent)
135+ :<|> "post" :> PostNoContent '[JSON] NoContent
136136
137137getApi :: Proxy GetApi
138138getApi = Proxy
@@ -141,9 +141,9 @@ getSpec :: Spec
141141getSpec = do
142142 describe "Servant.API.Get" $ do
143143 let server = return alice
144− :<|> return ()
145− :<|> return (addHeader 5 ())
146− :<|> return ()
144+ :<|> return NoContent
145+ :<|> return (addHeader 5 NoContent)
146+ :<|> return NoContent
147147
148148 with (return $ serve getApi server) $ do
149149
@@ -157,7 +157,7 @@ getSpec = do
157157 post "/empty" "" `shouldRespondWith` 405
158158
159159 it "returns headers" $ do
160− get "/emptyWithHeaders" `shouldRespondWith` 200 { matchHeaders = [ "H" <:> "5" ] }
160+ get "/emptyWithHeaders" `shouldRespondWith` 204 { matchHeaders = [ "H" <:> "5" ] }
161161
162162 it "returns 406 if the Accept header is not supported" $ do
163163 Test.Hspec.Wai.request methodGet "" [(hAccept, "crazy/mime")] ""
@@ -168,9 +168,9 @@ headSpec :: Spec
168168headSpec = do
169169 describe "Servant.API.Head" $ do
170170 let server = return alice
171− :<|> return ()
172− :<|> return (addHeader 5 ())
173− :<|> return ()
171+ :<|> return NoContent
172+ :<|> return (addHeader 5 NoContent)
173+ :<|> return NoContent
174174 with (return $ serve getApi server) $ do
175175
176176 it "allows to GET a Person" $ do
servant/src/Servant/API.hs+2−2
@@ -52,7 +52,7 @@ import Servant.API.Alternative ((:<|>) (..))
5252import Servant.API.Capture (Capture)
5353import Servant.API.ContentTypes (Accept (..), FormUrlEncoded,
5454 FromFormUrlEncoded (..), JSON,
55− MimeRender (..),
55+ MimeRender (..), NoContent (NoContent),
5656 MimeUnrender (..), OctetStream,
5757 PlainText, ToFormUrlEncoded (..))
5858import Servant.API.Header (Header (..))
@@ -77,7 +77,7 @@ import Servant.API.Verbs (Created, Delete, DeleteAccepted,
7777 GetNonAuthoritative,
7878 GetPartialContent,
7979 GetResetContent,
80− NoContent (NoContent), Patch,
80+ Patch,
8181 PatchAccepted, PatchNoContent,
8282 PatchNoContent,
8383 PatchNonAuthoritative, Post,
servant/src/Servant/API/ContentTypes.hs+26−32
@@ -11,11 +11,10 @@
1111{-# LANGUAGE TypeFamilies #-}
1212{-# LANGUAGE TypeOperators #-}
1313{-# LANGUAGE UndecidableInstances #-}
14−#if !MIN_VERSION_base(4,8,0)
15−{-# LANGUAGE OverlappingInstances #-}
16−#endif
1714{-# OPTIONS_HADDOCK not-home #-}
1815
16+#include "overlapping-compat.h"
17+
1918-- | A collection of basic Content-Types (also known as Internet Media
2019-- Types, or MIME types). Additionally, this module provides classes that
2120-- encapsulate how to serialize or deserialize values to or from
@@ -57,6 +56,9 @@ module Servant.API.ContentTypes
5756 , MimeRender(..)
5857 , MimeUnrender(..)
5958
59+ -- * NoContent
60+ , NoContent(..)
61+
6062 -- * Internal
6163 , AcceptHeader(..)
6264 , AllCTRender(..)
@@ -75,8 +77,7 @@ import Control.Applicative ((*>), (<*))
7577#endif
7678import Control.Arrow (left)
7779import Control.Monad
78−import Data.Aeson (FromJSON, ToJSON, encode,
79− parseJSON)
80+import Data.Aeson (FromJSON(..), ToJSON(..), encode)
8081import Data.Aeson.Parser (value)
8182import Data.Aeson.Types (parseEither)
8283import Data.Attoparsec.ByteString.Char8 (endOfInput, parseOnly,
@@ -168,10 +169,7 @@ class (AllMime list) => AllCTRender (list :: [*]) a where
168169 -- mimetype).
169170 handleAcceptH :: Proxy list -> AcceptHeader -> a -> Maybe (ByteString, ByteString)
170171
171−instance
172−#if MIN_VERSION_base(4,8,0)
173− {-# OVERLAPPABLE #-}
174−#endif
172+instance OVERLAPPABLE_
175173 (AllMimeRender (ct ': cts) a) => AllCTRender (ct ': cts) a where
176174 handleAcceptH _ (AcceptHeader accept) val = M.mapAcceptMedia lkup accept
177175 where pctyps = Proxy :: Proxy (ct ': cts)
@@ -275,20 +273,14 @@ instance ( MimeUnrender ctyp a
275273-- * MimeRender Instances
276274
277275-- | `encode`
278−instance
279−#if MIN_VERSION_base(4,8,0)
280− {-# OVERLAPPABLE #-}
281−#endif
276+instance OVERLAPPABLE_
282277 ToJSON a => MimeRender JSON a where
283278 mimeRender _ = encode
284279
285280-- | @encodeFormUrlEncoded . toFormUrlEncoded@
286281-- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only
287282-- holds if every element of x is non-null (i.e., not @("", "")@)
288−instance
289−#if MIN_VERSION_base(4,8,0)
290− {-# OVERLAPPABLE #-}
291−#endif
283+instance OVERLAPPABLE_
292284 ToFormUrlEncoded a => MimeRender FormUrlEncoded a where
293285 mimeRender _ = encodeFormUrlEncoded . toFormUrlEncoded
294286
@@ -312,25 +304,27 @@ instance MimeRender OctetStream ByteString where
312304instance MimeRender OctetStream BS.ByteString where
313305 mimeRender _ = fromStrict
314306
315−instance
316−#if MIN_VERSION_base(4,8,0)
317− {-# OVERLAPPING #-}
318−#endif
319− MimeRender JSON () where
307+-- | A type for responses with content-body.
308+data NoContent = NoContent
309+ deriving (Show, Eq, Read)
310+
311+instance FromJSON NoContent where
312+ parseJSON _ = return NoContent
313+
314+instance ToJSON NoContent where
315+ toJSON _ = ""
316+
317+
318+instance OVERLAPPING_
319+ MimeRender JSON NoContent where
320320 mimeRender _ _ = ""
321321
322−instance
323−#if MIN_VERSION_base(4,8,0)
324− {-# OVERLAPPING #-}
325−#endif
326− MimeRender PlainText () where
322+instance OVERLAPPING_
323+ MimeRender PlainText NoContent where
327324 mimeRender _ _ = ""
328325
329−instance
330−#if MIN_VERSION_base(4,8,0)
331− {-# OVERLAPPING #-}
332−#endif
333− MimeRender OctetStream () where
326+instance OVERLAPPING_
327+ MimeRender OctetStream NoContent where
334328 mimeRender _ _ = ""
335329
336330--------------------------------------------------------------------------
servant/src/Servant/API/Verbs.hs+16−16
@@ -11,6 +11,7 @@ import GHC.TypeLits (Nat)
1111import Network.HTTP.Types.Method (Method, StdMethod (..),
1212 methodDelete, methodGet, methodHead,
1313 methodPatch, methodPost, methodPut)
14+import Servant.API.ContentTypes (NoContent(..))
1415
1516-- | @Verb@ is a general type for representing HTTP verbs/methods. For
1617-- convenience, type synonyms for each verb with a 200 response code are
@@ -95,40 +96,40 @@ type PutNonAuthoritative contentTypes a = Verb 'PUT 203 contentTypes a
9596
9697-- ** 204 No Content
9798--
98−-- Indicates that no response body is being returned. Handlers for these must
99−-- return 'NoContent'.
99+-- Indicates that no response body is being returned. Handlers for these should
100+-- return 'NoContent', possibly with headers.
100101--
101102-- If the document view should be reset, use @205 Reset Content@.
102103
103104-- | 'GET' with 204 status code.
104−type GetNoContent contentTypes = Verb 'GET 204 contentTypes NoContent
105+type GetNoContent contentTypes noContent = Verb 'GET 204 contentTypes noContent
105106-- | 'POST' with 204 status code.
106−type PostNoContent contentTypes = Verb 'POST 204 contentTypes NoContent
107+type PostNoContent contentTypes noContent = Verb 'POST 204 contentTypes noContent
107108-- | 'DELETE' with 204 status code.
108−type DeleteNoContent contentTypes = Verb 'DELETE 204 contentTypes NoContent
109+type DeleteNoContent contentTypes noContent = Verb 'DELETE 204 contentTypes noContent
109110-- | 'PATCH' with 204 status code.
110−type PatchNoContent contentTypes = Verb 'PATCH 204 contentTypes NoContent
111+type PatchNoContent contentTypes noContent = Verb 'PATCH 204 contentTypes noContent
111112-- | 'PUT' with 204 status code.
112−type PutNoContent contentTypes = Verb 'PUT 204 contentTypes NoContent
113+type PutNoContent contentTypes noContent = Verb 'PUT 204 contentTypes noContent
113114
114115
115116-- ** 205 Reset Content
116117--
117−-- Indicates that no response body is being returned. Handlers for these must
118−-- return 'NoContent'.
118+-- Indicates that no response body is being returned. Handlers for these should
119+-- return 'NoContent', possibly with Headers.
119120--
120121-- If the document view should not be reset, use @204 No Content@.
121122
122123-- | 'GET' with 205 status code.
123−type GetResetContent contentTypes = Verb 'GET 205 contentTypes NoContent
124+type GetResetContent contentTypes noContent = Verb 'GET 205 contentTypes noContent
124125-- | 'POST' with 205 status code.
125−type PostResetContent contentTypes = Verb 'POST 205 contentTypes NoContent
126+type PostResetContent contentTypes noContent = Verb 'POST 205 contentTypes noContent
126127-- | 'DELETE' with 205 status code.
127−type DeleteResetContent contentTypes = Verb 'DELETE 205 contentTypes NoContent
128+type DeleteResetContent contentTypes noContent = Verb 'DELETE 205 contentTypes noContent
128129-- | 'PATCH' with 205 status code.
129−type PatchResetContent contentTypes = Verb 'PATCH 205 contentTypes NoContent
130+type PatchResetContent contentTypes noContent = Verb 'PATCH 205 contentTypes noContent
130131-- | 'PUT' with 205 status code.
131−type PutResetContent contentTypes = Verb 'PUT 205 contentTypes NoContent
132+type PutResetContent contentTypes noContent = Verb 'PUT 205 contentTypes noContent
132133
133134
134135-- ** 206 Partial Content
@@ -140,9 +141,8 @@ type PutResetContent contentTypes = Verb 'PUT 205 contentTypes NoContent
140141-- RFC7233 Section 4.1>
141142
142143-- | 'GET' with 206 status code.
143−type GetPartialContent contentTypes = Verb 'GET 205 contentTypes NoContent
144+type GetPartialContent contentTypes noContent = Verb 'GET 205 contentTypes noContent
144145
145−data NoContent = NoContent
146146
147147class ReflectMethod a where
148148 reflectMethod :: proxy a -> Method