module OANDA.Internal.Request
( OANDARequest (..)
, makeOandaRequest
, OANDAStreamingRequest (..)
, makeOandaStreamingRequest
, baseApiRequest
, baseStreamingRequest
, apiBaseURL
, streamingBaseURL
, formatTimeRFC3339
) where
import Control.Monad.IO.Class
import Control.Monad.Trans.Resource (MonadResource)
import qualified Data.ByteString as BS
import Data.Conduit
import qualified Network.HTTP.Client as H
import OANDA.Internal.Import
import OANDA.Internal.Types
newtype OANDARequest a = OANDARequest { unOANDARequest :: Request }
deriving (Show)
makeOandaRequest :: (MonadIO m, FromJSON a) => OANDARequest a -> m a
makeOandaRequest (OANDARequest request) = getResponseBody <$> httpJSON request
newtype OANDAStreamingRequest a = OANDAStreamingRequest { unOANDAStreamingRequest :: Request }
deriving (Show)
makeOandaStreamingRequest :: (MonadResource m, FromJSON a) => OANDAStreamingRequest a -> Source m a
makeOandaStreamingRequest (OANDAStreamingRequest request) = httpSource request parseBody
where
parseBody response = mapOutput (either error id . eitherDecodeStrict) $ getResponseBody response
apiBaseURL :: OandaEnv -> String
apiBaseURL env = apiEndpoint (apiType env)
where
apiEndpoint Practice = "https://api-fxpractice.oanda.com"
apiEndpoint Live = "https://api-fxtrade.oanda.com"
streamingBaseURL :: OandaEnv -> String
streamingBaseURL env = apiEndpoint (apiType env)
where
apiEndpoint Practice = "https://stream-fxpractice.oanda.com"
apiEndpoint Live = "https://stream-fxtrade.oanda.com"
baseRequest :: OandaEnv -> String -> String -> String -> Request
baseRequest env baseUrl requestType url =
unsafeParseRequest (requestType ++ " " ++ baseUrl ++ url)
& makeAuthHeader (accessToken env)
where
makeAuthHeader (AccessToken t) = addRequestHeader "Authorization" ("Bearer " `BS.append` t)
baseApiRequest :: OandaEnv -> String -> String -> Request
baseApiRequest env = baseRequest env (apiBaseURL env)
baseStreamingRequest :: OandaEnv -> String -> String -> Request
baseStreamingRequest env = baseRequest env (streamingBaseURL env)
unsafeParseRequest :: String -> Request
unsafeParseRequest = unsafeParseRequest' . H.parseUrlThrow
where
unsafeParseRequest' (Left err) = error $ show err
unsafeParseRequest' (Right request) = request
formatTimeRFC3339 :: ZonedTime -> String
formatTimeRFC3339 zt@(ZonedTime _ z) = formatTime defaultTimeLocale "%FT%T" zt <> printZone
where timeZoneStr = timeZoneOffsetString z
printZone = if timeZoneStr == timeZoneOffsetString utc
then "Z"
else take 3 timeZoneStr <> ":" <> drop 3 timeZoneStr
instance (Show a, Integral a) => ToJSON (DecimalRaw a) where
toJSON = toJSON . show
instance (Integral a) => FromJSON (DecimalRaw a) where
parseJSON (Number n) = readDecimalJSON n
parseJSON (String s) = readDecimalJSON (read (unpack s))
parseJSON _ = mempty
readDecimalJSON :: (Num i, Applicative f) => Scientific -> f (DecimalRaw i)
readDecimalJSON n = pure $ Decimal ((*) (1) $ fromIntegral $ base10Exponent n)
(fromIntegral $ coefficient n)