module OANDA.Internal.Request
( constructRequest
, baseURL
, commaList
, jsonOpts
, jsonResponse
, jsonResponseArray
, formatTimeRFC3339
) where
import qualified Data.Aeson.TH as TH
import qualified Data.ByteString as BS
import qualified Data.Map as Map
import OANDA.Internal.Import
import OANDA.Internal.Types
baseURL :: OandaEnv -> String
baseURL env = apiEndpoint (apiType env)
where apiEndpoint Sandbox = "http://api-sandbox.oanda.com"
apiEndpoint Practice = "https://api-fxpractice.oanda.com"
apiEndpoint Live = "https://api-fxtrade.oanda.com"
constructRequest :: OandaEnv -> String -> [(Text, Maybe [Text])] -> IO Request
constructRequest env url params = do
initRequest <- parseRequest url
return $
initRequest
& maybe id makeAuthHeader (accessToken env)
& setRequestQueryString params'
where
makeAuthHeader (AccessToken t) = addRequestHeader "Authorization" ("Bearer " `BS.append` t)
paramToMaybe (name, Just xs') = Just (encodeUtf8 name, Just $ encodeUtf8 $ commaList xs')
paramToMaybe (_, Nothing) = Nothing
params' = catMaybes $ fmap paramToMaybe params
commaList :: [Text] -> Text
commaList = intercalate ","
jsonOpts :: String -> TH.Options
jsonOpts s = TH.defaultOptions { TH.fieldLabelModifier = firstLower . drop (length s) }
where firstLower [] = []
firstLower (x:xs) = toLower x : xs
jsonResponse :: (FromJSON a) => Request -> IO a
jsonResponse request = getResponseBody <$> httpJSON request
jsonResponseArray :: (FromJSON a) => Request -> String -> IO a
jsonResponseArray request name =
do body <- jsonResponse request
return $ body Map.! (name :: String)
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 (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)