github-rest-1.1.3: src/GitHub/REST.hs
{-# LANGUAGE CPP #-}
{-|
Module : GitHub.REST
Maintainer : Brandon Chinn <brandonchinn178@gmail.com>
Stability : experimental
Portability : portable
Definitions for querying the GitHub REST API. See README.md for an example.
-}
module GitHub.REST (
-- * Monad transformer and type-class for querying the GitHub REST API
MonadGitHubREST (..),
GitHubT,
GitHubSettings (..),
runGitHubT,
-- * GitHub authentication
Token (..),
-- * GitHub Endpoints
GHEndpoint (..),
GitHubData,
EndpointVals,
-- * KeyValue pairs
KeyValue (..),
-- * Helpers
githubTry,
githubTry',
(.:),
-- * Re-exports
StdMethod (..),
) where
import Control.Monad.IO.Unlift (MonadUnliftIO)
import Data.Aeson (FromJSON, Value (..), decode, withObject)
import Data.Aeson.Types (parseEither, parseField)
import qualified Data.ByteString.Lazy as ByteStringL
import Data.Text (Text)
import Network.HTTP.Client (
HttpException (..),
HttpExceptionContent (..),
Response (..),
)
import Network.HTTP.Types (Status, StdMethod (..), status422)
import UnliftIO.Exception (handleJust)
#if MIN_VERSION_aeson(2,0,0)
import Data.Aeson.Key (fromText)
#endif
import GitHub.REST.Auth
import GitHub.REST.Endpoint
import GitHub.REST.KeyValue
import GitHub.REST.Monad
{- HTTP exception handling -}
-- | Handle 422 exceptions thrown by the GitHub REST API.
--
-- Most client errors are 422, since we should always be sending valid JSON. If an endpoint
-- throws different error codes, use githubTry'.
--
-- https://developer.github.com/v3/#client-errors
githubTry :: (MonadUnliftIO m) => m a -> m (Either Value a)
githubTry = githubTry' status422
-- | Handle the given exception thrown by the GitHub REST API.
githubTry' :: (MonadUnliftIO m) => Status -> m a -> m (Either Value a)
githubTry' status = handleJust statusException (return . Left) . fmap Right
where
statusException (HttpExceptionRequest _ (StatusCodeException r body))
| responseStatus r == status = decode $ ByteStringL.fromStrict body
statusException _ = Nothing
{- Aeson helpers -}
-- | Get the given key from the Value, erroring if it doesn't exist.
(.:) :: (FromJSON a) => Value -> Text -> a
(.:) v key = either error id $ parseEither parseObject v
where
parseObject = withObject "parseObject" (`parseField` fromText key)
#if !MIN_VERSION_aeson(2,0,0)
fromText :: Text -> Text
fromText = id
#endif