packages feed

pusher-http-haskell 0.2.1.0 → 0.3.0.0

raw patch · 7 files changed

+79/−88 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Network.Pusher.Protocol: FullUserCountResp :: Int -> FullChannelAttributeResp
- Network.Pusher.Protocol: OccupiedResp :: Bool -> FullChannelAttributeResp
- Network.Pusher.Protocol: SubscriptionCountResp :: Int -> FullChannelAttributeResp
- Network.Pusher.Protocol: UserCountResp :: Int -> ChannelInfoAttributeResp
- Network.Pusher.Protocol: data ChannelInfoAttributeResp
- Network.Pusher.Protocol: data FullChannelAttributeResp
- Network.Pusher.Protocol: instance Data.Hashable.Class.Hashable Network.Pusher.Protocol.ChannelInfoAttributeResp
- Network.Pusher.Protocol: instance Data.Hashable.Class.Hashable Network.Pusher.Protocol.FullChannelAttributeResp
- Network.Pusher.Protocol: instance GHC.Classes.Eq Network.Pusher.Protocol.ChannelInfoAttributeResp
- Network.Pusher.Protocol: instance GHC.Classes.Eq Network.Pusher.Protocol.FullChannelAttributeResp
- Network.Pusher.Protocol: instance GHC.Generics.Constructor Network.Pusher.Protocol.C1_0ChannelInfoAttributeResp
- Network.Pusher.Protocol: instance GHC.Generics.Constructor Network.Pusher.Protocol.C1_0FullChannelAttributeResp
- Network.Pusher.Protocol: instance GHC.Generics.Constructor Network.Pusher.Protocol.C1_1FullChannelAttributeResp
- Network.Pusher.Protocol: instance GHC.Generics.Constructor Network.Pusher.Protocol.C1_2FullChannelAttributeResp
- Network.Pusher.Protocol: instance GHC.Generics.Datatype Network.Pusher.Protocol.D1ChannelInfoAttributeResp
- Network.Pusher.Protocol: instance GHC.Generics.Datatype Network.Pusher.Protocol.D1FullChannelAttributeResp
- Network.Pusher.Protocol: instance GHC.Generics.Generic Network.Pusher.Protocol.ChannelInfoAttributeResp
- Network.Pusher.Protocol: instance GHC.Generics.Generic Network.Pusher.Protocol.FullChannelAttributeResp
- Network.Pusher.Protocol: instance GHC.Show.Show Network.Pusher.Protocol.ChannelInfoAttributeResp
- Network.Pusher.Protocol: instance GHC.Show.Show Network.Pusher.Protocol.FullChannelAttributeResp
- Network.Pusher.Protocol: newtype ChannelInfo
- Network.Pusher.Protocol: newtype FullChannelInfo
+ Data.Pusher: getPusherWithHost :: MonadIO m => Text -> Credentials -> m Pusher
+ Network.Pusher.Protocol: [channelInfoUserCount] :: ChannelInfo -> Maybe Int
+ Network.Pusher.Protocol: [fullChannelInfoOccupied] :: FullChannelInfo -> Bool
+ Network.Pusher.Protocol: [fullChannelInfoSubCount] :: FullChannelInfo -> Maybe Int
+ Network.Pusher.Protocol: [fullChannelInfoUserCount] :: FullChannelInfo -> Maybe Int
+ Network.Pusher.Protocol: data ChannelInfo
+ Network.Pusher.Protocol: data FullChannelInfo
+ Network.Pusher.Protocol: renderChannel :: Channel -> Text
+ Network.Pusher.Protocol: renderChannelPrefix :: ChannelType -> Text
- Data.Pusher: getPusherWithConnManager :: Manager -> Credentials -> Pusher
+ Data.Pusher: getPusherWithConnManager :: Manager -> Maybe Text -> Credentials -> Pusher
- Network.Pusher.Protocol: ChannelInfo :: (HashSet ChannelInfoAttributeResp) -> ChannelInfo
+ Network.Pusher.Protocol: ChannelInfo :: Maybe Int -> ChannelInfo
- Network.Pusher.Protocol: FullChannelInfo :: (HashSet FullChannelAttributeResp) -> FullChannelInfo
+ Network.Pusher.Protocol: FullChannelInfo :: Bool -> Maybe Int -> Maybe Int -> FullChannelInfo

Files

pusher-http-haskell.cabal view
@@ -1,5 +1,5 @@ name:                 pusher-http-haskell-version:              0.2.1.0+version:              0.3.0.0 cabal-version:        >=1.18 build-type:           Simple license:              MIT
src/Data/Pusher.hs view
@@ -13,12 +13,14 @@   ( Pusher(..)   , Credentials(..)   , getPusher+  , getPusherWithHost   , getPusherWithConnManager   ) where  import Control.Applicative ((<$>), (<*>)) import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Aeson ((.:))+import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import Data.Text.Encoding (encodeUtf8) import Network.HTTP.Client (Manager, defaultManagerSettings, newManager)@@ -26,7 +28,7 @@ import qualified Data.ByteString as B import qualified Data.Text as T -import Network.Pusher.Internal.Util (failExpectObj)+import Network.Pusher.Internal.Util (failExpectObj, show')  -- |All the required configuration needed to interact with the API. data Pusher = Pusher@@ -54,15 +56,26 @@ -- automatically. getPusher :: MonadIO m => Credentials -> m Pusher getPusher cred = do-  connManager <- liftIO $ newManager defaultManagerSettings-  return $ getPusherWithConnManager connManager cred+    connManager <- getConnManager+    return $ getPusherWithConnManager connManager Nothing cred -getPusherWithConnManager :: Manager -> Credentials -> Pusher-getPusherWithConnManager connManager cred =-  let path = "/apps/" <> T.pack (show $ credentialsAppID cred) <> "/" in+-- |Get a Pusher instance that uses a specific API endpoint.+getPusherWithHost :: MonadIO m => T.Text -> Credentials -> m Pusher+getPusherWithHost apiHost cred = do+  connManager <- getConnManager+  return $ getPusherWithConnManager connManager (Just apiHost) cred++-- |Get a Pusher instance with a given connection manager. This can be useful+-- if you want to share a connection with your application code.+getPusherWithConnManager :: Manager -> Maybe T.Text -> Credentials -> Pusher+getPusherWithConnManager connManager apiHost cred =+  let path = "/apps/" <> show' (credentialsAppID cred) <> "/" in   Pusher-    { pusherHost = "http://api.pusherapp.com"+    { pusherHost = fromMaybe "http://api.pusherapp.com" apiHost     , pusherPath = path     , pusherCredentials = cred     , pusherConnectionManager = connManager     }++getConnManager :: MonadIO m => m Manager+getConnManager = liftIO $ newManager defaultManagerSettings
src/Network/Pusher.hs view
@@ -29,12 +29,13 @@  @   let-    pusher = getPusher $ Credentials-      { credentials'appID = 123-      , credentials'appKey = wrd12344rcd234-      , credentials'appSecret = 124df34d545v+    credentials = Credentials+      { credentialsAppID = 123+      , credentialsAppKey = wrd12344rcd234+      , credentialsAppSecret = 124df34d545v       }-  result <- runPusherT (Pusher.trigger ["my-channel"] "my-event" "my-data") pusher+  pusher <- getPusher credentials+  result <- runPusherT (trigger [Channel Public "my-channel"] "my-event" "my-data" Nothing) pusher   case result of     Right resp -> print resp     Left e -> error e@@ -77,7 +78,6 @@   , makeQS   ) import Network.Pusher.Internal.HTTP (get, post)-import Network.Pusher.Internal.Util (show') import Network.Pusher.Protocol   ( Channel   , ChannelInfo@@ -87,6 +87,8 @@   , ChannelType   , FullChannelInfo   , Users+  , renderChannel+  , renderChannelPrefix   , toURLParam   ) @@ -106,7 +108,7 @@   let     body = A.object $       [ ("name", A.String event)-      , ("channels", A.toJSON (map (A.String . show') chans))+      , ("channels", A.toJSON (map (A.String . renderChannel) chans))       , ("data", A.String dat)       ] ++ maybeToList (fmap (\sID ->  ("socket_id", A.String sID)) socketId)     bodyBS = BL.toStrict $ A.encode body@@ -128,7 +130,7 @@   -> m ChannelsInfo -- ^The returned data channels channelTypeFilter prefixFilter attributes = do   let-    prefix = maybe "" show' channelTypeFilter <> prefixFilter+    prefix = maybe "" renderChannelPrefix channelTypeFilter <> prefixFilter     params =       [ ("info", encodeUtf8 $ toURLParam attributes)       , ("filter_by_prefix", encodeUtf8 prefix)@@ -146,7 +148,7 @@   -> m FullChannelInfo channel chan attributes = do   let params = [("info", encodeUtf8 $ toURLParam attributes)]-  (ep, path) <- getEndpoint $ "channels/" <> show' chan+  (ep, path) <- getEndpoint $ "channels/" <> renderChannel chan   qs <- makeQSWithTS "GET" path params ""   connManager <- asks pusherConnectionManager   get connManager (encodeUtf8 ep) qs@@ -157,7 +159,7 @@  => Channel  -> m Users users chan = do-  (ep, path) <- getEndpoint $ "channels/" <> show' chan <> "/users"+  (ep, path) <- getEndpoint $ "channels/" <> renderChannel chan <> "/users"   qs <- makeQSWithTS "GET" path [] ""   connManager <- asks pusherConnectionManager   get connManager (encodeUtf8 ep) qs
src/Network/Pusher/Internal/Auth.hs view
@@ -32,6 +32,7 @@ import qualified Crypto.MAC.HMAC as HMAC  import Data.Pusher (Credentials(..))+import Network.Pusher.Internal.Util (show')  -- |Generate the required query string parameters required to send API requests -- to Pusher.@@ -49,7 +50,7 @@     -- Generate all required parameters and add them to the list of existing ones     allParams = sortWith fst $ params ++       [ ("auth_key", appKey)-      , ("auth_timestamp", BC.pack $ show ts)+      , ("auth_timestamp", show' ts)       , ("auth_version", "1.0")       , ("body_md5", B16.encode (MD5.hash body))       ]@@ -113,4 +114,3 @@       )   in     credentialsAppKey cred <> ":" <> sig-
src/Network/Pusher/Internal/HTTP.hs view
@@ -40,6 +40,7 @@ import qualified Data.Text as T  import Control.Monad.Pusher.HTTP (MonadHTTP(httpLbs))+import Network.Pusher.Internal.Util (show')  import Debug.Trace @@ -84,7 +85,7 @@   -> Maybe BL.ByteString   -> m (Response BL.ByteString) makeRequest connManager ep qs body = do-  req <- either (throwError . T.pack . show) return (parseUrl $ BC.unpack ep)+  req <- either (throwError . show') return (parseUrl $ BC.unpack ep)   let     req' = setQueryString (map (second Just) qs) req     req'' = case body of@@ -102,7 +103,7 @@   if statusCode status == 200 then      run   else-     throwError $ either (T.pack . show) id $ decodeUtf8' $ statusMessage status+     throwError $ either show' id $ decodeUtf8' $ statusMessage status  errorOn200 :: (MonadError T.Text m) => Response BL.ByteString -> m () errorOn200 response = when200 response (return ())
src/Network/Pusher/Protocol.hs view
@@ -18,17 +18,17 @@   ( Channel(..)   , ChannelInfo(..)   , ChannelInfoAttributes(..)-  , ChannelInfoAttributeResp(..)   , ChannelInfoQuery(..)   , ChannelsInfo(..)   , ChannelsInfoQuery(..)   , ChannelsInfoAttributes(..)   , ChannelType(..)   , FullChannelInfo(..)-  , FullChannelAttributeResp(..)   , User(..)   , Users(..)   , parseChannel+  , renderChannel+  , renderChannelPrefix   , toURLParam   ) where @@ -37,6 +37,7 @@ import Data.Foldable (asum) import Data.Hashable (Hashable) import Data.Maybe (fromMaybe)+import Data.Monoid ((<>)) import GHC.Generics (Generic) import Test.QuickCheck (Arbitrary, arbitrary, elements) import qualified Data.Aeson as A@@ -44,35 +45,35 @@ import qualified Data.HashSet as HS import qualified Data.Text as T -import Network.Pusher.Internal.Util (failExpectObj, show')+import Network.Pusher.Internal.Util (failExpectObj)  -- |The possible types of Pusher channe.-data ChannelType = Public | Private | Presence deriving (Eq, Generic)+data ChannelType = Public | Private | Presence deriving (Eq, Generic, Show)  instance Hashable ChannelType -instance Show ChannelType where-  show Public = ""-  show Private = "private-"-  show Presence = "presence-"- instance Arbitrary ChannelType where   arbitrary = elements [Public, Private, Presence] +renderChannelPrefix :: ChannelType -> T.Text+renderChannelPrefix Public = ""+renderChannelPrefix Private = "private-"+renderChannelPrefix Presence = "presence-"+ -- |The channel name (not including the channel type prefix) and its type. data Channel = Channel   { channelType :: ChannelType   , channelName :: T.Text-  } deriving (Eq, Generic)+  } deriving (Eq, Generic, Show)  instance Hashable Channel -instance Show Channel where-  show (Channel chanType name) = show chanType ++ T.unpack name- instance Arbitrary Channel where   arbitrary = Channel <$> arbitrary <*> (T.pack <$> arbitrary) +renderChannel :: Channel -> T.Text+renderChannel (Channel cType cName) = renderChannelPrefix cType <> cName+ -- |Convert string representation, e.g. private-chan into the datatype parseChannel :: T.Text -> Channel parseChannel chan =@@ -82,7 +83,7 @@     (asum [parseChanAs Private,  parseChanAs Presence])  where   parseChanAs chanType =-    let split = T.splitOn (show' chanType) chan in+    let split = T.splitOn (renderChannelPrefix chanType) chan in     -- If the prefix appears at the start, then the first element will be empty     if length split > 1 && T.null (head split) then       Just $ Channel chanType (T.concat $ tail split)@@ -144,56 +145,30 @@       v1 -> failExpectObj v1   parseJSON v2 = failExpectObj v2 --- |A set of returned channel attributes for a single channel.-newtype ChannelInfo =-  ChannelInfo (HS.HashSet ChannelInfoAttributeResp)-  deriving (Eq, Show)+-- |The possible returned channel attributes when multiple when multiple+-- channels are queried.+data ChannelInfo = ChannelInfo+  { channelInfoUserCount :: Maybe Int+  } deriving (Eq, Show)  instance A.FromJSON ChannelInfo where-  parseJSON (A.Object v) = do-    maybeUserCount <- v .:? "user_count"-    return $ ChannelInfo $ maybe-      HS.empty-      (HS.singleton . UserCountResp)-      maybeUserCount+  parseJSON (A.Object v) = ChannelInfo <$> v .:? "user_count"   parseJSON v = failExpectObj v --- |An enumeration of possible returned channel attributes when multiple--- when multiple channels are queried.-data ChannelInfoAttributeResp = UserCountResp Int deriving (Eq, Generic, Show)--instance Hashable ChannelInfoAttributeResp---- |A set of returned channel attributes for a single channel.-newtype FullChannelInfo =-  FullChannelInfo (HS.HashSet FullChannelAttributeResp)-  deriving (Eq, Show)+-- |The possible values returned by a query to a single channel+data FullChannelInfo = FullChannelInfo+  { fullChannelInfoOccupied :: Bool+  , fullChannelInfoUserCount :: Maybe Int+  , fullChannelInfoSubCount :: Maybe Int+  } deriving (Eq, Show)  instance A.FromJSON FullChannelInfo where-  parseJSON (A.Object v) = do-    occupied <- v .: "occupied"-    maybeUserCount <- v .:? "user_count"-    maybeSubCount <- v .:? "subscription_count"-    let-      hs = HS.singleton (OccupiedResp occupied)-      hs' =  maybeInsert (FullUserCountResp <$> maybeUserCount) hs-      hs'' =  maybeInsert (SubscriptionCountResp <$> maybeSubCount) hs'-    return $ FullChannelInfo hs''-   where-    maybeInsert :: (Eq a, Hashable a) => Maybe a -> HS.HashSet a -> HS.HashSet a-    maybeInsert maybeVal hs = maybe hs (`HS.insert` hs) maybeVal-+  parseJSON (A.Object v) =+    FullChannelInfo+      <$> v .: "occupied"+      <*> v .:? "user_count"+      <*> v .:? "subscription_count"   parseJSON v = failExpectObj v---- |An enumeration of possible returned channel attributes when a single--- channel is queried-data FullChannelAttributeResp-  = OccupiedResp Bool-  | FullUserCountResp Int-  | SubscriptionCountResp Int-  deriving (Eq, Generic, Show)--instance Hashable FullChannelAttributeResp  -- |A list of users returned by querying for users in a presence channel. newtype Users = Users [User] deriving (Eq, Show)
test/Protocol.hs view
@@ -10,25 +10,26 @@ import Network.Pusher.Protocol   ( Channel(..)   , ChannelInfo(..)-  , ChannelInfoAttributeResp(UserCountResp)   , ChannelsInfo(..)   , ChannelType(Public, Presence, Private)+  , FullChannelInfo(..)   , User(..)   , Users(..)   , parseChannel+  , renderChannel   )  test :: Spec test = do   describe "Protocol.Channel" $ do     it "show instance works for public channels" $-      show (Channel Public "test") `shouldBe` "test"+      renderChannel (Channel Public "test") `shouldBe` "test"      it "show instance works for private channels" $-      show (Channel Private "test") `shouldBe` "private-test"+      renderChannel (Channel Private "test") `shouldBe` "private-test"      it "show instance is an inverse of parseChannel" $-      property $ \chan -> parseChannel (T.pack $ show chan) == chan+      property $ \chan -> parseChannel (renderChannel chan) == chan    describe "Protocol.ChannelsInfo" $     it "parsing works" $@@ -46,11 +47,11 @@ \       }"       `shouldBe`         (Just $ ChannelsInfo $ HM.fromList-          [ (Channel Presence "foobar", ChannelInfo $ HS.fromList [UserCountResp 42])-          , (Channel Presence "another", ChannelInfo $ HS.fromList [UserCountResp 123])+          [ (Channel Presence "foobar", ChannelInfo $ Just 42)+          , (Channel Presence "another", ChannelInfo $ Just 123)           ]) -  describe "Protocol.ChannelInfo" $+  describe "Protocol.FullChannelInfo" $     it "parsing works" $       -- Data from https://pusher.com/docs/rest_api#successful-response-2       A.decode@@ -60,8 +61,7 @@ \         \"subscription_count\": 42\ \       }"       `shouldBe`-        -- TODO: Currently incomplete due to ChannelInfo being incomplete-        (Just $ ChannelInfo $ HS.fromList [UserCountResp 42])+        (Just $ FullChannelInfo True (Just 42) (Just 42))    describe "Protocol.Users" $     it "parsing works" $