reddit-0.3.0.0: test-io/ConfigLoad.hs
module ConfigLoad where
import Control.Applicative
import Data.List
import Data.Semigroup (Semigroup(..))
import Data.Text (Text)
import Data.Yaml
import Network.HTTP.Client
import Network.HTTP.Client.TLS
import Reddit
import Reddit.Login
import System.Directory
import System.Environment
import System.Exit
import Utils
import qualified Data.Text as Text
newtype RunReddit = RunReddit
{ run :: forall a. Reddit a -> IO (Either (APIError RedditError) a) }
data Opts =
Opts { optsUsername :: Maybe Text
, optsPassword :: Maybe Text
, optsSubreddit :: Maybe Text
, optsClientId :: Maybe Text
, optsClientSecret :: Maybe Text
}
deriving (Show, Eq, Ord)
instance Semigroup Opts where
Opts u1 p1 s1 ci1 cs1 <> Opts u2 p2 s2 ci2 cs2 =
Opts (u2 <|> u1)
(p2 <|> p1)
(s2 <|> s1)
(ci2 <|> ci1)
(cs2 <|> cs1)
instance Monoid Opts where
mappend = (<>)
mempty = Opts mempty mempty mempty mempty mempty
instance FromJSON Opts where
parseJSON = withObject "Opts" $ \ o ->
Opts <$> o .:? "username"
<*> o .:? "password"
<*> o .:? "subreddit"
<*> o .:? "client_id"
<*> o .:? "client_secret"
getEnvOpts :: IO Opts
getEnvOpts =
Opts <$> lookupEnv' "REDDIT_USERNAME"
<*> lookupEnv' "REDDIT_PASSWORD"
<*> lookupEnv' "REDDIT_SUBREDDIT"
<*> lookupEnv' "REDDIT_CLIENT_ID"
<*> lookupEnv' "REDDIT_CLIENT_SECRET"
where
lookupEnv' x = fmap (fmap Text.pack) (lookupEnv x)
data Config =
Config { cfgUsername :: Text
, cfgPassword :: Text
, cfgSubreddit :: Text
, cfgClientId :: Text
, cfgClientSecret :: Text
}
deriving (Show, Eq, Ord)
newtype Validation e a = Validation (Either e a)
deriving (Show, Read, Eq, Ord, Functor)
instance Semigroup e => Applicative (Validation e) where
pure = Validation . Right
Validation a <*> Validation b =
Validation $ case (a, b) of
(Left ea, Left eb) -> Left (ea <> eb)
(Left ea, Right _) -> Left ea
(Right _, Left eb) -> Left eb
(Right f, Right x) -> Right (f x)
optionsToConfig :: Opts -> Validation [String] Config
optionsToConfig Opts{..} =
Config <$> get "optsUsername was missing" optsUsername
<*> get "optsPassword was missing" optsPassword
<*> get "optsSubreddit was missing" optsSubreddit
<*> get "optsClientId was missing" optsClientId
<*> get "optsClientSecret was missing" optsClientSecret
where
get :: String -> Maybe a -> Validation [String] a
get _ (Just x) = pure x
get msg Nothing = Validation (Left [msg])
loadConfig :: IO (RunReddit, Username, SubredditName)
loadConfig = do
jsonOpts <- do
let configFilePath = "test_config.yaml"
doesFileExist configFilePath >>= \case
False -> do
putStrLn "Warning: missing config, skipping authorized tests"
pure mempty
True ->
decodeFileEither "test_config.yaml" >>= \case
Left err -> do
print err
exitFailure
Right opts -> pure opts
envOpts <- getEnvOpts
case optionsToConfig (jsonOpts <> envOpts) of
Validation (Left errs) ->
error $
intercalate "\n" $
[ "Options were missing, got:"
, " " <> show (jsonOpts <> envOpts)
, "Errors:"
, intercalate "\n" $ map (" " <>) errs
]
Validation (Right (Config user pass sub ci cs)) -> do
manager <- newManager tlsManagerSettings
res <- runAnon $ login user pass (ClientParams ci cs)
case res of
Left err -> do
print err
exitFailure
Right details ->
return ( RunReddit $ runRedditWith $ RedditOptions True (Just manager) (StoredDetails details) (Just "reddit-haskell test suite")
, Username user
, R sub)