unleash-client-haskell 0.6.0 → 0.7.0
raw patch · 5 files changed
+95/−23 lines, 5 filesdep ~mtldep ~servantdep ~servant-client
Dependency ranges changed: mtl, servant, servant-client, unleash-client-haskell-core
Files
- CHANGELOG.md +14/−0
- example/Main.hs +1/−2
- src/Unleash/Client.hs +66/−7
- src/Unleash/Internal/HttpClient.hs +9/−8
- unleash-client-haskell.cabal +5/−6
CHANGELOG.md view
@@ -1,3 +1,17 @@+## <small>0.7.0 (2024-06-24)</small>++* Format all ([0910331](https://github.com/finn-no/unleash-client-haskell/commit/0910331))+* Reformat flake.nix ([42cd35a](https://github.com/finn-no/unleash-client-haskell/commit/42cd35a))+* Relax mtl and servant-* constraints ([82f3e1f](https://github.com/finn-no/unleash-client-haskell/commit/82f3e1f))+* Simplify isEnabled and add isEnabledWithContext ([6d67d38](https://github.com/finn-no/unleash-client-haskell/commit/6d67d38))+* Support custom strategies (#12) ([bf56170](https://github.com/finn-no/unleash-client-haskell/commit/bf56170)), closes [#12](https://github.com/finn-no/unleash-client-haskell/issues/12)+* unleash-client-haskell-core: 0.11.0 ([1685750](https://github.com/finn-no/unleash-client-haskell/commit/1685750))+* Update flake-compat ([4fcfc9a](https://github.com/finn-no/unleash-client-haskell/commit/4fcfc9a))+* Update nixpkgs ([0498877](https://github.com/finn-no/unleash-client-haskell/commit/0498877))+* Update unleash-client-haskell-core ([f147f09](https://github.com/finn-no/unleash-client-haskell/commit/f147f09))+++ ## <small>0.6.0 (2023-10-16)</small> * Set version to 0.6.0 ([925b4f6](https://github.com/finn-no/unleash-client-haskell/commit/925b4f6))
example/Main.hs view
@@ -18,7 +18,6 @@ import Data.Void (Void) import Servant.Client (BaseUrl (BaseUrl), Scheme (Http)) import System.Exit (die)-import Unleash (emptyContext) import Unleash.Client ( HasUnleash (..), UnleashConfig (..),@@ -60,7 +59,7 @@ application :: Program Void application = forever do- enabled <- isEnabled featureToggle emptyContext+ enabled <- isEnabled featureToggle liftIO . putStrLn $ T.unpack featureToggle <> " is " <> (if enabled then "enabled" else "disabled") liftIO . threadDelay $ 2 * 1000 * 1000
src/Unleash/Client.hs view
@@ -13,15 +13,23 @@ UnleashConfig (..), HasUnleash (..), registerClient,+ registerClientWithCustomStrategies, pollToggles,+ pollTogglesWithCustomStrategies, pushMetrics, isEnabled,+ isEnabledWithContext, tryIsEnabled,+ tryIsEnabledWithContext, getVariant, tryGetVariant, -- Re-exports Context (..), emptyContext,+ FeatureToggleName,+ Strategy (..),+ StrategyEvaluator,+ SupportedStrategies, VariantResponse (..), ) where @@ -33,12 +41,28 @@ import Data.Time (UTCTime, getCurrentTime) import Network.HTTP.Client.TLS (newTlsManager) import Servant.Client (BaseUrl, ClientEnv, ClientError, mkClientEnv)-import Unleash (Context (..), Features, MetricsPayload (..), RegisterPayload (..), VariantResponse (..), emptyContext, emptyVariantResponse, featureGetVariant, featureIsEnabled)+import Unleash (+ Context (..),+ FeatureToggleName,+ Features,+ MetricsPayload (..),+ RegisterPayload (..),+ Strategy (..),+ StrategyEvaluator,+ SupportedStrategies,+ VariantResponse (..),+ defaultStrategyEvaluator,+ defaultSupportedStrategies,+ emptyContext,+ emptyVariantResponse,+ featureGetVariant,+ featureIsEnabled,+ ) import Unleash.Internal.HttpClient (getAllClientFeatures, register, sendMetrics) -- | Smart constructor for Unleash client configuration. Initializes the mutable variables properly. makeUnleashConfig ::- MonadIO m =>+ (MonadIO m) => -- | Application name. Text -> -- | Instance identifier.@@ -96,7 +120,11 @@ -- | Register client for the Unleash server. Call this on application startup before calling the state poller and metrics pusher functions. registerClient :: (HasUnleash r, MonadReader r m, MonadIO m) => m (Either ClientError ())-registerClient = do+registerClient = registerClientWithCustomStrategies defaultSupportedStrategies++-- | Register client for the Unleash server. Custom strategies are added to default strategies. Call this on application startup before calling the state poller and metrics pusher functions.+registerClientWithCustomStrategies :: (HasUnleash r, MonadReader r m, MonadIO m) => SupportedStrategies -> m (Either ClientError ())+registerClientWithCustomStrategies customSupportedStrategies = do config <- asks getUnleashConfig now <- liftIO getCurrentTime let registrationPayload :: RegisterPayload@@ -104,6 +132,7 @@ RegisterPayload { appName = config.applicationName, instanceId = config.instanceId,+ strategies = defaultSupportedStrategies <> customSupportedStrategies, started = now, intervalSeconds = config.metricsPushIntervalInSeconds }@@ -111,16 +140,28 @@ -- | Fetch the most recent feature toggle set from the Unleash server. Meant to be run every statePollIntervalInSeconds. Non-blocking. pollToggles :: (HasUnleash r, MonadReader r m, MonadIO m) => m (Either ClientError ())-pollToggles = do+pollToggles = pollTogglesWithCustomStrategies defaultStrategyEvaluator++-- | Fetch the most recent feature toggle set from the Unleash server. Custom strategies are added to default strategies. Meant to be run every statePollIntervalInSeconds. Non-blocking.+pollTogglesWithCustomStrategies :: (HasUnleash r, MonadReader r m, MonadIO m) => StrategyEvaluator -> m (Either ClientError ())+pollTogglesWithCustomStrategies customStrategyEvaluator = do config <- asks getUnleashConfig- eitherFeatures <- getAllClientFeatures config.httpClientEnvironment config.apiKey+ eitherFeatures <- getAllClientFeatures config.httpClientEnvironment strategyEvaluator config.apiKey either (const $ pure ()) (updateState config.state) eitherFeatures pure . void $ eitherFeatures where+ strategyEvaluator :: StrategyEvaluator+ strategyEvaluator = withCustomStrategyEvaluator customStrategyEvaluator updateState state value = do isUpdated <- liftIO $ tryPutMVar state value liftIO . unless isUpdated . void $ swapMVar state value +withCustomStrategyEvaluator :: StrategyEvaluator -> StrategyEvaluator+withCustomStrategyEvaluator customStrategyEvaluator featureToggleName jsonStrategy ctx = do+ defaultResult <- defaultStrategyEvaluator featureToggleName jsonStrategy ctx+ customResult <- customStrategyEvaluator featureToggleName jsonStrategy ctx+ pure $ defaultResult || customResult+ -- | Push metrics to the Unleash server. Meant to be run every metricsPushIntervalInSeconds. Blocks if the mutable metrics variables are empty. pushMetrics :: (HasUnleash r, MonadReader r m, MonadIO m) => m (Either ClientError ()) pushMetrics = do@@ -144,10 +185,19 @@ -- | Feature toggle name. Text -> -- | Client context.+ m Bool+isEnabled feature = isEnabledWithContext feature emptyContext++-- | Check if a feature is enabled or not. Blocks until first feature toggle set is received. Blocks if the mutable metrics variables are empty.+isEnabledWithContext ::+ (HasUnleash r, MonadReader r m, MonadIO m) =>+ -- | Feature toggle name.+ Text ->+ -- | Client context. Context -> -- | Whether or not the feature toggle is enabled. m Bool-isEnabled feature context = do+isEnabledWithContext feature context = do config <- asks getUnleashConfig state <- liftIO . readMVar $ config.state enabled <- featureIsEnabled state feature context@@ -160,10 +210,19 @@ -- | Feature toggle name. Text -> -- | Client context.+ m Bool+tryIsEnabled feature = tryIsEnabledWithContext feature emptyContext++-- | Check if a feature is enabled or not. Returns false for all toggles until first toggle set is received. Blocks if the mutable metrics variables are empty.+tryIsEnabledWithContext ::+ (HasUnleash r, MonadReader r m, MonadIO m) =>+ -- | Feature toggle name.+ Text ->+ -- | Client context. Context -> -- | Whether or not the feature toggle is enabled. m Bool-tryIsEnabled feature context = do+tryIsEnabledWithContext feature context = do config <- asks getUnleashConfig maybeState <- liftIO . tryReadMVar $ config.state case maybeState of
src/Unleash/Internal/HttpClient.hs view
@@ -22,7 +22,8 @@ import Paths_unleash_client_haskell (version) import Servant.API (Accept (contentTypes), Get, Header, JSON, MimeRender (mimeRender), NoContent, PostNoContent, ReqBody, type (:<|>) (..), type (:>)) import Servant.Client (ClientEnv, ClientError, client, runClientM)-import Unleash.Internal.DomainTypes (Features, fromJsonFeatures, supportedStrategies)+import Unleash (Features, StrategyEvaluator)+import Unleash.Internal.DomainTypes (fromJsonFeatures) import Unleash.Internal.JsonTypes (FullMetricsBucket (..), FullMetricsPayload (..), FullRegisterPayload (..), MetricsPayload, RegisterPayload, YesAndNoes (..)) import qualified Unleash.Internal.JsonTypes as UJT @@ -46,28 +47,28 @@ "application" M.// "json" NE.:| ["application" M.// "json"] -instance {-# OVERLAPPABLE #-} ToJSON a => MimeRender CustomJSON a where+instance {-# OVERLAPPABLE #-} (ToJSON a) => MimeRender CustomJSON a where mimeRender _ = encode -register :: MonadIO m => ClientEnv -> Maybe ApiKey -> RegisterPayload -> m (Either ClientError NoContent)+register :: (MonadIO m) => ClientEnv -> Maybe ApiKey -> RegisterPayload -> m (Either ClientError NoContent) register clientEnv apiKey registerPayload = do let fullRegisterPayload = FullRegisterPayload { appName = registerPayload.appName, instanceId = registerPayload.instanceId, sdkVersion = "unleash-client-haskell:" <> (T.pack . showVersion) version,- strategies = supportedStrategies,+ strategies = registerPayload.strategies, started = registerPayload.started, interval = registerPayload.intervalSeconds * 1000 } liftIO $ runClientM (register' apiKey (Just "application/json") fullRegisterPayload) clientEnv -getAllClientFeatures :: MonadIO m => ClientEnv -> Maybe ApiKey -> m (Either ClientError Features)-getAllClientFeatures clientEnv apiKey = do+getAllClientFeatures :: (MonadIO m) => ClientEnv -> StrategyEvaluator -> Maybe ApiKey -> m (Either ClientError Features)+getAllClientFeatures clientEnv strategyEvaluator apiKey = do eitherFeatures <- liftIO $ runClientM (getAllClientFeatures' apiKey) clientEnv- pure $ fromJsonFeatures <$> eitherFeatures+ pure $ fromJsonFeatures strategyEvaluator <$> eitherFeatures -sendMetrics :: MonadIO m => ClientEnv -> Maybe ApiKey -> MetricsPayload -> m (Either ClientError NoContent)+sendMetrics :: (MonadIO m) => ClientEnv -> Maybe ApiKey -> MetricsPayload -> m (Either ClientError NoContent) sendMetrics clientEnv apiKey metricsPayload = do liftIO $ runClientM (sendMetrics' apiKey fullMetricsPayload) clientEnv where
unleash-client-haskell.cabal view
@@ -1,7 +1,7 @@ cabal-version: 3.0 name: unleash-client-haskell-version: 0.6.0+version: 0.7.0 synopsis: Unleash feature toggle client description: This is an [Unleash](https://www.getunleash.io/) client SDK for Haskell.@@ -65,14 +65,14 @@ aeson >= 2.0.3 && < 2.2, base >= 4.7 && < 5, containers >= 0.6.4 && < 0.7,- mtl >= 2.2.2 && < 2.3,+ mtl >= 2.2.2 && < 2.4, text >= 1.2.5 && < 2.1, time >= 1.9.3 && < 1.13, http-client-tls >= 0.3.6 && < 0.4, http-media >= 0.8.0 && < 0.9,- servant >= 0.19.1 && < 0.20,- servant-client >= 0.19 && < 0.20,- unleash-client-haskell-core >= 0.8.7 && < 0.11,+ servant >= 0.19.1 && < 0.21,+ servant-client >= 0.19 && < 0.21,+ unleash-client-haskell-core >= 0.11.0 && < 0.12, executable example import: all@@ -85,4 +85,3 @@ servant-client, text, unleash-client-haskell,- unleash-client-haskell-core,