{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE OverloadedStrings #-} module Snap.AtlassianConnect.NetworkCommon ( HttpResponseCode , ProductErrorResponse(..) , AccessTokenResponse(..) , AccessToken(..) , AccessTokenType(..) , responder ) where import Data.Aeson import qualified Data.ByteString.Lazy as BL import Data.Maybe (fromMaybe) import qualified Data.Text as T import qualified Data.Text.Encoding as T import Data.Time.Units import GHC.Generics import Text.Read (readMaybe) type HttpResponseCode = Int -- | If you fail to properly communicate with the Host application then you will get a product error response back. data ProductErrorResponse = ProductErrorResponse { perCode :: HttpResponseCode -- ^ The HTTP error code. , perMessage :: T.Text -- ^ The error message. } deriving (Show, Generic) -- | This represents the response that an OAuth Access token request should expect to recieve from auth.atlassian.io. data AccessTokenResponse = AccessTokenResponse { atrAccessToken :: AccessToken -- ^ The access token that can be used in subsequent HTTP requests. , atrExpiresIn :: Second -- ^ The duration until this access token expires and can no longer be used. , atrTokenType :: AccessTokenType -- ^ The type of token that was returned from auth.atlassian.io. } instance FromJSON AccessTokenResponse where parseJSON = withObject "AccessTokenResponse" $ \v -> AccessTokenResponse <$> (AccessToken <$> v .: "access_token") <*> (fromInteger <$> v .: "expires_in") <*> ((fromMaybe UnknownAccessTokenType . readMaybe) <$> v .: "token_type") -- | An access token that can be used in subsequent requests. data AccessToken = AccessToken T.Text -- | The type of access token that was returned from the token generation service. data AccessTokenType = Bearer -- ^ An OAuth 2 bearer token. | UnknownAccessTokenType -- ^ A token type that could not be parsed. deriving (Eq, Enum, Read) responder :: FromJSON a => Int -> BL.ByteString -> Either ProductErrorResponse (Maybe a) responder responseCode body | responseCode == 204 = Right Nothing | 200 <= responseCode && responseCode < 300 = case eitherDecode body of Right jsonResponse -> Right . Just $ jsonResponse Left err -> Left $ ProductErrorResponse responseCode (T.pack $ "Could not parse the json response: " ++ show err) | otherwise = Left $ ProductErrorResponse responseCode (T.decodeUtf8 . BL.toStrict $ body)