git.iko.soy/typeable/http-api-data
on master help sign in
Merge pull request #86 from fizruk/fix-set-cookie
283c747Oleg Grenrus
CHANGELOG.md+9−1
@@ -1,10 +1,18 @@
1+0.3.10
2+---
3+
4+* Fix 'SetCookie' instances
5+ (see [#86](https://github.com/fizruk/http-api-data/pull/86))
6+* Add support for `Fixed`
7+ (see [#78](https://github.com/fizruk/http-api-data/pull/87))
8+
10.3.9
90.3.9
2---
10---
3
11
4* GHC-8.6 support
12* GHC-8.6 support
5* Remove dependency on `uri-bytestring` and use functions from `http-types` instead
13* Remove dependency on `uri-bytestring` and use functions from `http-types` instead
6 (see [#75](https://github.com/fizruk/http-api-data/pull/78))
14 (see [#75](https://github.com/fizruk/http-api-data/pull/78))
7−* Add support for `Cookie`
15+* Add support for `SetCookie`
8 (see [#74](https://github.com/fizruk/http-api-data/pull/74))
16 (see [#74](https://github.com/fizruk/http-api-data/pull/74))
9
17
100.3.8.1
180.3.8.1
http-api-data.cabal+2−1
@@ -1,6 +1,6 @@
1cabal-version: >= 1.10
1cabal-version: >= 1.10
2name: http-api-data
2name: http-api-data
3−version: 0.3.9
3+version: 0.3.10
4
4
5synopsis: Converting to/from HTTP API data like URL pieces, headers and query parameters.
5synopsis: Converting to/from HTTP API data like URL pieces, headers and query parameters.
6category: Web
6category: Web
@@ -99,6 +99,7 @@ test-suite spec
99 , time
99 , time
100 , bytestring
100 , bytestring
101 , uuid-types
101 , uuid-types
102+ , cookie
102
103
103 build-depends: HUnit >= 1.6.0.0 && <1.7
104 build-depends: HUnit >= 1.6.0.0 && <1.7
104 , hspec >= 2.5.5 && <2.6
105 , hspec >= 2.5.5 && <2.6
src/Web/Internal/HttpApiData.hs+19−6
@@ -24,6 +24,7 @@ import Control.Monad ((<=<))
24
24
25import Data.ByteString (ByteString)
25import Data.ByteString (ByteString)
26import qualified Data.ByteString as BS
26import qualified Data.ByteString as BS
27+import qualified Data.ByteString.Lazy as LBS
27import Data.Monoid
28import Data.Monoid
28
29
29import qualified Data.Fixed as F
30import qualified Data.Fixed as F
@@ -32,7 +33,8 @@ import Data.Word
32
33
33import Data.Text (Text)
34import Data.Text (Text)
34import qualified Data.Text as T
35import qualified Data.Text as T
35−import Data.Text.Encoding (decodeUtf8', encodeUtf8)
36+import Data.Text.Encoding (decodeUtf8With, decodeUtf8', encodeUtf8)
37+import Data.Text.Encoding.Error (lenientDecode)
36import qualified Data.Text.Lazy as L
38import qualified Data.Text.Lazy as L
37import Data.Text.Read (Reader, decimal, rational,
39import Data.Text.Read (Reader, decimal, rational,
38 signed)
40 signed)
@@ -539,13 +541,19 @@ instance (ToHttpApiData a, ToHttpApiData b) => ToHttpApiData (Either a b) where
539 toUrlPiece (Left x) = "left " <> toUrlPiece x
541 toUrlPiece (Left x) = "left " <> toUrlPiece x
540 toUrlPiece (Right x) = "right " <> toUrlPiece x
542 toUrlPiece (Right x) = "right " <> toUrlPiece x
541
543
542−-- |
544+-- | /Note:/ this instance works correctly for alphanumeric name and value
545+--
543-- >>> let Right c = parseUrlPiece "SESSID=r2t5uvjq435r4q7ib3vtdjq120" :: Either Text SetCookie
546-- >>> let Right c = parseUrlPiece "SESSID=r2t5uvjq435r4q7ib3vtdjq120" :: Either Text SetCookie
544-- >>> toUrlPiece c
547-- >>> toUrlPiece c
548+-- "SESSID=r2t5uvjq435r4q7ib3vtdjq120"
549+--
550+-- >>> toHeader c
545−-- "\"SESSID=r2t5uvjq435r4q7ib3vtdjq120\""
551+-- "SESSID=r2t5uvjq435r4q7ib3vtdjq120"
552+--
546instance ToHttpApiData SetCookie where
553instance ToHttpApiData SetCookie where
554+ toUrlPiece = decodeUtf8With lenientDecode . toHeader
547− toUrlPiece = showt . BS.toLazyByteString . renderSetCookie
555+ toHeader = LBS.toStrict . BS.toLazyByteString . renderSetCookie
548− toEncodedUrlPiece = renderSetCookie
556+ -- toEncodedUrlPiece = renderSetCookie -- doesn't do things.
549
557
550-- |
558-- |
551-- >>> parseUrlPiece "_" :: Either Text ()
559-- >>> parseUrlPiece "_" :: Either Text ()
@@ -601,6 +609,9 @@ instance FromHttpApiData String where parseUrlPiece = Right . T.unpack
601instance FromHttpApiData Text where parseUrlPiece = Right
609instance FromHttpApiData Text where parseUrlPiece = Right
602instance FromHttpApiData L.Text where parseUrlPiece = Right . L.fromStrict
610instance FromHttpApiData L.Text where parseUrlPiece = Right . L.fromStrict
603
611
612+instance F.HasResolution a => FromHttpApiData (F.Fixed a) where
613+ parseUrlPiece = runReader rational
614+
604-- |
615-- |
605-- >>> toGregorian <$> parseUrlPiece "2016-12-01"
616-- >>> toGregorian <$> parseUrlPiece "2016-12-01"
606-- Right (2016,12,1)
617-- Right (2016,12,1)
@@ -681,12 +692,14 @@ instance FromHttpApiData a => FromHttpApiData (LenientData a) where
681 parseHeader = Right . LenientData . parseHeader
692 parseHeader = Right . LenientData . parseHeader
682 parseQueryParam = Right . LenientData . parseQueryParam
693 parseQueryParam = Right . LenientData . parseQueryParam
683
694
684−-- |
695+-- | /Note:/ this instance works correctly for alphanumeric name and value
696+--
685-- >>> parseUrlPiece "SESSID=r2t5uvjq435r4q7ib3vtdjq120" :: Either Text SetCookie
697-- >>> parseUrlPiece "SESSID=r2t5uvjq435r4q7ib3vtdjq120" :: Either Text SetCookie
686-- Right (SetCookie {setCookieName = "SESSID", setCookieValue = "r2t5uvjq435r4q7ib3vtdjq120", setCookiePath = Nothing, setCookieExpires = Nothing, setCookieMaxAge = Nothing, setCookieDomain = Nothing, setCookieHttpOnly = False, setCookieSecure = False, setCookieSameSite = Nothing})
698-- Right (SetCookie {setCookieName = "SESSID", setCookieValue = "r2t5uvjq435r4q7ib3vtdjq120", setCookiePath = Nothing, setCookieExpires = Nothing, setCookieMaxAge = Nothing, setCookieDomain = Nothing, setCookieHttpOnly = False, setCookieSecure = False, setCookieSameSite = Nothing})
687instance FromHttpApiData SetCookie where
699instance FromHttpApiData SetCookie where
688 parseUrlPiece = parseHeader . encodeUtf8
700 parseUrlPiece = parseHeader . encodeUtf8
689 parseHeader = Right . parseSetCookie
701 parseHeader = Right . parseSetCookie
702+
690-------------------------------------------------------------------------------
703-------------------------------------------------------------------------------
691-- Attoparsec helpers
704-- Attoparsec helpers
692-------------------------------------------------------------------------------
705-------------------------------------------------------------------------------
test/Web/Internal/HttpApiDataSpec.hs+26−59
@@ -5,14 +5,17 @@ module Web.Internal.HttpApiDataSpec (spec) where
5import Control.Applicative
5import Control.Applicative
6import qualified Data.Fixed as F
6import qualified Data.Fixed as F
7import Data.Int
7import Data.Int
8+import Data.Char
8import Data.Word
9import Data.Word
9import Data.Time
10import Data.Time
10import qualified Data.Text as T
11import qualified Data.Text as T
12+import qualified Data.Text.Encoding as TE
11import qualified Data.Text.Lazy as L
13import qualified Data.Text.Lazy as L
12import qualified Data.ByteString as BS
14import qualified Data.ByteString as BS
13import Data.ByteString.Builder (toLazyByteString)
15import Data.ByteString.Builder (toLazyByteString)
14import Data.Version
16import Data.Version
15import qualified Data.UUID.Types as UUID
17import qualified Data.UUID.Types as UUID
18+import Web.Cookie (SetCookie, defaultSetCookie, setCookieName, setCookieValue)
16
19
17import Data.Proxy
20import Data.Proxy
18
21
@@ -40,30 +43,24 @@ import Web.Internal.TestInstances
40encodedUrlPieceProp :: ToHttpApiData a => a -> Property
43encodedUrlPieceProp :: ToHttpApiData a => a -> Property
41encodedUrlPieceProp x = toLazyByteString (toEncodedUrlPiece (toUrlPiece x)) === toLazyByteString (toEncodedUrlPiece x)
44encodedUrlPieceProp x = toLazyByteString (toEncodedUrlPiece (toUrlPiece x)) === toLazyByteString (toEncodedUrlPiece x)
42
45
43−
46+-- | Check 'ToHttpApiData' and 'FromHttpApiData' compatibility
44checkUrlPiece :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Show a, Arbitrary a) => Proxy a -> String -> Spec
47checkUrlPiece :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Show a, Arbitrary a) => Proxy a -> String -> Spec
45−checkUrlPiece _ name = prop name (toUrlPiece <=> parseUrlPiece :: a -> Property)
48+checkUrlPiece _ = checkUrlPiece' (arbitrary :: Gen a)
46
49
47−-- | Check with given generator
48checkUrlPiece' :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Show a) => Gen a -> String -> Spec
50checkUrlPiece' :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Show a) => Gen a -> String -> Spec
51+checkUrlPiece' gen name = describe name $ do
49−checkUrlPiece' gen name = prop name $ forAll gen (toUrlPiece <=> parseUrlPiece)
52+ prop "toUrlPiece <=> parseUrlPiece" $ forAll gen (toUrlPiece <=> parseUrlPiece :: a -> Property)
53+ prop "toQueryParam <=> parseQueryParam" $ forAll gen (toQueryParam <=> parseQueryParam :: a -> Property)
54+ prop "toHeader <=> parseHeader" $ forAll gen (toHeader <=> parseHeader :: a -> Property)
55+ prop "toEncodedUrlPiece encodes correctly" $ forAll gen encodedUrlPieceProp
50
56
51-- | Check case insensitivity for @parseUrlPiece@.
57-- | Check case insensitivity for @parseUrlPiece@.
52checkUrlPieceI :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Arbitrary a) => Proxy a -> String -> Spec
58checkUrlPieceI :: forall a. (Eq a, ToHttpApiData a, FromHttpApiData a, Arbitrary a) => Proxy a -> String -> Spec
53checkUrlPieceI _ = checkUrlPiece (Proxy :: Proxy (RandomCase a))
59checkUrlPieceI _ = checkUrlPiece (Proxy :: Proxy (RandomCase a))
54
60
55−-- | Check that 'toEncodedUrlPiece' is equivallent to default implementation.
56−checkEncodedUrlPiece :: forall a. (Show a, ToHttpApiData a, Arbitrary a) => Proxy a -> String -> Spec
57−checkEncodedUrlPiece _ = checkEncodedUrlPiece' (arbitrary :: Gen a)
58−
59−-- | Check that 'toEncodedUrlPiece' is equivallent to default implementation.
60−-- Use a given generator.
61−checkEncodedUrlPiece' :: forall a. (Show a, ToHttpApiData a) => Gen a -> String -> Spec
62−checkEncodedUrlPiece' gen name = prop name $ forAll gen encodedUrlPieceProp
63−
64spec :: Spec
61spec :: Spec
65spec = do
62spec = do
66− describe "toUrlPiece <=> parseUrlPiece" $ do
63+ describe "Instances" $ do
67 checkUrlPiece (Proxy :: Proxy ()) "()"
64 checkUrlPiece (Proxy :: Proxy ()) "()"
68 checkUrlPiece (Proxy :: Proxy Char) "Char"
65 checkUrlPiece (Proxy :: Proxy Char) "Char"
69 checkUrlPieceI (Proxy :: Proxy Bool) "Bool"
66 checkUrlPieceI (Proxy :: Proxy Bool) "Bool"
@@ -90,6 +87,15 @@ spec = do
90 checkUrlPiece' nominalDiffTimeGen "NominalDiffTime"
87 checkUrlPiece' nominalDiffTimeGen "NominalDiffTime"
91 checkUrlPiece (Proxy :: Proxy Version) "Version"
88 checkUrlPiece (Proxy :: Proxy Version) "Version"
92 checkUrlPiece' uuidGen "UUID"
89 checkUrlPiece' uuidGen "UUID"
90+ checkUrlPiece' setCookieGen "Cookie"
91+
92+ checkUrlPiece (Proxy :: Proxy F.Uni) "Uni"
93+ checkUrlPiece (Proxy :: Proxy F.Deci) "Deci"
94+ checkUrlPiece (Proxy :: Proxy F.Centi) "Centi"
95+ checkUrlPiece (Proxy :: Proxy F.Milli) "Milli"
96+ checkUrlPiece (Proxy :: Proxy F.Micro) "Micro"
97+ checkUrlPiece (Proxy :: Proxy F.Nano) "Nano"
98+ checkUrlPiece (Proxy :: Proxy F.Pico) "Pico"
93
99
94 checkUrlPiece (Proxy :: Proxy (Maybe String)) "Maybe String"
100 checkUrlPiece (Proxy :: Proxy (Maybe String)) "Maybe String"
95 checkUrlPieceI (Proxy :: Proxy (Maybe Integer)) "Maybe Integer"
101 checkUrlPieceI (Proxy :: Proxy (Maybe Integer)) "Maybe Integer"
@@ -100,51 +106,6 @@ spec = do
100 checkUrlPiece (Proxy :: Proxy Natural) "Natural"
106 checkUrlPiece (Proxy :: Proxy Natural) "Natural"
101#endif
107#endif
102
108
103− describe "toEncodedUrlPiece encodes correctly" $ do
104− checkEncodedUrlPiece (Proxy :: Proxy ()) "()"
105− checkEncodedUrlPiece (Proxy :: Proxy Char) "Char"
106− checkEncodedUrlPiece (Proxy :: Proxy Bool) "Bool"
107− checkEncodedUrlPiece (Proxy :: Proxy Ordering) "Ordering"
108− checkEncodedUrlPiece (Proxy :: Proxy Int) "Int"
109− checkEncodedUrlPiece (Proxy :: Proxy Int8) "Int8"
110− checkEncodedUrlPiece (Proxy :: Proxy Int16) "Int16"
111− checkEncodedUrlPiece (Proxy :: Proxy Int32) "Int32"
112− checkEncodedUrlPiece (Proxy :: Proxy Int64) "Int64"
113− checkEncodedUrlPiece (Proxy :: Proxy Integer) "Integer"
114− checkEncodedUrlPiece (Proxy :: Proxy Word) "Word"
115− checkEncodedUrlPiece (Proxy :: Proxy Word8) "Word8"
116− checkEncodedUrlPiece (Proxy :: Proxy Word16) "Word16"
117− checkEncodedUrlPiece (Proxy :: Proxy Word32) "Word32"
118− checkEncodedUrlPiece (Proxy :: Proxy Word64) "Word64"
119− checkEncodedUrlPiece (Proxy :: Proxy String) "String"
120− checkEncodedUrlPiece (Proxy :: Proxy T.Text) "Text.Strict"
121− checkEncodedUrlPiece (Proxy :: Proxy L.Text) "Text.Lazy"
122− checkEncodedUrlPiece (Proxy :: Proxy Day) "Day"
123− checkEncodedUrlPiece' timeOfDayGen "TimeOfDay"
124− checkEncodedUrlPiece' localTimeGen "LocalTime"
125− checkEncodedUrlPiece' zonedTimeGen "ZonedTime"
126− checkEncodedUrlPiece' utcTimeGen "UTCTime"
127− checkEncodedUrlPiece' nominalDiffTimeGen "NominalDiffTime"
128− checkEncodedUrlPiece (Proxy :: Proxy Version) "Version"
129− checkEncodedUrlPiece' uuidGen "UUID"
130−
131− checkEncodedUrlPiece (Proxy :: Proxy (Maybe String)) "Maybe String"
132− checkEncodedUrlPiece (Proxy :: Proxy (Maybe Integer)) "Maybe Integer"
133− checkEncodedUrlPiece (Proxy :: Proxy (Either Integer T.Text)) "Either Integer Text"
134− checkEncodedUrlPiece (Proxy :: Proxy (Either Version Day)) "Either Version Day"
135−
136−#if MIN_VERSION_base(4,8,0)
137− checkEncodedUrlPiece (Proxy :: Proxy Natural) "Natural"
138−#endif
139−
140− checkEncodedUrlPiece (Proxy :: Proxy F.Uni) "Uni"
141− checkEncodedUrlPiece (Proxy :: Proxy F.Deci) "Deci"
142− checkEncodedUrlPiece (Proxy :: Proxy F.Centi) "Centi"
143− checkEncodedUrlPiece (Proxy :: Proxy F.Milli) "Milli"
144− checkEncodedUrlPiece (Proxy :: Proxy F.Micro) "Micro"
145− checkEncodedUrlPiece (Proxy :: Proxy F.Nano) "Nano"
146− checkEncodedUrlPiece (Proxy :: Proxy F.Pico) "Pico"
147−
148 it "bad integers are rejected" $ do
109 it "bad integers are rejected" $ do
149 parseUrlPieceMaybe (T.pack "123hello") `shouldBe` (Nothing :: Maybe Int)
110 parseUrlPieceMaybe (T.pack "123hello") `shouldBe` (Nothing :: Maybe Int)
150
111
@@ -179,3 +140,9 @@ utcTimeGen = UTCTime <$> arbitrary <*> fmap fromInteger (choose (0, 86400))
179
140
180nominalDiffTimeGen :: Gen NominalDiffTime
141nominalDiffTimeGen :: Gen NominalDiffTime
181nominalDiffTimeGen = fromInteger <$> arbitrary
142nominalDiffTimeGen = fromInteger <$> arbitrary
143+
144+setCookieGen :: Gen SetCookie
145+setCookieGen = do
146+ n <- TE.encodeUtf8 . T.pack . filter isAlphaNum <$> arbitrary
147+ v <- TE.encodeUtf8 . T.pack . filter isAlphaNum <$> arbitrary
148+ return $ defaultSetCookie { setCookieName = n, setCookieValue = v }