Skip to content

Commit

Permalink
prepare 3.1.0 release (#51)
Browse files Browse the repository at this point in the history
* initial implementation

* [ch57654] add circleci config (#3)

* [ch57656] cleanup dependencies (#2)

* [ch57662] Add repo templates (#5)

* [ch57657] Add setters for User and Config (#4)

* [ch57663] Only expose public modules in package

* [ch57666] Add Haddock (#7)

* log on authentication failure (#8)

* [ch57685] cleanup package (#9)

* [ch57695] metric event logic (#10)

* [ch58006] Hide typeclass instances with newtype public API (#11)

* fix contributor guide wrong link (#12)

* fix minor title-casing inconsistencies (#13)

* git ignore + re-ordering lists (#14)

* adding .gitignore

* re-ordering items

* fix typos

* [ch58315] Add OSX CI (#15)

* updated build instructions (#16)

* [tickets listed in body] Add misc configuration and close. (#17)

[ch58083] add close
[ch58076] make connection timeout configuration
[ch58072] add offline mode
[ch58435] make event sending configurable

* set version string to 0.1.0 (#18)

* add beta warning

* [ch58816] Fix user agent format, prepare release

* [ch59631] add haskell sdk to releaser (#20)

* [ch63561] Store V2 Interface (#21)

* [ch63772] Mock store interface tests (#22)

* [ch58074] Redis store implementation (#23)

* Linting and some refactoring of streaming code.

* [ch64438] dont send empty event payloads

* [ch43307] use last bucket as fallback

* Increased test coverage (#28)

* Custom serialization instances when using custom deserialization (#29)

* [ch64640] Add support for Ldd (#30)

* [ch65827] payload uuid and event send retry (#32)

* [ch66643] strict fields

* [ch67127] minimal version constraints (#34)

* [ch67145] update master export list

* [ch67148] Some doc typos (#36)

* [ch67154] Remove beta warning

* [ch67570] actually use store initialization status (#38)

* [ch69091] Add SSE timeout, fix streaming CPU burn (#39)

* [ch70425] standardize streaming behavior (#41)

* [ch73995] remove null user key support (#42)

* [ch76243] update sdk range for redis

* [ch92127] remove sel field from flag model

* Removed the guides link

* [ch99749] add alias and update event logic (#45)

* merge traffic allocation changes

* Updates docs URLs (#47)

* Use non-deprecated CircleCI Xcode image. (#48)

* Path is optional; should default to / (#53)

* Create index event when calling track (#51)

* Conditionally index users when processing eval events (#56)

* Trim trailing slashes from URIs (#52)

* Add event summary regardless of capacity (#55)

* Do not emit identify event if key is empty; notice user otherwise (#50)

If the provided user key is empty, we do not want to emit an identify
event. We will log a warning message to the customer instead.

If a valid user has been provided though, not only should we emit the
event, but also we should add that user to the LRU cache so that we
don't unnecessarily generate future index events.

* Support for both aeson < 2 and aeson > 2

* Unknown flags should return provided default value (#57)

* Exclude various fields from JSON payload if not required (#54)

* Adds links to Relay Proxy docs

* Add support for client side availability (#61)

* Add new all flags state method (#62)

* Add initial structure for SDK test harness (#63)

* Fix test if user attribute is null (#65)

* Fix negative index evaluation (#66)

* Track last known server time (#67)

* Introduce File and Test Data Sources (#68)

* Fix aeson 2.0 compatibility (#69)

A user submitted contribution was merged to support Aeson 2.0. While
all of our tests were passing, this was because we lacked a test
environment that actually used 2.0

This commit addresses the remaining compatibility changes and
introduces a later test environment to ensure we are actually building
with Aeson 2.0 support.

* Bump resource class for linux builds (#70)

* Add cabal file and ignore dist-newstyle. (#44)

* Add CI support for cabal and hlint (#71)

A customer recently provided two pull requests -- one to add the
generated cabal file to our repository and the second to update our
hlint configuration file.

While great, these change aren't sufficient because

- We have no way to enforce the generated cabal file is up to date
- We aren't running hlint during the CI process

This commit introduces CI behaviors to resolve both of these issues.

* Update releaser config to use docker instead of circleci (#72)

* Update releaser configuration (#73)

In a previous commit I updated the releaser config. However, I failed to
adjust two additional bits of configuration.

- The repository now includes a cabal file which contains version
  information. We will now update that.
- docs need to be copied into the appropriate releaser directory for the
  GH pages branch to be updated correctly.

* Speed up Haskell builds for OSX (#74)

The OSX builds have been taking a very long time, despite doing less
work than the Linux equivalents. A little digging has uncovered
incorrect CI caching for the OSX builds.

This small change has taken the build from approximately 45m to 9m per
OSX run.

* Expand upper versions on select packages (#77)

A customer contributed this fix. I made some minor tweaks, but testing
seems to indicate everything works as expected.

Co-authored-by: Veronika Romashkina <vrom911@gmail.com>

* Add application info support (#89)

---------

Co-authored-by: hroederld <hroeder@launchdarkly.com>
Co-authored-by: Ben Woskow <48036130+bwoskow-ld@users.noreply.github.com>
Co-authored-by: Gavin Whelan <gwhelan@launchdarkly.com>
Co-authored-by: LaunchDarklyCI <dev@launchdarkly.com>
Co-authored-by: ember-stevens <79482775+ember-stevens@users.noreply.github.com>
Co-authored-by: Matthew M. Keeler <keelerm84@gmail.com>
Co-authored-by: Alex Biehl <alex@scarf.sh>
Co-authored-by: Matthew M. Keeler <mkeeler@launchdarkly.com>
Co-authored-by: Ember Stevens <ember.stevens@launchdarkly.com>
Co-authored-by: Louis Chan <lchan@launchdarkly.com>
Co-authored-by: LaunchDarklyReleaseBot <launchdarklyreleasebot@launchdarkly.com>
Co-authored-by: Phil de Joux <philderbeast@gmail.com>
Co-authored-by: Veronika Romashkina <vrom911@gmail.com>
  • Loading branch information
14 people committed Jan 27, 2023
1 parent dceb217 commit 48f3622
Show file tree
Hide file tree
Showing 15 changed files with 199 additions and 52 deletions.
1 change: 1 addition & 0 deletions contract-tests/package.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies:
- generic-lens >=1.1.0.0 && <2.3
- http-types >=0.12.3 && <0.13
- launchdarkly-server-sdk
- lens >=4.17.1 && <5.3
- mtl >=2.2.2 && <2.3
- scientific >=0.3.6.2 && <0.4
- scotty <1.0
Expand Down
9 changes: 8 additions & 1 deletion contract-tests/src/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,14 @@ getAppStatus :: ActionM ()
getAppStatus = json AppStatus
{ name = "haskell-server-sdk"
, clientVersion = LD.clientVersion
, capabilities = ["server-side", "strongly-typed", "all-flags-with-reasons", "all-flags-client-side-only", "all-flags-details-only-for-tracked-flags"]
, capabilities =
["server-side"
, "strongly-typed"
, "all-flags-with-reasons"
, "all-flags-client-side-only"
, "all-flags-details-only-for-tracked-flags"
, "tags"
]
}

shutdownService :: MVar () -> ActionM ()
Expand Down
6 changes: 6 additions & 0 deletions contract-tests/src/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ data ConfigurationParams = ConfigurationParams
, initCanFail :: !(Maybe Bool)
, streaming :: !(Maybe StreamingParams)
, events :: !(Maybe EventParams)
, tags :: !(Maybe TagParams)
} deriving (FromJSON, ToJSON, Show, Generic)

data StreamingParams = StreamingParams
Expand All @@ -38,6 +39,11 @@ data EventParams = EventParams
, inlineUsers :: !(Maybe Bool)
} deriving (FromJSON, ToJSON, Show, Generic)

data TagParams = TagParams
{ applicationId :: !(Maybe Text)
, applicationVersion :: !(Maybe Text)
} deriving (FromJSON, ToJSON, Show, Generic)

data CommandParams = CommandParams
{ command :: !Text
, evaluate :: !(Maybe EvaluateFlagParams)
Expand Down
32 changes: 22 additions & 10 deletions contract-tests/src/Utils.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Utils where

import Control.Lens ((&))
import Control.Concurrent (threadDelay)
import Data.Text (Text)
import Data.Aeson (Value(..))
Expand All @@ -18,23 +19,34 @@ waitClient client = do
_ -> threadDelay (1 * 1000) >> waitClient client

createConfig :: ConfigurationParams -> LD.Config
createConfig p = do
let config = LD.makeConfig $ getField @"credential" p
streamerConfig = streamingConfig config $ getField @"streaming" p
eventConfig streamerConfig $ getField @"events" p
createConfig p = LD.makeConfig (getField @"credential" p)
& streamingConfig (getField @"streaming" p)
& tagsConfig (getField @"tags" p)
& eventConfig (getField @"events" p)

updateConfig :: (a -> LD.Config -> LD.Config) -> Maybe a -> LD.Config -> LD.Config
updateConfig f Nothing config = config
updateConfig f (Just x) config = f x config

-- TODO(mmk) We aren't handling the initialRetryDelayMs because the SDK doesn't seem to support it
streamingConfig :: LD.Config -> Maybe StreamingParams -> LD.Config
streamingConfig c Nothing = c
streamingConfig c (Just p) = updateConfig LD.configSetStreamURI (getField @"baseUri" p) c
streamingConfig :: Maybe StreamingParams -> LD.Config -> LD.Config
streamingConfig Nothing c = c
streamingConfig (Just p) c = updateConfig LD.configSetStreamURI (getField @"baseUri" p) c

eventConfig :: LD.Config -> Maybe EventParams -> LD.Config
eventConfig c Nothing = updateConfig LD.configSetSendEvents (Just False) c
eventConfig c (Just p) = updateConfig LD.configSetEventsURI (getField @"baseUri" p)
tagsConfig :: Maybe TagParams -> LD.Config -> LD.Config
tagsConfig Nothing c = c
tagsConfig (Just params) c = LD.configSetApplicationInfo appInfo c
where appInfo = LD.makeApplicationInfo
& setApplicationInfo "id" (getField @"applicationId" params)
& setApplicationInfo "version" (getField @"applicationVersion" params)

setApplicationInfo :: Text -> Maybe Text -> LD.ApplicationInfo -> LD.ApplicationInfo
setApplicationInfo _ Nothing appInfo = appInfo
setApplicationInfo key (Just value) appInfo = LD.withApplicationValue key value appInfo

eventConfig :: Maybe EventParams -> LD.Config -> LD.Config
eventConfig Nothing c = updateConfig LD.configSetSendEvents (Just False) c
eventConfig (Just p) c = updateConfig LD.configSetEventsURI (getField @"baseUri" p)
$ updateConfig LD.configSetEventsCapacity (getField @"capacity" p)
$ updateConfig LD.configSetAllAttributesPrivate (getField @"allAttributesPrivate" p)
$ updateConfig LD.configSetPrivateAttributeNames (getField @"globalPrivateAttributes" p)
Expand Down
1 change: 1 addition & 0 deletions launchdarkly-server-sdk.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ test-suite haskell-server-sdk-test
main-is: Spec.hs
other-modules:
Spec.Bucket
Spec.Config
Spec.DataSource
Spec.Evaluate
Spec.Features
Expand Down
10 changes: 8 additions & 2 deletions src/LaunchDarkly/AesonCompat.hs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import qualified Data.Text as T
#if MIN_VERSION_aeson(2,0,0)
type KeyMap = KeyMap.KeyMap

null :: KeyMap v -> Bool
null = KeyMap.null

emptyObject :: KeyMap v
emptyObject = KeyMap.empty

Expand Down Expand Up @@ -64,10 +67,13 @@ mapMaybeValues :: (v1 -> Maybe v2) -> KeyMap.KeyMap v1 -> KeyMap.KeyMap v2
mapMaybeValues = KeyMap.mapMaybe

keyMapUnion :: KeyMap.KeyMap v -> KeyMap.KeyMap v -> KeyMap.KeyMap v
keyMapUnion = KeyMap.union
keyMapUnion = KeyMap.union
#else
type KeyMap = HM.HashMap T.Text

null :: KeyMap v -> Bool
null = HM.null

emptyObject :: KeyMap v
emptyObject = HM.empty

Expand Down Expand Up @@ -117,5 +123,5 @@ mapMaybeValues :: (v1 -> Maybe v2) -> HM.HashMap T.Text v1 -> HM.HashMap T.Text
mapMaybeValues = HM.mapMaybe

keyMapUnion :: HM.HashMap T.Text v -> HM.HashMap T.Text v -> HM.HashMap T.Text v
keyMapUnion = HM.union
keyMapUnion = HM.union
#endif
4 changes: 4 additions & 0 deletions src/LaunchDarkly/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ module LaunchDarkly.Server
, configSetStoreTTL
, configSetUseLdd
, configSetDataSourceFactory
, configSetApplicationInfo
, ApplicationInfo
, makeApplicationInfo
, withApplicationValue
, User
, makeUser
, userSetKey
Expand Down
21 changes: 15 additions & 6 deletions src/LaunchDarkly/Server/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import LaunchDarkly.Server.Client.Internal (Client(..), Clien
import LaunchDarkly.Server.Client.Status (Status(..))
import LaunchDarkly.Server.Config.ClientContext (ClientContext(..))
import LaunchDarkly.Server.Config.HttpConfiguration (HttpConfiguration(..))
import LaunchDarkly.Server.Config.Internal (ConfigI, Config(..), shouldSendEvents)
import LaunchDarkly.Server.Config.Internal (ConfigI, Config(..), shouldSendEvents, getApplicationInfoHeader, ApplicationInfo)
import LaunchDarkly.Server.DataSource.Internal (DataSource(..), DataSourceFactory, DataSourceUpdates(..), defaultDataSourceUpdates, nullDataSourceFactory)
import LaunchDarkly.Server.Details (EvaluationDetail(..), EvaluationReason(..), EvalErrorKind(..))
import LaunchDarkly.Server.Evaluate (evaluateTyped, evaluateDetail)
Expand All @@ -66,7 +66,9 @@ import LaunchDarkly.Server.Network.Streaming (streamingThread)
import LaunchDarkly.Server.Store.Internal (makeStoreIO, getAllFlagsC)
import LaunchDarkly.Server.User.Internal (User(..), userSerializeRedacted)
import LaunchDarkly.AesonCompat (KeyMap, insertKey, emptyObject, mapValues, filterObject)

import Data.ByteString (ByteString)
import Network.HTTP.Types (HeaderName)


networkDataSourceFactory :: (ClientContext -> DataSourceUpdates -> LoggingT IO ()) -> DataSourceFactory
networkDataSourceFactory threadF clientContext dataSourceUpdates = do
Expand All @@ -91,11 +93,18 @@ networkDataSourceFactory threadF clientContext dataSourceUpdates = do
makeHttpConfiguration :: ConfigI -> IO HttpConfiguration
makeHttpConfiguration config = do
tlsManager <- newManager tlsManagerSettings
let defaultRequestHeaders = [ ("Authorization", encodeUtf8 $ getField @"key" config)
, ("User-Agent" , "HaskellServerClient/" <> encodeUtf8 clientVersion)
]
let headers = [ ("Authorization", encodeUtf8 $ getField @"key" config)
, ("User-Agent" , "HaskellServerClient/" <> encodeUtf8 clientVersion)
]
defaultRequestHeaders = addTagsHeader headers (getField @"applicationInfo" config)
defaultRequestTimeout = Http.responseTimeoutMicro $ fromIntegral $ getField @"requestTimeoutSeconds" config * 1000000
pure $ HttpConfiguration{..}
where
addTagsHeader :: [(HeaderName, ByteString)] -> Maybe ApplicationInfo -> [(HeaderName, ByteString)]
addTagsHeader headers Nothing = headers
addTagsHeader headers (Just info) = case getApplicationInfoHeader info of
Nothing -> headers
Just header -> ("X-LaunchDarkly-Tags", encodeUtf8 header) : headers

makeClientContext :: ConfigI -> IO ClientContext
makeClientContext config = do
Expand All @@ -119,7 +128,7 @@ makeClient (Config config) = mfix $ \(Client client) -> do
dataSource <- dataSourceFactory config clientContext dataSourceUpdates
eventThreadPair <- if not (shouldSendEvents config) then pure Nothing else do
sync <- newEmptyMVar
thread <- forkFinally (runLogger clientContext $ eventThread manager client) (\_ -> putMVar sync ())
thread <- forkFinally (runLogger clientContext $ eventThread manager client clientContext) (\_ -> putMVar sync ())
pure $ pure (thread, sync)

dataSourceStart dataSource
Expand Down
24 changes: 19 additions & 5 deletions src/LaunchDarkly/Server/Config.hs
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,26 @@ module LaunchDarkly.Server.Config
, configSetStoreTTL
, configSetUseLdd
, configSetDataSourceFactory
, configSetApplicationInfo
, ApplicationInfo
, makeApplicationInfo
, withApplicationValue
) where

import Control.Monad.Logger (LoggingT, runStdoutLoggingT)
import Data.Generics.Product (setField)
import Data.Set (Set)
import Data.Text (Text, dropWhileEnd)
import Data.Monoid (mempty)
import GHC.Natural (Natural)
import Network.HTTP.Client (Manager)

import LaunchDarkly.Server.Config.Internal (Config(..), mapConfig, ConfigI(..))
import LaunchDarkly.Server.Config.Internal (Config(..), mapConfig, ConfigI(..), ApplicationInfo, makeApplicationInfo, withApplicationValue)
import LaunchDarkly.Server.Store (StoreInterface)
import LaunchDarkly.Server.DataSource.Internal (DataSourceFactory)

-- | Create a default configuration from a given SDK key.
makeConfig :: Text -> Config
makeConfig key =
makeConfig key =
Config $ ConfigI
{ key = key
, baseURI = "https://app.launchdarkly.com"
Expand All @@ -61,8 +64,9 @@ makeConfig key =
, offline = False
, requestTimeoutSeconds = 30
, useLdd = False
, dataSourceFactory = Nothing
, dataSourceFactory = Nothing
, manager = Nothing
, applicationInfo = Nothing
}

-- | Set the SDK key used to authenticate with LaunchDarkly.
Expand Down Expand Up @@ -161,7 +165,7 @@ configSetRequestTimeoutSeconds = mapConfig . setField @"requestTimeoutSeconds"
configSetUseLdd :: Bool -> Config -> Config
configSetUseLdd = mapConfig . setField @"useLdd"

-- | Sets a data source to use instead of the default network based data source
-- | Sets a data source to use instead of the default network based data source
-- see "LaunchDarkly.Server.Integrations.FileData"
configSetDataSourceFactory :: Maybe DataSourceFactory -> Config -> Config
configSetDataSourceFactory = mapConfig . setField @"dataSourceFactory"
Expand All @@ -170,3 +174,13 @@ configSetDataSourceFactory = mapConfig . setField @"dataSourceFactory"
-- 'Manager' will be created when creating the client.
configSetManager :: Manager -> Config -> Config
configSetManager = mapConfig . setField @"manager" . Just

-- | 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.
--
-- If you want to set non-default values for any of these fields, provide the
-- appropriately configured dict to the 'Config' object.
configSetApplicationInfo :: ApplicationInfo -> Config -> Config
configSetApplicationInfo = mapConfig . setField @"applicationInfo" . Just
49 changes: 49 additions & 0 deletions src/LaunchDarkly/Server/Config/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,28 @@ module LaunchDarkly.Server.Config.Internal
, 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
Expand Down Expand Up @@ -47,4 +57,43 @@ data ConfigI = ConfigI
, useLdd :: !Bool
, dataSourceFactory :: !(Maybe DataSourceFactory)
, manager :: !(Maybe Manager)
, applicationInfo :: !(Maybe ApplicationInfo)
} deriving (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 (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
22 changes: 4 additions & 18 deletions src/LaunchDarkly/Server/Network/Common.hs
Original file line number Diff line number Diff line change
@@ -1,32 +1,26 @@
module LaunchDarkly.Server.Network.Common
( prepareRequest
, withResponseGeneric
( withResponseGeneric
, tryAuthorized
, checkAuthorization
, getServerTime
, tryHTTP
, addToAL
, handleUnauthorized
, handleUnauthorized
) where

import Data.ByteString (append)
import Data.ByteString.Internal (unpackChars)
import Network.HTTP.Client (HttpException, Manager, Request(..), Response(..), BodyReader, setRequestIgnoreStatus, responseOpen, responseTimeout, responseTimeoutMicro, responseClose)
import Network.HTTP.Client (HttpException, Manager, Request(..), Response(..), BodyReader, responseOpen, responseClose)
import Network.HTTP.Types.Header (hDate)
import Network.HTTP.Types.Status (unauthorized401, forbidden403)
import Data.Generics.Product (getField)
import Data.Text.Encoding (encodeUtf8)
import Data.Time.Format (parseTimeM, defaultTimeLocale, rfc822DateFormat)
import Data.Time.Clock.POSIX (utcTimeToPOSIXSeconds)
import Data.Function ((&))
import Data.Maybe (fromMaybe)
import Control.Monad (when)
import Control.Monad.Catch (Exception, MonadCatch, MonadMask, MonadThrow, try, bracket, throwM, handle)
import Control.Monad.Logger (MonadLogger, logError)
import Control.Monad.IO.Class (MonadIO, liftIO)

import LaunchDarkly.Server.Client.Internal (ClientI, Status(Unauthorized), clientVersion, setStatus)
import LaunchDarkly.Server.Config.Internal (ConfigI)
import LaunchDarkly.Server.Client.Internal (ClientI, Status(Unauthorized), setStatus)
import LaunchDarkly.Server.DataSource.Internal (DataSourceUpdates(..))

tryHTTP :: MonadCatch m => m a -> m (Either HttpException a)
Expand All @@ -35,14 +29,6 @@ tryHTTP = try
addToAL :: Eq k => [(k, v)] -> k -> v -> [(k, v)]
addToAL l k v = (k, v) : filter ((/=) k . fst) l

prepareRequest :: ConfigI -> Request -> Request
prepareRequest config request = request
{ requestHeaders = (requestHeaders request)
& \l -> addToAL l "Authorization" (encodeUtf8 $ getField @"key" config)
& \l -> addToAL l "User-Agent" (append "HaskellServerClient/" $ encodeUtf8 clientVersion)
, responseTimeout = responseTimeoutMicro $ (fromIntegral $ getField @"requestTimeoutSeconds" config) * 1000000
} & setRequestIgnoreStatus

withResponseGeneric :: (MonadIO m, MonadMask m) => Request -> Manager -> (Response BodyReader -> m a) -> m a
withResponseGeneric req man f = bracket (liftIO $ responseOpen req man) (liftIO . responseClose) f

Expand Down

0 comments on commit 48f3622

Please sign in to comment.