diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 312fa7caf..c4a68ca2b 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -3,6 +3,8 @@ name: CI on: pull_request: push: + branches: + - master jobs: cabal: @@ -20,6 +22,7 @@ jobs: - "9.2.8" - "9.4.5" - "9.6.2" + fail-fast: false steps: - uses: actions/checkout@v2 diff --git a/cabal.project b/cabal.project index 5663f806e..2239b77c9 100644 --- a/cabal.project +++ b/cabal.project @@ -53,10 +53,15 @@ tests: True optimization: False -- reorder-goals: True --- The switch to crypton breaks the build, so let's avoid it for now --- https://github.com/snoyberg/http-client/issues/508 -constraints: crypton < 0, crypton-connection < 0, crypton-x509 < 0, crypton-x509-store < 0, crypton-x509-system < 0, crypton-x509-validation < 0 -constraints: warp < 3.3.26 +-- Older warp versions fail to build with https >= 4.2: +-- Network/Wai/Handler/Warp/HTTP2.hs:48:16: error: +-- Error: • Constructor ‘H2.Config’ does not have the required strict field(s): confMySockAddr, +-- confPeerSockAddr +constraints: http2 <4.2 + +-- wreq-0.5.4.1 doesn't seem to work with ghc-8.6.5 +if (impl(ghc < 8.8)) + constraints: wreq == 0.5.4.0 allow-newer: servant-js:base diff --git a/doc/tutorial/Server.lhs b/doc/tutorial/Server.lhs index 265bd707c..7e4103de7 100644 --- a/doc/tutorial/Server.lhs +++ b/doc/tutorial/Server.lhs @@ -47,7 +47,6 @@ import System.Directory import Text.Blaze import Text.Blaze.Html.Renderer.Utf8 import Servant.Types.SourceT (source) -import qualified Data.Aeson.Parser import qualified Text.Blaze.Html ``` @@ -431,25 +430,11 @@ class Accept ctype => MimeUnrender ctype a where mimeUnrender :: Proxy ctype -> ByteString -> Either String a ``` -We don't have much work to do there either, `Data.Aeson.eitherDecode` is -precisely what we need. However, it only allows arrays and objects as toplevel -JSON values and this has proven to get in our way more than help us so we wrote -our own little function around **aeson** and **attoparsec** that allows any type of -JSON value at the toplevel of a "JSON document". Here's the definition in case -you are curious. - -``` haskell -eitherDecodeLenient :: FromJSON a => ByteString -> Either String a -eitherDecodeLenient input = do - v :: Value <- parseOnly (Data.Aeson.Parser.value <* endOfInput) (cs input) - parseEither parseJSON v -``` - -This function is exactly what we need for our `MimeUnrender` instance. +As with `MimeRender`, we can use a function already available in `aeson`: `Data.Aeson.eitherDecode`. ``` haskell ignore instance FromJSON a => MimeUnrender JSON a where - mimeUnrender _ = eitherDecodeLenient + mimeUnrender _ = eitherDecode ``` And this is all the code that lets you use `JSON` with `ReqBody`, `Get`, diff --git a/servant/CHANGELOG.md b/servant/CHANGELOG.md index 468d7884e..2846e4417 100644 --- a/servant/CHANGELOG.md +++ b/servant/CHANGELOG.md @@ -2,6 +2,11 @@ Package versions follow the [Package Versioning Policy](https://pvp.haskell.org/): in A.B.C, bumps to either A or B represent major versions. +0.20.1 +---- + +- Support aeson-2.2 [#1695](https://github.com/haskell-servant/servant/pull/1695) + 0.20 ---- diff --git a/servant/servant.cabal b/servant/servant.cabal index 331d4bc1c..bc681308e 100644 --- a/servant/servant.cabal +++ b/servant/servant.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: servant -version: 0.20 +version: 0.20.1 synopsis: A family of combinators for defining webservices APIs category: Servant, Web @@ -100,7 +100,7 @@ library -- Here can be exceptions if we really need features from the newer versions. build-depends: base-compat >= 0.10.5 && < 0.14 - , aeson >= 1.4.1.0 && < 2.2 + , aeson >= 1.4.1.0 && < 2.3 , attoparsec >= 0.13.2.2 && < 0.15 , bifunctors >= 5.5.3 && < 5.7 , case-insensitive >= 1.2.0.11 && < 1.3 diff --git a/servant/src/Servant/API/ContentTypes.hs b/servant/src/Servant/API/ContentTypes.hs index 8cfe2e1f5..24dc50779 100644 --- a/servant/src/Servant/API/ContentTypes.hs +++ b/servant/src/Servant/API/ContentTypes.hs @@ -75,13 +75,7 @@ import Control.Monad.Compat import Control.DeepSeq (NFData) import Data.Aeson - (FromJSON (..), ToJSON (..), encode) -import Data.Aeson.Parser - (value) -import Data.Aeson.Types - (parseEither) -import Data.Attoparsec.ByteString.Char8 - (endOfInput, parseOnly, skipSpace, ()) + (FromJSON (..), ToJSON (..), encode, eitherDecode) import Data.Bifunctor (bimap) import qualified Data.ByteString as BS @@ -371,28 +365,15 @@ instance NFData NoContent -------------------------------------------------------------------------- -- * MimeUnrender Instances --- | Like 'Data.Aeson.eitherDecode' but allows all JSON values instead of just --- objects and arrays. +-- | Deprecated: since aeson version 0.9 `eitherDecode` has lenient behavior. -- --- Will handle trailing whitespace, but not trailing junk. ie. --- --- >>> eitherDecodeLenient "1 " :: Either String Int --- Right 1 --- --- >>> eitherDecodeLenient "1 junk" :: Either String Int --- Left "trailing junk after valid JSON: endOfInput" eitherDecodeLenient :: FromJSON a => ByteString -> Either String a -eitherDecodeLenient input = - parseOnly parser (cs input) >>= parseEither parseJSON - where - parser = skipSpace - *> Data.Aeson.Parser.value - <* skipSpace - <* (endOfInput "trailing junk after valid JSON") +eitherDecodeLenient = eitherDecode +{-# DEPRECATED eitherDecodeLenient "use eitherDecode instead" #-} -- | `eitherDecode` instance FromJSON a => MimeUnrender JSON a where - mimeUnrender _ = eitherDecodeLenient + mimeUnrender _ = eitherDecode -- | @urlDecodeAsForm@ -- Note that the @mimeUnrender p (mimeRender p x) == Right x@ law only diff --git a/servant/test/Servant/API/ContentTypesSpec.hs b/servant/test/Servant/API/ContentTypesSpec.hs index eb6d2a969..8de7a1d87 100644 --- a/servant/test/Servant/API/ContentTypesSpec.hs +++ b/servant/test/Servant/API/ContentTypesSpec.hs @@ -12,7 +12,7 @@ import Prelude () import Prelude.Compat import Data.Aeson - (FromJSON, ToJSON (..), Value, decode, encode, object, (.=)) + (FromJSON, ToJSON (..), Value, decode, encode, object, (.=), eitherDecode) import Data.ByteString.Char8 (ByteString, append, pack) import qualified Data.ByteString.Lazy as BSL @@ -219,15 +219,13 @@ spec = describe "Servant.API.ContentTypes" $ do handleCTypeH (Proxy :: Proxy '[JSONorText]) "image/jpeg" "foobar" `shouldBe` (Nothing :: Maybe (Either String Int)) - -- aeson >= 0.9 decodes top-level strings - describe "eitherDecodeLenient" $ do + describe "eitherDecode is lenient" $ do + -- Since servant-0.20.1 MimeUnrender JSON instance uses eitherDecode, + -- as aeson >= 0.9 supports decoding top-level strings and numbers. it "parses top-level strings" $ do - let toMaybe = either (const Nothing) Just - -- The Left messages differ, so convert to Maybe - property $ \x -> toMaybe (eitherDecodeLenient x) - `shouldBe` (decode x :: Maybe String) - + property $ \x -> mimeUnrender (Proxy :: Proxy JSON) x + `shouldBe` (eitherDecode x :: Either String String) data SomeData = SomeData { record1 :: String, record2 :: Int } deriving (Generic, Eq, Show)