on master help sign in
Fix overlapping instance for WithStatus (#1361)

We do not need the `ToJSON` instance for `WithStatus` it would cause an overlap between: ``` ToJSON a => MimeRender JSON a ``` and ``` forall cty a. MimeRendercty a => MimeRender cty (WithStatus a) ``` and Servant just needs the `MimeRender` typeclass for it to work * Add some more docs to the UVerb module * cookbook/uverb: Change GHC versions CI was complaining some version did not exist. Trying to bump Also added 8.10.1 * doc/cookbook/uverb: Remove 8.4.4 from tested versions CI was running into a cabal bug for some reason

commit339eec6a90327870fd14085f7adb782eead9c051 authorArian van Putten <arian.vanputten@gmail.com> dateWed 18 Nov 2020, 17:33 (6 years ago) parent8e2a654
3 files changed+35−16
.travis.yml+11−2 doc/cookbook/uverb/uverb.cabal+1−1 servant/src/Servant/API/UVerb.hs+23−13
.travis.yml+11−2
@@ -148,6 +148,7 @@ install:
148148 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/generic" >> cabal.project ; fi
149149 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/pagination" >> cabal.project ; fi
150150 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/testing" >> cabal.project ; fi
151+ if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/uverb" >> cabal.project ; fi
151152 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/structuring-apis" >> cabal.project ; fi
152153 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/using-custom-monad" >> cabal.project ; fi
153154 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: doc/cookbook/using-free-client" >> cabal.project ; fi
@@ -191,6 +192,8 @@ install:
191192 - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
192193 - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-testing' >> cabal.project ; fi
193194 - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
195+ - if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-uverb' >> cabal.project ; fi
196+ - "if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
194197 - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi
195198 - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
196199 - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi
@@ -210,7 +213,7 @@ install:
210213 echo "allow-newer: servant-multipart:servant-client-core" >> cabal.project
211214 echo "allow-newer: servant-js:servant" >> cabal.project
212215 echo "optimization: False" >> cabal.project
213− - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|cookbook-testing|cookbook-using-custom-monad|cookbook-using-free-client|servant|servant-client|servant-client-core|servant-conduit|servant-docs|servant-foreign|servant-http-streams|servant-machines|servant-pipes|servant-server|tutorial)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
216+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|cookbook-testing|cookbook-using-custom-monad|cookbook-using-free-client|cookbook-uverb|servant|servant-client|servant-client-core|servant-conduit|servant-docs|servant-foreign|servant-http-streams|servant-machines|servant-pipes|servant-server|tutorial)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
214217 - cat cabal.project || true
215218 - cat cabal.project.local || true
216219 - if [ -f "servant/configure.ac" ]; then (cd "servant" && autoreconf -i); fi
@@ -233,6 +236,7 @@ install:
233236 - if [ -f "doc/cookbook/generic/configure.ac" ]; then (cd "doc/cookbook/generic" && autoreconf -i); fi
234237 - if [ -f "doc/cookbook/pagination/configure.ac" ]; then (cd "doc/cookbook/pagination" && autoreconf -i); fi
235238 - if [ -f "doc/cookbook/testing/configure.ac" ]; then (cd "doc/cookbook/testing" && autoreconf -i); fi
239+ - if [ -f "doc/cookbook/uverb/configure.ac" ]; then (cd "doc/cookbook/uverb" && autoreconf -i); fi
236240 - if [ -f "doc/cookbook/structuring-apis/configure.ac" ]; then (cd "doc/cookbook/structuring-apis" && autoreconf -i); fi
237241 - if [ -f "doc/cookbook/using-custom-monad/configure.ac" ]; then (cd "doc/cookbook/using-custom-monad" && autoreconf -i); fi
238242 - if [ -f "doc/cookbook/using-free-client/configure.ac" ]; then (cd "doc/cookbook/using-free-client" && autoreconf -i); fi
@@ -271,6 +275,7 @@ script:
271275 - PKGDIR_cookbook_generic="$(find . -maxdepth 1 -type d -regex '.*/cookbook-generic-[0-9.]*')"
272276 - PKGDIR_cookbook_pagination="$(find . -maxdepth 1 -type d -regex '.*/cookbook-pagination-[0-9.]*')"
273277 - PKGDIR_cookbook_testing="$(find . -maxdepth 1 -type d -regex '.*/cookbook-testing-[0-9.]*')"
278+ - PKGDIR_cookbook_uverb="$(find . -maxdepth 1 -type d -regex '.*/cookbook-uverb-[0-9.]*')"
274279 - PKGDIR_cookbook_structuring_apis="$(find . -maxdepth 1 -type d -regex '.*/cookbook-structuring-apis-[0-9.]*')"
275280 - PKGDIR_cookbook_using_custom_monad="$(find . -maxdepth 1 -type d -regex '.*/cookbook-using-custom-monad-[0-9.]*')"
276281 - PKGDIR_cookbook_using_free_client="$(find . -maxdepth 1 -type d -regex '.*/cookbook-using-free-client-[0-9.]*')"
@@ -298,6 +303,7 @@ script:
298303 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_generic}" >> cabal.project ; fi
299304 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_pagination}" >> cabal.project ; fi
300305 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_testing}" >> cabal.project ; fi
306+ if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_uverb}" >> cabal.project ; fi
301307 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_structuring_apis}" >> cabal.project ; fi
302308 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_using_custom_monad}" >> cabal.project ; fi
303309 if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo "packages: ${PKGDIR_cookbook_using_free_client}" >> cabal.project ; fi
@@ -341,6 +347,8 @@ script:
341347 - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
342348 - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-testing' >> cabal.project ; fi
343349 - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
350+ - if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-uverb' >> cabal.project ; fi
351+ - "if ! $GHCJS && { [ $HCNUMVER -ge 80600 ] && [ $HCNUMVER -lt 80800 ] || [ $HCNUMVER -ge 80804 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
344352 - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-structuring-apis' >> cabal.project ; fi
345353 - "if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
346354 - if ! $GHCJS && { [ $HCNUMVER -ge 80400 ] && [ $HCNUMVER -lt 80804 ] || [ $HCNUMVER -ge 81000 ] && [ $HCNUMVER -lt 81002 ]; } ; then echo 'package cookbook-using-custom-monad' >> cabal.project ; fi
@@ -360,7 +368,7 @@ script:
360368 echo "allow-newer: servant-multipart:servant-client-core" >> cabal.project
361369 echo "allow-newer: servant-js:servant" >> cabal.project
362370 echo "optimization: False" >> cabal.project
363− - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|cookbook-testing|cookbook-using-custom-monad|cookbook-using-free-client|servant|servant-client|servant-client-core|servant-conduit|servant-docs|servant-foreign|servant-http-streams|servant-machines|servant-pipes|servant-server|tutorial)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
371+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(cookbook-basic-auth|cookbook-basic-streaming|cookbook-curl-mock|cookbook-custom-errors|cookbook-db-postgres-pool|cookbook-file-upload|cookbook-generic|cookbook-pagination|cookbook-structuring-apis|cookbook-testing|cookbook-using-custom-monad|cookbook-using-free-client|cookbook-uverb|servant|servant-client|servant-client-core|servant-conduit|servant-docs|servant-foreign|servant-http-streams|servant-machines|servant-pipes|servant-server|tutorial)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
364372 - cat cabal.project || true
365373 - cat cabal.project.local || true
366374 - |
@@ -386,6 +394,7 @@ script:
386394 cookbook-generic) echo ${PKGDIR_cookbook_generic} ;;
387395 cookbook-pagination) echo ${PKGDIR_cookbook_pagination} ;;
388396 cookbook-testing) echo ${PKGDIR_cookbook_testing} ;;
397+ cookbook-uverb) echo ${PKGDIR_cookbook_uverb} ;;
389398 cookbook-structuring-apis) echo ${PKGDIR_cookbook_structuring_apis} ;;
390399 cookbook-using-custom-monad) echo ${PKGDIR_cookbook_using_custom_monad} ;;
391400 cookbook-using-free-client) echo ${PKGDIR_cookbook_using_free_client} ;;
doc/cookbook/uverb/uverb.cabal+1−1
@@ -10,7 +10,7 @@ maintainer: haskell-servant-maintainers@googlegroups.com
1010category: Servant
1111build-type: Simple
1212cabal-version: >=1.10
13−tested-with: GHC==8.4.4, GHC==8.6.5, GHC==8.8.2
13+tested-with: GHC==8.6.5, GHC==8.8.4, GHC==8.10.1
1414
1515executable cookbook-uverb
1616 main-is: UVerb.lhs
servant/src/Servant/API/UVerb.hs+23−13
@@ -4,14 +4,13 @@
44{-# LANGUAGE DeriveGeneric #-}
55{-# LANGUAGE FlexibleContexts #-}
66{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE KindSignatures #-}
87{-# LANGUAGE MultiParamTypeClasses #-}
98{-# LANGUAGE RankNTypes #-}
109{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeApplications #-}
1210{-# LANGUAGE TypeFamilies #-}
1311{-# LANGUAGE TypeOperators #-}
1412{-# LANGUAGE UndecidableInstances #-}
13+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1514
1615-- | An alternative to 'Verb' for end-points that respond with a resource value of any of an
1716-- open union of types, and specific status codes for each type in this union. (`UVerb` is
@@ -34,12 +33,10 @@ module Servant.API.UVerb
3433 )
3534where
3635
37−import Data.Aeson (FromJSON, ToJSON)
3836import Data.Proxy (Proxy (Proxy))
39−import qualified GHC.Generics as GHC
4037import GHC.TypeLits (Nat)
4138import Network.HTTP.Types (Status, StdMethod)
42−import Servant.API.ContentTypes (MimeRender (mimeRender), MimeUnrender (mimeUnrender), NoContent)
39+import Servant.API.ContentTypes (NoContent, MimeRender(mimeRender), MimeUnrender(mimeUnrender))
4340import Servant.API.Status (KnownStatus, statusVal)
4441import Servant.API.UVerb.Union
4542
@@ -49,9 +46,6 @@ class KnownStatus (StatusOf a) => HasStatus (a :: *) where
4946statusOf :: forall a proxy. HasStatus a => proxy a -> Status
5047statusOf = const (statusVal (Proxy :: Proxy (StatusOf a)))
5148
52−instance KnownStatus n => HasStatus (WithStatus n a) where
53− type StatusOf (WithStatus n a) = n
54−
5549-- | If an API can respond with 'NoContent' we assume that this will happen
5650-- with the status code 204 No Content. If this needs to be overridden,
5751-- 'WithStatus' can be used.
@@ -70,12 +64,10 @@ instance (HasStatus a, HasStatuses as) => HasStatuses (a ': as) where
7064 type Statuses (a ': as) = StatusOf a ': Statuses as
7165 statuses _ = statusOf (Proxy :: Proxy a) : statuses (Proxy :: Proxy as)
7266
67+-- | A simple newtype wrapper that pairs a type with its status code. It
68+-- implements all the content types that Servant ships with by default.
7369newtype WithStatus (k :: Nat) a = WithStatus a
74− deriving (Eq, Show, GHC.Generic)
75−
76−instance (GHC.Generic (WithStatus n a), ToJSON a) => ToJSON (WithStatus n a)
77−
78−instance (GHC.Generic (WithStatus n a), FromJSON a) => FromJSON (WithStatus n a)
70+ deriving (Eq, Show)
7971
8072instance MimeRender ctype a => MimeRender ctype (WithStatus _status a) where
8173 mimeRender contentTypeProxy (WithStatus a) = mimeRender contentTypeProxy a
@@ -84,6 +76,24 @@ instance MimeUnrender ctype a => MimeUnrender ctype (WithStatus _status a) where
8476 mimeUnrender contentTypeProxy input =
8577 WithStatus <$> mimeUnrender contentTypeProxy input
8678
79+-- | an instance of this typeclass assigns a HTTP status code to a return type
80+--
81+-- Example:
82+--
83+-- @
84+-- data NotFoundError = NotFoundError String
85+--
86+-- instance HasStatus NotFoundError where
87+-- type StatusOf NotFoundError = 404
88+-- @
89+--
90+-- You can also use the convience newtype wrapper 'WithStatus' if you want to
91+-- avoid writing a 'HasStatus' instance manually. It also has the benefit of
92+-- showing the status code in the type; which might aid in readability.
93+instance KnownStatus n => HasStatus (WithStatus n a) where
94+ type StatusOf (WithStatus n a) = n
95+
96+
8797-- | A variant of 'Verb' that can have any of a number of response values and status codes.
8898--
8999-- FUTUREWORK: it would be nice to make 'Verb' a special case of 'UVerb', and only write