module Network.Mattermost.Connection where
import Control.Arrow (left)
import Control.Exception (throwIO)
import qualified Data.Aeson as A
import qualified Data.ByteString.Char8 as B
import qualified Data.ByteString.Lazy.Char8 as BL
import qualified Data.List as List
import qualified Data.Text as T
import qualified Network.HTTP.Base as HTTP
import qualified Network.HTTP.Headers as HTTP
import qualified Network.HTTP.Stream as HTTP
import qualified Network.URI as URI
import Network.Mattermost.Exceptions
import Network.Mattermost.Types
import Network.Mattermost.Types.Internal
import Network.Mattermost.Util
mmPath :: String -> IO URI.URI
mmPath str =
noteE (URI.parseRelativeReference str)
(URIParseException ("mmPath: " ++ str))
jsonResponse :: A.FromJSON t => HTTP.Response_String -> IO t
jsonResponse rsp = do
contentType <- mmGetHeader rsp HTTP.HdrContentType
assertE (contentType ~= "application/json")
(ContentTypeException
("Expected content type 'application/json'" ++
" found " ++ contentType))
hoistE $ left (\s -> JSONDecodeException s (HTTP.rspBody rsp))
(A.eitherDecode (BL.pack (HTTP.rspBody rsp)))
bytestringResponse :: HTTP.Response_String -> IO B.ByteString
bytestringResponse rsp =
return (B.pack (HTTP.rspBody rsp))
noResponse :: HTTP.Response_String -> IO ()
noResponse _ = return ()
mmGetHeader :: HTTP.Response_String -> HTTP.HeaderName -> IO String
mmGetHeader rsp hdr =
noteE (HTTP.lookupHeader hdr (HTTP.rspHeaders rsp))
(HeaderNotFoundException ("mmGetHeader: " ++ show hdr))
mmGetJSONBody :: A.FromJSON t => String -> HTTP.Response_String -> IO (t)
mmGetJSONBody label rsp = do
contentType <- mmGetHeader rsp HTTP.HdrContentType
assertE (contentType ~= "application/json")
(ContentTypeException
("mmGetJSONBody: " ++ label ++ ": " ++
"Expected content type 'application/json'" ++
" found " ++ contentType))
let value = left (\s -> JSONDecodeException ("mmGetJSONBody: " ++ label ++ ": " ++ s)
(HTTP.rspBody rsp))
(A.eitherDecode (BL.pack (HTTP.rspBody rsp)))
hoistE $ do
y <- value
return (y)
doRequest :: HTTP.RequestMethod -> String -> B.ByteString -> Session -> IO HTTP.Response_String
doRequest method uri payload (Session cd token) = do
path <- mmPath ("/api/v4" ++ uri)
rawResponse <- withConnection cd $ \con -> do
let contentLength = B.length payload
request = HTTP.Request
{ HTTP.rqURI = path
, HTTP.rqMethod = method
, HTTP.rqHeaders =
[ HTTP.mkHeader HTTP.HdrAuthorization ("Bearer " ++ getTokenString token)
, HTTP.mkHeader HTTP.HdrHost (T.unpack $ cdHostname cd)
, HTTP.mkHeader HTTP.HdrUserAgent HTTP.defaultUserAgent
, HTTP.mkHeader HTTP.HdrContentType "application/json"
, HTTP.mkHeader HTTP.HdrContentLength (show contentLength)
] ++ autoCloseToHeader (cdAutoClose cd)
, HTTP.rqBody = B.unpack payload
}
runLogger cd "doRequest" (HttpRequest method uri Nothing)
HTTP.simpleHTTP_ con request
rsp <- hoistE (left ConnectionException rawResponse)
case HTTP.rspCode rsp of
(2, _, _) -> return rsp
code -> do
case A.eitherDecode (BL.pack (HTTP.rspBody rsp)) of
Right err ->
throwIO (err :: MattermostError)
Left _ ->
throwIO (HTTPResponseException ("Server returned unexpected " ++ show code ++ " response"))
mkQueryString :: [Maybe (String, String)] -> String
mkQueryString ls =
List.intercalate "&" [ k ++ "=" ++ v | Just (k, v) <- ls ]
jsonBody :: A.ToJSON i => i -> B.ByteString
jsonBody = BL.toStrict . A.encode
noBody :: B.ByteString
noBody = B.empty
inPost
:: String
-> B.ByteString
-> (HTTP.Response_String -> IO o)
-> Session
-> IO o
inPost uri payload k session =
doRequest HTTP.POST uri payload session >>= k
inPut
:: String
-> B.ByteString
-> (HTTP.Response_String -> IO o)
-> Session
-> IO o
inPut uri payload k session =
doRequest HTTP.PUT uri payload session >>= k
inGet
:: String
-> B.ByteString
-> (HTTP.Response_String -> IO o)
-> Session
-> IO o
inGet uri payload k session =
doRequest HTTP.GET uri payload session >>= k
inDelete
:: String
-> B.ByteString
-> (HTTP.Response_String -> IO o)
-> Session
-> IO o
inDelete uri payload k session =
doRequest HTTP.DELETE uri payload session >>= k
doUnauthRequest :: HTTP.RequestMethod -> String -> B.ByteString -> ConnectionData -> IO HTTP.Response_String
doUnauthRequest method uri payload cd = do
path <- mmPath ("/api/v4" ++ uri)
rawResponse <- withConnection cd $ \con -> do
let contentLength = B.length payload
request = HTTP.Request
{ HTTP.rqURI = path
, HTTP.rqMethod = method
, HTTP.rqHeaders =
[ HTTP.mkHeader HTTP.HdrHost (T.unpack $ cdHostname cd)
, HTTP.mkHeader HTTP.HdrUserAgent HTTP.defaultUserAgent
, HTTP.mkHeader HTTP.HdrContentType "application/json"
, HTTP.mkHeader HTTP.HdrContentLength (show contentLength)
] ++ autoCloseToHeader (cdAutoClose cd)
, HTTP.rqBody = B.unpack payload
}
HTTP.simpleHTTP_ con request
rsp <- hoistE (left ConnectionException rawResponse)
case HTTP.rspCode rsp of
(2, _, _) -> return rsp
code -> throwIO (HTTPResponseException ("Server returned unexpected " ++ show code ++ " response"))