diff --git a/README.md b/README.md index deb156c..0144970 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ main = unsafePartial $ void $ launchAff $ do See the module documentation for a [full list of these helpers](docs/Database.Mongo.Mongo.md#find) +See the test cases for [more examples](test/Database/Mongo/) + ### Module documentation - [Database.Mongo.Mongo](docs/Database.Mongo.Mongo.md) diff --git a/bower.json b/bower.json index ef65cd7..b9fd953 100644 --- a/bower.json +++ b/bower.json @@ -13,13 +13,14 @@ "tests" ], "dependencies": { - "purescript-aff": "^1.0.0", - "purescript-argonaut": "^1.0.0", - "purescript-datetime": "^1.0.0", - "purescript-foldable-traversable": "^1.0.0", - "purescript-foreign": "^1.0.0", - "purescript-string-parsers": "^1.0.1", - "purescript-uri": "^1.0.0" + "purescript-aff": "^2.0.2", + "purescript-argonaut": "^2.0.0", + "purescript-datetime": "^2.0.0", + "purescript-foldable-traversable": "^2.0.0", + "purescript-foreign": "^3.0.1", + "purescript-string-parsers": "^2.0.0", + "purescript-uri": "^2.0.0", + "purescript-transformers": "^2.1.0" }, "homepage": "https://github.com/dicefm/purescript-node-mongodb", "authors": [ @@ -34,5 +35,9 @@ "mongodb", "mongo" ], - "license": "MIT" + "license": "MIT", + "devDependencies": { + "purescript-debug": "^2.0.0", + "purescript-assert": "^2.0.0" + } } diff --git a/examples/Examples/Data/Event.purs b/examples/Examples/Data/Event.purs deleted file mode 100644 index 3fcd40a..0000000 --- a/examples/Examples/Data/Event.purs +++ /dev/null @@ -1,35 +0,0 @@ -module Data.Event where - -import Data.Argonaut ((~>), (:=), (.?), jsonEmptyObject, printJson) -import Data.Argonaut.Core (Json(), JString(), toString) -import Data.Argonaut.Encode (EncodeJson, encodeJson) -import Data.Argonaut.Decode (DecodeJson, decodeJson) -import Data.Date -import Data.Either -import Data.Maybe (Maybe(..), maybe) -import Data.Traversable (traverse) - -import Debug.Trace - -newtype Event = Event - { name :: Maybe String - } - -instance decodeJsonEvent :: DecodeJson Event where - decodeJson json = do - obj <- decodeJson json - name <- obj .? "name" - pure $ Event - { name : name - } - -instance encodeJsonEvent :: EncodeJson Event where - encodeJson (Event e) - = "name" := e.name - ~> jsonEmptyObject - -instance showEvent :: Show Event where - show (Event e) = "Event " ++ - "{ name: " ++ show e.name ++ - "}" - \ No newline at end of file diff --git a/examples/Examples/Database/Mongo/Find.purs b/examples/Examples/Database/Mongo/Find.purs deleted file mode 100644 index a0b8df6..0000000 --- a/examples/Examples/Database/Mongo/Find.purs +++ /dev/null @@ -1,44 +0,0 @@ -module Examples.Database.Mongo.Find where - -import Database.Mongo.Mongo -import Database.Mongo.Bson.BsonValue - -import Control.Monad.Aff -import Control.Monad.Eff -import Control.Monad.Eff.Class -import Control.Monad.Eff.Exception - -import Data.Argonaut (printJson) -import Data.Argonaut.Core (Json(..)) -import Data.Argonaut.Decode (DecodeJson, decodeJson) -import Data.Either -import Data.Event -import Data.Maybe -import Data.String.Regex -import Data.URI - -import Debug.Trace - -foreign import traceAny - """ - function traceAny(a){ - return function () { - console.log(a); - return {}; - }; - } - """ :: forall e a. a -> Eff (trace :: Trace | e) Unit - -uri :: String -uri = "mongodb://127.0.0.1/events" - -main = launchAff $ do - - Right database <- attempt $ connect uri - col <- collection "events" database - cur <- find [ "name" := "Wow!" ] [ "name" := 1 ] col - res <- collect cur - - liftEff $ traceAny (res :: [Event]) - - close database diff --git a/examples/Examples/Database/Mongo/Insert.purs b/examples/Examples/Database/Mongo/Insert.purs deleted file mode 100644 index 7056e6d..0000000 --- a/examples/Examples/Database/Mongo/Insert.purs +++ /dev/null @@ -1,49 +0,0 @@ -module Examples.Database.Mongo.Insert where - -import Database.Mongo.Mongo -import Database.Mongo.Options -import Database.Mongo.Bson.BsonValue - -import Control.Monad.Aff -import Control.Monad.Eff -import Control.Monad.Eff.Class -import Control.Monad.Eff.Exception - -import Data.Argonaut (printJson) -import Data.Argonaut.Core (Json(..)) -import Data.Argonaut.Encode (EncodeJson, encodeJson) -import Data.Either -import Data.Event -import Data.Maybe -import Data.String.Regex -import Data.URI - -import Debug.Trace - -foreign import traceAny - """ - function traceAny(a){ - return function () { - console.log(a); - return {}; - }; - } - """ :: forall e a. a -> Eff (trace :: Trace | e) Unit - -evt :: Event -evt = Event - { name : Just "Wow!" - } - -uri :: String -uri = "mongodb://127.0.0.1/events" - -main = launchAff $ do - - Right database <- attempt $ connect uri - col <- collection "events" database - res <- insertOne evt defaultInsertOptions col - - liftEff $ traceAny res - - close database diff --git a/examples/Examples/Database/Mongo/Update.purs b/examples/Examples/Database/Mongo/Update.purs deleted file mode 100644 index 86ec970..0000000 --- a/examples/Examples/Database/Mongo/Update.purs +++ /dev/null @@ -1,49 +0,0 @@ -module Examples.Database.Mongo.Update where - -import Database.Mongo.Mongo -import Database.Mongo.Options -import Database.Mongo.Bson.BsonValue - -import Control.Monad.Aff -import Control.Monad.Eff -import Control.Monad.Eff.Class -import Control.Monad.Eff.Exception - -import Data.Argonaut (printJson) -import Data.Argonaut.Core (Json(..)) -import Data.Argonaut.Encode (EncodeJson, encodeJson) -import Data.Either -import Data.Event -import Data.Maybe -import Data.String.Regex -import Data.URI - -import Debug.Trace - -foreign import traceAny - """ - function traceAny(a){ - return function () { - console.log(a); - return {}; - }; - } - """ :: forall e a. a -> Eff (trace :: Trace | e) Unit - -evt :: Event -evt = Event - { name : Just "Wow!" - } - -uri :: String -uri = "mongodb://127.0.0.1/events" - -main = launchAff $ do - - Right database <- attempt $ connect uri - col <- collection "events" database - res <- updateMany [] ["$set" := encodeJson evt] defaultUpdateOptions col - - liftEff $ traceAny res - - close database diff --git a/src/Database/Mongo/Bson/BsonValue.purs b/src/Database/Mongo/Bson/BsonValue.purs index 313ccca..884d7f0 100644 --- a/src/Database/Mongo/Bson/BsonValue.purs +++ b/src/Database/Mongo/Bson/BsonValue.purs @@ -10,8 +10,8 @@ module Database.Mongo.Bson.BsonValue ) where import Data.Argonaut.Core (Json()) -import Data.String.Regex -import Data.Tuple +import Data.String.Regex (Regex) +import Data.Tuple (Tuple(..)) type Field = Tuple String BsonValue diff --git a/src/Database/Mongo/Mongo.purs b/src/Database/Mongo/Mongo.purs index 22eb045..30d4ce0 100644 --- a/src/Database/Mongo/Mongo.purs +++ b/src/Database/Mongo/Mongo.purs @@ -17,7 +17,7 @@ module Database.Mongo.Mongo , updateMany, updateMany' ) where -import Prelude +import Prelude (class Show, Unit, show, ($), (<<<)) import Control.Monad.Aff (Aff(), makeAff', Canceler(), nonCanceler) import Control.Monad.Eff (Eff()) import Control.Monad.Eff.Exception (Error(), error) @@ -25,15 +25,15 @@ import Control.Monad.Eff.Exception (Error(), error) import Data.Argonaut.Core (Json()) import Data.Argonaut.Encode (class EncodeJson, encodeJson) import Data.Argonaut.Decode (class DecodeJson, decodeJson) -import Data.Either +import Data.Either (Either(..)) import Data.Function.Uncurried (Fn3(), runFn3, Fn4(), runFn4, Fn5(), runFn5, Fn6(), runFn6, Fn7(), runFn7, Fn8(), runFn8) import Database.Mongo.Options (InsertOptions(), insertOptions, UpdateOptions(), updateOptions) import Database.Mongo.Results (WriteResult()) import Database.Mongo.Bson.BsonValue (Document(), printBson) -import Data.URI +import Data.URI (printURIRef, runParseURIRef) -import Text.Parsing.StringParser +import Text.Parsing.StringParser (ParseError) -- | The effect type for DB request made with Mongo foreign import data DB :: ! diff --git a/src/Database/Mongo/Options.purs b/src/Database/Mongo/Options.purs index 9558017..d77653e 100644 --- a/src/Database/Mongo/Options.purs +++ b/src/Database/Mongo/Options.purs @@ -6,13 +6,12 @@ module Database.Mongo.Options , defaultUpdateOptions, updateOptions ) where -import Prelude +import Prelude (pure, bind, ($)) import Data.Argonaut ((~>), (:=), (.?), jsonEmptyObject) import Data.Argonaut.Core (Json()) import Data.Argonaut.Encode (class EncodeJson, encodeJson) import Data.Argonaut.Decode (class DecodeJson, decodeJson) -import Data.Either -import Data.Maybe +import Data.Maybe (Maybe(..)) -- | The type of WriteConcern type WriteConcern = Number diff --git a/src/Database/Mongo/Results.purs b/src/Database/Mongo/Results.purs index 1331702..3329b44 100644 --- a/src/Database/Mongo/Results.purs +++ b/src/Database/Mongo/Results.purs @@ -2,12 +2,12 @@ module Database.Mongo.Results ( WriteResult() ) where -import Prelude -import Data.Argonaut ((.?), jsonEmptyObject) +import Prelude (pure, bind, ($)) +import Data.Argonaut ((.?), (:=), (~>), jsonEmptyObject) import Data.Argonaut.Encode (class EncodeJson) import Data.Argonaut.Decode (class DecodeJson, decodeJson) -import Data.Either -import Data.Maybe +import Data.Either (Either(..)) +import Data.Maybe (Maybe(..), fromMaybe) newtype WriteResult = WriteResult { success :: Boolean @@ -33,14 +33,23 @@ instance decodeJsonWriteResult :: DecodeJson WriteResult where } instance encodeJsonWriteResult :: EncodeJson WriteResult where - encodeJson (WriteResult w) = jsonEmptyObject + encodeJson (WriteResult w) + = "ok" := boolToJsNumber w.success + ~> "n" := w.total + ~> "nInserted" := fromMaybe 0.0 w.inserted + ~> "nModified" := fromMaybe 0.0 w.modified + ~> jsonEmptyObject + +boolToJsNumber :: Boolean -> Int +boolToJsNumber false = 0 +boolToJsNumber true = 1 -- node mongodb module sends back `1` to mean `true`, this is why we need types -- as Javascript is abused! -jsNumberToBool :: Either String Int -> Boolean +jsNumberToBool :: Int -> Boolean jsNumberToBool e = case e of - Left _ -> false - Right x -> if x == 1 then true else false + 1 -> true + _ -> false extract :: Either String Number -> Maybe Number extract e = case e of diff --git a/test/Data/Event.purs b/test/Data/Event.purs new file mode 100644 index 0000000..a8662e3 --- /dev/null +++ b/test/Data/Event.purs @@ -0,0 +1,32 @@ +module Test.Data.Event where + +import Prelude +import Data.Argonaut (jsonEmptyObject, (~>), (:=), (.?)) +import Data.Argonaut.Encode.Class (class EncodeJson) +import Data.Argonaut.Decode (decodeJson) +import Data.Argonaut.Decode.Class (class DecodeJson) +import Data.Maybe (Maybe) + +newtype Event = Event + { name :: Maybe String + } + +instance decodeJsonEvent :: DecodeJson Event where + decodeJson json = do + obj <- decodeJson json + name <- obj .? "name" + pure $ Event + { name : name + } + +instance encodeJsonEvent :: EncodeJson Event where + encodeJson (Event e) + = "name" := e.name + ~> jsonEmptyObject + +instance showEvent :: Show Event where + show (Event e) = "Event " <> + "{ name: " <> show e.name <> + "}" + + diff --git a/test/Database/Mongo/Find.purs b/test/Database/Mongo/Find.purs new file mode 100644 index 0000000..bc0d8fe --- /dev/null +++ b/test/Database/Mongo/Find.purs @@ -0,0 +1,25 @@ +module Test.Database.Mongo.Find where + +import Prelude (Unit, bind, (>), ($)) +import Data.Array (length) +import Data.Maybe (Maybe(..)) +import Database.Mongo.Bson.BsonValue ((:=)) +import Control.Monad.Eff.Class (liftEff) +import Control.Monad.Eff.Console (log) +import Test.Data.Event (Event(..)) +import Test.Assert (assert) +import Database.Mongo.Mongo (Database, collect, find, collection) +import Test.Type (Test) + +evt :: Event +evt = Event + { name : Just "Wow" + } + +main :: Database -> Test Unit +main database = do + liftEff $ log "should find" + col <- collection "events" database + cur <- find [ "name" := "Wow" ] [ "name" := 1.0 ] col + res <- collect cur + liftEff $ assert $ length (res :: Array Event) > 0 diff --git a/test/Database/Mongo/Insert.purs b/test/Database/Mongo/Insert.purs new file mode 100644 index 0000000..c48314b --- /dev/null +++ b/test/Database/Mongo/Insert.purs @@ -0,0 +1,32 @@ +module Test.Database.Mongo.Insert where + +import Prelude (Unit, bind, ($), (==), (<<<)) +import Data.Maybe (Maybe(..)) +import Data.Tuple (Tuple) +import Data.StrMap (fromFoldable) +import Data.Argonaut (fromObject, (:=)) +import Data.Argonaut.Encode (encodeJson) +import Data.Argonaut.Core (Json) +import Database.Mongo.Mongo (Database, insertOne, collection) +import Database.Mongo.Options (defaultInsertOptions) +import Control.Monad.Eff.Class (liftEff) +import Control.Monad.Eff.Console (log) + +import Test.Data.Event (Event(..)) +import Test.Assert +import Test.Type (Test) + +evt :: Event +evt = Event + { name : Just "Wow" + } + +obj :: Array (Tuple String Json) -> Json +obj = fromObject <<< fromFoldable + +main :: Database -> Test Unit +main database = do + liftEff $ log "should insert" + col <- collection "events" database + res <- insertOne evt defaultInsertOptions col + liftEff $ assert $ (obj [ "ok" := 1, "n" := 1, "nInserted" := 0, "nModified" := 0]) == (encodeJson res) diff --git a/test/Database/Mongo/Update.purs b/test/Database/Mongo/Update.purs new file mode 100644 index 0000000..6225b8a --- /dev/null +++ b/test/Database/Mongo/Update.purs @@ -0,0 +1,32 @@ +module Test.Database.Mongo.Update where + +import Prelude (Unit, bind, (==), ($), (<<<)) +import Data.Maybe (Maybe(..)) +import Data.Tuple (Tuple) +import Data.StrMap (fromFoldable) +import Data.Argonaut as A +import Data.Argonaut.Core (Json) +import Data.Argonaut.Encode (encodeJson) +import Control.Monad.Eff.Class (liftEff) +import Control.Monad.Eff.Console (log) +import Test.Data.Event (Event(..)) +import Test.Assert (assert) +import Database.Mongo.Mongo (Database, updateOne, collection) +import Database.Mongo.Bson.BsonValue as B +import Database.Mongo.Options +import Test.Type (Test) + +evt :: Event +evt = Event + { name : Just "Wow" + } + +obj :: Array (Tuple String Json) -> Json +obj = A.fromObject <<< fromFoldable + +main :: Database -> Test Unit +main database = do + liftEff $ log "should update" + col <- collection "events" database + res <- updateOne ["name" B.:= "Wow"] ["name" B.:= "lol"] defaultUpdateOptions col + liftEff $ assert $ (obj [ "ok" A.:= 1, "n" A.:= 1, "nModified" A.:= 1, "nInserted" A.:= 0]) == (encodeJson res) diff --git a/test/Main.purs b/test/Main.purs index cb63cf0..32e57c0 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -1,5 +1,44 @@ module Test.Main where -import Control.Monad.Eff.Console +import Prelude +import Data.Either (Either(..)) +import Control.Monad.Eff (Eff) +import Control.Monad.Eff.Console (log) as Eff +import Control.Monad.Eff.Exception (EXCEPTION, Error) +import Control.Monad.Aff (Aff, launchAff, attempt) +import Control.Monad.Aff.Console (CONSOLE, log, logShow) +import Database.Mongo.Mongo (DB, Database, close, connect) +import Test.Database.Mongo.Insert as Insert +import Test.Database.Mongo.Find as Find +import Test.Database.Mongo.Update as Update +import Test.Assert (ASSERT) +import Test.Type (Test) -main = log "Write some tests." +connectDB :: forall e. String -> Aff ( db :: DB | e) (Either Error Database) +connectDB dbUri = attempt $ connect dbUri + +testInsert :: Database -> Test Unit +testInsert = Insert.main + +testFind :: Database -> Test Unit +testFind = Find.main + +testUpdate :: Database -> Test Unit +testUpdate = Update.main + +uri :: String +uri = "mongodb://127.0.0.1:27017/purescript-node-mongodb-test" + +main :: Eff (console :: CONSOLE, db :: DB, err :: EXCEPTION, assert :: ASSERT) Unit +main = do + Eff.log "Testing purescript-node-mongodb" + void $ launchAff do + log "connecting db" + eitherDatabase <- connectDB uri + case eitherDatabase of + Left error -> logShow error + Right database -> do + testInsert database + testFind database + testUpdate database + close database diff --git a/test/Type.purs b/test/Type.purs new file mode 100644 index 0000000..e3e4d47 --- /dev/null +++ b/test/Type.purs @@ -0,0 +1,8 @@ +module Test.Type where + +import Control.Monad.Aff (Aff) +import Control.Monad.Aff.Console (CONSOLE) +import Database.Mongo.Mongo (DB) +import Test.Assert (ASSERT) + +type Test a = forall e. Aff (console :: CONSOLE, db :: DB, assert :: ASSERT | e) a