fb 0.3.1 → 0.4
raw patch · 8 files changed
+153/−22 lines, 8 filesdep +QuickCheckdep +old-localePVP ok
version bump matches the API change (PVP)
Dependencies added: QuickCheck, old-locale
API changes (from Hackage documentation)
+ Facebook: (#=) :: SimpleType a => Ascii -> a -> Argument
+ Facebook: Id :: Ascii -> Id
+ Facebook: class SimpleType a
+ Facebook: encodeFbParam :: SimpleType a => a -> Text
+ Facebook: idCode :: Id -> Ascii
+ Facebook: newtype Id
- Facebook: createAction :: ResourceIO m => Action -> SimpleQuery -> AccessToken User -> FacebookT Auth m Id
+ Facebook: createAction :: ResourceIO m => Action -> [Argument] -> AccessToken User -> FacebookT Auth m Id
- Facebook: getObject :: (ResourceIO m, FromJSON a) => Ascii -> SimpleQuery -> Maybe (AccessToken kind) -> FacebookT anyAuth m a
+ Facebook: getObject :: (ResourceIO m, FromJSON a) => Ascii -> [Argument] -> Maybe (AccessToken kind) -> FacebookT anyAuth m a
- Facebook: getUserAccessTokenStep2 :: ResourceIO m => RedirectUrl -> SimpleQuery -> FacebookT Auth m (AccessToken User)
+ Facebook: getUserAccessTokenStep2 :: ResourceIO m => RedirectUrl -> [Argument] -> FacebookT Auth m (AccessToken User)
- Facebook: postObject :: (ResourceIO m, FromJSON a) => Ascii -> SimpleQuery -> AccessToken kind -> FacebookT Auth m a
+ Facebook: postObject :: (ResourceIO m, FromJSON a) => Ascii -> [Argument] -> AccessToken kind -> FacebookT Auth m a
Files
- fb.cabal +3/−1
- src/Facebook.hs +3/−0
- src/Facebook/Auth.hs +2/−2
- src/Facebook/Base.hs +2/−2
- src/Facebook/Graph.hs +2/−2
- src/Facebook/OpenGraph.hs +87/−6
- src/Facebook/Types.hs +6/−0
- tests/runtests.hs +48/−9
fb.cabal view
@@ -1,5 +1,5 @@ name: fb-version: 0.3.1+version: 0.4 license: BSD3 license-file: LICENSE author: Felipe Lessa@@ -61,6 +61,7 @@ , attoparsec-conduit >= 0.0 && < 0.1 , aeson >= 0.5 && < 0.6 , time >= 1.2 && < 1.5+ , old-locale extensions: DeriveDataTypeable EmptyDataDecls@@ -87,6 +88,7 @@ -- Test-only dependencies , HUnit+ , QuickCheck , hspec == 0.9.* , fb extensions:
src/Facebook.hs view
@@ -28,10 +28,13 @@ -- * Facebook's Open Graph API , createAction , Action+ , (#=)+ , SimpleType(..) -- * Raw access to the Graph API , getObject , postObject+ , Id(..) -- * Exceptions , FacebookException(..)
src/Facebook/Auth.hs view
@@ -25,7 +25,7 @@ import qualified Data.Text.Encoding as TE import qualified Data.Text.Encoding.Error as TE import qualified Network.HTTP.Conduit as H-import qualified Network.HTTP.Types as HT+-- import qualified Network.HTTP.Types as HT import Facebook.Types@@ -78,7 +78,7 @@ getUserAccessTokenStep2 :: C.ResourceIO m => RedirectUrl -- ^ Should be exactly the same -- as in 'getUserAccessTokenStep1'.- -> HT.SimpleQuery+ -> [Argument] -> FacebookT Auth m (AccessToken User) getUserAccessTokenStep2 redirectUrl query = case query of
src/Facebook/Base.hs view
@@ -42,8 +42,8 @@ } --- | Class for types that may be passed on queries to Facebook's--- API.+-- | Internal class for types that may be passed on queries to+-- Facebook's API. class ToSimpleQuery a where -- | Prepend to the given query the parameters necessary to -- pass this data type to Facebook.
src/Facebook/Graph.hs view
@@ -29,7 +29,7 @@ -- raw JSON 'A.Value'. getObject :: (C.ResourceIO m, A.FromJSON a) => Ascii -- ^ Path (should begin with a slash @\/@)- -> HT.SimpleQuery -- ^ Arguments to be passed to Facebook+ -> [Argument] -- ^ Arguments to be passed to Facebook -> Maybe (AccessToken kind) -- ^ Optional access token -> FacebookT anyAuth m a getObject path query mtoken =@@ -41,7 +41,7 @@ -- a raw JSON 'A.Value'. postObject :: (C.ResourceIO m, A.FromJSON a) => Ascii -- ^ Path (should begin with a slash @\/@)- -> HT.SimpleQuery -- ^ Arguments to be passed to Facebook+ -> [Argument] -- ^ Arguments to be passed to Facebook -> AccessToken kind -- ^ Access token -> FacebookT Auth m a postObject path query token =
src/Facebook/OpenGraph.hs view
@@ -1,21 +1,29 @@ module Facebook.OpenGraph ( createAction , Action(..)+ , (#=)+ , SimpleType(..) ) where -- import Control.Applicative -- import Control.Monad (mzero) -- import Data.ByteString.Char8 (ByteString)--- import Data.Text (Text)+import Data.Text (Text) -- import Data.Typeable (Typeable, Typeable1)+import Data.Int (Int8, Int16, Int32)+import Data.Word (Word8, Word16, Word32, Word) import Data.String (IsString(..)) import Network.HTTP.Types (Ascii)+import System.Locale (defaultTimeLocale) -- import qualified Control.Exception.Lifted as E -- import qualified Data.Aeson as A+-- import qualified Data.ByteString.Char8 as B import qualified Data.Conduit as C--- import qualified Data.Text as T-import qualified Network.HTTP.Types as HT+import qualified Data.Text as T+import qualified Data.Text.Encoding as TE+import qualified Data.Time as TI+-- import qualified Network.HTTP.Types as HT import Facebook.Types@@ -25,10 +33,16 @@ -- | Creates an Open Graph action on the user's timeline. Returns--- the 'Id' of the newly created action.+-- the 'Id' of the newly created action. For example:+--+-- > now <- liftIO getCurrentTime+-- > createAction "cook"+-- > [ "recipe" #= "http://example.com/cookie.html"+-- > , "when" #= now ]+-- > token createAction :: C.ResourceIO m =>- Action -- ^ Action kind to be created.- -> HT.SimpleQuery -- ^ Arguments of the action.+ Action -- ^ Action kind to be created.+ -> [Argument] -- ^ Arguments of the action. -> AccessToken User -> FacebookT Auth m Id createAction (Action action) query token = do@@ -59,3 +73,70 @@ fromString = Action . fromString +-- | Create an 'Argument' with a 'SimpleType'. See the docs on+-- 'createAction' for an example.+(#=) :: SimpleType a => Ascii -> a -> Argument+p #= v = (p, TE.encodeUtf8 (encodeFbParam v))++++-- | Class for data types that may be represented as a Facebook+-- simple type. (see+-- <https://developers.facebook.com/docs/opengraph/simpletypes/>).+class SimpleType a where+ encodeFbParam :: a -> Text++-- | Facebook's simple type @Boolean@.+instance SimpleType Bool where+ encodeFbParam b = if b then "1" else "0"++-- | Facebook's simple type @DateTime@ with only the date.+instance SimpleType TI.Day where+ encodeFbParam = T.pack . TI.formatTime defaultTimeLocale "%Y-%m-%d"+-- | Facebook's simple type @DateTime@.+instance SimpleType TI.UTCTime where+ encodeFbParam = T.pack . TI.formatTime defaultTimeLocale "%Y%m%dT%H%MZ"+-- | Facebook's simple type @DateTime@.+instance SimpleType TI.ZonedTime where+ encodeFbParam = encodeFbParam . TI.zonedTimeToUTC++-- @Enum@ doesn't make sense to support as a Haskell data type.++-- | Facebook's simple type @Float@ with less precision than supported.+instance SimpleType Float where+ encodeFbParam = showT+-- | Facebook's simple type @Float@.+instance SimpleType Double where+ encodeFbParam = showT++-- | Facebook's simple type @Integer@.+instance SimpleType Int where+ encodeFbParam = showT+-- | Facebook's simple type @Integer@.+instance SimpleType Word where+ encodeFbParam = showT+-- | Facebook's simple type @Integer@.+instance SimpleType Int8 where+ encodeFbParam = showT+-- | Facebook's simple type @Integer@.+instance SimpleType Word8 where+ encodeFbParam = showT+-- | Facebook's simple type @Integer@.+instance SimpleType Int16 where+ encodeFbParam = showT+-- | Facebook's simple type @Integer@.+instance SimpleType Word16 where+ encodeFbParam = showT+-- | Facebook's simple type @Integer@.+instance SimpleType Int32 where+ encodeFbParam = showT+-- | Facebook's simple type @Integer@.+instance SimpleType Word32 where+ encodeFbParam = showT++-- | Facebook's simple type @String@.+instance SimpleType Text where+ encodeFbParam = id++showT :: Show a => a -> Text+showT = T.pack . show
src/Facebook/Types.hs view
@@ -6,9 +6,11 @@ , accessTokenExpires , User , App+ , Argument , (<>) ) where +import Data.ByteString (ByteString) import Data.Monoid (Monoid, mappend) import Data.Time (UTCTime) import Data.Typeable (Typeable, Typeable1)@@ -72,6 +74,10 @@ -- | Phantom type used mark an 'AccessToken' as an app access -- token. data App deriving (Typeable)+++-- | An argument given to an API call.+type Argument = (ByteString, ByteString) -- | Synonym for 'mappend'.
tests/runtests.hs view
@@ -3,8 +3,10 @@ import Control.Applicative import Control.Monad.IO.Class (MonadIO(liftIO)) import Control.Monad.Trans.Class (lift)+import Data.Int (Int8, Int16, Int32) import Data.Text (Text) import Data.Time (parseTime)+import Data.Word (Word8, Word16, Word32, Word) import System.Environment (getEnv) import System.Exit (exitFailure) import System.IO.Error (isDoesNotExistError)@@ -12,14 +14,17 @@ import qualified Data.Aeson as A import qualified Data.Aeson.Types as A import qualified Data.ByteString.Char8 as B+import qualified Data.Text as T+import qualified Data.Time as TI import qualified Control.Exception.Lifted as E import qualified Facebook as FB import qualified Network.HTTP.Conduit as H -import qualified Test.HUnit as HU+import Test.HUnit ((@?=)) import Test.Hspec.Monadic--- import Test.Hspec.QuickCheck+import Test.Hspec.QuickCheck import Test.Hspec.HUnit ()+import qualified Test.QuickCheck as QC -- | Grab the Facebook credentials from the environment.@@ -77,7 +82,7 @@ runNoAuth :: FB.FacebookT FB.NoAuth IO a -> IO a runNoAuth = FB.runNoAuthFacebookT manager hspecX $ do- describe "Facebook.getAppAccessToken" $ do+ describe "getAppAccessToken" $ do it "works and returns a valid app access token" $ runAuth $ do token <- FB.getAppAccessToken@@ -89,13 +94,13 @@ Right token -> fail $ show token Left (_ :: FB.FacebookException) -> lift (return () :: IO ()) - describe "Facebook.isValid" $ do+ describe "isValid" $ do it "returns False on a clearly invalid user access token" $ runNoAuth $ FB.isValid invalidUserAccessToken #?= False it "returns False on a clearly invalid app access token" $ runNoAuth $ FB.isValid invalidAppAccessToken #?= False - describe "Facebook.OpenGraph.getObject" $ do+ describe "getObject" $ do it "is able to fetch Facebook's own page" $ runNoAuth $ do A.Object obj <- FB.getObject "/19292868552" [] Nothing@@ -104,15 +109,49 @@ <*> obj A..:? "website" <*> obj A..:? "name" just x = Just (x :: Text)- r @?= ( just "19292868552"+ r &?= ( just "19292868552" , just "http://developers.facebook.com" , just "Facebook Platform" ) + describe "SimpleType" $ do+ it "works for Bool" $ (map FB.encodeFbParam [True, False]) @?= ["1", "0"] + let day = TI.fromGregorian 2012 12 21+ time = TI.TimeOfDay 11 37 22+ diffTime = TI.secondsToDiffTime (11*3600 + 37*60)+ utcTime = TI.UTCTime day diffTime+ localTime = TI.LocalTime day time+ zonedTime = TI.ZonedTime localTime (TI.minutesToTimeZone 30)+ it "works for Day" $ FB.encodeFbParam day @?= "2012-12-21"+ it "works for UTCTime" $ FB.encodeFbParam utcTime @?= "20121221T1137Z"+ it "works for ZonedTime" $ FB.encodeFbParam zonedTime @?= "20121221T1107Z"++ let propShowRead :: (Show a, Read a, Eq a, FB.SimpleType a) => a -> Bool+ propShowRead x = read (T.unpack $ FB.encodeFbParam x) == x+ prop "works for Float" (propShowRead :: Float -> Bool)+ prop "works for Double" (propShowRead :: Double -> Bool)+ prop "works for Int" (propShowRead :: Int -> Bool)+ prop "works for Int8" (propShowRead :: Int8 -> Bool)+ prop "works for Int16" (propShowRead :: Int16 -> Bool)+ prop "works for Int32" (propShowRead :: Int32 -> Bool)+ prop "works for Word" (propShowRead :: Word -> Bool)+ prop "works for Word8" (propShowRead :: Word8 -> Bool)+ prop "works for Word16" (propShowRead :: Word16 -> Bool)+ prop "works for Word32" (propShowRead :: Word32 -> Bool)++ prop "works for Text" (\t -> FB.encodeFbParam t == t)++ -- Wrappers for HUnit operators using MonadIO -(@?=) :: (Eq a, Show a, MonadIO m) => a -> a -> m ()-v @?= e = liftIO (v HU.@?= e)+(&?=) :: (Eq a, Show a, MonadIO m) => a -> a -> m ()+v &?= e = liftIO (v @?= e) (#?=) :: (Eq a, Show a, MonadIO m) => m a -> a -> m ()-m #?= e = m >>= (@?= e)+m #?= e = m >>= (&?= e)+++-- | Sad, orphan instance.+instance QC.Arbitrary Text where+ arbitrary = T.pack <$> QC.arbitrary+ shrink = map T.pack . QC.shrink . T.unpack