unleash-client-haskell-core 0.11.0 → 0.12.0
raw patch · 6 files changed
+120/−97 lines, 6 filesdep ~aesondep ~hspecdep ~textPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson, hspec, text, text-show
API changes (from Hackage documentation)
Files
- CHANGELOG.md +11/−0
- README.md +4/−1
- src/Unleash/Internal/DomainTypes.hs +19/−14
- test/UnleashClientSpecificationRoundtripSpec.hs +7/−9
- test/UnleashSpecificationSpec.hs +5/−4
- unleash-client-haskell-core.cabal +74/−69
CHANGELOG.md view
@@ -1,3 +1,14 @@+## <small>0.12.0 (2025-01-21)</small>++* Add source-repository ([70fe283](https://github.com/finn-no/unleash-client-haskell-core/commit/70fe283))+* client-specification: v5.0.0 ([8e28782](https://github.com/finn-no/unleash-client-haskell-core/commit/8e28782))+* Format Cabal file ([e1479d3](https://github.com/finn-no/unleash-client-haskell-core/commit/e1479d3))+* Tidy up ([b4718a1](https://github.com/finn-no/unleash-client-haskell-core/commit/b4718a1))+* Update CHANGELOG ([660c81a](https://github.com/finn-no/unleash-client-haskell-core/commit/660c81a))+* Update Nixpkgs ([dcf0002](https://github.com/finn-no/unleash-client-haskell-core/commit/dcf0002))+++ ## <small>0.11.0 (2024-06-24)</small> * Handle strategy parameter lists without spaces between values ([88aeb01](https://github.com/finn-no/unleash-client-haskell-core/commit/88aeb01))
README.md view
@@ -1,6 +1,9 @@ # unleash-client-haskell-core -This is a library for evaluating [Unleash](https://www.getunleash.io/) feature toggles. It currently meets all [client specifications per v4.2.2](https://github.com/Unleash/client-specification/releases/tag/v4.2.2).+This is a library for evaluating [Unleash](https://www.getunleash.io/) feature toggles. It currently meets all client specifications per [v5.0.0](https://github.com/Unleash/client-specification/releases/tag/v5.0.0), except:++- Strategy variants+- Dependent features The `Unleash` module provides functions and types for checking feature toggles and variants.
src/Unleash/Internal/DomainTypes.hs view
@@ -20,7 +20,7 @@ StrategyEvaluator, ) where -import Control.Applicative (liftA2, (<|>))+import Control.Applicative ((<|>)) import Control.Monad.IO.Class (MonadIO) import Data.Hash.Murmur (murmur3) import Data.List (find)@@ -83,9 +83,7 @@ fromJsonFeature strategyEvaluator segmentMap jsonFeature = ( jsonFeature.name, Feature- { isEnabled = IsEnabled $ \ctx -> do- isAnyStrategyEnabled <- anyStrategyEnabled ctx- pure $ jsonFeature.enabled && (null jsonFeature.strategies || isAnyStrategyEnabled),+ { isEnabled = IsEnabled isEnabled, getVariant = GetVariant $ \ctx -> if not jsonFeature.enabled then pure emptyVariantResponse@@ -103,18 +101,26 @@ Nothing -> do -- Does not have overrides let maybeStickiness = find ("default" /=) . catMaybes $ (.stickiness) <$> variants- case maybeStickiness of- Just stickiness -> do- -- Has non-default stickiness- let identifier = lookupContextValue stickiness ctx- selectVariant variants identifier jsonFeature.name- Nothing -> do- -- Default stickiness- let identifier = ctx.userId <|> ctx.sessionId <|> ctx.remoteAddress- selectVariant variants identifier jsonFeature.name+ enabled <- isEnabled ctx+ if enabled+ then case maybeStickiness of+ Just stickiness -> do+ -- Has non-default stickiness+ let identifier = lookupContextValue stickiness ctx+ selectVariant variants identifier jsonFeature.name+ Nothing -> do+ -- Default stickiness+ let identifier = ctx.userId <|> ctx.sessionId <|> ctx.remoteAddress+ selectVariant variants identifier jsonFeature.name+ else pure emptyVariantResponse } ) where+ isEnabled :: (MonadIO m) => JsonTypes.Context -> m Bool+ isEnabled ctx = do+ isAnyStrategyEnabled <- anyStrategyEnabled ctx+ pure $ jsonFeature.enabled && (null jsonFeature.strategies || isAnyStrategyEnabled)+ anyStrategyEnabled :: (MonadIO m) => JsonTypes.Context -> m Bool anyStrategyEnabled ctx = or <$> traverse (\f -> f ctx) strategyPredicates @@ -340,7 +346,6 @@ getNormalizedNumberN identifier groupId n = do let s = groupId <> ":" <> identifier let hash :: Word32 = murmur3 (0 :: Word32) $ encodeUtf8 s- fromIntegral $ (mod hash n) + 1 getNormalizedNumber :: Text -> Text -> Int
test/UnleashClientSpecificationRoundtripSpec.hs view
@@ -13,21 +13,19 @@ import qualified Data.ByteString as BS (readFile) import qualified Data.ByteString.Lazy as BSL import Data.Foldable (traverse_)-import Data.List (isPrefixOf) import Test.Hspec import UnleashSpecificationJsonTypes (Specification) spec :: Spec spec =- describe "Parse json specification" do- it "parse all specification files and check if input json is the same as serialized json" do- Right filepaths <- eitherDecodeFileStrict @([FilePath]) "./client-specification/specifications/index.json"-+ describe "Parse JSON specification" do+ it "parse all specification files and check if input JSON is the same as serialized JSON" do+ Right allFiles <- eitherDecodeFileStrict @([FilePath]) "./client-specification/specifications/index.json" -- Test 13 contains null as a value that is deleted by WithoutNothing- let filepathsWO13 = filter (not . isPrefixOf "13") filepaths-- let filepaths' = ("./client-specification/specifications/" <>) <$> filepathsWO13- traverse_ roundtrip filepaths'+ let exclusions = ["13-constraint-operators.json", "16-strategy-variants.json", "17-dependent-features.json"]+ let files = filter (not . \path -> any ((==) path) exclusions) allFiles+ let paths = ("./client-specification/specifications/" <>) <$> files+ traverse_ roundtrip paths roundtrip :: FilePath -> Expectation roundtrip filePath = do
test/UnleashSpecificationSpec.hs view
@@ -15,10 +15,11 @@ spec = describe "Run all specifications" do it "run all specification files" do- Right filepaths <- eitherDecodeFileStrict @([FilePath]) "./client-specification/specifications/index.json"- let filepaths' = ("./client-specification/specifications/" <>) <$> filepaths- let implemented = filepaths'- traverse_ runSpecification implemented+ Right allFiles <- eitherDecodeFileStrict @([FilePath]) "./client-specification/specifications/index.json"+ let exclusions = ["16-strategy-variants.json", "17-dependent-features.json"]+ let files = filter (not . \path -> any ((==) path) exclusions) allFiles+ let paths = ("./client-specification/specifications/" <>) <$> files+ traverse_ runSpecification paths runSpecification :: FilePath -> Expectation runSpecification filePath = do
unleash-client-haskell-core.cabal view
@@ -1,77 +1,82 @@-cabal-version: 3.0--name: unleash-client-haskell-core-version: 0.11.0-synopsis: Unleash feature toggle client core+cabal-version: 3.0+name: unleash-client-haskell-core+version: 0.12.0+synopsis: Unleash feature toggle client core description:- This is a library for evaluating [Unleash](https://www.getunleash.io/) feature toggles.- The 'Unleash' module provides functions and types for checking feature toggles and variants.-category: Web-author: FINN.no-homepage: https://github.com/finn-no/unleash-client-haskell-core-maintainer: See README.md-license: MIT-copyright: Copyright © FINN.no AS, Inc. All rights reserved.-extra-doc-files: CHANGELOG.md+ This is a library for evaluating [Unleash](https://www.getunleash.io/) feature toggles.+ The 'Unleash' module provides functions and types for checking feature toggles and variants.++category: Web+author: FINN.no+homepage: https://github.com/finn-no/unleash-client-haskell-core+maintainer: See README.md+license: MIT+copyright: Copyright © FINN.no AS, Inc. All rights reserved.+extra-doc-files: CHANGELOG.md extra-source-files: README.md +source-repository head+ type: git+ location: https://github.com/finn-no/unleash-client-haskell-core+ common all- default-extensions:- BlockArguments- DeriveFoldable- DeriveFunctor- DeriveTraversable- DerivingStrategies- DuplicateRecordFields- LambdaCase- OverloadedRecordDot- OverloadedStrings- RankNTypes- ScopedTypeVariables- TypeApplications- ghc-options:- -haddock- -W- -Wno-unused-top-binds- -Wredundant-constraints- -Wunused-packages- default-language: Haskell2010+ default-extensions:+ BlockArguments+ DeriveFoldable+ DeriveFunctor+ DeriveTraversable+ DerivingStrategies+ DuplicateRecordFields+ LambdaCase+ OverloadedRecordDot+ OverloadedStrings+ RankNTypes+ ScopedTypeVariables+ TypeApplications + ghc-options:+ -haddock -W -Wno-unused-top-binds -Wredundant-constraints+ -Wunused-packages++ default-language: Haskell2010+ library- import: all- exposed-modules:- Unleash- Unleash.Internal.DomainTypes- Unleash.Internal.JsonTypes- Unleash.Internal.Predicates- hs-source-dirs: src- build-depends:- aeson >= 2.0.3 && < 2.2,- base >= 4.7 && < 5,- containers >= 0.6.4 && < 0.7,- random >= 1.2.1 && < 1.3,- text >= 1.2.5 && < 2.1,- time >= 1.9.3 && < 1.13,- murmur3 >= 1.0.5 && < 1.1,- text-show >= 3.9.7 && < 3.11,- versions >= 5.0.4 && < 6.1,+ import: all+ exposed-modules:+ Unleash+ Unleash.Internal.DomainTypes+ Unleash.Internal.JsonTypes+ Unleash.Internal.Predicates + hs-source-dirs: src+ build-depends:+ , aeson >=2.0.3 && <2.3+ , base >=4.7 && <5+ , containers >=0.6.4 && <0.7+ , murmur3 >=1.0.5 && <1.1+ , random >=1.2.1 && <1.3+ , text >=1.2.5 && <2.2+ , text-show >=3.9.7 && <3.12+ , time >=1.9.3 && <1.13+ , versions >=5.0.4 && <6.1+ test-suite unleash-client-haskell-core-test- import: all- ghc-options: -Wno-missing-home-modules- type: exitcode-stdio-1.0- main-is: Spec.hs- hs-source-dirs: test- other-modules:- UnleashClientSpecificationRoundtripSpec- UnleashSpecificationJsonTypes- UnleashSpecificationSpec- build-depends:- bytestring >= 0.10.12 && < 0.12,- attoparsec >= 0.14.4 && < 0.15,- aeson-pretty >= 0.8.9 && < 0.9,- hspec >= 2.8.5 && < 2.11,- aeson,- base,- text,- unleash-client-haskell-core,+ import: all+ ghc-options: -Wno-missing-home-modules+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs: test+ other-modules:+ UnleashClientSpecificationRoundtripSpec+ UnleashSpecificationJsonTypes+ UnleashSpecificationSpec++ build-depends:+ , aeson+ , aeson-pretty >=0.8.9 && <0.9+ , attoparsec >=0.14.4 && <0.15+ , base+ , bytestring >=0.10.12 && <0.12+ , hspec >=2.8.5 && <2.12+ , text+ , unleash-client-haskell-core