packages feed

hasbolt 0.1.4.5 → 0.1.5.0

raw patch · 6 files changed

+76/−37 lines, 6 filesdep ~hspecPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: hspec

API changes (from Hackage documentation)

+ Database.Bolt: [authType] :: BoltCfg -> Text
+ Database.Bolt.Lazy: [authType] :: BoltCfg -> Text
- Database.Bolt: BoltCfg :: Word32 -> Word32 -> Text -> Word16 -> Int -> String -> Int -> Text -> Text -> Bool -> BoltCfg
+ Database.Bolt: BoltCfg :: Word32 -> Word32 -> Text -> Word16 -> Int -> String -> Int -> Text -> Text -> Text -> Bool -> BoltCfg
- Database.Bolt.Lazy: BoltCfg :: Word32 -> Word32 -> Text -> Word16 -> Int -> String -> Int -> Text -> Text -> Bool -> BoltCfg
+ Database.Bolt.Lazy: BoltCfg :: Word32 -> Word32 -> Text -> Word16 -> Int -> String -> Int -> Text -> Text -> Text -> Bool -> BoltCfg

Files

hasbolt.cabal view
@@ -1,6 +1,6 @@ cabal-version: >=1.10 name:          hasbolt-version:       0.1.4.5+version:       0.1.5.0 license:       BSD3 license-file:  LICENSE copyright:     (c) 2018 Pavel Yakovlev@@ -19,11 +19,15 @@     .     -Cypher queries and responses     .+    -Transactions and error handling+    .     -Authentification     .     -TLS/SSL connection     .-    The code was tested with neo4j versions 3.0 — 3.4 and GrapheneDB service+    -Bolt protocol version 3 initial support+    .+    The code was tested with neo4j versions 3.0 — 3.5 and GrapheneDB service  category:      Database build-type:    Simple@@ -82,7 +86,7 @@     build-depends:         base >=4.8 && <5,         hasbolt -any,-        hspec >=2.4.1 && <2.8,+        hspec >=2.4.1 && <2.9,         QuickCheck >=2.9 && <2.15,         text >=1.2.4.1 && <1.3,         containers >=0.6.2.1 && <0.7,
src/Database/Bolt/Connection.hs view
@@ -15,6 +15,7 @@ import           Database.Bolt.Connection.Pipe import           Database.Bolt.Connection.Instances import           Database.Bolt.Connection.Type+import           Database.Bolt.Value.Helpers import           Database.Bolt.Value.Type import           Database.Bolt.Record @@ -58,7 +59,7 @@  -- |Runs Cypher query with parameters and ignores response queryP_ :: MonadIO m => HasCallStack => Text -> Map Text Value -> BoltActionT m ()-queryP_ cypher params = do void $ sendRequest cypher params+queryP_ cypher params = do void $ sendRequest cypher params empty                            ask >>= liftE . discardAll  -- |Runs Cypher query and ignores response@@ -68,14 +69,14 @@ -- Helper functions  querySL :: MonadIO m => HasCallStack => Bool -> Text -> Map Text Value -> BoltActionT m [Record]-querySL strict cypher params = do keys <- pullKeys cypher params+querySL strict cypher params = do keys <- pullKeys cypher params empty                                   pullRecords strict keys -pullKeys :: MonadIO m => HasCallStack => Text -> Map Text Value -> BoltActionT m [Text]-pullKeys cypher params = do pipe <- ask-                            status <- sendRequest cypher params-                            liftE $ flush pipe RequestPullAll-                            mkKeys status+pullKeys :: MonadIO m => HasCallStack => Text -> Map Text Value -> Map Text Value -> BoltActionT m [Text]+pullKeys cypher params ext = do pipe <- ask+                                status <- sendRequest cypher params ext+                                liftE $ flush pipe RequestPullAll+                                mkKeys status   where     mkKeys :: MonadIO m => Response -> BoltActionT m [Text]     mkKeys (ResponseSuccess response) = response `at` "fields" `catchError` \(RecordHasNoKey _) -> pure []@@ -88,7 +89,7 @@   where     cases :: MonadIO m => Response -> BoltActionT m [Record]     cases resp | isSuccess resp = pure []-               | isFailure resp = do ask >>= ackFailure+               | isFailure resp = do ask >>= processError                                      throwError $ ResponseError (mkFailure resp)                | otherwise      = parseRecord resp @@ -102,13 +103,15 @@         pure (record:rest)  -- |Sends request to database and makes an action-sendRequest :: MonadIO m => HasCallStack => Text -> Map Text Value -> BoltActionT m Response-sendRequest cypher params =+sendRequest :: MonadIO m => HasCallStack => Text -> Map Text Value -> Map Text Value -> BoltActionT m Response+sendRequest cypher params ext =   do pipe <- ask      liftE $ do-         flush pipe $ RequestRun cypher params+         if isNewVersion (pipe_version pipe)+            then flush pipe $ RequestRunV3 cypher params ext+            else flush pipe $ RequestRun cypher params          status <- fetch pipe          if isSuccess status            then pure status-           else do ackFailure pipe+           else do processError pipe                    throwError $ ResponseError (mkFailure status)
src/Database/Bolt/Connection/Instances.hs view
@@ -10,16 +10,19 @@ import           Database.Bolt.Value.Type  import           Control.Monad.Except           (MonadError (..))-import           Data.Map.Strict                (Map, fromList, empty, (!))+import           Data.Map.Strict                (Map, insert, fromList, empty, (!)) import           Data.Text                      (Text)  instance ToStructure Request where-  toStructure RequestInit{..} = Structure sigInit [T agent, M $ tokenMap token]-  toStructure RequestRun{..}  = Structure sigRun [T statement, M parameters]+  toStructure RequestInit{..}           = Structure sigInit $ if isHello then [M $ helloMap agent token]+                                                                         else [T agent, M $ tokenMap token]+  toStructure RequestRun{..}            = Structure sigRun [T statement, M parameters]+  toStructure RequestRunV3{..}          = Structure sigRun [T statement, M parameters, M extra]   toStructure RequestReset              = Structure sigReset []   toStructure RequestAckFailure         = Structure sigAFail []   toStructure RequestPullAll            = Structure sigPAll []   toStructure RequestDiscardAll         = Structure sigDAll []+  toStructure RequestGoodbye            = Structure sigGBye []  instance FromStructure Response where   fromStructure Structure{..}@@ -54,19 +57,23 @@  createInit :: BoltCfg -> Request createInit BoltCfg{..} = RequestInit userAgent-                                     AuthToken { scheme      = "basic"+                                     AuthToken { scheme      = authType                                                , principal   = user                                                , credentials = password                                                }+                                     (isNewVersion version)  createRun :: Text -> Request createRun stmt = RequestRun stmt empty  +helloMap :: Text -> AuthToken  -> Map Text Value +helloMap a = insert "user_agent" (T a) . tokenMap+ tokenMap :: AuthToken -> Map Text Value-tokenMap at = fromList [ ("scheme",      T $ scheme at)-                       , ("principal",   T $ principal at)-                       , ("credentials", T $ credentials at)+tokenMap at = fromList [ "scheme"     =: scheme at+                       , "principal"   =: principal at+                       , "credentials" =: credentials at                        ]  extractMap :: MonadError UnpackError m => Value -> m (Map Text Value)
src/Database/Bolt/Connection/Pipe.hs view
@@ -1,9 +1,11 @@ {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RecordWildCards #-} module Database.Bolt.Connection.Pipe where  import           Database.Bolt.Connection.Instances import           Database.Bolt.Connection.Type+import           Database.Bolt.Value.Helpers import           Database.Bolt.Value.Instances import           Database.Bolt.Value.Type import qualified Database.Bolt.Connection.Connection as C (close, connect, recv,@@ -27,13 +29,15 @@   where     connect' :: MonadPipe m => BoltCfg -> m Pipe     connect' bcfg = do conn <- C.connect (secure bcfg) (host bcfg) (fromIntegral $ port bcfg) (socketTimeout bcfg)-                       let pipe = Pipe conn (maxChunkSize bcfg)+                       let pipe = Pipe conn (maxChunkSize bcfg) (version bcfg)                        handshake pipe bcfg                        pure pipe  -- |Closes 'Pipe' close :: MonadIO m => HasCallStack => Pipe -> m ()-close = C.close . connection+close pipe = do liftIO $ print "Closed"+                when (isNewVersion $ pipe_version pipe) $ makeIO (`flush` RequestGoodbye) pipe+                C.close $ connection pipe  -- |Resets current sessions reset :: MonadIO m => HasCallStack => Pipe -> m ()@@ -53,6 +57,12 @@                          Left  e -> liftIO $ throwIO e  -- = Internal interfaces++-- |Processes error via ackFailure or reset+processError :: MonadIO m => HasCallStack => Pipe -> m ()+processError pipe@Pipe{..} = if isNewVersion pipe_version+                               then reset pipe+                               else makeIO ackFailure pipe  ackFailure :: MonadPipe m => HasCallStack => Pipe -> m () ackFailure pipe = flush pipe RequestAckFailure >> void (fetch pipe)
src/Database/Bolt/Connection/Type.hs view
@@ -14,7 +14,7 @@  import           Data.Default                    (Default (..)) import           Data.Map.Strict                 (Map)-import           Data.Monoid                     ((<>))+import           Data.Monoid                     () import           Data.Text                       (Text, unpack) import           Data.Word                       (Word16, Word32) import           GHC.Stack                       (HasCallStack, callStack, prettyCallStack)@@ -71,12 +71,13 @@  -- |Configuration of driver connection data BoltCfg = BoltCfg { magic         :: Word32  -- ^'6060B017' value-                       , version       :: Word32  -- ^'00000001' value+                       , version       :: Word32  -- ^Major version number (e.g. '00000104' for 4.1)                        , userAgent     :: Text    -- ^Driver user agent                        , maxChunkSize  :: Word16  -- ^Maximum chunk size of request                        , socketTimeout :: Int     -- ^Driver socket timeout in seconds                        , host          :: String  -- ^Neo4j server hostname                        , port          :: Int     -- ^Neo4j server port+                       , authType      :: Text    -- ^Neo4j auth schema                        , user          :: Text    -- ^Neo4j user                        , password      :: Text    -- ^Neo4j password                        , secure        :: Bool    -- ^Use TLS or not@@ -85,12 +86,13 @@  instance Default BoltCfg where   def = BoltCfg { magic         = 1616949271-                , version       = 1-                , userAgent     = "hasbolt/1.4"+                , version       = 3+                , userAgent     = "hasbolt/1.5"                 , maxChunkSize  = 65535                 , socketTimeout = 5                 , host          = "127.0.0.1"                 , port          = 7687+                , authType      = "basic"                 , user          = ""                 , password      = ""                 , secure        = False@@ -103,8 +105,9 @@         -- ^ Timeout in microseconds       } -data Pipe = Pipe { connection :: ConnectionWithTimeout -- ^Driver connection socket-                 , mcs        :: Word16                -- ^Driver maximum chunk size of request+data Pipe = Pipe { connection   :: ConnectionWithTimeout -- ^Driver connection socket+                 , mcs          :: Word16                -- ^Driver maximum chunk size of request+                 , pipe_version :: Word32                -- ^Connection version 0000mnMJ                  }  data AuthToken = AuthToken { scheme      :: Text@@ -119,14 +122,20 @@               | ResponseFailure { failMap   :: Map Text Value }   deriving (Eq, Show) -data Request = RequestInit { agent :: Text-                           , token :: AuthToken-                           }-             | RequestRun  { statement  :: Text-                           , parameters :: Map Text Value-                           }+data Request = RequestInit  { agent   :: Text+                            , token   :: AuthToken+                            , isHello :: Bool+                            }+             | RequestRun   { statement  :: Text+                            , parameters :: Map Text Value+                            }+             | RequestRunV3 { statement  :: Text+                            , parameters :: Map Text Value+                            , extra      :: Map Text Value+                            }              | RequestAckFailure              | RequestReset              | RequestDiscardAll              | RequestPullAll+             | RequestGoodbye   deriving (Eq, Show)
src/Database/Bolt/Value/Helpers.hs view
@@ -2,7 +2,7 @@  import           Control.Applicative (liftA2, liftA3, pure) import           Data.Bits           ((.&.))-import           Data.Word           (Word8)+import           Data.Word           (Word8, Word32)  -- = Checkers @@ -57,6 +57,9 @@ isStruct :: Word8 -> Bool isStruct = liftA3 (\x y z -> x || y || z) (== struct8Code) (== struct16Code) isTinyStruct +isNewVersion :: Word32 -> Bool+isNewVersion v = (v .&. 255) >= 3+ -- = Constants  -- == Null@@ -175,6 +178,9 @@  sigPAll :: Word8 sigPAll = 63++sigGBye :: Word8 +sigGBye = 2  -- == BOLT responses signatures