{-# LANGUAGE BlockArguments #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} module App.AWS.Env ( awsLogger , mkEnv , newAwsLogger , setEnvEndpoint ) where import App.Show (tshow) import Control.Concurrent (myThreadId) import Control.Lens ((.~), (%~)) import Control.Monad (when, forM_) import Data.ByteString (ByteString) import Data.ByteString.Builder (toLazyByteString) import Data.Function ((&)) import Data.Generics.Product.Any (the) import Network.HTTP.Client (HttpException (..), HttpExceptionContent (..)) import qualified Amazonka as AWS import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as L import qualified Data.ByteString.Lazy as LBS import qualified Data.ByteString.Lazy.Char8 as LC8 import qualified Data.Text.Encoding as T import qualified App.Console as CIO import qualified System.IO as IO setEnvEndpoint :: Maybe (ByteString, Int, Bool) -> IO AWS.Env -> IO AWS.Env setEnvEndpoint mHostEndpoint getEnv = do env <- getEnv case mHostEndpoint of Just (host, port, ssl) -> pure $ env & the @"overrides" .~ \svc -> do svc & the @"endpoint" %~ \mkEndpoint region -> do mkEndpoint region & the @"host" .~ host & the @"port" .~ port & the @"secure" .~ ssl Nothing -> pure env mkEnv :: AWS.Region -> (AWS.LogLevel -> LBS.ByteString -> IO ()) -> IO AWS.Env mkEnv region lg = do lgr <- newAwsLogger lg discoveredEnv <- AWS.newEnv AWS.discover pure discoveredEnv { AWS.logger = lgr , AWS.region = region , AWS.retryCheck = retryPolicy 5 } newAwsLogger :: Monad m => (AWS.LogLevel -> LBS.ByteString -> IO ()) -> m AWS.Logger newAwsLogger lg = return $ \y b -> let lazyMsg = toLazyByteString b in case L.toStrict lazyMsg of msg | BS.isInfixOf "404 Not Found" msg -> lg AWS.Debug lazyMsg msg | BS.isInfixOf "304 Not Modified" msg -> lg AWS.Debug lazyMsg _ -> lg y lazyMsg retryPolicy :: Int -> Int -> AWS.HttpException -> Bool retryPolicy maxNum attempt ex = (attempt <= maxNum) && shouldRetry ex shouldRetry :: AWS.HttpException -> Bool shouldRetry ex = case ex of HttpExceptionRequest _ ctx -> case ctx of ResponseTimeout -> True ConnectionTimeout -> True ConnectionFailure _ -> True InvalidChunkHeaders -> True ConnectionClosed -> True InternalException _ -> True NoResponseDataReceived -> True ResponseBodyTooShort _ _ -> True _ -> False _ -> False awsLogger :: Maybe AWS.LogLevel -> AWS.LogLevel -> LC8.ByteString -> IO () awsLogger maybeConfigLogLevel msgLogLevel message = forM_ maybeConfigLogLevel $ \configLogLevel -> when (msgLogLevel <= configLogLevel) do threadId <- myThreadId CIO.hPutStrLn IO.stderr $ "[" <> tshow msgLogLevel <> "] [tid: " <> tshow threadId <> "]" <> text where text = T.decodeUtf8 $ LBS.toStrict message