packages feed

launchdarkly-server-sdk-4.2.0: src/LaunchDarkly/Server/Config/Internal.hs

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

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

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

-- | Config allows advanced configuration of the LaunchDarkly client.
data Config = Config
    { key :: !Text
    , baseURI :: !Text
    , streamURI :: !Text
    , eventsURI :: !Text
    , storeBackend :: !(Maybe PersistentDataStore)
    , storeTTLSeconds :: !Natural
    , streaming :: !Bool
    , initialRetryDelay :: !Int
    , allAttributesPrivate :: !Bool
    , privateAttributeNames :: !(Set Reference)
    , flushIntervalSeconds :: !Natural
    , pollIntervalSeconds :: !Natural
    , contextKeyLRUCapacity :: !Natural
    , eventsCapacity :: !Natural
    , logger :: !(LoggingT IO () -> IO ())
    , sendEvents :: !Bool
    , offline :: !Bool
    , requestTimeoutSeconds :: !Natural
    , useLdd :: !Bool
    , dataSourceFactory :: !(Maybe DataSourceFactory)
    , manager :: !(Maybe Manager)
    , applicationInfo :: !(Maybe ApplicationInfo)
    , omitAnonymousContexts :: !Bool
    }
    deriving (Generic)

shouldSendEvents :: Config -> Bool
shouldSendEvents config = (not $ getField @"offline" config) && (getField @"sendEvents" config)

-- |
-- 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 (Show, Eq)

-- | Create a default instance
makeApplicationInfo :: ApplicationInfo
makeApplicationInfo = ApplicationInfo 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 _ "" 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