module OANDA.Util
( constructOpts
, baseURL
, makeParams
, commaList
, jsonOpts
, jsonResponse
, jsonResponseArray
, jsonDelete
, formatTimeRFC3339
) where
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative (Applicative, pure)
import System.Locale (defaultTimeLocale)
#endif
import Control.Lens
import Data.Aeson
import qualified Data.Aeson.TH as TH
import Data.ByteString (append)
import Data.Char (toLower)
import Data.Decimal
import qualified Data.Map as Map
import Data.Monoid (
#if !MIN_VERSION_base(4,8,0)
mempty,
#endif
(<>))
import Data.Scientific
import Data.Text (Text, intercalate, unpack)
import Data.Time
import Network.Wreq
import OANDA.Types
baseURL :: OandaEnv -> String
baseURL env = apiEndpoint (apiType env)
constructOpts :: OandaEnv -> [(Text, [Text])] -> Options
constructOpts env = constructOpts' (accessToken env)
constructOpts' :: Maybe AccessToken -> [(Text, [Text])] -> Options
constructOpts' maybeTok ps = defaults & params' & header'
where params' = makeParams ps
header' = maybe id makeHeader maybeTok
makeHeader (AccessToken t) = header "Authorization" .~ ["Bearer " `append` t]
makeParams :: [(Text, [Text])] -> Options -> Options
makeParams xs = params .~ params'
where params' = [(name, commaList p) | (name, p) <- xs]
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) => String -> Options -> IO a
jsonResponse url opts =
do r <- asJSON =<< getWith opts url
return $ r ^. responseBody
jsonResponseArray :: (FromJSON a) => String -> Options -> String -> IO a
jsonResponseArray url opts name =
do body <- jsonResponse url opts
return $ body Map.! (name :: String)
jsonDelete :: (FromJSON a) => String -> Options -> IO a
jsonDelete url opts =
do r <- asJSON =<< deleteWith opts url
return $ r ^. responseBody
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)