packages feed

hurriyet (empty) → 0.1.0.0

raw patch · 13 files changed

+496/−0 lines, 13 filesdep +aesondep +basedep +bytestringsetup-changed

Dependencies added: aeson, base, bytestring, hspec, http-client, http-client-tls, hurriyet, text

Files

+ LICENSE view
@@ -0,0 +1,7 @@+Copyright 2017 Yiğit Özkavcı++Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hurriyet.cabal view
@@ -0,0 +1,52 @@+-- Initial haq.cabal generated by cabal init.  For further documentation, +-- see http://haskell.org/cabal/users-guide/++name:               hurriyet+version:            0.1.0.0+license:            BSD3+license-file:       LICENSE+author:             Yiğit Özkavcı <yigitozkavci8@gmail.com>+maintainer:         Yiğit Özkavcı <yigitozkavci8@gmail.com>+build-type:         Simple+cabal-version:      >=1.10+category:           Network+homepage:           https://github.com/yigitozkavci/hurriyet-haskell+bug-reports:        https://github.com/yigitozkavci/hurriyet-haskell/issues+synopsis:           Haskell bindings for Hurriyet API+description:        hurriyet-haskell is the client library for communicating with Hurriyet API (developers.hurriyet.com.tr)+                    .+                    To get started, see @Hurriyet@ module below.++library+  build-depends:    base >= 4.5 && <= 4.9,+                    aeson,+                    bytestring,+                    http-client,+                    http-client-tls,+                    text+  exposed-modules:  Hurriyet,+                    Hurriyet.Services,+                    Hurriyet.Services.Article,+                    Hurriyet.Services.Page,+                    Hurriyet.Services.NewsPhotoGallery,+                    Hurriyet.Services.Column,+                    Hurriyet.Services.Path,+                    Hurriyet.Services.Writer,+                    Hurriyet.Services.File+  hs-source-dirs:   lib+  default-language: Haskell2010++test-suite tests+  ghc-options:        -Wall+  default-extensions: OverloadedStrings+  type:               exitcode-stdio-1.0+  main-is:            Spec.hs+  build-depends:      base,+                      hurriyet,+                      hspec >= 1.8+  hs-source-dirs:     tests+  default-language:   Haskell2010++source-repository   head+  type:             git+  location:         https://github.com/yigitozkavci/hurriyet-haskell
+ lib/Hurriyet.hs view
@@ -0,0 +1,186 @@+{-# LANGUAGE OverloadedStrings #-}++{-|+Module      : Hurriyet+Copyright   : (c) Yiğit Özkavcı, 2017+License     : WTFPL+Maintainer  : yigitozkavci8@gmail.com+Stability   : experimental+Portability : GHC++This module controls all the data flow and client interface required to interact with the library.+-}+module Hurriyet where++import Network.HTTP.Client+import Network.HTTP.Client.TLS+import Data.Text (Text)+import Data.ByteString.Lazy as L+import Data.ByteString.Internal as I+import Data.ByteString.Char8 as C+import Hurriyet.Services+import Data.Aeson (decode, eitherDecode)+++{- Planned client usage:+   defaultOptions :: Options+   getClient :: I.ByteString -> Client+   getArticles :: Client -> IO (Either String [Articles])+-}++{-| Type synonym for Id which will be used for endpoints+-}+type Id = String++{-| Type synonym for api key+-}+type ApiKey = String++{-| Client will be used and passed through every+    API call+-}+newtype Client = Client+  { apiKey :: ApiKey+  }++{-| This is how you construct the client. Takes apiKey as+    an argument+-}+getClient :: String -> Client+getClient = Client++{-| Each of these resources represent services. These are+    used for passing service-spesific data such as endpoint string+-}+data Resource = ArticleResource+              | PageResource+              | NewsPhotoGalleryResource+              | ColumnResource+              | PathResource+              | WriterResource++{-| Each resource represents an endpoint string+-}+instance Show Resource where+  show ArticleResource          = "articles"+  show PageResource             = "pages"+  show NewsPhotoGalleryResource = "newsphotogalleries"+  show ColumnResource           = "columns"+  show PathResource             = "paths"+  show WriterResource           = "writers"++{-| For now, Hurriyet API only consists of 2 operations. As we have+    more, new operations will be added into here+-}+data Operation = List Resource+               | Show Resource Id++{-| Base url of the Hurriyet API+-}+baseUrl :: String+baseUrl =+  "https://api.hurriyet.com.tr/v1/"++{-| This method constructs url given the operation. Operation already+    contains enough data to construct the url+-}+getUrl :: Operation -> String+getUrl operation =+  case operation of+    List resource    -> baseUrl ++ show resource+    Show resource id -> baseUrl ++ show resource ++ "/" ++ id++{-| Given a client and a operation, this method can conquer the world. But prefers+    not to and returns a bytestring representing json string of the response body+-}+fetchResource :: Client -> Operation -> IO L.ByteString+fetchResource client operation = do+  manager <- newManager tlsManagerSettings+  initialRequest <- parseRequest $ getUrl operation+  let request = initialRequest { method = "GET", requestHeaders = [("apikey", C.pack $ apiKey client)] }+  response <- httpLbs request manager+  return $ responseBody response++{-| Get a single page+-}+getPage :: Client -> Id -> IO (Either String Page)+getPage client id =+  fetchResource client (Show PageResource id) >>= \str ->+    return $ eitherDecode str++{-| Get all pages+-}+getPages :: Client -> IO (Either String [Page])+getPages client =+  fetchResource client (List PageResource) >>= \str ->+    return $ eitherDecode str++{-| Get a single news photo gallery+-}+getNewsPhotoGallery :: Client -> Id -> IO (Either String NewsPhotoGallery)+getNewsPhotoGallery client id =+  fetchResource client (Show NewsPhotoGalleryResource id) >>= \str ->+    return $ eitherDecode str++{-| Get all news photo galleries+-}+getNewsPhotoGalleries :: Client -> IO (Either String [NewsPhotoGallery])+getNewsPhotoGalleries client =+  fetchResource client (List NewsPhotoGalleryResource) >>= \str ->+    return $ eitherDecode str++{-| Get a single column+-}+getColumn :: Client -> Id -> IO (Either String Column)+getColumn client id =+  fetchResource client (Show ColumnResource id) >>= \str ->+    return $ eitherDecode str++{-| Get all columns+-}+getColumns :: Client -> IO (Either String [Column])+getColumns client =+  fetchResource client (List ColumnResource) >>= \str ->+    return $ eitherDecode str++{-| Get a single path+-}+getPath :: Client -> Id -> IO (Either String Path)+getPath client id =+  fetchResource client (Show PathResource id) >>= \str ->+    return $ eitherDecode str++{-| Get all paths+-}+getPaths :: Client -> IO (Either String [Path])+getPaths client =+  fetchResource client (List PathResource) >>= \str ->+    return $ eitherDecode str++{-| Get a single writer+-}+getWriter :: Client -> Id -> IO (Either String Writer)+getWriter client id =+  fetchResource client (Show WriterResource id) >>= \str ->+    return $ eitherDecode str++{-| Get all writers+-}+getWriters :: Client -> IO (Either String [Writer])+getWriters client =+  fetchResource client (List WriterResource) >>= \str ->+    return $ eitherDecode str++{-| Get single article+-}+getArticle :: Client -> Id -> IO (Either String Article)+getArticle client id =+  fetchResource client (Show ArticleResource id) >>= \str ->+    return $ eitherDecode str++{-| Get all articles+-}+getArticles :: Client -> IO (Either String [Article])+getArticles client =+  fetchResource client (List ArticleResource) >>= \str ->+    return $ eitherDecode str
+ lib/Hurriyet/Services.hs view
@@ -0,0 +1,18 @@+module Hurriyet.Services+  ( Article+  , Column+  , File+  , Metadata+  , NewsPhotoGallery+  , Page+  , Path+  , Writer+  ) where++import Hurriyet.Services.Article+import Hurriyet.Services.Column+import Hurriyet.Services.File+import Hurriyet.Services.NewsPhotoGallery+import Hurriyet.Services.Page+import Hurriyet.Services.Path+import Hurriyet.Services.Writer
+ lib/Hurriyet/Services/Article.hs view
@@ -0,0 +1,39 @@+{-# LANGUAGE OverloadedStrings, DeriveGeneric, RecordWildCards #-}++module Hurriyet.Services.Article where++import Data.Aeson+import GHC.Generics+import Hurriyet.Services.File++data Article = Article+  { _id          :: String+  , contentType  :: String+  , createdDate  :: String+  , description  :: String+  {- Hurriyet API does not include modifiedDate in their `show` response for articles yet.+     Tracking issue: https://github.com/hurriyet/developers.hurriyet.com.tr/issues/27+  -}+  -- , modifiedDate :: String+  , path         :: String+  , files        :: [File]+  , startDate    :: String+  , title        :: String+  , url          :: String+  } deriving (Generic, Show)++instance FromJSON Article where+  parseJSON = withObject "article" $ \o -> do+    _id          <- o .: "Id"+    contentType  <- o .: "ContentType"+    createdDate  <- o .: "CreatedDate"+    description  <- o .: "Description"+    files        <- o .: "Files"+    -- modifiedDate <- o .: "ModifiedDate"+    path         <- o .: "Path"+    startDate    <- o .: "StartDate"+    title        <- o .: "Title"+    url          <- o .: "Url"+    return Article {..}++instance ToJSON Article
+ lib/Hurriyet/Services/Column.hs view
@@ -0,0 +1,38 @@+{-# LANGUAGE OverloadedStrings, DeriveGeneric, RecordWildCards #-}++module Hurriyet.Services.Column where++import Data.Aeson+import GHC.Generics+import Hurriyet.Services.File++data Column = Column+  { _id          :: String+  , fullName     :: String+  , contentType  :: String+  , createdDate  :: String+  , description  :: String+  , files        :: [File]+  , path         :: String+  , startDate    :: String+  , title        :: String+  , url          :: String+  , writerId     :: String+  } deriving (Generic, Show)++instance FromJSON Column where+  parseJSON = withObject "column" $ \o -> do+    _id          <- o .: "Id"+    fullName     <- o .: "Fullname"+    contentType  <- o .: "ContentType"+    createdDate  <- o .: "CreatedDate"+    description  <- o .: "Description"+    files        <- o .: "Files"+    path         <- o .: "Path"+    startDate    <- o .: "StartDate"+    title        <- o .: "Title"+    url          <- o .: "Url"+    writerId     <- o .: "WriterId"+    return Column {..}++instance ToJSON Column
+ lib/Hurriyet/Services/File.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE OverloadedStrings, DeriveGeneric, RecordWildCards #-}++module Hurriyet.Services.File where++import Data.Aeson+import GHC.Generics++data File = File+  { fileUrl  :: String+  , metadata :: Metadata+  } deriving(Generic, Show)++instance FromJSON File where+  parseJSON = withObject "file" $ \o -> do+    fileUrl  <- o .: "FileUrl"+    metadata <- o .: "Metadata"+    return File{..}++instance ToJSON File++newtype Metadata = Metadata+  { title       :: String+  -- , description :: String -- This field does not exist on article show page. Tracking issue: https://github.com/hurriyet/developers.hurriyet.com.tr/issues/28+  } deriving(Generic, Show)++instance FromJSON Metadata where+  parseJSON = withObject "metadata" $ \o -> do+    title       <- o .: "Title"+    -- description <- o .: "Description"+    return Metadata{..}++instance ToJSON Metadata
+ lib/Hurriyet/Services/NewsPhotoGallery.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE OverloadedStrings, DeriveGeneric, RecordWildCards #-}++module Hurriyet.Services.NewsPhotoGallery where++import Data.Aeson+import GHC.Generics+import Hurriyet.Services.File++data NewsPhotoGallery = NewsPhotoGallery+  { _id          :: String+  , contentType  :: String+  , createdDate  :: String+  , description  :: String+  , files        :: [File]+  , modifiedDate :: String+  , path         :: String+  , startDate    :: String+  , title        :: String+  , url          :: String+  } deriving (Generic, Show)++instance FromJSON NewsPhotoGallery where+  parseJSON = withObject "news_photo_gallery" $ \o -> do+    _id          <- o .: "Id"+    contentType  <- o .: "ContentType"+    createdDate  <- o .: "CreatedDate"+    description  <- o .: "Description"+    files        <- o .: "Files"+    modifiedDate <- o .: "ModifiedDate"+    path         <- o .: "Path"+    startDate    <- o .: "StartDate"+    title        <- o .: "Title"+    url          <- o .: "Url"+    return NewsPhotoGallery {..}++instance ToJSON NewsPhotoGallery
+ lib/Hurriyet/Services/Page.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE OverloadedStrings, DeriveGeneric, RecordWildCards #-}++module Hurriyet.Services.Page where++import Data.Aeson+import GHC.Generics++{- TODO: Add relatedNews -}+data Page = Page+  { _id :: String+  , createdDate :: String+  , title :: String+  {- TODO: Check if pages have file field. If so, add it.+     Currently pages endpoint does not respond.+  -}+  , url :: String+  } deriving (Generic, Show)++instance FromJSON Page where+  parseJSON = withObject "page" $ \o -> do+    _id          <- o .: "Id"+    createdDate  <- o .: "CreatedDate"+    title        <- o .: "Title"+    url          <- o .: "Url"+    return Page {..}
+ lib/Hurriyet/Services/Path.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE OverloadedStrings, DeriveGeneric, RecordWildCards #-}++module Hurriyet.Services.Path where++import Data.Aeson+import GHC.Generics++data Path = Path+  { _id          :: String+  , path         :: String+  , title        :: String+  } deriving (Generic, Show)++instance FromJSON Path where+  parseJSON = withObject "path" $ \o -> do+    _id   <- o .: "Id"+    path  <- o .: "Path"+    title <- o .: "Title"+    return Path {..}++instance ToJSON Path
+ lib/Hurriyet/Services/Writer.hs view
@@ -0,0 +1,30 @@+{-# LANGUAGE OverloadedStrings, DeriveGeneric, RecordWildCards #-}++module Hurriyet.Services.Writer where++import Data.Aeson+import GHC.Generics+import Hurriyet.Services.File++data Writer = Writer+  { _id         :: String+  , fullName    :: String+  , contentType :: String+  , createdDate :: String+  , files       :: [File]+  , path        :: String+  , url         :: String+  } deriving (Generic, Show)++instance FromJSON Writer where+  parseJSON = withObject "writer" $ \o -> do+    _id         <- o .: "Id"+    fullName    <- o .: "Fullname"+    contentType <- o .: "ContentType"+    createdDate <- o .: "CreatedDate"+    files       <- o .: "Files"+    path        <- o .: "Path"+    url         <- o .: "Url"+    return Writer {..}++instance ToJSON Writer
+ tests/Spec.hs view
@@ -0,0 +1,10 @@+module Main where+ +import Hurriyet+import Test.Hspec+ +main :: IO ()+main = hspec $+  describe "apiKey returns api key string" $+    it "returns" $+      take 1 (show apiKey) `shouldBe` "\""