git.iko.soy/typeable/servant
on master help sign in
Merge pull request #59 from codedmart/deleteResponseBody
c758e4bJulian Arni
servant-client/CHANGELOG.md+1
@@ -1,5 +1,6 @@
10.3
10.3
2---
2---
3+* `Delete` now is like `Get`, `Post`, `Put`, and `Patch` and returns a response body
3* Support content-type aware combinators and `Accept`/`Content-type` headers
4* Support content-type aware combinators and `Accept`/`Content-type` headers
4* Added a lot of tests
5* Added a lot of tests
5* Support multiple concurrent threads
6* Support multiple concurrent threads
servant-client/src/Servant/Client.hs+33−3
@@ -117,11 +117,41 @@ instance (KnownSymbol capture, ToText a, HasClient sublayout)
117-- side querying function that is created when calling 'client'
117-- side querying function that is created when calling 'client'
118-- will just require an argument that specifies the scheme, host
118-- will just require an argument that specifies the scheme, host
119-- and port to send the request to.
119-- and port to send the request to.
120+instance
121+#if MIN_VERSION_base(4,8,0)
122+ {-# OVERLAPPABLE #-}
123+#endif
120−instance HasClient Delete where
124+ (MimeUnrender ct a) => HasClient (Delete (ct ': cts) a) where
121− type Client Delete = BaseUrl -> EitherT ServantError IO ()
125+ type Client (Delete (ct ': cts) a) = BaseUrl -> EitherT ServantError IO a
126+ clientWithRoute Proxy req host =
127+ snd <$> performRequestCT (Proxy :: Proxy ct) H.methodDelete req [200, 202] host
122
128
129+-- | If you have a 'Delete xs ()' endpoint, the client expects a 204 No Content
130+-- HTTP header.
131+instance
132+#if MIN_VERSION_base(4,8,0)
133+ {-# OVERLAPPING #-}
134+#endif
135+ HasClient (Delete (ct ': cts) ()) where
136+ type Client (Delete (ct ': cts) ()) = BaseUrl -> EitherT ServantError IO ()
123 clientWithRoute Proxy req host =
137 clientWithRoute Proxy req host =
124− void $ performRequest H.methodDelete req (`elem` [200, 202, 204]) host
138+ void $ performRequestNoBody H.methodDelete req [204] host
139+
140+-- | If you have a 'Delete xs (Headers ls x)' endpoint, the client expects the
141+-- corresponding headers.
142+instance
143+#if MIN_VERSION_base(4,8,0)
144+ {-# OVERLAPPING #-}
145+#endif
146+ ( MimeUnrender ct a, BuildHeadersTo ls
147+ ) => HasClient (Delete (ct ': cts) (Headers ls a)) where
148+ type Client (Delete (ct ': cts) (Headers ls a)) = BaseUrl -> EitherT ServantError IO (Headers ls a)
149+ clientWithRoute Proxy req host = do
150+ (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodDelete req [200, 202] host
151+ return $ Headers { getResponse = resp
152+ , getHeadersHList = buildHeadersTo hdrs
153+ }
154+
125
155
126-- | If you have a 'Get' endpoint in your API, the client
156-- | If you have a 'Get' endpoint in your API, the client
127-- side querying function that is created when calling 'client'
157-- side querying function that is created when calling 'client'
servant-client/test/Servant/ClientSpec.hs+12−4
@@ -79,7 +79,8 @@ type TestHeaders = '[Header "X-Example1" Int, Header "X-Example2" String]
79
79
80type Api =
80type Api =
81 "get" :> Get '[JSON] Person
81 "get" :> Get '[JSON] Person
82− :<|> "delete" :> Delete
82+ :<|> "delete" :> Delete '[JSON] ()
83+ :<|> "deleteString" :> Delete '[JSON] String
83 :<|> "capture" :> Capture "name" String :> Get '[JSON,FormUrlEncoded] Person
84 :<|> "capture" :> Capture "name" String :> Get '[JSON,FormUrlEncoded] Person
84 :<|> "body" :> ReqBody '[FormUrlEncoded,JSON] Person :> Post '[JSON] Person
85 :<|> "body" :> ReqBody '[FormUrlEncoded,JSON] Person :> Post '[JSON] Person
85 :<|> "param" :> QueryParam "name" String :> Get '[FormUrlEncoded,JSON] Person
86 :<|> "param" :> QueryParam "name" String :> Get '[FormUrlEncoded,JSON] Person
@@ -104,6 +105,7 @@ server :: Application
104server = serve api (
105server = serve api (
105 return alice
106 return alice
106 :<|> return ()
107 :<|> return ()
108+ :<|> return "ok"
107 :<|> (\ name -> return $ Person name 0)
109 :<|> (\ name -> return $ Person name 0)
108 :<|> return
110 :<|> return
109 :<|> (\ name -> case name of
111 :<|> (\ name -> case name of
@@ -129,6 +131,7 @@ withServer action = withWaiDaemon (return server) action
129
131
130getGet :: BaseUrl -> EitherT ServantError IO Person
132getGet :: BaseUrl -> EitherT ServantError IO Person
131getDelete :: BaseUrl -> EitherT ServantError IO ()
133getDelete :: BaseUrl -> EitherT ServantError IO ()
134+getDeleteString :: BaseUrl -> EitherT ServantError IO String
132getCapture :: String -> BaseUrl -> EitherT ServantError IO Person
135getCapture :: String -> BaseUrl -> EitherT ServantError IO Person
133getBody :: Person -> BaseUrl -> EitherT ServantError IO Person
136getBody :: Person -> BaseUrl -> EitherT ServantError IO Person
134getQueryParam :: Maybe String -> BaseUrl -> EitherT ServantError IO Person
137getQueryParam :: Maybe String -> BaseUrl -> EitherT ServantError IO Person
@@ -147,6 +150,7 @@ getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])]
147getRespHeaders :: BaseUrl -> EitherT ServantError IO (Headers TestHeaders Bool)
150getRespHeaders :: BaseUrl -> EitherT ServantError IO (Headers TestHeaders Bool)
148( getGet
151( getGet
149 :<|> getDelete
152 :<|> getDelete
153+ :<|> getDeleteString
150 :<|> getCapture
154 :<|> getCapture
151 :<|> getBody
155 :<|> getBody
152 :<|> getQueryParam
156 :<|> getQueryParam
@@ -183,8 +187,12 @@ spec = do
183 it "Servant.API.Get" $ withServer $ \ host -> do
187 it "Servant.API.Get" $ withServer $ \ host -> do
184 (Arrow.left show <$> runEitherT (getGet host)) `shouldReturn` Right alice
188 (Arrow.left show <$> runEitherT (getGet host)) `shouldReturn` Right alice
185
189
186− it "Servant.API.Delete" $ withServer $ \ host -> do
190+ context "Servant.API.Delete" $ do
191+ it "return no body" $ withServer $ \ host -> do
187− (Arrow.left show <$> runEitherT (getDelete host)) `shouldReturn` Right ()
192+ (Arrow.left show <$> runEitherT (getDelete host)) `shouldReturn` Right ()
193+
194+ it "return body" $ withServer $ \ host -> do
195+ (Arrow.left show <$> runEitherT (getDeleteString host)) `shouldReturn` Right "ok"
188
196
189 it "Servant.API.Capture" $ withServer $ \ host -> do
197 it "Servant.API.Capture" $ withServer $ \ host -> do
190 (Arrow.left show <$> runEitherT (getCapture "Paula" host)) `shouldReturn` Right (Person "Paula" 0)
198 (Arrow.left show <$> runEitherT (getCapture "Paula" host)) `shouldReturn` Right (Person "Paula" 0)
@@ -268,7 +276,7 @@ spec = do
268 Left FailureResponse{..} <- runEitherT (getResponse host)
276 Left FailureResponse{..} <- runEitherT (getResponse host)
269 responseStatus `shouldBe` (Status 500 "error message")
277 responseStatus `shouldBe` (Status 500 "error message")
270 mapM_ test $
278 mapM_ test $
271− (WrappedApi (Proxy :: Proxy Delete), "Delete") :
279+ (WrappedApi (Proxy :: Proxy (Delete '[JSON] ())), "Delete") :
272 (WrappedApi (Proxy :: Proxy (Get '[JSON] ())), "Get") :
280 (WrappedApi (Proxy :: Proxy (Get '[JSON] ())), "Get") :
273 (WrappedApi (Proxy :: Proxy (Post '[JSON] ())), "Post") :
281 (WrappedApi (Proxy :: Proxy (Post '[JSON] ())), "Post") :
274 (WrappedApi (Proxy :: Proxy (Put '[JSON] ())), "Put") :
282 (WrappedApi (Proxy :: Proxy (Put '[JSON] ())), "Put") :
servant-docs/CHANGELOG.md+1
@@ -1,5 +1,6 @@
10.4
10.4
2---
2---
3+* `Delete` now is like `Get`, `Post`, `Put`, and `Patch` and returns a response body
3* Allow for extra information to be added to the docs
4* Allow for extra information to be added to the docs
4* Support content-type aware combinators of *servant-0.3*
5* Support content-type aware combinators of *servant-0.3*
5* Render endpoints in a canonical order (https://github.com/haskell-servant/servant-docs/pull/15)
6* Render endpoints in a canonical order (https://github.com/haskell-servant/servant-docs/pull/15)
servant-docs/example/greet.hs+5−2
@@ -53,6 +53,9 @@ instance ToParam (MatrixParam "lang" String) where
53 "Get the greeting message selected language. Default is en."
53 "Get the greeting message selected language. Default is en."
54 Normal
54 Normal
55
55
56+instance ToSample () () where
57+ toSample _ = Just ()
58+
56instance ToSample Greet Greet where
59instance ToSample Greet Greet where
57 toSample _ = Just $ Greet "Hello, haskeller!"
60 toSample _ = Just $ Greet "Hello, haskeller!"
58
61
@@ -90,7 +93,7 @@ type TestApi =
90 :<|> "greet" :> ReqBody '[JSON] Greet :> Post '[JSON] (Headers '[Header "X-Example" Int] Greet)
93 :<|> "greet" :> ReqBody '[JSON] Greet :> Post '[JSON] (Headers '[Header "X-Example" Int] Greet)
91
94
92 -- DELETE /greet/:greetid
95 -- DELETE /greet/:greetid
93− :<|> "greet" :> Capture "greetid" Text :> Delete
96+ :<|> "greet" :> Capture "greetid" Text :> Delete '[JSON] ()
94
97
95testApi :: Proxy TestApi
98testApi :: Proxy TestApi
96testApi = Proxy
99testApi = Proxy
@@ -100,7 +103,7 @@ testApi = Proxy
100-- notes.
103-- notes.
101extra :: ExtraInfo TestApi
104extra :: ExtraInfo TestApi
102extra =
105extra =
103− extraInfo (Proxy :: Proxy ("greet" :> Capture "greetid" Text :> Delete)) $
106+ extraInfo (Proxy :: Proxy ("greet" :> Capture "greetid" Text :> Delete '[JSON] ())) $
104 defAction & headers <>~ ["unicorns"]
107 defAction & headers <>~ ["unicorns"]
105 & notes <>~ [ DocNote "Title" ["This is some text"]
108 & notes <>~ [ DocNote "Title" ["This is some text"]
106 , DocNote "Second secton" ["And some more"]
109 , DocNote "Second secton" ["And some more"]
servant-docs/src/Servant/Docs/Internal.hs+27−3
@@ -651,14 +651,38 @@ instance (KnownSymbol sym, ToCapture (Capture sym a), HasDocs sublayout)
651 symP = Proxy :: Proxy sym
651 symP = Proxy :: Proxy sym
652
652
653
653
654+instance
655+#if MIN_VERSION_base(4,8,0)
656+ {-# OVERLAPPABLe #-}
657+#endif
658+ (ToSample a b, IsNonEmpty cts, AllMimeRender cts b, SupportedTypes cts)
654−instance HasDocs Delete where
659+ => HasDocs (Delete cts a) where
655 docsFor Proxy (endpoint, action) =
660 docsFor Proxy (endpoint, action) =
656 single endpoint' action'
661 single endpoint' action'
657
662
658 where endpoint' = endpoint & method .~ DocDELETE
663 where endpoint' = endpoint & method .~ DocDELETE
664+ action' = action & response.respBody .~ sampleByteStrings t p
665+ & response.respTypes .~ supportedTypes t
666+ t = Proxy :: Proxy cts
667+ p = Proxy :: Proxy a
668+
669+instance
670+#if MIN_VERSION_base(4,8,0)
671+ {-# OVERLAPPING #-}
672+#endif
673+ (ToSample a b, IsNonEmpty cts, AllMimeRender cts b, SupportedTypes cts
674+ , AllHeaderSamples ls , GetHeaders (HList ls) )
675+ => HasDocs (Delete cts (Headers ls a)) where
676+ docsFor Proxy (endpoint, action) =
677+ single endpoint' action'
659
678
679+ where hdrs = allHeaderToSample (Proxy :: Proxy ls)
680+ endpoint' = endpoint & method .~ DocDELETE
660− action' = action & response.respBody .~ []
681+ action' = action & response.respBody .~ sampleByteStrings t p
661− & response.respStatus .~ 204
682+ & response.respTypes .~ supportedTypes t
683+ & response.respHeaders .~ hdrs
684+ t = Proxy :: Proxy cts
685+ p = Proxy :: Proxy a
662
686
663instance
687instance
664#if MIN_VERSION_base(4,8,0)
688#if MIN_VERSION_base(4,8,0)
servant-jquery/CHANGELOG.md+1
@@ -1,5 +1,6 @@
10.3
10.3
2---
2---
3+* `Delete` now is like `Get`, `Post`, `Put`, and `Patch` and returns a response body
3* Extend `HeaderArg` to support more advanced HTTP header handling (https://github.com/haskell-servant/servant-jquery/pull/6)
4* Extend `HeaderArg` to support more advanced HTTP header handling (https://github.com/haskell-servant/servant-jquery/pull/6)
4* Support content-type aware combinators (but require that endpoints support JSON)
5* Support content-type aware combinators (but require that endpoints support JSON)
5* Add support for Matrix params (https://github.com/haskell-servant/servant-jquery/pull/11)
6* Add support for Matrix params (https://github.com/haskell-servant/servant-jquery/pull/11)
servant-jquery/src/Servant/JQuery/Internal.hs+2−2
@@ -215,8 +215,8 @@ instance (KnownSymbol sym, HasJQ sublayout)
215
215
216 where str = symbolVal (Proxy :: Proxy sym)
216 where str = symbolVal (Proxy :: Proxy sym)
217
217
218−instance HasJQ Delete where
218+instance Elem JSON list => HasJQ (Delete list a) where
219− type JQ Delete = AjaxReq
219+ type JQ (Delete list a) = AjaxReq
220
220
221 jqueryFor Proxy req =
221 jqueryFor Proxy req =
222 req & funcName %~ ("delete" <>)
222 req & funcName %~ ("delete" <>)
servant-server/CHANGELOG.md+1
@@ -1,5 +1,6 @@
10.3
10.3
2---
2---
3+* `Delete` now is like `Get`, `Post`, `Put`, and `Patch` and returns a response body
3* Add a `RouteMismatch` constructor for arbitrary HTTP response codes (https://github.com/haskell-servant/servant-server/pull/22)
4* Add a `RouteMismatch` constructor for arbitrary HTTP response codes (https://github.com/haskell-servant/servant-server/pull/22)
4* Add support for the `Patch` combinator
5* Add support for the `Patch` combinator
5* Support for `Accept`/`Content-type` headers and for the content-type aware combinators in *servant-0.3*
6* Support for `Accept`/`Content-type` headers and for the content-type aware combinators in *servant-0.3*
servant-server/example/greet.hs+1−1
@@ -34,7 +34,7 @@ type TestApi =
34 :<|> "greet" :> ReqBody '[JSON] Greet :> Post '[JSON] Greet
34 :<|> "greet" :> ReqBody '[JSON] Greet :> Post '[JSON] Greet
35
35
36 -- DELETE /greet/:greetid
36 -- DELETE /greet/:greetid
37− :<|> "greet" :> Capture "greetid" Text :> Delete
37+ :<|> "greet" :> Capture "greetid" Text :> Delete '[JSON] ()
38
38
39testApi :: Proxy TestApi
39testApi :: Proxy TestApi
40testApi = Proxy
40testApi = Proxy
servant-server/src/Servant/Server/Internal.hs+58−4
@@ -258,20 +258,74 @@ instance (KnownSymbol capture, FromText a, HasServer sublayout)
258-- to be returned. You can use 'Control.Monad.Trans.Either.left' to
258-- to be returned. You can use 'Control.Monad.Trans.Either.left' to
259-- painlessly error out if the conditions for a successful deletion
259-- painlessly error out if the conditions for a successful deletion
260-- are not met.
260-- are not met.
261+instance
262+#if MIN_VERSION_base(4,8,0)
263+ {-# OVERLAPPABLE #-}
264+#endif
265+ ( AllCTRender ctypes a
266+ ) => HasServer (Delete ctypes a) where
267+
268+ type ServerT (Delete ctypes a) m = m a
269+
270+ route Proxy action request respond
271+ | pathIsEmpty request && requestMethod request == methodDelete = do
272+ e <- runEitherT action
273+ respond $ case e of
274+ Right output -> do
275+ let accH = fromMaybe ct_wildcard $ lookup hAccept $ requestHeaders request
276+ case handleAcceptH (Proxy :: Proxy ctypes) (AcceptHeader accH) output of
277+ Nothing -> failWith UnsupportedMediaType
278+ Just (contentT, body) -> succeedWith $
279+ responseLBS status200 [ ("Content-Type" , cs contentT)] body
280+ Left err -> succeedWith $ responseServantErr err
281+ | pathIsEmpty request && requestMethod request /= methodDelete =
282+ respond $ failWith WrongMethod
283+ | otherwise = respond $ failWith NotFound
284+
285+instance
286+#if MIN_VERSION_base(4,8,0)
287+ {-# OVERLAPPING #-}
288+#endif
261−instance HasServer Delete where
289+ HasServer (Delete ctypes ()) where
262
290
263− type ServerT Delete m = m ()
291+ type ServerT (Delete ctypes ()) m = m ()
264
292
265 route Proxy action request respond
293 route Proxy action request respond
266 | pathIsEmpty request && requestMethod request == methodDelete = do
294 | pathIsEmpty request && requestMethod request == methodDelete = do
267 e <- runEitherT action
295 e <- runEitherT action
268− respond $ succeedWith $ case e of
296+ respond . succeedWith $ case e of
269− Right () -> responseLBS status204 [] ""
297+ Right () -> responseLBS noContent204 [] ""
270 Left err -> responseServantErr err
298 Left err -> responseServantErr err
271 | pathIsEmpty request && requestMethod request /= methodDelete =
299 | pathIsEmpty request && requestMethod request /= methodDelete =
272 respond $ failWith WrongMethod
300 respond $ failWith WrongMethod
273 | otherwise = respond $ failWith NotFound
301 | otherwise = respond $ failWith NotFound
274
302
303+-- Add response headers
304+instance
305+#if MIN_VERSION_base(4,8,0)
306+ {-# OVERLAPPING #-}
307+#endif
308+ ( GetHeaders (Headers h v), AllCTRender ctypes v
309+ ) => HasServer (Delete ctypes (Headers h v)) where
310+
311+ type ServerT (Delete ctypes (Headers h v)) m = m (Headers h v)
312+
313+ route Proxy action request respond
314+ | pathIsEmpty request && requestMethod request == methodDelete = do
315+ e <- runEitherT action
316+ respond $ case e of
317+ Right output -> do
318+ let accH = fromMaybe ct_wildcard $ lookup hAccept $ requestHeaders request
319+ headers = getHeaders output
320+ case handleAcceptH (Proxy :: Proxy ctypes) (AcceptHeader accH) (getResponse output) of
321+ Nothing -> failWith UnsupportedMediaType
322+ Just (contentT, body) -> succeedWith $
323+ responseLBS status200 ( ("Content-Type" , cs contentT) : headers) body
324+ Left err -> succeedWith $ responseServantErr err
325+ | pathIsEmpty request && requestMethod request /= methodDelete =
326+ respond $ failWith WrongMethod
327+ | otherwise = respond $ failWith NotFound
328+
275-- | When implementing the handler for a 'Get' endpoint,
329-- | When implementing the handler for a 'Get' endpoint,
276-- just like for 'Servant.API.Delete.Delete', 'Servant.API.Post.Post'
330-- just like for 'Servant.API.Delete.Delete', 'Servant.API.Post.Post'
277-- and 'Servant.API.Put.Put', the handler code runs in the
331-- and 'Servant.API.Put.Put', the handler code runs in the
servant-server/test/Servant/ServerSpec.hs+2−2
@@ -443,7 +443,7 @@ patchSpec = do
443 , "application/nonsense")]
443 , "application/nonsense")]
444 patch'' "/" "anything at all" `shouldRespondWith` 415
444 patch'' "/" "anything at all" `shouldRespondWith` 415
445
445
446−type HeaderApi a = Header "MyHeader" a :> Delete
446+type HeaderApi a = Header "MyHeader" a :> Delete '[JSON] ()
447headerApi :: Proxy (HeaderApi a)
447headerApi :: Proxy (HeaderApi a)
448headerApi = Proxy
448headerApi = Proxy
449
449
@@ -503,7 +503,7 @@ type AlternativeApi =
503 :<|> "foo" :> Get '[PlainText] T.Text
503 :<|> "foo" :> Get '[PlainText] T.Text
504 :<|> "bar" :> Post '[JSON] Animal
504 :<|> "bar" :> Post '[JSON] Animal
505 :<|> "bar" :> Put '[JSON] Animal
505 :<|> "bar" :> Put '[JSON] Animal
506− :<|> "bar" :> Delete
506+ :<|> "bar" :> Delete '[JSON] ()
507unionApi :: Proxy AlternativeApi
507unionApi :: Proxy AlternativeApi
508unionApi = Proxy
508unionApi = Proxy
509
509
servant/CHANGELOG.md+1
@@ -1,5 +1,6 @@
10.3
10.3
2---
2---
3+* `Delete` now is like `Get`, `Post`, `Put`, and `Patch` and returns a response body
3* Multiple content-type/accept support for all the relevant combinators
4* Multiple content-type/accept support for all the relevant combinators
4* Provide *JSON*, *PlainText*, *OctetStream* and *FormUrlEncoded* content types out of the box
5* Provide *JSON*, *PlainText*, *OctetStream* and *FormUrlEncoded* content types out of the box
5* Type-safe link generation to API endpoints
6* Type-safe link generation to API endpoints
servant/src/Servant/API/Delete.hs+3−1
@@ -1,4 +1,6 @@
1+{-# LANGUAGE DataKinds #-}
1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE KindSignatures #-}
2{-# OPTIONS_HADDOCK not-home #-}
4{-# OPTIONS_HADDOCK not-home #-}
3module Servant.API.Delete (Delete) where
5module Servant.API.Delete (Delete) where
4
6
@@ -10,7 +12,7 @@ import Data.Typeable ( Typeable )
10--
12--
11-- >>> -- DELETE /books/:isbn
13-- >>> -- DELETE /books/:isbn
12-- >>> type MyApi = "books" :> Capture "isbn" Text :> Delete
14-- >>> type MyApi = "books" :> Capture "isbn" Text :> Delete
13−data Delete
15+data Delete (contentTypes :: [*]) a
14 deriving Typeable
16 deriving Typeable
15
17
16
18
servant/src/Servant/Utils/Links.hs+10−9
@@ -22,7 +22,7 @@
22-- >>>
22-- >>>
23-- >>>
23-- >>>
24-- >>> type Hello = "hello" :> Get '[JSON] Int
24-- >>> type Hello = "hello" :> Get '[JSON] Int
25−-- >>> type Bye = "bye" :> QueryParam "name" String :> Delete
25+-- >>> type Bye = "bye" :> QueryParam "name" String :> Delete '[JSON] ()
26-- >>> type API = Hello :<|> Bye
26-- >>> type API = Hello :<|> Bye
27-- >>> let api = Proxy :: Proxy API
27-- >>> let api = Proxy :: Proxy API
28--
28--
@@ -48,11 +48,11 @@
48-- If the API has an endpoint with parameters then we can generate links with
48-- If the API has an endpoint with parameters then we can generate links with
49-- or without those:
49-- or without those:
50--
50--
51−-- >>> let with = Proxy :: Proxy ("bye" :> QueryParam "name" String :> Delete)
51+-- >>> let with = Proxy :: Proxy ("bye" :> QueryParam "name" String :> Delete '[JSON] ())
52-- >>> print $ safeLink api with "Hubert"
52-- >>> print $ safeLink api with "Hubert"
53-- bye?name=Hubert
53-- bye?name=Hubert
54--
54--
55−-- >>> let without = Proxy :: Proxy ("bye" :> Delete)
55+-- >>> let without = Proxy :: Proxy ("bye" :> Delete '[JSON] ())
56-- >>> print $ safeLink api without
56-- >>> print $ safeLink api without
57-- bye
57-- bye
58--
58--
@@ -70,15 +70,15 @@
70-- Attempting to construct a link to an endpoint that does not exist in api
70-- Attempting to construct a link to an endpoint that does not exist in api
71-- will result in a type error like this:
71-- will result in a type error like this:
72--
72--
73−-- >>> let bad_link = Proxy :: Proxy ("hello" :> Delete)
73+-- >>> let bad_link = Proxy :: Proxy ("hello" :> Delete '[JSON] ())
74-- >>> safeLink api bad_link
74-- >>> safeLink api bad_link
75-- <BLANKLINE>
75-- <BLANKLINE>
76-- <interactive>:64:1:
76-- <interactive>:64:1:
77-- Could not deduce (Or
77-- Could not deduce (Or
78−-- (IsElem' Delete (Get '[JSON] Int))
78+-- (IsElem' (Delete '[JSON] ()) (Get '[JSON] Int))
79-- (IsElem'
79-- (IsElem'
80−-- ("hello" :> Delete)
80+-- ("hello" :> Delete '[JSON] ())
81−-- ("bye" :> (QueryParam "name" String :> Delete))))
81+-- ("bye" :> (QueryParam "name" String :> Delete '[JSON] ()))))
82-- arising from a use of ‘safeLink’
82-- arising from a use of ‘safeLink’
83-- In the expression: safeLink api bad_link
83-- In the expression: safeLink api bad_link
84-- In an equation for ‘it’: it = safeLink api bad_link
84-- In an equation for ‘it’: it = safeLink api bad_link
@@ -176,6 +176,7 @@ type family IsElem endpoint api :: Constraint where
176 IsElem (Get ct typ) (Get ct' typ) = IsSubList ct ct'
176 IsElem (Get ct typ) (Get ct' typ) = IsSubList ct ct'
177 IsElem (Post ct typ) (Post ct' typ) = IsSubList ct ct'
177 IsElem (Post ct typ) (Post ct' typ) = IsSubList ct ct'
178 IsElem (Put ct typ) (Put ct' typ) = IsSubList ct ct'
178 IsElem (Put ct typ) (Put ct' typ) = IsSubList ct ct'
179+ IsElem (Delete ct typ) (Delete ct' typ) = IsSubList ct ct'
179 IsElem e e = ()
180 IsElem e e = ()
180 IsElem e a = IsElem' e a
181 IsElem e a = IsElem' e a
181
182
@@ -349,8 +350,8 @@ instance HasLink (Put y r) where
349 type MkLink (Put y r) = URI
350 type MkLink (Put y r) = URI
350 toLink _ = linkURI
351 toLink _ = linkURI
351
352
352−instance HasLink Delete where
353+instance HasLink (Delete y r) where
353− type MkLink Delete = URI
354+ type MkLink (Delete y r) = URI
354 toLink _ = linkURI
355 toLink _ = linkURI
355
356
356instance HasLink Raw where
357instance HasLink Raw where
servant/test/Servant/Utils/LinksSpec.hs+9−9
@@ -12,20 +12,20 @@ import Servant.API
12
12
13type TestApi =
13type TestApi =
14 -- Capture and query/matrix params
14 -- Capture and query/matrix params
15− "hello" :> Capture "name" String :> QueryParam "capital" Bool :> Delete
15+ "hello" :> Capture "name" String :> QueryParam "capital" Bool :> Delete '[JSON] ()
16
16
17 :<|> "parent" :> MatrixParams "name" String :> "child"
17 :<|> "parent" :> MatrixParams "name" String :> "child"
18 :> MatrixParam "gender" String :> Get '[JSON] String
18 :> MatrixParam "gender" String :> Get '[JSON] String
19
19
20 -- Flags
20 -- Flags
21− :<|> "ducks" :> MatrixFlag "yellow" :> MatrixFlag "loud" :> Delete
21+ :<|> "ducks" :> MatrixFlag "yellow" :> MatrixFlag "loud" :> Delete '[JSON] ()
22− :<|> "balls" :> QueryFlag "bouncy" :> QueryFlag "fast" :> Delete
22+ :<|> "balls" :> QueryFlag "bouncy" :> QueryFlag "fast" :> Delete '[JSON] ()
23
23
24 -- All of the verbs
24 -- All of the verbs
25 :<|> "get" :> Get '[JSON] ()
25 :<|> "get" :> Get '[JSON] ()
26 :<|> "put" :> Put '[JSON] ()
26 :<|> "put" :> Put '[JSON] ()
27 :<|> "post" :> ReqBody '[JSON] 'True :> Post '[JSON] ()
27 :<|> "post" :> ReqBody '[JSON] 'True :> Post '[JSON] ()
28− :<|> "delete" :> Header "ponies" :> Delete
28+ :<|> "delete" :> Header "ponies" :> Delete '[JSON] ()
29 :<|> "raw" :> Raw
29 :<|> "raw" :> Raw
30
30
31type TestLink = "hello" :> "hi" :> Get '[JSON] Bool
31type TestLink = "hello" :> "hi" :> Get '[JSON] Bool
@@ -55,12 +55,12 @@ shouldBeURI link expected =
55spec :: Spec
55spec :: Spec
56spec = describe "Servant.Utils.Links" $ do
56spec = describe "Servant.Utils.Links" $ do
57 it "Generates correct links for capture query and matrix params" $ do
57 it "Generates correct links for capture query and matrix params" $ do
58− let l1 = Proxy :: Proxy ("hello" :> Capture "name" String :> Delete)
58+ let l1 = Proxy :: Proxy ("hello" :> Capture "name" String :> Delete '[JSON] ())
59 apiLink l1 "hi" `shouldBeURI` "hello/hi"
59 apiLink l1 "hi" `shouldBeURI` "hello/hi"
60
60
61 let l2 = Proxy :: Proxy ("hello" :> Capture "name" String
61 let l2 = Proxy :: Proxy ("hello" :> Capture "name" String
62 :> QueryParam "capital" Bool
62 :> QueryParam "capital" Bool
63− :> Delete)
63+ :> Delete '[JSON] ())
64 apiLink l2 "bye" True `shouldBeURI` "hello/bye?capital=true"
64 apiLink l2 "bye" True `shouldBeURI` "hello/bye?capital=true"
65
65
66 let l3 = Proxy :: Proxy ("parent" :> MatrixParams "name" String
66 let l3 = Proxy :: Proxy ("parent" :> MatrixParams "name" String
@@ -73,12 +73,12 @@ spec = describe "Servant.Utils.Links" $ do
73
73
74 it "Generates correct links for query and matrix flags" $ do
74 it "Generates correct links for query and matrix flags" $ do
75 let l1 = Proxy :: Proxy ("balls" :> QueryFlag "bouncy"
75 let l1 = Proxy :: Proxy ("balls" :> QueryFlag "bouncy"
76− :> QueryFlag "fast" :> Delete)
76+ :> QueryFlag "fast" :> Delete '[JSON] ())
77 apiLink l1 True True `shouldBeURI` "balls?bouncy&fast"
77 apiLink l1 True True `shouldBeURI` "balls?bouncy&fast"
78 apiLink l1 False True `shouldBeURI` "balls?fast"
78 apiLink l1 False True `shouldBeURI` "balls?fast"
79
79
80 let l2 = Proxy :: Proxy ("ducks" :> MatrixFlag "yellow"
80 let l2 = Proxy :: Proxy ("ducks" :> MatrixFlag "yellow"
81− :> MatrixFlag "loud" :> Delete)
81+ :> MatrixFlag "loud" :> Delete '[JSON] ())
82 apiLink l2 True True `shouldBeURI` "ducks;yellow;loud"
82 apiLink l2 True True `shouldBeURI` "ducks;yellow;loud"
83 apiLink l2 False True `shouldBeURI` "ducks;loud"
83 apiLink l2 False True `shouldBeURI` "ducks;loud"
84
84
@@ -86,5 +86,5 @@ spec = describe "Servant.Utils.Links" $ do
86 apiLink (Proxy :: Proxy ("get" :> Get '[JSON] ())) `shouldBeURI` "get"
86 apiLink (Proxy :: Proxy ("get" :> Get '[JSON] ())) `shouldBeURI` "get"
87 apiLink (Proxy :: Proxy ("put" :> Put '[JSON] ())) `shouldBeURI` "put"
87 apiLink (Proxy :: Proxy ("put" :> Put '[JSON] ())) `shouldBeURI` "put"
88 apiLink (Proxy :: Proxy ("post" :> Post '[JSON] ())) `shouldBeURI` "post"
88 apiLink (Proxy :: Proxy ("post" :> Post '[JSON] ())) `shouldBeURI` "post"
89− apiLink (Proxy :: Proxy ("delete" :> Delete)) `shouldBeURI` "delete"
89+ apiLink (Proxy :: Proxy ("delete" :> Delete '[JSON] ())) `shouldBeURI` "delete"
90 apiLink (Proxy :: Proxy ("raw" :> Raw)) `shouldBeURI` "raw"
90 apiLink (Proxy :: Proxy ("raw" :> Raw)) `shouldBeURI` "raw"