{-# LANGUAGE DataKinds #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE UnicodeSyntax #-} {-# LANGUAGE ViewPatterns #-} -- | StackExchange API request manipulation routines module Network.StackExchange.Request ( -- * Type Request(..), Auth(..), SE(..), Object(..) -- * Constructing requests , host, path, method, parse , query, token, key, site, filter, state, Scope(..), scope , client, redirectURI, secret, code ) where import Data.Monoid (Monoid(..), (<>)) import GHC.TypeLits import Prelude hiding (filter) import Control.Lens hiding (query) import Data.ByteString.Lazy (ByteString) import Data.Aeson (FromJSON) import Data.Aeson.Types (Value) import Data.Default (Default(..)) import Data.Map (Map) import qualified Data.Map as M import Data.Text.Lazy (Text) import qualified Data.Text.Lazy as T import Data.Text.Lazy.Builder (toLazyText) import Data.Text.Lazy.Builder.Int (decimal) -- | Whether to use authentication at all. Currently isn't used data Auth = RequireToken | Ready -- | SE response type data Object = AccessToken -- ^ | AccountMerge -- ^ | Answer -- ^ | Badge -- ^ | Comment -- ^ | Error -- ^ | Event -- ^ | Filter -- ^ | InboxItem -- ^ | Info -- ^ | NetworkUser -- ^ | Notification -- ^ | Post -- ^ | Privilege -- ^ | Question -- ^ | QuestionTimeline -- ^ | Reputation -- ^ | ReputationHistory -- ^ | Revision -- ^ | Site -- ^ | SuggestedEdit -- ^ | Tag -- ^ | TagScore -- ^ | TagSynonym -- ^ | TagWiki -- ^ | TopTag -- ^ | User -- ^ | UserTimeline -- ^ | WritePermission -- ^ -- | SE response value wrapper newtype SE (a ∷ Object) = SE { unSE ∷ Value } deriving (Show, FromJSON) -- | StackExchange API Request data type. -- -- @a@ is a phantom type showing whether authentication is enabled -- -- @n@ is a phantom type dissallowing combination of -- different API calls in one request -- -- @r@ is a type of parsed API call result data Request (a ∷ Auth) (n ∷ Symbol) r = Request { _host ∷ Text -- ^ API host link , _path ∷ Text -- ^ API call link , _method ∷ Text -- ^ API call method (GET/POST) , _query ∷ Map Text Text -- ^ API call query parameters , _parse ∷ Maybe (ByteString → r) -- ^ API call result parsing function } makeLensesFor [ ("_host", "__host") , ("_path", "__path") , ("_method", "__method") , ("_query", "__query") , ("_parse", "__parse") ] ''Request -- | Subject to monoid and idempotent laws, they all are checked in request test suite instance Monoid (Request a n r) where mempty = Request { _host = mempty , _path = mempty , _method = mempty , _query = mempty , _parse = Nothing } l `mappend` r = Request { _host = _host $ if T.null $ _host r then l else r , _path = _path $ if T.null $ _path r then l else r , _method = _method $ if T.null $ _method r then l else r , _query = _query r <> _query l , _parse = _parse $ case _parse r of Just _ → r; Nothing → l } -- | Useful if what's needed is immediate result parse instance Functor (Request a n) where fmap f = over __parse (fmap (f .)) {-# INLINE fmap #-} -- | Default StackExchange API request, defines only host link instance Default (Request a n r) where def = mempty % __host .~ "https://api.stackexchange.com/2.1" % __method .~ "GET" {-# INLINE def #-} -- | Request defining only API call host -- -- Primarily used in Auth, not intended for usage by library user host ∷ Text → Request a n r host p = mempty % __host .~ p {-# INLINE host #-} -- | Request defining only API call path -- -- Primarily used in API call wrappers, not intended for usage by library user path ∷ Text → Request a n r path p = mempty % __path .~ p {-# INLINE path #-} -- | Request defining only call method -- -- Primarily used in API call wrappers, not intended for usage by library user method ∷ Text → Request a n r method m = mempty % __method .~ m {-# INLINE method #-} -- | Request defining only API call result parsing function -- -- Primarily used in API call wrappers, not intended for usage by library user parse ∷ (ByteString → r) → Request a n r parse f = mempty % __parse ?~ f {-# INLINE parse #-} -- | Request defining only API call query parameters -- -- Rather low level interface. For more specific usage 'site', -- 'filter', etc calls may be more convenient -- -- -- Takes a list of (key, value) parameters such as @[("order", "asc"), ("sort", "rank")]@ query ∷ [(Text, Text)] → Request a n r query q = mempty % __query .~ M.fromList q {-# INLINE query #-} -- | Convert token requiring Request into ready one token ∷ Text → Request RequireToken n r → Request Ready n r token t = over __query (M.insert "access_token" t) {-# INLINE token #-} -- | Request defining only App key key ∷ Text → Request a n r key s = mempty % __query .~ M.singleton "key" s {-# INLINE key #-} -- | Request defining only API call site query parameter site ∷ Text → Request a n r site s = mempty % __query .~ M.singleton "site" s {-# INLINE site #-} -- | Request defining only API call filter query parameter filter ∷ Text → Request a n r filter f = mempty % __query .~ M.singleton "filter" f {-# INLINE filter #-} -- | Request defining only API call state query parameter state ∷ Text → Request a n r state s = mempty % __query .~ M.singleton "state" s {-# INLINE state #-} -- | Scope defines permission granted for application by user data Scope = ReadInbox | NoExpiry | WriteAccess | PrivateInfo -- | Request defining only API call scope query parameter scope ∷ [Scope] → Request a n r scope ss = mempty % __query .~ (M.singleton "scope" $ scopie ss) where scopie xs = T.intercalate "," . flip map xs $ \case ReadInbox → "read_inbox" NoExpiry → "no_expiry" WriteAccess → "write_access" PrivateInfo → "private_info" -- | Request defining only Authentication API call application id -- -- Primarily used in Authentication API call wrappers, not intended for usage by library user client ∷ Int → Request a n r client (toLazyText . decimal → c) = mempty % __query .~ M.singleton "client_id" c {-# INLINE client #-} -- | Request defining only Authentication API call redirect url -- -- Primarily used in Authentication API call wrappers, not intended for usage by library user redirectURI ∷ Text → Request a n r redirectURI r = mempty % __query .~ M.singleton "redirect_uri" r {-# INLINE redirectURI #-} -- | Request defining only Authentication API call application secret -- -- Primarily used in Authentication API call wrappers, not intended for usage by library user secret ∷ Text → Request a n r secret c = mempty % __query .~ M.singleton "client_secret" c {-# INLINE secret #-} -- | Request defining only Authentication API call code -- -- Primarily used in Authentication API call wrappers, not intended for usage by library user code ∷ Text → Request a n r code c = mempty % __query .~ M.singleton "code" c {-# INLINE code #-}