Hello community, here is the log from the commit of package ghc-http-api-data for openSUSE:Factory checked in at 2018-05-30 12:09:28 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/ghc-http-api-data (Old) and /work/SRC/openSUSE:Factory/.ghc-http-api-data.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "ghc-http-api-data" Wed May 30 12:09:28 2018 rev:10 rq:607819 version:0.3.8.1 Changes: -------- --- /work/SRC/openSUSE:Factory/ghc-http-api-data/ghc-http-api-data.changes 2017-09-15 21:50:29.396551735 +0200 +++ /work/SRC/openSUSE:Factory/.ghc-http-api-data.new/ghc-http-api-data.changes 2018-05-30 12:26:02.780857700 +0200 @@ -1,0 +2,12 @@ +Mon May 14 17:02:11 UTC 2018 - [email protected] + +- Update http-api-data to version 0.3.8.1. + * GHC-8.4.1 support + * Stable URL-encoding for `Form`s (see [#67](https://github.com/fizruk/http-api-data/pull/67)): + * Introduce `urlEncodeParams` and `urlDecodeParams`; + * Introduce `urlEncodeAsFormStable` and use stable encoding for doctests; + * Add `toEntriesByKeyStable` and `toListStable`; + * Add `Semigroup` instance for `Form` (see [#69](https://github.com/fizruk/http-api-data/pull/69)); + * Relax upper bound on Cabal (see [#73](https://github.com/fizruk/http-api-data/pull/73)). + +------------------------------------------------------------------- Old: ---- http-api-data-0.3.7.1.tar.gz New: ---- http-api-data-0.3.8.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ ghc-http-api-data.spec ++++++ --- /var/tmp/diff_new_pack.rjfCTh/_old 2018-05-30 12:26:03.528832637 +0200 +++ /var/tmp/diff_new_pack.rjfCTh/_new 2018-05-30 12:26:03.532832502 +0200 @@ -1,7 +1,7 @@ # # spec file for package ghc-http-api-data # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -19,7 +19,7 @@ %global pkg_name http-api-data %bcond_with tests Name: ghc-%{pkg_name} -Version: 0.3.7.1 +Version: 0.3.8.1 Release: 0 Summary: Converting to/from HTTP API data like URL pieces, headers and query parameters License: BSD-2-Clause @@ -49,11 +49,13 @@ BuildRequires: ghc-filepath-devel BuildRequires: ghc-hspec-devel BuildRequires: ghc-quickcheck-instances-devel -BuildRequires: ghc-uuid-devel %endif %description -Converting to/from HTTP API data like URL pieces, headers and query parameters. +This package defines typeclasses used for converting Haskell data types to and +from HTTP API data. + +Please see README.md. %package devel Summary: Haskell %{pkg_name} library development files @@ -85,7 +87,7 @@ %ghc_pkg_recache %files -f %{name}.files -%doc LICENSE +%license LICENSE %files devel -f %{name}-devel.files %doc CHANGELOG.md README.md ++++++ http-api-data-0.3.7.1.tar.gz -> http-api-data-0.3.8.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/http-api-data-0.3.7.1/CHANGELOG.md new/http-api-data-0.3.8.1/CHANGELOG.md --- old/http-api-data-0.3.7.1/CHANGELOG.md 2017-05-15 14:30:30.000000000 +0200 +++ new/http-api-data-0.3.8.1/CHANGELOG.md 2018-03-15 00:11:02.000000000 +0100 @@ -1,3 +1,25 @@ +0.3.8.1 +--- + +* GHC-8.4.1 support + +0.3.8 +--- + +* Minor changes: + * Stable URL-encoding for `Form`s (see [#67](https://github.com/fizruk/http-api-data/pull/67)): + * Introduce `urlEncodeParams` and `urlDecodeParams`; + * Introduce `urlEncodeAsFormStable` and use stable encoding for doctests; + * Add `toEntriesByKeyStable` and `toListStable`; + * Add `Semigroup` instance for `Form` (see [#69](https://github.com/fizruk/http-api-data/pull/69)); + * Relax upper bound on Cabal (see [#73](https://github.com/fizruk/http-api-data/pull/73)). + +0.3.7.2 +--- + +* Allow http-types-0.12 +* .cabal file adjustments + 0.3.7.1 --- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/http-api-data-0.3.7.1/http-api-data.cabal new/http-api-data-0.3.8.1/http-api-data.cabal --- old/http-api-data-0.3.7.1/http-api-data.cabal 2017-05-15 14:28:55.000000000 +0200 +++ new/http-api-data-0.3.8.1/http-api-data.cabal 2018-03-15 00:11:02.000000000 +0100 @@ -1,11 +1,14 @@ name: http-api-data -version: 0.3.7.1 +version: 0.3.8.1 license: BSD3 license-file: LICENSE author: Nickolay Kudasov <[email protected]> maintainer: Nickolay Kudasov <[email protected]> synopsis: Converting to/from HTTP API data like URL pieces, headers and query parameters. -description: Please see README.md +description: + This package defines typeclasses used for converting Haskell data types to and from HTTP API data. + . + Please see README.md homepage: http://github.com/fizruk/http-api-data category: Web stability: unstable @@ -20,13 +23,14 @@ GHC==7.8.4, GHC==7.10.3, GHC==8.0.2, - GHC==8.1.* + GHC==8.2.2, + GHC==8.4.1 custom-setup setup-depends: - base >= 4.7 && <4.11, - Cabal >= 1.18 && <2.1, - cabal-doctest >=1.0.1 && <1.1 + base >= 4.7 && <4.12, + Cabal >= 1.18 && <2.3, + cabal-doctest >=1.0.6 && <1.1 flag use-text-show description: Use text-show library for efficient ToHttpApiData implementations. @@ -36,19 +40,21 @@ library hs-source-dirs: src/ include-dirs: include/ - build-depends: base >= 4.7 && < 4.11 - , attoparsec >= 0.13.0.1 && < 0.14 - , attoparsec-iso8601 >= 1.0.0.0 && < 1.1 - , bytestring - , containers - , hashable - , http-types - , text >= 0.5 - , time - , time-locale-compat >=0.1.1.0 && <0.2 - , unordered-containers - , uri-bytestring - , uuid-types >= 1.0.2 && <1.1 + build-depends: base >= 4.7 && < 4.12 + , attoparsec >= 0.13.0.1 && < 0.14 + , attoparsec-iso8601 >= 1.0.0.0 && < 1.1 + , bytestring >= 0.10.4.0 && < 0.11 + , containers >= 0.5.5.1 && < 0.6 + , hashable >= 1.1.2.4 && < 1.3 + , http-types >= 0.8.6 && < 0.13 + , text >= 1.1.1.3 && < 1.3 + , time >= 1.4.2 && < 1.9 + , time-locale-compat >= 0.1.1.0 && < 0.2 + , unordered-containers >= 0.2.6.0 && < 0.3 + , uri-bytestring >= 0.1.7 && < 0.4 + , uuid-types >= 1.0.2 && <1.1 + if !impl(ghc >= 8.0) + build-depends: semigroups >= 0.16 && < 0.19 if flag(use-text-show) cpp-options: -DUSE_TEXT_SHOW build-depends: text-show >= 2 @@ -70,9 +76,10 @@ hs-source-dirs: test ghc-options: -Wall default-language: Haskell2010 + build-tool-depends: hspec-discover:hspec-discover >= 2.4.7 && <2.5 build-depends: HUnit - , hspec >= 1.3 - , base >= 4 && < 5 + , hspec >= 2.4.7 + , base , bytestring , QuickCheck >=2.9 , quickcheck-instances >= 0.3.12 @@ -81,14 +88,14 @@ , text , time , bytestring - , uuid + , uuid-types test-suite doctests ghc-options: -Wall build-depends: base, directory >= 1.0, - doctest >= 0.11 && <0.12, + doctest >= 0.11 && <0.16, filepath default-language: Haskell2010 hs-source-dirs: test diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/http-api-data-0.3.7.1/src/Web/FormUrlEncoded.hs new/http-api-data-0.3.8.1/src/Web/FormUrlEncoded.hs --- old/http-api-data-0.3.7.1/src/Web/FormUrlEncoded.hs 2016-11-14 21:27:04.000000000 +0100 +++ new/http-api-data-0.3.8.1/src/Web/FormUrlEncoded.hs 2018-03-15 00:11:02.000000000 +0100 @@ -14,9 +14,11 @@ -- * Encoding and decoding @'Form'@s urlEncodeAsForm, + urlEncodeAsFormStable, urlDecodeAsForm, urlEncodeForm, + urlEncodeFormStable, urlDecodeForm, -- * 'Generic's @@ -28,7 +30,9 @@ defaultFormOptions, -- * Helpers + toListStable, toEntriesByKey, + toEntriesByKeyStable, fromEntriesByKey, lookupAll, @@ -38,6 +42,9 @@ parseAll, parseMaybe, parseUnique, + + urlEncodeParams, + urlDecodeParams, ) where import Web.Internal.FormUrlEncoded diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/http-api-data-0.3.7.1/src/Web/Internal/FormUrlEncoded.hs new/http-api-data-0.3.8.1/src/Web/Internal/FormUrlEncoded.hs --- old/http-api-data-0.3.7.1/src/Web/Internal/FormUrlEncoded.hs 2016-11-14 21:28:23.000000000 +0100 +++ new/http-api-data-0.3.8.1/src/Web/Internal/FormUrlEncoded.hs 2018-03-15 00:11:02.000000000 +0100 @@ -35,10 +35,12 @@ import Data.Int import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap -import Data.List (intersperse) +import Data.List (intersperse, sortBy) import Data.Map (Map) import qualified Data.Map as Map import Data.Monoid +import qualified Data.Semigroup as Semi +import Data.Ord (comparing) import Data.Text (Text) import qualified Data.Text as Text @@ -182,17 +184,23 @@ -- -- 'Form' can be URL-encoded with 'urlEncodeForm' and URL-decoded with 'urlDecodeForm'. newtype Form = Form { unForm :: HashMap Text [Text] } - deriving (Eq, Read, Generic, Monoid) + deriving (Eq, Read, Generic, Semi.Semigroup, Monoid) instance Show Form where showsPrec d form = showParen (d > 10) $ - showString "fromList " . shows (toList form) + showString "fromList " . shows (toListStable form) +-- | _NOTE:_ 'toList' is unstable and may result in different key order (but not values). +-- For a stable conversion use 'toListStable'. instance IsList Form where type Item Form = (Text, Text) fromList = Form . HashMap.fromListWith (flip (<>)) . fmap (\(k, v) -> (k, [v])) toList = concatMap (\(k, vs) -> map ((,) k) vs) . HashMap.toList . unForm +-- | A stable version of 'toList'. +toListStable :: Form -> [(Text, Text)] +toListStable = sortOn fst . toList + -- | Convert a value into 'Form'. -- -- An example type and instance: @@ -303,7 +311,7 @@ -- instance 'ToForm' Post -- @ -- --- >>> urlEncodeAsForm Post { title = "Test", subtitle = Nothing, comments = ["Nice post!", "+1"] } +-- >>> urlEncodeAsFormStable Post { title = "Test", subtitle = Nothing, comments = ["Nice post!", "+1"] } -- "comments=Nice%20post%21&comments=%2B1&title=Test" genericToForm :: forall a. (Generic a, GToForm a (Rep a)) => FormOptions -> a -> Form genericToForm opts = gToForm (Proxy :: Proxy a) opts . from @@ -388,6 +396,7 @@ instance FromForm Form where fromForm = pure +-- | _NOTE:_ this conversion is unstable and may result in different key order (but not values). instance (FromFormKey k, FromHttpApiData v) => FromForm [(k, v)] where fromForm = fmap (concatMap (\(k, vs) -> map ((,) k) vs)) . toEntriesByKey @@ -402,13 +411,22 @@ -- | Parse a 'Form' into a list of entries groupped by key. -- --- >>> toEntriesByKey [("name", "Nick"), ("color", "red"), ("color", "white")] :: Either Text [(Text, [Text])] --- Right [("color",["red","white"]),("name",["Nick"])] +-- _NOTE:_ this conversion is unstable and may result in different key order +-- (but not values). For a stable encoding see 'toEntriesByKeyStable'. toEntriesByKey :: (FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])] toEntriesByKey = traverse parseGroup . HashMap.toList . unForm where parseGroup (k, vs) = (,) <$> parseFormKey k <*> traverse parseQueryParam vs +-- | Parse a 'Form' into a list of entries groupped by key. +-- +-- >>> toEntriesByKeyStable [("name", "Nick"), ("color", "red"), ("color", "white")] :: Either Text [(Text, [Text])] +-- Right [("color",["red","white"]),("name",["Nick"])] +-- +-- For an unstable (but faster) conversion see 'toEntriesByKey'. +toEntriesByKeyStable :: (Ord k, FromFormKey k, FromHttpApiData v) => Form -> Either Text [(k, [v])] +toEntriesByKeyStable = fmap (sortOn fst) . toEntriesByKey + -- | A 'Generic'-based implementation of 'fromForm'. -- This is used as a default implementation in 'FromForm'. -- @@ -482,40 +500,54 @@ -- | Encode a 'Form' to an @application/x-www-form-urlencoded@ 'BSL.ByteString'. -- +-- _NOTE:_ this encoding is unstable and may result in different key order +-- (but not values). For a stable encoding see 'urlEncodeFormStable'. +urlEncodeForm :: Form -> BSL.ByteString +urlEncodeForm = urlEncodeParams . toList + +-- | Encode a 'Form' to an @application/x-www-form-urlencoded@ 'BSL.ByteString'. +-- +-- For an unstable (but faster) encoding see 'urlEncodeForm'. +-- -- Key-value pairs get encoded to @key=value@ and separated by @&@: -- --- >>> urlEncodeForm [("name", "Julian"), ("lastname", "Arni")] +-- >>> urlEncodeFormStable [("name", "Julian"), ("lastname", "Arni")] -- "lastname=Arni&name=Julian" -- -- Keys with empty values get encoded to just @key@ (without the @=@ sign): -- --- >>> urlEncodeForm [("is_test", "")] +-- >>> urlEncodeFormStable [("is_test", "")] -- "is_test" -- -- Empty keys are allowed too: -- --- >>> urlEncodeForm [("", "foobar")] +-- >>> urlEncodeFormStable [("", "foobar")] -- "=foobar" -- --- However, if not key and value are empty, the key-value pair is ignored. --- (This prevents @'urlDecodeForm' . 'urlEncodeForm'@ from being a true isomorphism). +-- However, if both key and value are empty, the key-value pair is ignored. +-- (This prevents @'urlDecodeForm' . 'urlEncodeFormStable'@ from being a true isomorphism). -- --- >>> urlEncodeForm [("", "")] +-- >>> urlEncodeFormStable [("", "")] -- "" -- -- Everything is escaped with @'escapeURIString' 'isUnreserved'@: -- --- >>> urlEncodeForm [("fullname", "Andres Löh")] +-- >>> urlEncodeFormStable [("fullname", "Andres Löh")] -- "fullname=Andres%20L%C3%B6h" -urlEncodeForm :: Form -> BSL.ByteString -urlEncodeForm = toLazyByteString . mconcat . intersperse (shortByteString "&") . map encodePair . toList +urlEncodeFormStable :: Form -> BSL.ByteString +urlEncodeFormStable = urlEncodeParams . sortOn fst . toList + +-- | Encode a list of key-value pairs to an @application/x-www-form-urlencoded@ 'BSL.ByteString'. +-- +-- See also 'urlEncodeFormStable'. +urlEncodeParams :: [(Text, Text)] -> BSL.ByteString +urlEncodeParams = toLazyByteString . mconcat . intersperse (shortByteString "&") . map encodePair where escape = urlEncodeQuery . Text.encodeUtf8 encodePair (k, "") = escape k encodePair (k, v) = escape k <> shortByteString "=" <> escape v - -- | Decode an @application/x-www-form-urlencoded@ 'BSL.ByteString' to a 'Form'. -- -- Key-value pairs get decoded normally: @@ -548,7 +580,13 @@ -- >>> urlDecodeForm "this=has=too=many=equals" -- Left "not a valid pair: this=has=too=many=equals" urlDecodeForm :: BSL.ByteString -> Either Text Form -urlDecodeForm bs = toForm <$> traverse parsePair pairs +urlDecodeForm = fmap toForm . urlDecodeParams + +-- | Decode an @application/x-www-form-urlencoded@ 'BSL.ByteString' to a list of key-value pairs. +-- +-- See also 'urlDecodeForm'. +urlDecodeParams :: BSL.ByteString -> Either Text [(Text, Text)] +urlDecodeParams bs = traverse parsePair pairs where pairs = map (BSL8.split '=') (BSL8.split '&' bs) @@ -560,6 +598,7 @@ [k] -> return (k, "") xs -> Left $ "not a valid pair: " <> Text.intercalate "=" xs + -- | This is a convenience function for decoding a -- @application/x-www-form-urlencoded@ 'BSL.ByteString' directly to a datatype -- that has an instance of 'FromForm'. @@ -577,11 +616,22 @@ -- -- This is effectively @'urlEncodeForm' . 'toForm'@. -- --- >>> urlEncodeAsForm Person {name = "Dennis", age = 22} --- "age=22&name=Dennis" +-- _NOTE:_ this encoding is unstable and may result in different key order +-- (but not values). For a stable encoding see 'urlEncodeAsFormStable'. urlEncodeAsForm :: ToForm a => a -> BSL.ByteString urlEncodeAsForm = urlEncodeForm . toForm +-- | This is a convenience function for encoding a datatype that has instance +-- of 'ToForm' directly to a @application/x-www-form-urlencoded@ +-- 'BSL.ByteString'. +-- +-- This is effectively @'urlEncodeFormStable' . 'toForm'@. +-- +-- >>> urlEncodeAsFormStable Person {name = "Dennis", age = 22} +-- "age=22&name=Dennis" +urlEncodeAsFormStable :: ToForm a => a -> BSL.ByteString +urlEncodeAsFormStable = urlEncodeFormStable . toForm + -- | Find all values corresponding to a given key in a 'Form'. -- -- >>> lookupAll "name" [] @@ -692,8 +742,8 @@ -- 'fromForm' = 'genericFromForm' myOptions -- @ -- --- >>> urlEncodeAsForm Project { projectName = "http-api-data", projectSize = 172 } --- "size=172&name=http-api-data" +-- >>> urlEncodeAsFormStable Project { projectName = "http-api-data", projectSize = 172 } +-- "name=http-api-data&size=172" -- >>> urlDecodeAsForm "name=http-api-data&size=172" :: Either Text Project -- Right (Project {projectName = "http-api-data", projectSize = 172}) data FormOptions = FormOptions @@ -712,3 +762,6 @@ defaultFormOptions = FormOptions { fieldLabelModifier = id } + +sortOn :: Ord b => (a -> b) -> [a] -> [a] +sortOn f = sortBy (comparing f) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/http-api-data-0.3.7.1/src/Web/Internal/HttpApiData.hs new/http-api-data-0.3.8.1/src/Web/Internal/HttpApiData.hs --- old/http-api-data-0.3.7.1/src/Web/Internal/HttpApiData.hs 2017-04-21 09:03:49.000000000 +0200 +++ new/http-api-data-0.3.8.1/src/Web/Internal/HttpApiData.hs 2018-03-15 00:11:02.000000000 +0100 @@ -367,7 +367,7 @@ -- Right 1991-06-02 -- -- This parser is case sensitive and will not match @'showTextData'@ --- in presense of letters: +-- in presence of letters: -- -- >>> readTextData (showTextData True) :: Either Text Bool -- Left "could not parse: `true'" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/http-api-data-0.3.7.1/test/Web/Internal/HttpApiDataSpec.hs new/http-api-data-0.3.8.1/test/Web/Internal/HttpApiDataSpec.hs --- old/http-api-data-0.3.7.1/test/Web/Internal/HttpApiDataSpec.hs 2017-05-15 14:28:55.000000000 +0200 +++ new/http-api-data-0.3.8.1/test/Web/Internal/HttpApiDataSpec.hs 2018-03-15 00:11:02.000000000 +0100 @@ -11,7 +11,7 @@ import qualified Data.ByteString as BS import Data.ByteString.Builder (toLazyByteString) import Data.Version -import qualified Data.UUID as UUID +import qualified Data.UUID.Types as UUID import Data.Proxy
