{-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_HADDOCK prune #-} -- | A thin wrapper for the Hyperpublic API, which uses -- "Network.HTTP.Enumerator" to fetch results and "Data.Aeson" to return them -- as JSON. The "Web.Hyperpublic.Places" and "Web.Hyperpublic.Offers" modules -- wrap @find@ and @show@ methods of the Places+ -- (<http://www.hyperpublic.com/placesplus>) and Geo Deals and Events -- (<http://www.hyperpublic.com/deals>) endpoints, respectively. The next -- release of this package will also wrap the @create@ method of Places+. Full -- API documentation can be found at <http://developer.hyperpublic.com/>. -- -- "Web.Hyperpublic.Places" and "Web.Hyperpublic.Offers" export the functions -- @find@ and @show@, where each works similarly across the two modules. The -- next release of this package will also wrap the @create@ method of Places+. -- -- Examples (included in the distribution as @Examples.hs@): -- -- >{-# LANGUAGE OverloadedStrings #-} -- > -- >module Main where -- > -- >import Data.Aeson -- >import Data.ByteString.Char8 () -- >import qualified Data.Map as M -- >import Data.Maybe -- >import Data.Text -- >import qualified Data.Vector as V -- > -- >import Web.Hyperpublic -- >import qualified Web.Hyperpublic.Places as Places -- >import qualified Web.Hyperpublic.Offers as Offers -- > -- >-- Sequence the two examples. -- >main :: IO () -- >main = placeNamesNearHq >> offerDescr -- > -- >-- Find places near Hyperpublic HQ and print the name of each. -- >placeNamesNearHq :: IO () -- >placeNamesNearHq = -- > let json = Places.find auth [( "address" -- > , "416 W 13th St, New York, NY 10014" )] -- > in json >>= putStrLn . show . getNames -- > where -- > getNames (Array arr) = mapMaybe getName $ V.toList arr -- > getName (Object obj) = getTextField obj "display_name" -- > -- >-- Find the offer with id 4e90567c297a200001008db9 and print its description. -- >offerDescr :: IO () -- >offerDescr = -- > let json = Offers.show auth "4e90567c297a200001008db9" -- > in json >>= putStrLn . show . getDescr -- > where -- > getDescr (Object obj) = maybe "" id $ getTextField obj "description" -- > -- >-- Create an authorization record. Get your own credentials at -- >-- http://www.hyperpublic.com/registerapi -- >auth :: HpAuth -- >auth = HpAuth { clientId = "8UufhI6bCKQXKMBn7AUWO67Yq6C8RkfD0BGouTke" -- > , clientSecret = "zdoROY5XRN0clIWsEJyKzHedSK4irYee8jpnOXaP" } -- > -- >-- Extract a text field from an 'Data.Aeson.Object'. -- >getTextField :: Object -> Text -> Maybe Text -- >getTextField obj txt = M.lookup txt obj >>= resultToMaybe . fromJSON -- > where -- > resultToMaybe (Success a) = Just a -- > resultToMaybe (Error _) = Nothing module Web.Hyperpublic ( HpAuth (..) , callApi ) where import Data.Aeson ( Value , json ) import Data.Attoparsec ( eitherResult , parse ) import Data.ByteString ( ByteString , append ) import Data.ByteString.Char8 ( unpack ) import qualified Data.ByteString.Lazy as Lazy import Network.HTTP.Enumerator ( Request (..) , Response (..) , def , httpLbsRedirect , withManager ) import Network.HTTP.Types ( SimpleQuery , simpleQueryToQuery ) callApi :: HpAuth -> ByteString -> SimpleQuery -> IO Value callApi auth urlPath query = do let query' = [ ("client_id", clientId auth) , ("client_secret", clientSecret auth) ] ++ filter cleanQuery query rsp <- withManager (httpLbsRedirect $ request urlPath query') eitherToIO $ Right rsp >>= readResponse >>= eitherResult . parse json where cleanQuery (k, _) = k /= "client_id" && k /= "client_secret" -- | A record for passing around API authorization credentials. data HpAuth = HpAuth { clientId :: ByteString , clientSecret :: ByteString } deriving (Show) request :: ByteString -> SimpleQuery -> Request m request urlPath query = def { host = "api.hyperpublic.com" , path = "/api/v1" `append` urlPath , queryString = simpleQueryToQuery query , port = 443 , secure = True } readResponse :: Response -> Either String ByteString readResponse rsp = let strictBody = foldr1 append $ Lazy.toChunks $ responseBody rsp in if statusCode rsp == 200 then Right strictBody else Left $ unpack strictBody eitherToIO :: Either String a -> IO a eitherToIO (Right a) = return a eitherToIO (Left str) = fail str