{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module Grakn.Client ( Client(Client, keyspace, url) , Concept(Concept, cid, clabel, ctype, value) , GraknError , Result(AnswersResult, AnswerResult, AskResult, CountResult, DeleteResult) , Options(Options, infer) , defaultUrl , defaultKeyspace , execute , execute_ ) where import Data.Aeson (FromJSON, Object, parseJSON, withObject, (.:), (.:?)) import Data.Aeson.Types (Parser) import Data.Foldable (asum) import Data.Map (Map) import Data.Proxy (Proxy (Proxy)) import Data.Text (Text) import Grakn.Property (Label, Value, Var) import Grakn.Query (IsQuery (queryString)) import Network.HTTP.Client (defaultManagerSettings, newManager) import Servant.API ((:>), Capture, JSON, PlainText, Post, QueryParam, ReqBody) import Servant.Client (BaseUrl (BaseUrl), ClientEnv (ClientEnv), ClientM, Scheme (Http), ServantError, client, runClientM) data Client = Client { url :: BaseUrl , keyspace :: String } newtype GraknError = GraknError String deriving (Eq, Show) -- |A result of a query data Result = AnswersResult [Map Var Concept] | AnswerResult (Map Var Concept) | DeleteResult | AskResult Bool | CountResult Integer deriving (Show, Eq) -- |A concept in the knowledge base data Concept = Concept { cid :: Text , clabel :: Maybe Label , ctype :: Maybe Label , value :: Maybe Value } deriving (Show, Eq) -- |The default Grakn URL, accessing localhost defaultUrl :: BaseUrl defaultUrl = BaseUrl Http "localhost" 4567 "" -- |The default Grakn keyspace defaultKeyspace :: String defaultKeyspace = "grakn" data Options = Options { infer :: Bool } type ExecuteResponse = IO (Either ServantError Result) execute :: IsQuery q => Client -> q -> ExecuteResponse execute = executeOpts Nothing execute_ :: IsQuery q => Options -> Client -> q -> ExecuteResponse execute_ opts = executeOpts (Just opts) executeOpts :: IsQuery q => Maybe Options -> Client -> q -> ExecuteResponse executeOpts opts (Client u ks) query = do manager <- newManager defaultManagerSettings let env = ClientEnv manager u runClientM (graqlGet opts (queryString query) ks) env type Infer = QueryParam "infer" Bool type Query = ReqBody '[ PlainText] String type Keyspace = Capture "keyspace" String -- |A type describing the REST API we use to execute queries type GraknAPI = "kb" :> Keyspace :> "graql" :> Infer :> Query :> Post '[ JSON] Result graknAPI :: Proxy GraknAPI graknAPI = Proxy graqlGet :: Maybe Options -> String -> String -> ClientM Result graqlGet opts query ks = client graknAPI ks (infer <$> opts) query -- This is a helper method for chaining accessing optional JSON fields (.:>) :: FromJSON a => Parser (Maybe Object) -> Text -> Parser (Maybe a) maybeParser .:> key = traverse (.: key) =<< maybeParser instance FromJSON Concept where parseJSON = withObject "Concept" $ \obj -> do cid <- obj .: "id" clabel <- obj .:? "label" ctype <- obj .:? "type" .:> "label" value <- obj .:? "value" return Concept {..} instance FromJSON Result where parseJSON val = asum [ AnswersResult <$> parseJSON val , AnswerResult <$> parseJSON val , AskResult <$> parseJSON val , CountResult <$> parseJSON val , pure DeleteResult ]