Skip to content

Commit 70979cc

Browse files
committed
Create better domain/data layer separation
1 parent 2b8acef commit 70979cc

File tree

5 files changed

+61
-37
lines changed

5 files changed

+61
-37
lines changed

src/Control/Flipper/Adapters/Postgres.hs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import qualified Data.Map.Strict as Map
1515
import Database.Persist.Postgresql (ConnectionPool)
1616

1717
import Control.Flipper.Adapters.Postgres.DBAccess (DBAccess, db)
18-
import Control.Flipper.Adapters.Postgres.Models (Entity(..), modelsToFeatures, modelToFeature)
1918
import qualified Control.Flipper.Adapters.Postgres.Query as Q
2019
import Control.Flipper.Types (FeatureName,
2120
Features (..),
@@ -37,21 +36,16 @@ newtype FlipperT m a = FlipperT { unFlipper :: ReaderT Config m a }
3736
)
3837

3938
instance (MonadIO m) => HasFeatureFlags (FlipperT m) where
40-
getFeatures = ask >>= \Config{..} ->
41-
modelsToFeatures <$> Q.getFeatures appDB
39+
getFeatures = ask >>= \Config{..} -> Q.getFeatures appDB
4240

43-
getFeature name = ask >>= \Config{..} -> do
44-
mFeature <- Q.getFeatureByName name appDB
45-
case mFeature of
46-
Nothing -> return Nothing
47-
(Just (Entity _ feature)) -> return $ Just (modelToFeature feature)
41+
getFeature name = ask >>= \Config{..} -> Q.getFeatureByName name appDB
4842

4943
instance (MonadIO m) => ModifiesFeatureFlags (FlipperT m) where
5044
updateFeatures features =
5145
void $ Map.traverseWithKey updateFeature (unFeatures features)
5246

53-
updateFeature fName feature = ask >>= \Config{..} ->
54-
Q.upsertFeature fName feature appDB
47+
updateFeature _ feature = ask >>= \Config{..} ->
48+
Q.upsertFeature feature appDB
5549

5650
{- |
5751
Evaluates a feature-switched computation, returning the final value

src/Control/Flipper/Adapters/Postgres/Models.hs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,11 @@ module Control.Flipper.Adapters.Postgres.Models
1818
, module Database.Persist.Postgresql
1919
) where
2020

21-
import qualified Data.ByteString.Char8 as C8
2221
import qualified Data.Map.Strict as Map
2322
import Data.Monoid ((<>))
2423
import qualified Data.Set as S
2524
import Data.Text (Text)
2625
import qualified Data.Text as T
27-
import qualified Data.Text.Encoding as T
2826
import Data.Time (UTCTime (..), getCurrentTime)
2927
import Database.Persist.Postgresql
3028
import Database.Persist.TH
@@ -49,7 +47,7 @@ share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
4947
|]
5048

5149
instance PersistField F.FeatureName where
52-
toPersistValue = PersistDbSpecific . T.encodeUtf8 . F.unFeatureName
50+
toPersistValue = PersistText . F.unFeatureName
5351
fromPersistValue (PersistText name) = Right (F.FeatureName name)
5452
fromPersistValue name = Left ("Not PersistText " <> T.pack (show name))
5553

@@ -59,9 +57,9 @@ instance PersistField F.ActorId where
5957
fromPersistValue e = Left ("Not PersistByteString " <> T.pack (show e))
6058

6159
instance PersistField F.Percentage where
62-
toPersistValue (F.Percentage pct) = PersistDbSpecific . C8.pack $ show pct
60+
toPersistValue (F.Percentage pct) = PersistInt64 (fromIntegral pct)
6361
fromPersistValue (PersistInt64 pct) = Right (F.Percentage (fromIntegral pct))
64-
fromPersistValue e = Left ("Not PersistDbSpecific " <> T.pack (show e))
62+
fromPersistValue e = Left ("Not PersistInt64 " <> T.pack (show e))
6563

6664
{- |
6765
Convienience constructor

src/Control/Flipper/Adapters/Postgres/Query.hs

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,69 @@ import Control.Monad.IO.Class (MonadIO, liftIO)
2020

2121
{- |
2222
Selects all feature records
23+
Returns domain model
2324
-}
2425
getFeatures :: (MonadIO app, Monad m)
26+
=> DBAccess m -> app T.Features
27+
getFeatures dbAccess = modelsToFeatures <$> getFeatures' dbAccess
28+
29+
{- |
30+
Selects all feature records
31+
Returns database entities
32+
-}
33+
getFeatures' :: (MonadIO app, Monad m)
2534
=> DBAccess m -> app [Entity Feature]
26-
getFeatures DBAccess{..} = liftIO $ runDb selectFeatures
35+
getFeatures' DBAccess{..} = liftIO $ runDb selectFeatures
2736

2837
{- |
2938
Selects a feature record by its unique name
39+
Returns a domain model
3040
-}
3141
getFeatureByName :: (MonadIO app, Monad m)
42+
=> T.FeatureName -> DBAccess m -> app (Maybe T.Feature)
43+
getFeatureByName fName dbAccess = do
44+
mFeatureEnt <- getFeatureByName' fName dbAccess
45+
case mFeatureEnt of
46+
Nothing -> return Nothing
47+
(Just (Entity _ feature)) -> return . Just . modelToFeature $ feature
48+
49+
{- |
50+
Selects a feature record by its unique name
51+
Returns a database entity
52+
-}
53+
getFeatureByName' :: (MonadIO app, Monad m)
3254
=> T.FeatureName -> DBAccess m -> app (Maybe (Entity Feature))
33-
getFeatureByName fName DBAccess{..} = liftIO $ runDb (findFeature fName)
55+
getFeatureByName' fName DBAccess{..} = liftIO $ runDb (findFeature fName)
3456

3557
{- |
3658
Inserts a new feature record if one with a matching name does not already exist.
3759
Updates an existing feature record if one with a matching name already exists.
3860
-}
3961
upsertFeature :: (MonadIO app, Monad m)
40-
=> T.FeatureName -> T.Feature -> DBAccess m -> app ()
41-
upsertFeature fName feature dbAccess = do
42-
mFeature <- getFeatureByName fName dbAccess
62+
=> T.Feature -> DBAccess m -> app ()
63+
upsertFeature feature dbAccess = do
64+
mFeature <- getFeatureByName' (T.featureName feature) dbAccess
4365
case mFeature of
4466
Nothing ->
45-
liftIO (featureToModel feature) >>= void . flip addFeature dbAccess
67+
liftIO (featureToModel feature) >>= void . flip addFeature' dbAccess
4668
(Just (Entity fId f)) ->
4769
replaceFeature fId (f { featureEnabled = T.isEnabled feature }) dbAccess
4870

4971
{- |
5072
Inserts a new feature record.
5173
-}
5274
addFeature :: (MonadIO app, Monad m)
75+
=> T.Feature -> DBAccess m -> app (Key Feature)
76+
addFeature feature dbAccess = do
77+
model <- liftIO $ featureToModel feature
78+
addFeature' model dbAccess
79+
80+
{- |
81+
Inserts a new feature record.
82+
-}
83+
addFeature' :: (MonadIO app, Monad m)
5384
=> Feature -> DBAccess m -> app (Key Feature)
54-
addFeature feature DBAccess{..} = liftIO $ runDb (insertFeature feature)
85+
addFeature' feature DBAccess{..} = liftIO $ runDb (insertFeature feature)
5586

5687
{- |
5788
Updates an existing feature record.

test/Control/Flipper/Postgres/QuerySpec.hs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import Control.Monad (void)
44
import Test.Hspec
55

66
import Control.Flipper.Adapters.Postgres
7-
import Control.Flipper.Adapters.Postgres.Models as M
87
import qualified Control.Flipper.Adapters.Postgres.Query as Q
98
import qualified Control.Flipper.Types as T
109
import qualified Helpers.Config as Cfg
@@ -18,18 +17,18 @@ spec = around Cfg.withConfig $ do
1817
it "creates a new feature when no feature by the given name exists" $ \(Config _ db) -> do
1918
let name = (T.FeatureName "experimental-feature")
2019
let feature = (T.mkFeature name) { isEnabled = True }
21-
Q.upsertFeature name feature db
22-
(Just (Entity _ f)) <- Q.getFeatureByName name db
23-
M.featureName f `shouldBe` name
24-
M.featureEnabled f `shouldBe` True
20+
Q.upsertFeature feature db
21+
(Just f) <- Q.getFeatureByName name db
22+
T.featureName f `shouldBe` name
23+
T.isEnabled f `shouldBe` True
2524

2625
it "updates an existing feature when a feature by the given name exists" $ \(Config _ db) -> do
2726
let name = (T.FeatureName "experimental-feature")
28-
f <- M.mkFeature name True
27+
let f = (T.mkFeature name) { isEnabled = True }
2928
void $ Q.addFeature f db
3029
Q.featureCount db `shouldReturn` 1
3130

32-
let f' = (modelToFeature f) { isEnabled = False }
33-
Q.upsertFeature name f' db
34-
(Just (Entity _ feature)) <- Q.getFeatureByName name db
35-
M.featureEnabled feature `shouldBe` False
31+
let f' = f { isEnabled = False }
32+
Q.upsertFeature f' db
33+
(Just feature) <- Q.getFeatureByName name db
34+
T.isEnabled feature `shouldBe` False

test/Control/Flipper/PostgresSpec.hs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,17 @@ spec = around Cfg.withConfig $ do
4747
st `shouldBe` MyState 0
4848

4949
describe "a persisted feature" $ do
50-
it "runs features when it is enabled" $ \(Config pool dbAccess) -> do
51-
f <- Q.mkFeature (FP.FeatureName "enabled-feature") True
50+
it "runs a feature when it is enabled" $ \(Config pool dbAccess) -> do
51+
let f = (FP.mkFeature "enabled-feature") { isEnabled = True }
5252
void $ Q.addFeature f dbAccess
5353

5454
(_, st) <- runMyContext pool (MyState 0) $ do
5555
whenEnabled "enabled-feature" (void $ put (MyState 1))
5656

5757
st `shouldBe` MyState 1
5858

59-
it "does not run features it is are disabled" $ \(Config pool dbAccess) -> do
60-
f <- Q.mkFeature (FP.FeatureName "disabled-feature") False
59+
it "does not run disabled features" $ \(Config pool dbAccess) -> do
60+
let f = (FP.mkFeature "disabled-feature") { isEnabled = False }
6161
void $ Q.addFeature f dbAccess
6262

6363
(_, st) <- runMyContext pool (MyState 0) $ do
@@ -100,4 +100,6 @@ spec = around Cfg.withConfig $ do
100100
fs'' <- FP.getFeatures
101101
liftIO $ all (\f -> isEnabled f == False) (Map.elems (unFeatures fs'')) `shouldBe` True
102102

103-
103+
describe "enabling a feature on a per-user basis" $ do
104+
it "runs a feature for enabled users" $ \(Config _ _) -> do
105+
pending

0 commit comments

Comments
 (0)