curl-aeson (empty) → 0.0.3
raw patch · 4 files changed
+239/−0 lines, 4 filesdep +aesondep +basedep +bytestringsetup-changed
Dependencies added: aeson, base, bytestring, curl, text
Files
- LICENSE +24/−0
- Setup.hs +2/−0
- curl-aeson.cabal +38/−0
- src/Network/Curl/Aeson.hs +175/−0
+ LICENSE view
@@ -0,0 +1,24 @@+Copyright (c) 2013, Joel Lehtonen+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:+ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ * Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.+ * Neither the name of the <organization> nor the+ names of its contributors may be used to endorse or promote products+ derived from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE+DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ curl-aeson.cabal view
@@ -0,0 +1,38 @@+name: curl-aeson+version: 0.0.3+synopsis: Communicate with HTTP service using JSON +description: A library for communicating with JSON over HTTP connection.+ Supports rich set of HTTP connectivity features provided by+ libcurl combined to the performance and elegancy of aeson.+ .+ All HTTP methods are supported. Instances of 'ToJSON' and+ 'FromJSON' typeclasses can be transferred via this library.+ Session cookies and other HTTP options may be passed to libcurl+ if needed.+ .+ This library is at its best when communicating with simple,+ non-standardized JSON interfaces. If you are implementing+ JSON-RPC compliant client or server, take a look at+ <http://hackage.haskell.org/package/jmacro-rpc>.+category: Network, Web, JSON+license: BSD3+license-file: LICENSE+author: Joel Lehtonen+maintainer: joel.lehtonen+curlaeson@iki.fi+homepage: https://github.com/zouppen/haskell-curl-aeson+build-type: Simple+cabal-version: >= 1.6++library+ hs-source-dirs: src+ exposed-modules: Network.Curl.Aeson+ build-depends:+ aeson >= 0.6.0.0,+ base >= 4 && < 5,+ bytestring,+ curl >= 1.3.7,+ text++source-repository head+ type: git+ location: git://github.com/zouppen/haskell-curl-aeson.git
+ src/Network/Curl/Aeson.hs view
@@ -0,0 +1,175 @@+{-# LANGUAGE DeriveDataTypeable, RecordWildCards #-}+-- |+-- Module : Network.Curl.Aeson+-- Copyright : (c) 2013, Joel Lehtonen+-- License : BSD3+--+-- Maintainer: Joel Lehtonen <joel.lehtonen+curlaeson@iki.fi>+-- Stability : experimental+-- Portability: portable+--+-- Functions for communicating with JSON over HTTP connection.++module Network.Curl.Aeson+ ( -- * How to use this library+ -- $use+ + -- * Sending HTTP request+ curlAesonGet+ , curlAesonGetWith+ , curlAeson+ -- * Helper functions+ , cookie+ , rawJson+ , (...)+ , noData+ -- * Exception handling+ , CurlAesonException(..)+ ) where++import Control.Exception+import Control.Monad+import Data.Aeson+import Data.Aeson.Types+import Data.ByteString.Lazy.Char8 (pack,unpack)+import Data.Maybe+import Data.Text (Text)+import Data.Typeable+import Network.Curl++-- | Shorthand for doing just a HTTP GET request and parsing the output to+-- any FromJSON instance.+curlAesonGet :: (FromJSON a) => URLString -> IO a+curlAesonGet = curlAesonGetWith parseJSON++-- | Shorthand for doing just a HTTP GET request and parsing the+-- output with given parser /p/.+curlAesonGetWith :: (Value -> Parser a) -> URLString -> IO a+curlAesonGetWith p url = curlAeson p "GET" url [] noData++-- | Send single HTTP request.+-- +-- The request automatically has @Content-type: application/json@+-- header if you pass any data. This function is lenient on response+-- content type: everything is accepted as long as it is parseable+-- with 'decode'.+-- +-- If you need authentication, you need to pass session cookie or+-- other means of authentication tokens via 'CurlOption' list.+curlAeson ::+ (ToJSON a)+ => (Value -> Parser b) -- ^ Parser for response. Use 'parseJSON' if+ -- you like want to use FromJSON instance or+ -- 'pure' if you want it in AST format.+ -> String -- ^ Request method+ -> URLString -- ^ Request URL+ -> [CurlOption] -- ^ Session cookies, or other cURL+ -- options. Use empty list if you don't need+ -- any.+ -> Maybe a -- ^ JSON data to send, or Nothing when+ -- sending request without any content.+ -> IO b -- ^ Received JSON data+curlAeson parser method url extraOpts maybeContent = do+ (curlCode,received) <- curlGetString url curlOpts+ when (curlCode /= CurlOK) $ throw CurlAesonException{errorMsg="HTTP error",..}+ let ast = case decode $ pack received of+ Nothing -> throw CurlAesonException{errorMsg="JSON parsing failed",..}+ Just x -> x+ return $ case parseEither parser ast of+ Left errorMsg -> throw CurlAesonException{..}+ Right x -> x+ where+ curlOpts = commonOpts++dataOpts++extraOpts+ commonOpts = [CurlCustomRequest method]+ dataOpts = case maybeContent of+ Nothing -> []+ Just a -> [CurlPostFields [unpack $ encode a]+ ,CurlHttpHeaders ["Content-type: application/json"]+ ]++-- | Helper function for writing parsers for JSON objects which are+-- not needed to be parsed completely.+--+-- In this example we are parsing JSON from+-- <http://json.org/example.html>. Note the use of the+-- @OverloadedStrings@ language extension which enables 'Text' values+-- to be written as string literals.+--+-- @p ('Object' o) = 'pure' obj'...'\"glossary\"'...'\"title\"+--p _ = 'mzero'+-- @+(...) :: FromJSON b+ => Parser Object -- ^ Parser to JSON object to look into+ -> Text -- ^ Key to look for+ -> Parser b -- ^ Parser to the resulting field+(...) p s = do+ o <- p+ o .: s++-- Precedence should be higher than >> and >>= but lower than +++infixl 4 ...++-- | Single cookie of given key and value.+cookie :: String -> String -> CurlOption+cookie key value = CurlCookie $ key++"="++value++-- | Useful for just giving the JSON as string when it is static+-- anyway and doesn't need to be programmatically crafted.+rawJson :: String -> Maybe Value+rawJson = decode . pack++-- |To avoid ambiguity in type checker you may pass this value instead+-- of Nothing to 'curlAeson'.+noData :: Maybe Value+noData = Nothing++-- | This exception is is thrown when Curl doesn't finish cleanly or+-- the parsing of JSON response fails.+data CurlAesonException = CurlAesonException { url :: URLString+ , curlCode :: CurlCode+ , curlOpts :: [CurlOption]+ , received :: String+ , errorMsg :: String+ } deriving (Show, Typeable)+instance Exception CurlAesonException++-- $use+--+-- To get bid and ask levels as a pair from a Bitcoin exchange using its public+-- API:+--+-- @{-\# LANGUAGE OverloadedStrings #-}+--import Control.Monad+--import Data.Aeson+--import Network.Curl.Aeson+--+--ticker :: 'IO' ('Double','Double')+--ticker = 'curlAesonGetWith' p \"https:\/\/bitcoin-central.net\/api\/v1\/ticker\/eur\"+-- where+-- p ('Object' o) = do+-- bid <- o '.:' \"bid\"+-- ask <- o '.:' \"ask\"+-- 'return' (bid,ask)+-- p _ = 'mzero'+-- @+-- +-- The same as above, but we define our own data type which is an+-- instance of FromJSON:+-- +-- @{-\# LANGUAGE OverloadedStrings #-}+--import Control.Applicative+--import Control.Monad+--import Data.Aeson+--import Network.Curl.Aeson+--+--data Ticker = Ticker { bid :: 'Double'+-- , ask :: 'Double'+-- } deriving ('Show')+--+--instance 'FromJSON' Ticker where+-- parseJSON ('Object' o) = Ticker '<$>' o '.:' \"bid\" '<*>' o '.:' \"ask\"+-- parseJSON _ = 'mzero'+--+--ticker :: 'IO' Ticker+--ticker = 'curlAesonGet' \"https:\/\/bitcoin-central.net\/api\/v1\/ticker\/eur\"+-- @