Skip to content

Commit 339eec6

Browse files
authored
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
1 parent 8e2a654 commit 339eec6

File tree

3 files changed

+35
-16
lines changed

3 files changed

+35
-16
lines changed

.travis.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ maintainer: [email protected]
1010
category: Servant
1111
build-type: Simple
1212
cabal-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

1515
executable cookbook-uverb
1616
main-is: UVerb.lhs

servant/src/Servant/API/UVerb.hs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -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
)
3534
where
3635

37-
import Data.Aeson (FromJSON, ToJSON)
3836
import Data.Proxy (Proxy (Proxy))
39-
import qualified GHC.Generics as GHC
4037
import GHC.TypeLits (Nat)
4138
import 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))
4340
import Servant.API.Status (KnownStatus, statusVal)
4441
import Servant.API.UVerb.Union
4542

@@ -49,9 +46,6 @@ class KnownStatus (StatusOf a) => HasStatus (a :: *) where
4946
statusOf :: forall a proxy. HasStatus a => proxy a -> Status
5047
statusOf = 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.
7369
newtype 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

8072
instance 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

0 commit comments

Comments
 (0)