packages feed

apioiaf-client (empty) → 0.1.0.0

raw patch · 5 files changed

+303/−0 lines, 5 filesdep +aesondep +apioiaf-clientdep +basesetup-changed

Dependencies added: aeson, apioiaf-client, base, bytestring, lens, wreq

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Author name here (c) 2016++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 Author name here nor the names of other+      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 THE COPYRIGHT+OWNER OR CONTRIBUTORS 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
+ apioiaf-client.cabal view
@@ -0,0 +1,42 @@+name:                apioiaf-client+version:             0.1.0.0+synopsis:            Consumer library for anapioficeandfire.com+description:         +  .+  A wrapper library for consuming the data provided by <anapioficeandfire.com>+  .+  See README for API details <https://github.com/kberger/anapioficeandfire-haskell#readme>+homepage:            https://github.com/kberger/anapioficeandfire-haskell#readme+license:             BSD3+license-file:        LICENSE+author:              Karl Berger+maintainer:          krab.berger@gmail.com+copyright:           2016 Karl Berger+category:            Web+build-type:          Simple+-- extra-source-files:+cabal-version:       >=1.10++library+  ghc-options:         -Wall+  hs-source-dirs:      src+  exposed-modules:     IceAndFire+  build-depends:       base >= 4.7 && < 5+                     , wreq+                     , aeson+                     , lens+                     , bytestring+  default-language:    Haskell2010++test-suite apioiaf-client-test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Spec.hs+  build-depends:       base+                     , apioiaf-client+  ghc-options:         -Wall -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/kberger/anapioficeandfire-haskell
+ src/IceAndFire.hs view
@@ -0,0 +1,227 @@+{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}++module IceAndFire+    ( Book(..)+    , Character(..)+    , House(..)+    , getBookById+    , getCharacterById+    , getCharactersByName+    , getCharactersByCulture+    , getCharactersByGender+    , getHouseById+    , getHouseByName+    ) where++import Network.Wreq+import Data.Aeson+import Data.Aeson.Types+import Data.ByteString.Char8 (unpack)+import Control.Lens++data Book = Book+    { bookUrl :: String+    , bookName :: String+    , isbn :: String+    , authors :: [String]+    , numberOfPages :: Integer+    , publisher :: String+    , country :: String+    , mediaType :: String+    , released :: String+    , characters :: [String]+    , povCharacters :: [String] +    } deriving Show++instance FromJSON Book where+    parseJSON (Object v) = Book <$>+                           v .: "url" <*>+                           v .: "name" <*>+                           v .: "isbn" <*>+                           v .: "authors" <*>+                           v .: "numberOfPages" <*>+                           v .: "publisher" <*>+                           v .: "country" <*>+                           v .: "mediaType" <*>+                           v .: "released" <*>+                           v .: "characters" <*>+                           v .: "povCharacters"+    parseJSON invalid    = typeMismatch "Book" invalid++data Character = Character+    { charUrl :: String+    , charName :: String+    , gender :: String+    , culture :: String+    , born :: String+    , died :: String+    , charTitles :: [String]+    , aliases :: [String]+    , father :: String+    , mother :: String+    , spouse :: String+    , allegiances :: [String]+    , books :: [String]+    , povBooks :: [String]+    , tvSeries :: [String]+    , playedBy :: [String]+    } deriving Show++instance FromJSON Character where+    parseJSON (Object v) = Character <$>+                           v .: "url" <*>+                           v .: "name" <*>+                           v .: "gender" <*>+                           v .: "culture" <*>+                           v .: "born" <*>+                           v .: "died" <*>+                           v .: "titles" <*>+                           v .: "aliases" <*>+                           v .: "father" <*>+                           v .: "mother" <*>+                           v .: "spouse" <*>+                           v .: "allegiances" <*>+                           v .: "books" <*>+                           v .: "povBooks" <*>+                           v .: "tvSeries" <*>+                           v .: "playedBy"+    parseJSON invalid    = typeMismatch "Character" invalid++data House = House+    { houseUrl :: String+    , houseName :: String+    , region :: String+    , coatOfArms :: String+    , words :: String+    , houseTitles :: [String]+    , seats :: [String]+    , currentLord :: String+    , heir :: String+    , overlord :: String+    , founded :: String+    , founder :: String+    , diedOut :: String+    , ancestralWeapons :: [String]+    , cadetBranches :: [String]+    , swornMembers :: [String]+    } deriving Show++instance FromJSON House where+    parseJSON (Object v) = House <$>+                           v .: "url" <*>+                           v .: "name" <*>+                           v .: "region" <*>+                           v .: "coatOfArms" <*>+                           v .: "words" <*>+                           v .: "titles" <*>+                           v .: "seats" <*>+                           v .: "currentLord" <*>+                           v .: "heir" <*>+                           v .: "overlord" <*>+                           v .: "founded" <*>+                           v .: "founder" <*>+                           v .: "diedOut" <*>+                           v .: "ancestralWeapons" <*>+                           v .: "cadetBranches" <*>+                           v .: "swornMembers"+    parseJSON invalid    = typeMismatch "House" invalid++baseUrl :: String+baseUrl = "http://www.anapioficeandfire.com/api"++-- | Get Book by id number+getBookById :: Int -> IO (Maybe Book)+getBookById = loadSingleById "books"++-- | Get Book by name+getBookByName :: String -> IO [Book]+getBookByName bName = entityQuery "books" [("name", bName)]++-- | Get Character by id number+getCharacterById :: Int -> IO (Maybe Character)+getCharacterById = loadSingleById "characters"++-- | Get Characters by name+getCharactersByName :: String -> IO [Character]+getCharactersByName cName = entityQuery "characters" [("name", cName)]++-- | Get Character by culture+--+-- Example:+--+-- @+-- 'getCharactersByCulture' \"dothraki\"+-- @+--+-- >>> d <- getCharactersByCulture "dothraki"+-- >>> List.length d+-- 23+getCharactersByCulture :: String -> IO [Character]+getCharactersByCulture cCulture = entityQuery "characters" [("culture", cCulture)]++-- | Get Character by gender+--+-- Example:+--+-- @+-- 'getCharactersByGender' \"female\"+-- @+--+-- >>> f <- getCharactersByGender "female"+-- >>> List.length f+-- 461+getCharactersByGender :: String -> IO [Character]+getCharactersByGender cGender = entityQuery "characters" [("gender", cGender)]++-- | Get House by id number+getHouseById :: Int -> IO (Maybe House)+getHouseById = loadSingleById "houses"++-- | Get House by name+getHouseByName :: String -> IO [House]+getHouseByName hName = entityQuery "houses" [("name", hName)]++-- | Get House by region+--+-- Example:+--+-- @+-- 'getHousesByRegion' \"The Crownlands\"+-- @+--+-- >>> c <- getHousesByRegion "The Crownlands"+-- >>> List.length c+-- 49+getHousesByRegion :: String -> IO [House]+getHousesByRegion hRegion = entityQuery "houses" [("region", hRegion)]++-- | Get a House by its words+getHouseByWords :: String -> IO [House]+getHouseByWords hWords = entityQuery "houses" [("hasWords", "true"), ("words", hWords)]++loadSingleById :: (FromJSON a) => String -> Int -> IO (Maybe a)+loadSingleById entityType entityId = do+    response <- get (baseUrl ++ "/" ++ entityType ++ "/" ++ (show entityId) :: String)+    let entityJson = decode (view responseBody response)+    return entityJson++--Mainly used to control common query paramaters like pageSize+--Probably where caching should go+entityQuery :: (FromJSON a) => String -> [(String,String)] -> IO [a]+entityQuery entityType qParams = +    loadFromQueryUrl (baseUrl ++ "/" ++ entityType ++ "/?pageSize=50" ++ paramsToStr qParams)+        where paramsToStr = foldl (\ acc (k,v) -> acc ++ "&" ++ k ++ "=" ++ v) ""++loadFromQueryUrl :: (FromJSON a) => String -> IO [a]+loadFromQueryUrl url = do+    response <- get url+    let entity = decode (view responseBody response)+    let unpacked = case entity of (Just [])   -> []+                                  (Just list) -> list+                                  Nothing     -> []+    -- Greedily consume and append 'next' links until none more+    let nextLink = (response ^? responseLink "rel" "next" . linkURL)+    let next = case nextLink of (Just linkUrl) -> loadFromQueryUrl (unpack linkUrl)+                                Nothing        -> return ([] :: [a])+    nextList <- next+    return (unpacked ++ nextList)
+ test/Spec.hs view
@@ -0,0 +1,2 @@+main :: IO ()+main = putStrLn "Test suite not yet implemented"