module LaunchDarkly.Server.Config.Internal
    ( Config(..)
    , mapConfig
    , ConfigI(..)
    , shouldSendEvents
    , ApplicationInfo
    , makeApplicationInfo
    , withApplicationValue
    , getApplicationInfoHeader
    ) where

import Control.Monad.Logger               (LoggingT)
import Data.Generics.Product              (getField)
import Data.Text                          (Text)
import qualified Data.Text as T
import Data.Set                           (Set)
import GHC.Natural                        (Natural)
import GHC.Generics                       (Generic)
import Network.HTTP.Client                (Manager)

import LaunchDarkly.Server.Store               (StoreInterface)
import LaunchDarkly.Server.DataSource.Internal (DataSourceFactory)
import LaunchDarkly.AesonCompat (KeyMap, insertKey, emptyObject, toList)
import qualified LaunchDarkly.AesonCompat as AesonCompat
import Data.List (sortBy)
import Control.Lens ((&))
import Data.Ord (comparing)

mapConfig :: (ConfigI -> ConfigI) -> Config -> Config
mapConfig :: (ConfigI -> ConfigI) -> Config -> Config
mapConfig ConfigI -> ConfigI
f (Config ConfigI
c) = ConfigI -> Config
Config forall a b. (a -> b) -> a -> b
$ ConfigI -> ConfigI
f ConfigI
c

shouldSendEvents :: ConfigI -> Bool
shouldSendEvents :: ConfigI -> Bool
shouldSendEvents ConfigI
config = (Bool -> Bool
not forall a b. (a -> b) -> a -> b
$ forall (f :: Symbol) a s. HasField' f s a => s -> a
getField @"offline" ConfigI
config) Bool -> Bool -> Bool
&& (forall (f :: Symbol) a s. HasField' f s a => s -> a
getField @"sendEvents" ConfigI
config)

-- | Config allows advanced configuration of the LaunchDarkly client.
newtype Config = Config ConfigI

data ConfigI = ConfigI
    { ConfigI -> Text
key                   :: !Text
    , ConfigI -> Text
baseURI               :: !Text
    , ConfigI -> Text
streamURI             :: !Text
    , ConfigI -> Text
eventsURI             :: !Text
    , ConfigI -> Maybe StoreInterface
storeBackend          :: !(Maybe StoreInterface)
    , ConfigI -> Natural
storeTTLSeconds       :: !Natural
    , ConfigI -> Bool
streaming             :: !Bool
    , ConfigI -> Bool
allAttributesPrivate  :: !Bool
    , ConfigI -> Set Text
privateAttributeNames :: !(Set Text)
    , ConfigI -> Natural
flushIntervalSeconds  :: !Natural
    , ConfigI -> Natural
pollIntervalSeconds   :: !Natural
    , ConfigI -> Natural
userKeyLRUCapacity    :: !Natural
    , ConfigI -> Bool
inlineUsersInEvents   :: !Bool
    , ConfigI -> Natural
eventsCapacity        :: !Natural
    , ConfigI -> LoggingT IO () -> IO ()
logger                :: !(LoggingT IO () -> IO ())
    , ConfigI -> Bool
sendEvents            :: !Bool
    , ConfigI -> Bool
offline               :: !Bool
    , ConfigI -> Natural
requestTimeoutSeconds :: !Natural
    , ConfigI -> Bool
useLdd                :: !Bool
    , ConfigI -> Maybe DataSourceFactory
dataSourceFactory     :: !(Maybe DataSourceFactory)
    , ConfigI -> Maybe Manager
manager               :: !(Maybe Manager)
    , ConfigI -> Maybe ApplicationInfo
applicationInfo       :: !(Maybe ApplicationInfo)
    } deriving (forall x. Rep ConfigI x -> ConfigI
forall x. ConfigI -> Rep ConfigI x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep ConfigI x -> ConfigI
$cfrom :: forall x. ConfigI -> Rep ConfigI x
Generic)

-- | An object that allows configuration of application metadata.
--
-- Application metadata may be used in LaunchDarkly analytics or other product
-- features, but does not affect feature flag evaluations.
--
-- To use these properties, provide an instance of ApplicationInfo to the 'Config' with 'configSetApplicationInfo'.
newtype ApplicationInfo = ApplicationInfo (KeyMap Text) deriving (Int -> ApplicationInfo -> ShowS
[ApplicationInfo] -> ShowS
ApplicationInfo -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ApplicationInfo] -> ShowS
$cshowList :: [ApplicationInfo] -> ShowS
show :: ApplicationInfo -> String
$cshow :: ApplicationInfo -> String
showsPrec :: Int -> ApplicationInfo -> ShowS
$cshowsPrec :: Int -> ApplicationInfo -> ShowS
Show, ApplicationInfo -> ApplicationInfo -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ApplicationInfo -> ApplicationInfo -> Bool
$c/= :: ApplicationInfo -> ApplicationInfo -> Bool
== :: ApplicationInfo -> ApplicationInfo -> Bool
$c== :: ApplicationInfo -> ApplicationInfo -> Bool
Eq)

-- | Create a default instance
makeApplicationInfo :: ApplicationInfo
makeApplicationInfo :: ApplicationInfo
makeApplicationInfo = KeyMap Text -> ApplicationInfo
ApplicationInfo forall v. KeyMap v
emptyObject

-- | Set a new name / value pair into the application info instance.
--
-- Values have the following restrictions:
-- - Cannot be empty
-- - Cannot exceed 64 characters in length
-- - Can only contain a-z, A-Z, 0-9, period (.), dash (-), and underscore (_).
--
-- Invalid values or unsupported keys will be ignored.
withApplicationValue :: Text -> Text -> ApplicationInfo -> ApplicationInfo
withApplicationValue :: Text -> Text -> ApplicationInfo -> ApplicationInfo
withApplicationValue Text
_ Text
"" ApplicationInfo
info = ApplicationInfo
info
withApplicationValue Text
name Text
value info :: ApplicationInfo
info@(ApplicationInfo KeyMap Text
map)
    | (Text
name forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text
"id", Text
"version"]) forall a. Eq a => a -> a -> Bool
== Bool
False = ApplicationInfo
info
    | Text -> Int
T.length(Text
value) forall a. Ord a => a -> a -> Bool
> Int
64 = ApplicationInfo
info
    | (forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Char
'a'..Char
'z'] forall a. [a] -> [a] -> [a]
++ [Char
'A' .. Char
'Z'] forall a. [a] -> [a] -> [a]
++ [Char
'0' .. Char
'9'] forall a. [a] -> [a] -> [a]
++ [Char
'.', Char
'-', Char
'_']) (Text -> String
T.unpack Text
value)) forall a. Eq a => a -> a -> Bool
== Bool
False = ApplicationInfo
info
    | Bool
otherwise = KeyMap Text -> ApplicationInfo
ApplicationInfo forall a b. (a -> b) -> a -> b
$ forall v. Text -> v -> KeyMap v -> KeyMap v
insertKey Text
name Text
value KeyMap Text
map

getApplicationInfoHeader :: ApplicationInfo -> Maybe Text
getApplicationInfoHeader :: ApplicationInfo -> Maybe Text
getApplicationInfoHeader (ApplicationInfo KeyMap Text
values)
    | forall v. KeyMap v -> Bool
AesonCompat.null KeyMap Text
values = forall a. Maybe a
Nothing
    | Bool
otherwise = forall v. KeyMap v -> [(Text, v)]
toList KeyMap Text
values
        forall a b. a -> (a -> b) -> b
& forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (forall a b. Ord a => (b -> a) -> b -> b -> Ordering
comparing forall a b. (a, b) -> a
fst)
        forall a b. a -> (a -> b) -> b
& forall a b. (a -> b) -> [a] -> [b]
map forall {a}. (Semigroup a, IsString a) => (a, a) -> a
makeTag
        forall a b. a -> (a -> b) -> b
& [Text] -> Text
T.unwords
        forall a b. a -> (a -> b) -> b
& forall a. a -> Maybe a
Just
    where makeTag :: (a, a) -> a
makeTag (a
key, a
value) = a
"application-" forall a. Semigroup a => a -> a -> a
<> a
key forall a. Semigroup a => a -> a -> a
<> a
"/" forall a. Semigroup a => a -> a -> a
<> a
value