packages feed

cayley-client 0.1.5.1 → 0.2.0.0

raw patch · 7 files changed

+168/−36 lines, 7 filesdep +cayley-clientdep +hspecdep ~aesondep ~basedep ~unordered-containersPVP ok

version bump matches the API change (PVP)

Dependencies added: cayley-client, hspec

Dependency ranges changed: aeson, base, unordered-containers

API changes (from Hackage documentation)

- Database.Cayley.Internal: apiRequest :: Manager -> String -> Int -> RequestBody -> ReaderT CayleyConfig IO (Maybe Value)
- Database.Cayley.Internal: getConfig :: CayleyConnection -> CayleyConfig
- Database.Cayley.Internal: getManager :: CayleyConnection -> Manager
- Database.Cayley.Internal: toRequestBody :: [Quad] -> RequestBody
- Database.Cayley.Types: instance Data.Aeson.Types.Class.FromJSON Database.Cayley.Types.Quad
- Database.Cayley.Types: instance Data.Aeson.Types.Class.ToJSON Database.Cayley.Types.Quad
+ Database.Cayley.Client: data Shape
+ Database.Cayley.Client: queryShape :: CayleyConnection -> Query -> IO (Result Shape)
+ Database.Cayley.Types: Link :: Integer -> Integer -> Integer -> Link
+ Database.Cayley.Types: Node :: Integer -> Maybe [Tag] -> Maybe [Value] -> Bool -> Bool -> Node
+ Database.Cayley.Types: Shape :: [Node] -> [Link] -> Shape
+ Database.Cayley.Types: [id] :: Node -> Integer
+ Database.Cayley.Types: [isFixed] :: Node -> Bool
+ Database.Cayley.Types: [isLinkNode] :: Node -> Bool
+ Database.Cayley.Types: [linkNode] :: Link -> Integer
+ Database.Cayley.Types: [links] :: Shape -> [Link]
+ Database.Cayley.Types: [nodes] :: Shape -> [Node]
+ Database.Cayley.Types: [source] :: Link -> Integer
+ Database.Cayley.Types: [tags] :: Node -> Maybe [Tag]
+ Database.Cayley.Types: [target] :: Link -> Integer
+ Database.Cayley.Types: [values] :: Node -> Maybe [Value]
+ Database.Cayley.Types: data Link
+ Database.Cayley.Types: data Node
+ Database.Cayley.Types: data Shape
+ Database.Cayley.Types: instance Data.Aeson.Types.FromJSON.FromJSON Database.Cayley.Types.Quad
+ Database.Cayley.Types: instance Data.Aeson.Types.FromJSON.FromJSON Database.Cayley.Types.Shape
+ Database.Cayley.Types: instance Data.Aeson.Types.ToJSON.ToJSON Database.Cayley.Types.Quad
+ Database.Cayley.Types: instance GHC.Classes.Eq Database.Cayley.Types.Link
+ Database.Cayley.Types: instance GHC.Classes.Eq Database.Cayley.Types.Node
+ Database.Cayley.Types: instance GHC.Classes.Eq Database.Cayley.Types.Shape
+ Database.Cayley.Types: instance GHC.Show.Show Database.Cayley.Types.Link
+ Database.Cayley.Types: instance GHC.Show.Show Database.Cayley.Types.Node
+ Database.Cayley.Types: instance GHC.Show.Show Database.Cayley.Types.Shape
+ Database.Cayley.Types: parseLink :: Value -> Parser Link
+ Database.Cayley.Types: parseNode :: Value -> Parser Node
+ Database.Cayley.Types: type Tag = Text
+ Database.Cayley.Types: type Value = Text

Files

Database/Cayley/Client.hs view
@@ -3,11 +3,16 @@ {-# LANGUAGE RecordWildCards   #-}  module Database.Cayley.Client (+       Quad (..)+     -- * Connect & query     , defaultCayleyConfig     , connectCayley     , query+    , Shape+    , queryShape+     -- * REST API operations     , write     , writeQuad@@ -16,10 +21,12 @@     , delete     , deleteQuad     , deleteQuads+     -- * Utils     , createQuad     , isValid     , successfulResults+     ) where  import           Control.Applicative                   ((<|>))@@ -57,12 +64,8 @@     doQuery m _q = do         CayleyConfig {..} <- ask         r <- apiRequest-                 m ("http://"-                    ++ serverName-                    ++ "/api/v"-                    ++ show apiVersion-                    ++ "/query/"-                    ++ show queryLang)+                 m (urlBase serverName apiVersion+                   ++ "/query/" ++ show queryLang)                  serverPort (RequestBodyBS _q)         return $ case r of             Just a  ->@@ -73,11 +76,25 @@                           Just e  ->                               case A.fromJSON e of                                   A.Success s -> Left s-                                  A.Error _e   -> Left _e+                                  A.Error _e  -> Left _e                           Nothing ->                               Left "No JSON response from Cayley server"             Nothing -> Left "Can't get any response from Cayley server" +-- | Return the description of the given query.+queryShape :: CayleyConnection -> Query -> IO (A.Result Shape)+queryShape c q =+    runReaderT (doShape (getManager c) (encodeUtf8 q)) (getConfig c)+  where+    doShape m _q = do+        CayleyConfig {..} <- ask+        r <- apiRequest+                 m (urlBase serverName apiVersion ++ "/shape/" ++ show queryLang)+                 serverPort (RequestBodyBS _q)+        case r of+            Just o  -> return $ A.fromJSON o+            Nothing -> return $ A.Error "API request error"+ -- | Write a 'Quad' with the given subject, predicate, object and optional -- label. Throw result or extract amount of query 'successfulResults' -- from it.@@ -85,7 +102,12 @@ -- >λ> writeQuad conn "Humphrey" "loves" "Lauren" (Just "In love") -- >Just (Object (fromList [("result",String "Successfully wrote 1 quads.")])) ---writeQuad :: CayleyConnection -> Subject -> Predicate -> Object -> Maybe Label -> IO (Maybe A.Value)+writeQuad :: CayleyConnection+          -> Subject+          -> Predicate+          -> Object+          -> Maybe Label+          -> IO (Maybe A.Value) writeQuad c s p o l =    writeQuads c [Quad { subject = s, predicate = p, object = o, label = l }] @@ -95,7 +117,12 @@  -- | Delete the 'Quad' defined by the given subject, predicate, object -- and optional label.-deleteQuad :: CayleyConnection -> Subject -> Predicate -> Object -> Maybe Label -> IO (Maybe A.Value)+deleteQuad :: CayleyConnection+           -> Subject+           -> Predicate+           -> Object+           -> Maybe Label+           -> IO (Maybe A.Value) deleteQuad c s p o l =     deleteQuads c [Quad { subject = s, predicate = p, object = o, label = l }] @@ -111,11 +138,7 @@     _write m _qs = do          CayleyConfig {..} <- ask          apiRequest-             m ("http://"-                ++ serverName-                ++ "/api/v"-                ++ show apiVersion-                ++ "/write")+             m (urlBase serverName apiVersion ++ "/write")              serverPort (toRequestBody _qs)  -- | Delete the given list of 'Quad'(s).@@ -126,11 +149,7 @@     _delete m _qs = do         CayleyConfig {..} <- ask         apiRequest-            m ("http://"-               ++ serverName-               ++ "/api/v"-               ++ show apiVersion-               ++ "/delete")+            m (urlBase serverName apiVersion ++ "/delete")             serverPort             (toRequestBody _qs) @@ -148,11 +167,7 @@   where     writenq m _p = do         CayleyConfig {..} <- ask-        r <- parseUrl ("http://"-                       ++ serverName-                       ++ "/api/v"-                       ++ show apiVersion-                       ++ "/write/file/nquad")+        r <- parseUrl (urlBase serverName apiVersion ++ "/write/file/nquad")                  >>= \r -> return r { port = serverPort }         t <- liftIO $                  try $@@ -160,7 +175,7 @@                         =<< formDataBody [partFileSource "NQuadFile" _p] r         return $ case t of             Right _r -> A.decode $ responseBody _r-            Left e  -> Just $+            Left e   -> Just $                 A.object ["error" A..= T.pack (show (e :: SomeException))]  -- | A valid 'Quad' has its subject, predicate and object not empty.
Database/Cayley/Internal.hs view
@@ -29,11 +29,14 @@                     A.object ["error" A..= T.pack (show (e :: SomeException))]  toRequestBody :: [Quad] -> RequestBody-toRequestBody qs = RequestBodyLBS $ A.encode $ fromList $ map A.toJSON qs+toRequestBody = RequestBodyLBS . A.encode . fromList . map A.toJSON  getManager :: CayleyConnection -> Manager getManager (CayleyConnection (_,m)) = m  getConfig :: CayleyConnection -> CayleyConfig getConfig (CayleyConnection (c,_)) = c++urlBase :: String -> APIVersion -> String+urlBase s a = "http://" ++ s ++ "/api/v" ++ show a 
Database/Cayley/Types.hs view
@@ -2,9 +2,11 @@  module Database.Cayley.Types where -import           Control.Monad+import           Control.Monad       (mzero) import qualified Data.Aeson          as A+import qualified Data.Aeson.Types    as AT import qualified Data.Text           as T+import qualified Data.Vector         as V import           Network.HTTP.Client (Manager)  data APIVersion = V1@@ -34,7 +36,7 @@     , queryLang  = Gremlin     } -data CayleyConnection = CayleyConnection (CayleyConfig,Manager)+data CayleyConnection = CayleyConnection (CayleyConfig, Manager)  data Quad = Quad     { subject   :: T.Text       -- ^ Subject node@@ -63,11 +65,11 @@     Quad s p o l == Quad s' p' o' l' = s == s' && p == p' && o == o' && l == l'  instance A.ToJSON Quad where-    toJSON (Quad _subject _predicate _object _label) =-        A.object [ "subject"   A..= _subject-                 , "predicate" A..= _predicate-                 , "object"    A..= _object-                 , "label"     A..= _label+    toJSON (Quad s p o l) =+        A.object [ "subject"   A..= s+                 , "predicate" A..= p+                 , "object"    A..= o+                 , "label"     A..= l                  ]  instance A.FromJSON Quad where@@ -78,6 +80,50 @@                              v A..: "label"     parseJSON _            = mzero +data Shape = Shape+    { nodes :: [Node]+    , links :: [Link]+    } deriving (Eq, Show)++instance A.FromJSON Shape where+    parseJSON (A.Object v) = do+        vnds <- v A..: "nodes"+        nds  <- mapM parseNode $ V.toList vnds+        vlks <- v A..: "links"+        lks  <- mapM parseLink $ V.toList vlks+        return Shape { nodes = nds, links = lks}+    parseJSON _            = mzero++parseNode :: A.Value -> AT.Parser Node+parseNode (A.Object v) = Node <$>+                       v A..: "id"<*>+                       v A..:? "tags" <*>+                       v A..:? "values" <*>+                       v A..: "is_link_node" <*>+                       v A..: "is_fixed"+parseNode _            = fail "Node expected"++parseLink :: AT.Value -> AT.Parser Link+parseLink (A.Object v) = Link <$>+                       v A..: "source" <*>+                       v A..: "target" <*>+                       v A..: "link_node"+parseLink _            = fail "Link expected"++data Node = Node+    { id           :: Integer+    , tags         :: Maybe [Tag]   -- ^ list of tags from the query+    , values       :: Maybe [Value] -- ^ Known values from the query+    , isLinkNode   :: Bool          -- ^ Does the node represent the link or the node (the oval shapes)+    , isFixed      :: Bool          -- ^ Is the node a fixed starting point of the query+    } deriving (Eq, Show)++data Link = Link+    { source    :: Integer -- ^ Node ID+    , target    :: Integer -- ^ Node ID+    , linkNode  :: Integer -- ^ Node ID+    } deriving (Eq, Show)+ type Query = T.Text  type Subject = T.Text@@ -87,4 +133,8 @@ type Object = T.Text  type Label = T.Text++type Tag = T.Text++type Value = T.Text 
+ README.md view
@@ -0,0 +1,3 @@+#A Cayley client for Haskell [![Build Status](https://travis-ci.org/MichelBoucey/cayley-client.svg?branch=master)](https://travis-ci.org/MichelBoucey/cayley-client)++Cayley-client is a Haskell library that implements the [RESTful API](https://github.com/google/cayley/blob/master/docs/HTTP.md) of [Cayley database graph](https://github.com/google/cayley).
cayley-client.cabal view
@@ -1,5 +1,5 @@ name:                cayley-client-version:             0.1.5.1+version:             0.2.0.0 synopsis:            A Haskell client for the Cayley graph database description:         cayley-client implements the RESTful API of the Cayley graph database. homepage:            https://github.com/MichelBoucey/cayley-client@@ -11,6 +11,8 @@ category:            Database build-type:          Simple cabal-version:       >=1.10+extra-source-files:  README.md+                   , tests/testdata.nq  Source-Repository head   Type: git@@ -19,7 +21,7 @@ library   exposed-modules:     Database.Cayley.Client                      , Database.Cayley.Types-                     , Database.Cayley.Internal+  other-modules:       Database.Cayley.Internal   other-extensions:    OverloadedStrings   build-depends:       base >=4.7 && <5                      , mtl >=2.1@@ -36,4 +38,16 @@                      , unordered-containers >=0.2                      , exceptions >= 0.6   default-language:    Haskell2010-  GHC-Options:    -Wall+  GHC-Options:       -Wall++test-suite tests+  type:                exitcode-stdio-1.0+  hs-source-dirs:      tests+  main-is:             hspec.hs+  build-depends:       hspec >= 2.2.2+                     , base >= 4.7 && < 5+                     , cayley-client+                     , aeson >=0.8+                     , unordered-containers >= 0.2.5+  default-language:    Haskell2010+
+ tests/hspec.hs view
@@ -0,0 +1,32 @@+{-# LANGUAGE OverloadedStrings #-}++import           Data.Aeson+import           Data.HashMap.Strict+import           Database.Cayley.Client as C+import           Database.Cayley.Types  as CT+import           Test.Hspec++main :: IO ()+main = hspec $ do++    describe "write" $+        it "writes a quad to Cayley server" $ do+            c <- connectCayley defaultCayleyConfig+            write c Quad { subject = "danny", predicate = "follows", C.object = "sandy", label = Nothing } `shouldReturn` Just (Object (fromList [("result",String "Successfully wrote 1 quads.")]))++    describe "query" $+        it "query Cayley server" $ do+            c <- connectCayley defaultCayleyConfig+            Right a <- query c "graph.Vertex('sandy').In('follows').All()"+            encode a `shouldBe` "[{\"id\":\"danny\"}]"++    describe "shape" $+        it "returns the description of the last query executed" $ do+            c <- connectCayley defaultCayleyConfig+            queryShape c "graph.Vertex('sandy').In('follows').All()" `shouldReturn` Success (Shape {nodes = [Node {CT.id = 4, tags = Nothing, values = Just ["sandy"], isLinkNode = False, isFixed = True},Node {CT.id = 8, tags = Nothing, values = Just ["follows"], isLinkNode = False, isFixed = True},Node {CT.id = 2, tags = Nothing, values = Nothing, isLinkNode = True, isFixed = False},Node {CT.id = 1, tags = Just ["id"], values = Nothing, isLinkNode = False, isFixed = False}], links = [Link {source = 1, target = 4, linkNode = 2}]})++    describe "writeNQuadFile" $+        it "writes quads from a file to Cayley server" $ do+            c <- connectCayley defaultCayleyConfig+            (writeNQuadFile c "./tests/testdata.nq" >>= successfulResults) `shouldReturn` Right 15+
+ tests/testdata.nq view
@@ -0,0 +1,15 @@+<alice> <follows> <bob> .+<bob> <follows> <fred> .+<bob> <status> "cool_person" .+<charlie> <follows> <bob> .+<charlie> <follows> <dani> .+<dani> <follows> <bob> .+<dani> <follows> <greg> .+<dani> <status> "cool_person" .+<emily> <follows> <fred> .+<fred> <follows> <greg> .+<greg> <status> "cool_person" .+<predicates> <are> <follows> .+<predicates> <are> <status> .+<emily> <status> "smart_person" "smart_graph" .+<greg> <status> "smart_person" "smart_graph" .