Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow aeson-2.2 #1695

Merged
merged 8 commits into from
Oct 2, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ name: CI
on:
pull_request:
push:
branches:
- master
Comment on lines +6 to +7

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems unrelated to this PR.


jobs:
cabal:
Expand All @@ -20,6 +22,7 @@ jobs:
- "9.2.8"
- "9.4.5"
- "9.6.2"
fail-fast: false

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seems unrelated to this PR.


steps:
- uses: actions/checkout@v2
Expand Down
13 changes: 9 additions & 4 deletions cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
19 changes: 2 additions & 17 deletions doc/tutorial/Server.lhs
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down Expand Up @@ -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`,
Expand Down
5 changes: 5 additions & 0 deletions servant/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
----

Expand Down
4 changes: 2 additions & 2 deletions servant/servant.cabal
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
29 changes: 5 additions & 24 deletions servant/src/Servant/API/ContentTypes.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -371,28 +365,15 @@ instance NFData NoContent
--------------------------------------------------------------------------
-- * MimeUnrender Instances

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
-- | Deprecated: since aeson version 0.9 `eitherDecode` has lenient behavior.
--
eitherDecodeLenient :: FromJSON a => ByteString -> Either String a
eitherDecodeLenient = eitherDecode
{-# DEPRECATED eitherDecodeLenient "use eitherDecode instead" #-}

-- | 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
Expand Down
14 changes: 6 additions & 8 deletions servant/test/Servant/API/ContentTypesSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
maksbotan marked this conversation as resolved.
Show resolved Hide resolved
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)
Expand Down
Loading