@@ -1,4 +1,6 @@
1{-# LANGUAGE EmptyCase #-}
1{-# LANGUAGE EmptyCase #-}
2+{-# LANGUAGE MultiParamTypeClasses #-}
3+{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE TemplateHaskell #-}
4{-# LANGUAGE TemplateHaskell #-}
3{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
4{-# LANGUAGE TupleSections #-}
6{-# LANGUAGE TupleSections #-}
@@ -16,58 +18,108 @@
16{-# LANGUAGE TypeFamilies #-}
18{-# LANGUAGE TypeFamilies #-}
17module Backend where
19module Backend where
19import Common.Route
23import Common.Route
21import Data.Text (Text)
24import Data.Text (Text)
22−import Data.Functor.Identity
23import Data.Map.Monoidal (MonoidalMap (..))
25import Data.Map.Monoidal (MonoidalMap (..))
24−import qualified Data.Map.Monoidal as Map
26+import Data.Pool
26import Obelisk.Backend
27import Obelisk.Backend
27import Obelisk.Route
28import Obelisk.Route
28import Obelisk.Api
29import Obelisk.Api
29−import Obelisk.Api.Pipeline --TODO: Move Request to Obelisk.Api
30import Obelisk.Db
30import Obelisk.Db
31import Obelisk.Postgres.LogicalDecoding.Plugins.TestDecoding
31import Obelisk.Postgres.LogicalDecoding.Plugins.TestDecoding
32−import Obelisk.Request.TH
32+import qualified Data.Map as Map
33−import Reflex.Class
33+import Data.Sequence (Seq)
34−import Reflex.Query.Class
38−newtype VS k v a = VS (MonoidalMap k a)
39− deriving (Semigroup, Monoid, Functor, Foldable, Traversable, Group, ToJSON, FromJSON, Eq)
41−newtype V k v a = V (MonoidalMap k (a, First (Maybe v)))
35+import Database.Beam
42− deriving (Semigroup, Monoid, Functor, Foldable, Traversable, ToJSON, FromJSON, Eq)
36+import Database.Beam.Postgres
37+import Database.Beam.Postgres.Migrate
38+import Database.Beam.Migrate.Simple
39+import Database.Beam.Schema.Tables
40+import Database.PostgreSQL.Simple.Types
41+import Control.Monad.Reader
44−instance FunctorMaybe (V k v) where
43+import Data.Proxy
45− fmapMaybe f (V m) = V $ Map.mapMaybe (\(a, v) -> (,v) <$> f a) m
47−instance (Ord k, Semigroup a) => Query (VS k v a) where
45+import Data.Functor.Identity
48− type QueryResult (VS k v a) = V k v a
46+import Data.Functor.Const
49− crop _ = id
47+import Control.Lens
51−data MyRequest a where
52− MyRequest_Echo :: Text -> MyRequest Text
54−makeRequestForData ''MyRequest
49+import Debug.Trace
56backend :: Backend BackendRoute FrontendRoute
51backend :: Backend BackendRoute FrontendRoute
57backend = Backend
52backend = Backend
58 { _backend_run = \serve -> do
53 { _backend_run = \serve -> do
59 withDbUri "db" $ \dbUri -> do
54 withDbUri "db" $ \dbUri -> do
60− print dbUri
55+ withConnectionPool dbUri $ \pool -> do
56+ withResource pool $ \conn -> do
57+ runBeamPostgres conn $ autoMigrate migrationBackend checkedDb
61 let onNotify :: forall a. VS Int Text a -> Transaction -> ReadDb (V Int Text a)
58 let onNotify :: forall a. VS Int Text a -> Transaction -> ReadDb (V Int Text a)
62 onNotify (VS _) txn = do
59 onNotify (VS _) txn = do
63 traceM $ show txn
60 traceM $ show txn
61+ traceM $ show $ decodeTransaction txn
62+ tasks <- runSelectReturningList' $ select $ all_ $ _db_tasks db
64 pure $ V $ MonoidalMap mempty
64 pure $ V $ MonoidalMap mempty
65 onSubscribe :: forall a. VS Int Text a -> ReadDb (V Int Text a)
65 onSubscribe :: forall a. VS Int Text a -> ReadDb (V Int Text a)
66 onSubscribe (VS _) = do
66 onSubscribe (VS _) = do
67+ tasks <- runSelectReturningList' $ select $ all_ $ _db_tasks db
67 pure $ V $ MonoidalMap mempty
69 pure $ V $ MonoidalMap mempty
70+ onRequest :: forall a. MyRequest a -> WriteDb a
72+ MyRequest_Echo a -> do
73+ runInsert' $ insert (_db_tasks db) $ insertExpressions $ (:[]) $ Task
74+ { _task_id = default_
75+ , _task_description = val_ a
76+ , _task_completed = val_ False
68− withApiHandler dbUri (\(MyRequest_Echo a) -> pure a) onNotify onSubscribe $ \handler -> do
79+ withApiHandler dbUri onRequest onNotify onSubscribe $ \handler -> do
69 serve $ \case
80 serve $ \case
70 BackendRoute_Api :/ () -> handler
81 BackendRoute_Api :/ () -> handler
71 _ -> return ()
82 _ -> return ()
72 , _backend_routeEncoder = backendRouteEncoder
83 , _backend_routeEncoder = backendRouteEncoder
86+class MonadBeamRead m where
87+ runSelectReturningList' :: FromBackendRow Postgres a => SqlSelect Postgres a -> m [a]
88+ runSelectReturningOne' :: FromBackendRow Postgres a => SqlSelect Postgres a -> m (Maybe a)
90+unsafeRunPgReadDb :: Pg a -> ReadDb a
91+unsafeRunPgReadDb a = ReadDb $ do
93+ lift $ runBeamPostgres conn a
95+unsafeRunPgWriteDb :: Pg a -> WriteDb a
96+unsafeRunPgWriteDb a = WriteDb $ do
98+ lift $ runBeamPostgres conn a
100+instance MonadBeamRead WriteDb where
101+ runSelectReturningList' = unsafeRunPgWriteDb . runSelectReturningList
102+ runSelectReturningOne' = unsafeRunPgWriteDb . runSelectReturningOne
104+instance MonadBeamRead ReadDb where
105+ runSelectReturningList' = unsafeRunPgReadDb . runSelectReturningList
106+ runSelectReturningOne' = unsafeRunPgReadDb . runSelectReturningOne
108+class MonadBeamWrite m where
109+ runInsert' :: SqlInsert Postgres table -> m ()
110+ runUpdate' :: SqlUpdate Postgres table -> m ()
111+ runDelete' :: SqlDelete Postgres table -> m ()
113+instance MonadBeamWrite WriteDb where
114+ runInsert' = unsafeRunPgWriteDb . runInsert
115+ runUpdate' = unsafeRunPgWriteDb . runUpdate
116+ runDelete' = unsafeRunPgWriteDb . runDelete
118+decodeTransaction :: Transaction -> Db (Const (Seq Change))
119+decodeTransaction (_, tableChanges) = runIdentity $ zipTables (Proxy :: Proxy Postgres) f db db
121+ f :: IsDatabaseEntity Postgres entityType => DatabaseEntity Postgres Db entityType -> DatabaseEntity Postgres Db entityType -> Identity (Const (Seq Change) entityType)
122+ f (DatabaseEntity descriptor) _ = pure $ Const $
123+ let n = view dbEntityName descriptor
124+ s = Just "public" -- view dbEntitySchema descriptor --TODO: Beam has 'Nothing' while the replication connection reports 'Just "public"'
125+ in trace (show (s, n)) $ Map.findWithDefault mempty (QualifiedIdentifier s n) tableChanges