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 f (Config c) = Config $ f c
shouldSendEvents :: ConfigI -> Bool
shouldSendEvents config = (not $ getField @"offline" config) && (getField @"sendEvents" config)
newtype Config = Config ConfigI
data ConfigI = ConfigI
{ key :: !Text
, baseURI :: !Text
, streamURI :: !Text
, eventsURI :: !Text
, storeBackend :: !(Maybe StoreInterface)
, storeTTLSeconds :: !Natural
, streaming :: !Bool
, allAttributesPrivate :: !Bool
, privateAttributeNames :: !(Set Text)
, flushIntervalSeconds :: !Natural
, pollIntervalSeconds :: !Natural
, userKeyLRUCapacity :: !Natural
, inlineUsersInEvents :: !Bool
, eventsCapacity :: !Natural
, logger :: !(LoggingT IO () -> IO ())
, sendEvents :: !Bool
, offline :: !Bool
, requestTimeoutSeconds :: !Natural
, useLdd :: !Bool
, dataSourceFactory :: !(Maybe DataSourceFactory)
, manager :: !(Maybe Manager)
, applicationInfo :: !(Maybe ApplicationInfo)
} deriving (Generic)
newtype ApplicationInfo = ApplicationInfo (KeyMap Text) deriving (Show, Eq)
makeApplicationInfo :: ApplicationInfo
makeApplicationInfo = ApplicationInfo emptyObject
withApplicationValue :: Text -> Text -> ApplicationInfo -> ApplicationInfo
withApplicationValue _ "" info = info
withApplicationValue name value info@(ApplicationInfo map)
| (name `elem` ["id", "version"]) == False = info
| T.length(value) > 64 = info
| (all (`elem` ['a'..'z'] ++ ['A' .. 'Z'] ++ ['0' .. '9'] ++ ['.', '-', '_']) (T.unpack value)) == False = info
| otherwise = ApplicationInfo $ insertKey name value map
getApplicationInfoHeader :: ApplicationInfo -> Maybe Text
getApplicationInfoHeader (ApplicationInfo values)
| AesonCompat.null values = Nothing
| otherwise = toList values
& sortBy (comparing fst)
& map makeTag
& T.unwords
& Just
where makeTag (key, value) = "application-" <> key <> "/" <> value