-- SPDX-FileCopyrightText: 2020 Tocqueville Group
--
-- SPDX-License-Identifier: LicenseRef-MIT-TQ

-- | Morley client initialization.
module Morley.Client.Init
  ( MorleyClientConfig (..)
  , mkMorleyClientEnv
  , mkLogAction

    -- * Lens
  , mccAliasPrefixL
  , mccEndpointUrlL
  , mccTezosClientPathL
  , mccMbTezosClientDataDirL
  , mccVerbosityL
  , mccSecretKeyL
  ) where

import Colog (cmap, fmtMessage, logTextStderr, msgSeverity)
import Colog.Core (Severity(..), filterBySeverity)
import Morley.Util.Lens
import Servant.Client (BaseUrl(..))
import System.Environment (lookupEnv)

import Morley.Client.Env
import Morley.Client.Logging (ClientLogAction, logFlush)
import Morley.Client.RPC.HttpClient
import Morley.Client.TezosClient.Impl (getTezosClientConfig)
import Morley.Client.TezosClient.Types
import qualified Morley.Tezos.Crypto.Ed25519 as Ed25519

-- | Data necessary for morley client initialization.
data MorleyClientConfig = MorleyClientConfig
  { MorleyClientConfig -> Maybe Text
mccAliasPrefix :: Maybe Text
  -- ^ Optional prefix for aliases that will be passed to @tezos-client@.
  , MorleyClientConfig -> Maybe BaseUrl
mccEndpointUrl :: Maybe BaseUrl
  -- ^ URL of tezos endpoint on which operations are performed
  , MorleyClientConfig -> FilePath
mccTezosClientPath :: FilePath
  -- ^ Path to @tezos-client@ binary through which operations are
  -- performed
  , MorleyClientConfig -> Maybe FilePath
mccMbTezosClientDataDir :: Maybe FilePath
  -- ^ Path to @tezos-client@ data directory.
  , MorleyClientConfig -> Word
mccVerbosity :: Word
  -- ^ Verbosity level. @0@ means that only important messages will be
  -- printed. The greater this value is, the more messages will be
  -- printed during execution. After some small unspecified limit
  -- increasing this value does not change anything.
  , MorleyClientConfig -> Maybe SecretKey
mccSecretKey :: Maybe Ed25519.SecretKey
  -- ^ Custom secret key to use for signing.
  } deriving stock Int -> MorleyClientConfig -> ShowS
[MorleyClientConfig] -> ShowS
MorleyClientConfig -> FilePath
(Int -> MorleyClientConfig -> ShowS)
-> (MorleyClientConfig -> FilePath)
-> ([MorleyClientConfig] -> ShowS)
-> Show MorleyClientConfig
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [MorleyClientConfig] -> ShowS
$cshowList :: [MorleyClientConfig] -> ShowS
show :: MorleyClientConfig -> FilePath
$cshow :: MorleyClientConfig -> FilePath
showsPrec :: Int -> MorleyClientConfig -> ShowS
$cshowsPrec :: Int -> MorleyClientConfig -> ShowS
Show

makeLensesWith postfixLFields ''MorleyClientConfig

-- | Construct 'MorleyClientEnv'.
--
-- * @tezos-client@ path is taken from 'MorleyClientConfig', but can be
-- overridden using @MORLEY_TEZOS_CLIENT@ environment variable.
-- * Node data is taken from @tezos-client@ config and can be overridden
-- by 'MorleyClientConfig'.
-- * The rest is taken from 'MorleyClientConfig' as is.
mkMorleyClientEnv :: MonadIO m => MorleyClientConfig -> IO (MorleyClientEnv' m)
mkMorleyClientEnv :: MorleyClientConfig -> IO (MorleyClientEnv' m)
mkMorleyClientEnv MorleyClientConfig{FilePath
Maybe FilePath
Maybe Text
Maybe BaseUrl
Maybe SecretKey
Word
mccSecretKey :: Maybe SecretKey
mccVerbosity :: Word
mccMbTezosClientDataDir :: Maybe FilePath
mccTezosClientPath :: FilePath
mccEndpointUrl :: Maybe BaseUrl
mccAliasPrefix :: Maybe Text
mccSecretKey :: MorleyClientConfig -> Maybe SecretKey
mccVerbosity :: MorleyClientConfig -> Word
mccMbTezosClientDataDir :: MorleyClientConfig -> Maybe FilePath
mccTezosClientPath :: MorleyClientConfig -> FilePath
mccEndpointUrl :: MorleyClientConfig -> Maybe BaseUrl
mccAliasPrefix :: MorleyClientConfig -> Maybe Text
..} = do
  Maybe FilePath
envTezosClientPath <- FilePath -> IO (Maybe FilePath)
lookupEnv FilePath
"MORLEY_TEZOS_CLIENT"
  let tezosClientPath :: FilePath
tezosClientPath = FilePath -> Maybe FilePath -> FilePath
forall a. a -> Maybe a -> a
fromMaybe FilePath
mccTezosClientPath Maybe FilePath
envTezosClientPath
  TezosClientConfig {BaseUrl
tcEndpointUrl :: TezosClientConfig -> BaseUrl
tcEndpointUrl :: BaseUrl
..} <- FilePath -> Maybe FilePath -> IO TezosClientConfig
getTezosClientConfig FilePath
tezosClientPath Maybe FilePath
mccMbTezosClientDataDir
  let
    endpointUrl :: BaseUrl
endpointUrl = BaseUrl -> Maybe BaseUrl -> BaseUrl
forall a. a -> Maybe a -> a
fromMaybe BaseUrl
tcEndpointUrl Maybe BaseUrl
mccEndpointUrl
    tezosClientEnv :: TezosClientEnv
tezosClientEnv = TezosClientEnv :: Maybe Text
-> BaseUrl -> FilePath -> Maybe FilePath -> TezosClientEnv
TezosClientEnv
      { tceAliasPrefix :: Maybe Text
tceAliasPrefix = Maybe Text
mccAliasPrefix
      , tceEndpointUrl :: BaseUrl
tceEndpointUrl = BaseUrl
endpointUrl
      , tceTezosClientPath :: FilePath
tceTezosClientPath = FilePath
tezosClientPath
      , tceMbTezosClientDataDir :: Maybe FilePath
tceMbTezosClientDataDir = Maybe FilePath
mccMbTezosClientDataDir
      }

  ClientEnv
clientEnv <- BaseUrl -> IO ClientEnv
newClientEnv BaseUrl
endpointUrl
  pure MorleyClientEnv :: forall (m :: * -> *).
TezosClientEnv
-> ClientLogAction m
-> Maybe SecretKey
-> ClientEnv
-> MorleyClientEnv' m
MorleyClientEnv
    { mceTezosClient :: TezosClientEnv
mceTezosClient = TezosClientEnv
tezosClientEnv
    , mceLogAction :: ClientLogAction m
mceLogAction = Word -> ClientLogAction m
forall (m :: * -> *). MonadIO m => Word -> ClientLogAction m
mkLogAction Word
mccVerbosity
    , mceSecretKey :: Maybe SecretKey
mceSecretKey = Maybe SecretKey
mccSecretKey
    , mceClientEnv :: ClientEnv
mceClientEnv = ClientEnv
clientEnv
    }

-- | Make appropriate 'ClientLogAction' based on verbosity specified by the user.
mkLogAction :: MonadIO m => Word -> ClientLogAction m
mkLogAction :: Word -> ClientLogAction m
mkLogAction Word
verbosity =
  Severity
-> (Msg Severity -> Severity)
-> ClientLogAction m
-> ClientLogAction m
forall (m :: * -> *) a.
Applicative m =>
Severity -> (a -> Severity) -> LogAction m a -> LogAction m a
filterBySeverity Severity
severity Msg Severity -> Severity
forall sev. Msg sev -> sev
msgSeverity (Msg Severity -> Text
fmtMessage (Msg Severity -> Text) -> LogAction m Text -> ClientLogAction m
forall a b (m :: * -> *).
(a -> b) -> LogAction m b -> LogAction m a
`cmap` LogAction m Text
logTextStderrFlush)
  where
    severity :: Severity
severity = case Word
verbosity of
      Word
0 -> Severity
Warning
      Word
1 -> Severity
Info
      Word
_ -> Severity
Debug
    logTextStderrFlush :: LogAction m Text
logTextStderrFlush = LogAction m Text
forall (m :: * -> *). MonadIO m => LogAction m Text
logTextStderr LogAction m Text -> LogAction m Text -> LogAction m Text
forall a. Semigroup a => a -> a -> a
<> Handle -> LogAction m Text
forall (m :: * -> *) a. MonadIO m => Handle -> LogAction m a
logFlush Handle
stderr