packages feed

quokka 0.1.1 → 0.1.2

raw patch · 5 files changed

+199/−27 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Quokka.Functions: build1With1CustomRel :: ToRow q => Connection -> Relation -> ChildTable -> q -> IO (Maybe Id)
+ Quokka.Functions: build1WithManyCustomRels :: ToRow q => Connection -> [Relation] -> ChildTable -> q -> IO (Maybe Id)
+ Quokka.Functions: buildWith1CustomRel :: ToRow q => Connection -> Relation -> ChildTable -> [q] -> IO [Id]
+ Quokka.Functions: buildWithManyCustomRels :: ToRow q => Connection -> [Relation] -> ChildTable -> [q] -> IO [Id]
+ Quokka.Functions: insertStatementWith1CustomRel :: Relation -> ChildTable -> Query
+ Quokka.Functions: insertStatementWithManyCustomRels :: [Relation] -> ChildTable -> Query
+ Quokka.Types: FK :: Text -> FK
+ Quokka.Types: Relation :: ParentTable -> FK -> Relation
+ Quokka.Types: data Relation
+ Quokka.Types: newtype FK

Files

CHANGELOG.md view
@@ -1,5 +1,13 @@ # Revision history for quokka -## 0.1.0.0 -- 2019-10-30 +## 0.1.0 -- 2019-10-30   * First version. Released on an unsuspecting world.++## 0.1.1 -- 2019-10-30++* Minor build update to fix a release issue with verion 0.1.0.++## 0.1.2++* Added support for building insert statements for child tables that have foreign key columns that do not follow a standard naming convention which Quokka understands. 
quokka.cabal view
@@ -1,6 +1,6 @@ cabal-version:       >=1.10 name:                quokka-version:             0.1.1+version:             0.1.2 license:             MIT license-file:        LICENSE author:              Shirren Premaratne
src/Quokka/Functions.hs view
@@ -15,32 +15,41 @@   build , build1 , buildWith1Rel+, buildWith1CustomRel , build1With1Rel+, build1With1CustomRel , buildWithManyRels+, buildWithManyCustomRels , build1WithManyRels+, build1WithManyCustomRels , delete , deleteStatement , id' , insertStatement , insertStatementWith1Rel+, insertStatementWith1CustomRel , insertStatementWithManyRels+, insertStatementWithManyCustomRels  , mapFromIdToResult ) where +import Data.Functor (void) import Data.Int (Int64) import Data.Text (intercalate) import Data.Text.Encoding (encodeUtf8) import Database.PostgreSQL.Simple (Connection, ToRow, execute_, returning, query) import Database.PostgreSQL.Simple.Types (Query (Query)) import Quokka.Types (ChildTable (ChildTable)+                    , FK (FK)                     , Id (getId)                     , ParentTable (ParentTable)                     , Table (Table)+                    , Relation (Relation)                     , Result (SingleResult)) import Quokka.Text.Countable (singularize)  --- Build a prepared statement to insert data into the database`+-- | Build a prepared statement to insert data into the database` build   :: (ToRow q)   => Connection@@ -54,7 +63,7 @@   returning conn qry  --- Similar to the build function but we only ever return+-- | Similar to the build function but we only ever return -- a single optional result, and only take 1 value. build1   :: (ToRow q)@@ -69,8 +78,8 @@   fmap build1Helper . query conn qry  --- Build a prepared statement for a child table with a single foreign--- key table+-- | Build a prepared statement for a child table with a single foreign+-- key to the nominated parent table.  buildWith1Rel   :: (ToRow q)   => Connection@@ -85,7 +94,23 @@   returning conn qry  --- Build a prepared statement for a child table with a single foreign+-- | Build a prepared statement for a child table with a single foreign+-- key to the nominated parent table through a custom relation.+buildWith1CustomRel+  :: (ToRow q)+  => Connection+  -> Relation +  -> ChildTable+  -> [q]+  -> IO [Id]+buildWith1CustomRel conn relation child =+  let+    qry = insertStatementWith1CustomRel relation child+  in+  returning conn qry+++-- | Build a prepared statement for a child table with a single foreign -- key table build1With1Rel   :: (ToRow q)@@ -101,7 +126,23 @@   fmap build1Helper . query conn qry  --- Build a prepared statement for a child table with more than 1 parent+-- | Build a prepared statement for a child table with a single foreign+-- key table mapped through a custom relation.+build1With1CustomRel+  :: (ToRow q)+  => Connection+  -> Relation +  -> ChildTable+  -> q+  -> IO (Maybe Id)+build1With1CustomRel conn relation child =+  let+    qry = insertStatementWith1CustomRel relation child+  in+  fmap build1Helper . query conn qry+++-- | Build a prepared statement for a child table with more than 1 parent buildWithManyRels   :: (ToRow q)   => Connection@@ -116,7 +157,23 @@   returning conn qry  --- Build a prepared statement for a child table with more than 1 parent+-- | Build a prepared statement for a child table with more than 1 parent+-- mapped through a custom relation.+buildWithManyCustomRels+  :: (ToRow q)+  => Connection+  -> [Relation]+  -> ChildTable+  -> [q]+  -> IO [Id]+buildWithManyCustomRels conn relations child =+  let+    qry = insertStatementWithManyCustomRels relations child+  in+  returning conn qry+++-- | Build a prepared statement for a child table with more than 1 parent build1WithManyRels   :: (ToRow q)   => Connection@@ -131,7 +188,23 @@   fmap build1Helper . query conn qry  --- Perform a truncate with cascade action on the Table+-- | Build a prepared statement for a child table with more than 1 parent mapped+-- through a custom relation.+build1WithManyCustomRels+  :: (ToRow q)+  => Connection+  -> [Relation]+  -> ChildTable+  -> q+  -> IO (Maybe Id)+build1WithManyCustomRels conn relations child =+  let+    qry = insertStatementWithManyCustomRels relations child+  in+  fmap build1Helper . query conn qry+++-- | Perform a truncate with cascade action on the Table delete   :: Connection   -> Table@@ -140,11 +213,11 @@   let     alter = alterSequenceStatement tbl     qry   = deleteStatement tbl-  _ <- execute_ conn alter+  void $ execute_ conn alter   execute_ conn qry  --- Create an insert statement for a table+-- | Create an insert statement for a table insertStatement   :: ParentTable   -> Query@@ -157,7 +230,7 @@   Query (encodeUtf8 $ baseInsert <> " values (" <> valuesAsText <> ") returning id;")  --- Creates an insert statement for a table, and uses the parent table to also incude+-- | Creates an insert statement for a table, and uses the parent table to also incude -- a foreign key in the generation of the statement. insertStatementWith1Rel   :: ParentTable@@ -167,23 +240,47 @@   insertStatementWithManyRels [parent]  --- Creates an insert statement for a table, and uses multiple parent tables to also incude+-- | Creates an insert statement for a table, and uses the parent table custom relation+-- to build an insert statement for a child table using the relation+insertStatementWith1CustomRel+  :: Relation+  -> ChildTable+  -> Query+insertStatementWith1CustomRel relation =+  insertStatementWithManyCustomRels [relation]+++-- | Creates an insert statement for a table, and uses multiple parent tables to also include -- foreign keys in the generation of the statement. insertStatementWithManyRels   :: [ParentTable]   -> ChildTable   -> Query-insertStatementWithManyRels parents (ChildTable name columns) =+insertStatementWithManyRels parents child =   let-    updatedColumns = columns ++ map (\(ParentTable parentName _) -> singularize parentName <> "_id") parents-    columnsAsText = intercalate "," updatedColumns-    valuesAsText  = intercalate "," (map (const "?") updatedColumns)-    baseInsert    = "insert into " <> name <> " (" <> columnsAsText <> ")"+    buildFK name = FK (singularize name <> "_id")+    relations    = map (\p@(ParentTable name _) -> Relation p (buildFK name)) parents   in+  insertStatementWithManyCustomRels relations child+++-- | Creates an insert statement for a table where the relationship between the parent and child+-- is modelled using a custom key.+insertStatementWithManyCustomRels+  :: [Relation]+  -> ChildTable+  -> Query+insertStatementWithManyCustomRels relations (ChildTable name columns) =+  let+    updatedColumns = columns ++ map (\(Relation _ (FK fkName)) -> fkName) relations+    columnsAsText  = intercalate "," updatedColumns+    valuesAsText   = intercalate "," (map (const "?") updatedColumns)+    baseInsert     = "insert into " <> name <> " (" <> columnsAsText <> ")"+  in   Query (encodeUtf8 $ baseInsert <> " values (" <> valuesAsText <> ") returning id;")  --- Generate an alter sequence statement for a table+-- | Generate an alter sequence statement for a table alterSequenceStatement   :: Table   -> Query@@ -191,7 +288,7 @@   Query (encodeUtf8 $ "alter sequence " <> name <> "_id_seq restart;")  --- Generate a delete statement for a table+-- | Generate a delete statement for a table deleteStatement   :: Table   -> Query@@ -199,7 +296,7 @@   Query (encodeUtf8 $ "truncate table " <> name <> " cascade;")  --- Helper function to extract the underlying Int value from+-- | Helper function to extract the underlying Int value from -- the first value in the list id' :: [Id] -> Int id' (x:_) =@@ -207,7 +304,7 @@ id' [] =   -1 --- Postgres Simple does not have a function which maps from [r] -> Maybe r+-- | Postgres Simple does not have a function which maps from [r] -> Maybe r -- so we've written one that takes the head or returns Nothing in a safe -- manner. build1Helper@@ -219,7 +316,7 @@   Nothing  --- Function to map from IO [Id] -> IO [Result]+-- | Function to map from IO [Id] -> IO [Result] mapFromIdToResult   :: ParentTable   -> [Id]
src/Quokka/Types.hs view
@@ -13,9 +13,11 @@ module Quokka.Types (   ChildTable (..) , Data+, FK (..) , Id (..) , ParentTable (..) , Table (..)+, Relation (..) , Result (..) , Row (..) ) where@@ -27,10 +29,11 @@ import Database.PostgreSQL.Simple.ToField (toField, ToField)  +-- | Alias for a list of values. type Data = [Text]  --- Represents the identity column of a row in a table. I.e. the+-- | Represents the identity column of a row in a table. I.e. the -- primary key of a table which is limited to integers. newtype Id   = Id { getId :: Int }@@ -45,21 +48,39 @@     toField val  +-- | A child table represents a relation in Postgres with a foreign key+-- to a parent table. data ChildTable   = ChildTable Text [Text]  +-- | A parent table represents a relation in Postgres with no foreign keys. data ParentTable   = ParentTable Text [Text]  +-- | This type is used to model and create deletes. newtype Table   = Table Text ++-- | A relation is defined as a parent table that is related+-- through a custom foreign key.+data Relation+  = Relation ParentTable FK+++-- | Column that represents a foreign key in a database.+newtype FK+  = FK Text+++-- | Represents a result retrieved by Quokka via Postgres-simple. data Result   = SingleResult ParentTable Id  +-- | A row represents a Postgres row retrieved for a relation. newtype Row a   = Row [a] 
test/Quokka/CreateSpec.hs view
@@ -8,13 +8,16 @@ import Data.Text (Text) import Data.Text.Encoding (encodeUtf8) import Database.PostgreSQL.Simple.Types (Query (Query))-import Quokka.Types (ChildTable (..), Id (..), ParentTable (..))+import Quokka.Types (ChildTable (..), FK (..), Id (..), ParentTable (..), Relation (..)) import Quokka.Functions (build1                         , build1With1Rel+                        , build1With1CustomRel                         , id'                         , insertStatement                         , insertStatementWith1Rel-                        , insertStatementWithManyRels)+                        , insertStatementWith1CustomRel+                        , insertStatementWithManyRels+                        , insertStatementWithManyCustomRels) import Quokka.Helper (setupDb, withDatabaseConnection) import Quokka.Tables (accountTableAsChild                      , insertAccounts@@ -50,7 +53,38 @@        stmt `shouldBe` encodeUtf8 "insert into accounts (name,user_id) values (?,?) returning id;" +  +  describe "insertStatementWith1CustomRel" $+    it "should return an insert statement with the custom FK set" $ do+      let+        relation    = Relation (ParentTable "users" ["firstname", "lastname", "age"]) (FK "user")+        childTable  = ChildTable "accounts" ["name"]+        Query stmt  = insertStatementWith1CustomRel relation childTable +      stmt `shouldBe` encodeUtf8 "insert into accounts (name,user) values (?,?) returning id;"+++  describe "insertStatementWithManyCustomRels" $ do+    context "for 2 parent tables" $+      it "should return an insert statement with 2 custom FKs set" $ do+        let+          parentTable1 = ParentTable "users" ["firstname", "lastname", "age"]+          parentTable2 = ParentTable "accounts" ["name"]+          childTable   = ChildTable "profiles" ["active"]+          Query stmt   = insertStatementWithManyCustomRels +                          [Relation parentTable1 (FK "userfk"), Relation parentTable2 (FK "accountfk")]+                          childTable++        stmt `shouldBe` encodeUtf8 "insert into profiles (active,userfk,accountfk) values (?,?,?) returning id;"+    +    context "for no parents" $+      it "should return an insert statement with no FKs set" $ do+        let+          childTable   = ChildTable "profiles" ["active"]+          Query stmt  = insertStatementWithManyCustomRels [] childTable++        stmt `shouldBe` encodeUtf8 "insert into profiles (active) values (?) returning id;"+   describe "insertStatementWithManyRels" $ do     context "for 2 parent tables" $       it "should return an insert statement with 2 FKs set" $ do@@ -93,6 +127,18 @@              accountId `shouldBe` Just (Id 1) ++        context "for a table with a single custom relationship" $+          it "should insert parent and child into the database" $ \conn -> do+            let+              insertUser    = build1 conn userTable+              customRel     = Relation userTable (FK "user_id")+              insertAccount = build1With1CustomRel conn customRel accountTableAsChild+            userId    <- insertUser ("John" :: Text, "Doe" :: Text, 1 :: Int)+            accountId <- insertAccount ("Account-1" :: Text, "Description" :: Text, fromJust userId)++            accountId `shouldBe` Just (Id 1)+              describe "insert" $ do         context "for a table with no relationships" $