OrchestrateDB 1.0.0.0 → 1.0.0.1
raw patch · 10 files changed
+659/−658 lines, 10 filesdep ~basePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: base
API changes (from Hackage documentation)
- Orchestrate: OrchestrateApplication :: String -> String -> String -> OrchestrateApplication
- Orchestrate: OrchestrateCollection :: String -> OrchestrateCollection
- Orchestrate: apiKey :: OrchestrateApplication -> String
- Orchestrate: applicationName :: OrchestrateApplication -> String
- Orchestrate: collectionName :: OrchestrateCollection -> String
- Orchestrate: createStdApplication :: String -> String -> OrchestrateApplication
- Orchestrate: createStdCollection :: String -> OrchestrateCollection
- Orchestrate: data OrchestrateApplication
- Orchestrate: data OrchestrateCollection
- Orchestrate: httpsEndpoint :: OrchestrateApplication -> String
- Orchestrate: orchestrateCollectionDelete :: OrchestrateApplication -> OrchestrateCollection -> IO Bool
- Orchestrate: orchestrateCollectionDeleteKey :: OrchestrateApplication -> OrchestrateCollection -> String -> IO Bool
- Orchestrate: orchestrateCollectionGet :: FromJSON res => OrchestrateApplication -> OrchestrateCollection -> String -> IO (Maybe res)
- Orchestrate: orchestrateCollectionList :: OrchestrateApplication -> OrchestrateCollection -> Integer -> IO (Maybe [Object])
- Orchestrate: orchestrateCollectionPut :: ToJSON obj => OrchestrateApplication -> OrchestrateCollection -> String -> obj -> IO Bool
- Orchestrate: orchestrateCollectionPutWithoutKey :: ToJSON obj => OrchestrateApplication -> OrchestrateCollection -> obj -> IO Bool
- Orchestrate: orchestrateCollectionSearch :: OrchestrateApplication -> OrchestrateCollection -> String -> IO (Maybe ([Object], Bool))
- Orchestrate: orchestrateCollectionSearchWithOffset :: OrchestrateApplication -> OrchestrateCollection -> String -> Integer -> Integer -> IO (Maybe ([Object], Bool))
- Orchestrate: validateApplication :: OrchestrateApplication -> IO Bool
+ Database.Orchestrate: OrchestrateApplication :: String -> String -> String -> OrchestrateApplication
+ Database.Orchestrate: OrchestrateCollection :: String -> OrchestrateCollection
+ Database.Orchestrate: apiKey :: OrchestrateApplication -> String
+ Database.Orchestrate: applicationName :: OrchestrateApplication -> String
+ Database.Orchestrate: collectionName :: OrchestrateCollection -> String
+ Database.Orchestrate: createStdApplication :: String -> String -> OrchestrateApplication
+ Database.Orchestrate: createStdCollection :: String -> OrchestrateCollection
+ Database.Orchestrate: data OrchestrateApplication
+ Database.Orchestrate: data OrchestrateCollection
+ Database.Orchestrate: httpsEndpoint :: OrchestrateApplication -> String
+ Database.Orchestrate: orchestrateCollectionDelete :: OrchestrateApplication -> OrchestrateCollection -> IO Bool
+ Database.Orchestrate: orchestrateCollectionDeleteKey :: OrchestrateApplication -> OrchestrateCollection -> String -> IO Bool
+ Database.Orchestrate: orchestrateCollectionGet :: FromJSON res => OrchestrateApplication -> OrchestrateCollection -> String -> IO (Maybe res)
+ Database.Orchestrate: orchestrateCollectionList :: OrchestrateApplication -> OrchestrateCollection -> Integer -> IO (Maybe [Object])
+ Database.Orchestrate: orchestrateCollectionPut :: ToJSON obj => OrchestrateApplication -> OrchestrateCollection -> String -> obj -> IO Bool
+ Database.Orchestrate: orchestrateCollectionPutWithoutKey :: ToJSON obj => OrchestrateApplication -> OrchestrateCollection -> obj -> IO Bool
+ Database.Orchestrate: orchestrateCollectionSearch :: OrchestrateApplication -> OrchestrateCollection -> String -> IO (Maybe ([Object], Bool))
+ Database.Orchestrate: orchestrateCollectionSearchWithOffset :: OrchestrateApplication -> OrchestrateCollection -> String -> Integer -> Integer -> IO (Maybe ([Object], Bool))
+ Database.Orchestrate: validateApplication :: OrchestrateApplication -> IO Bool
+ Database.Orchestrate.REST: orchestrateCollectionDelete :: OrchestrateApplication -> OrchestrateCollection -> IO Bool
+ Database.Orchestrate.REST: orchestrateCollectionDeleteKey :: OrchestrateApplication -> OrchestrateCollection -> String -> IO Bool
+ Database.Orchestrate.REST: orchestrateCollectionGet :: FromJSON res => OrchestrateApplication -> OrchestrateCollection -> String -> IO (Maybe res)
+ Database.Orchestrate.REST: orchestrateCollectionList :: OrchestrateApplication -> OrchestrateCollection -> Integer -> IO (Maybe [Object])
+ Database.Orchestrate.REST: orchestrateCollectionPut :: ToJSON obj => OrchestrateApplication -> OrchestrateCollection -> String -> obj -> IO Bool
+ Database.Orchestrate.REST: orchestrateCollectionPutWithoutKey :: ToJSON obj => OrchestrateApplication -> OrchestrateCollection -> obj -> IO Bool
+ Database.Orchestrate.REST: orchestrateCollectionSearch :: OrchestrateApplication -> OrchestrateCollection -> String -> IO (Maybe ([Object], Bool))
+ Database.Orchestrate.REST: orchestrateCollectionSearchWithOffset :: OrchestrateApplication -> OrchestrateCollection -> String -> Integer -> Integer -> IO (Maybe ([Object], Bool))
+ Database.Orchestrate.REST: validateApplication :: OrchestrateApplication -> IO Bool
+ Database.Orchestrate.Types: OrchestrateApplication :: String -> String -> String -> OrchestrateApplication
+ Database.Orchestrate.Types: OrchestrateCollection :: String -> OrchestrateCollection
+ Database.Orchestrate.Types: OrchestrateListResult :: OrchestratePath -> Object -> Integer -> OrchestrateListResult
+ Database.Orchestrate.Types: OrchestratePath :: String -> String -> String -> String -> Integer -> OrchestratePath
+ Database.Orchestrate.Types: OrchestrateQueryResult :: OrchestratePath -> Object -> Double -> Integer -> OrchestrateQueryResult
+ Database.Orchestrate.Types: apiKey :: OrchestrateApplication -> String
+ Database.Orchestrate.Types: applicationName :: OrchestrateApplication -> String
+ Database.Orchestrate.Types: collectionName :: OrchestrateCollection -> String
+ Database.Orchestrate.Types: data OrchestrateApplication
+ Database.Orchestrate.Types: data OrchestrateCollection
+ Database.Orchestrate.Types: data OrchestrateListResult
+ Database.Orchestrate.Types: data OrchestratePath
+ Database.Orchestrate.Types: data OrchestrateQueryResult
+ Database.Orchestrate.Types: httpsEndpoint :: OrchestrateApplication -> String
+ Database.Orchestrate.Types: instance Constructor C1_0OrchestrateListResult
+ Database.Orchestrate.Types: instance Constructor C1_0OrchestratePath
+ Database.Orchestrate.Types: instance Constructor C1_0OrchestrateQueryResult
+ Database.Orchestrate.Types: instance Datatype D1OrchestrateListResult
+ Database.Orchestrate.Types: instance Datatype D1OrchestratePath
+ Database.Orchestrate.Types: instance Datatype D1OrchestrateQueryResult
+ Database.Orchestrate.Types: instance FromJSON OrchestrateListResult
+ Database.Orchestrate.Types: instance FromJSON OrchestratePath
+ Database.Orchestrate.Types: instance FromJSON OrchestrateQueryResult
+ Database.Orchestrate.Types: instance Generic OrchestrateListResult
+ Database.Orchestrate.Types: instance Generic OrchestratePath
+ Database.Orchestrate.Types: instance Generic OrchestrateQueryResult
+ Database.Orchestrate.Types: instance OrchestrateIntermediateResult OrchestrateListResult
+ Database.Orchestrate.Types: instance OrchestrateIntermediateResult OrchestrateQueryResult
+ Database.Orchestrate.Types: instance Read OrchestratePath
+ Database.Orchestrate.Types: instance Selector S1_0_0OrchestrateListResult
+ Database.Orchestrate.Types: instance Selector S1_0_0OrchestratePath
+ Database.Orchestrate.Types: instance Selector S1_0_0OrchestrateQueryResult
+ Database.Orchestrate.Types: instance Selector S1_0_1OrchestrateListResult
+ Database.Orchestrate.Types: instance Selector S1_0_1OrchestratePath
+ Database.Orchestrate.Types: instance Selector S1_0_1OrchestrateQueryResult
+ Database.Orchestrate.Types: instance Selector S1_0_2OrchestrateListResult
+ Database.Orchestrate.Types: instance Selector S1_0_2OrchestratePath
+ Database.Orchestrate.Types: instance Selector S1_0_2OrchestrateQueryResult
+ Database.Orchestrate.Types: instance Selector S1_0_3OrchestratePath
+ Database.Orchestrate.Types: instance Selector S1_0_3OrchestrateQueryResult
+ Database.Orchestrate.Types: instance Selector S1_0_4OrchestratePath
+ Database.Orchestrate.Types: instance Show OrchestrateListResult
+ Database.Orchestrate.Types: instance Show OrchestratePath
+ Database.Orchestrate.Types: instance Show OrchestrateQueryResult
+ Database.Orchestrate.Types: instance ToJSON OrchestrateListResult
+ Database.Orchestrate.Types: instance ToJSON OrchestratePath
+ Database.Orchestrate.Types: instance ToJSON OrchestrateQueryResult
+ Database.Orchestrate.Types: orchestrateListResultPath :: OrchestrateListResult -> OrchestratePath
+ Database.Orchestrate.Types: orchestrateListResultReftimeQueryResult :: OrchestrateListResult -> Integer
+ Database.Orchestrate.Types: orchestrateListResultValue :: OrchestrateListResult -> Object
+ Database.Orchestrate.Types: orchestratePathCollection :: OrchestratePath -> String
+ Database.Orchestrate.Types: orchestratePathKey :: OrchestratePath -> String
+ Database.Orchestrate.Types: orchestratePathKind :: OrchestratePath -> String
+ Database.Orchestrate.Types: orchestratePathRef :: OrchestratePath -> String
+ Database.Orchestrate.Types: orchestratePathReftime :: OrchestratePath -> Integer
+ Database.Orchestrate.Types: orchestrateQueryResultPath :: OrchestrateQueryResult -> OrchestratePath
+ Database.Orchestrate.Types: orchestrateQueryResultReftimeQueryResult :: OrchestrateQueryResult -> Integer
+ Database.Orchestrate.Types: orchestrateQueryResultScore :: OrchestrateQueryResult -> Double
+ Database.Orchestrate.Types: orchestrateQueryResultValue :: OrchestrateQueryResult -> Object
+ Database.Orchestrate.Types: resultValuesAsList :: OrchestrateIntermediateResult a => [a] -> [Object]
Files
- Database/Orchestrate.hs +151/−0
- Database/Orchestrate/REST.hs +386/−0
- Database/Orchestrate/Types.hs +108/−0
- OrchestrateDB.cabal +9/−9
- README.md +3/−2
- examples/Test.hs +1/−1
- examples/Test2.hs +1/−1
- src/Orchestrate.hs +0/−151
- src/Orchestrate/REST.hs +0/−386
- src/Orchestrate/Types.hs +0/−108
+ Database/Orchestrate.hs view
@@ -0,0 +1,151 @@+{-|+Module : Database.Orchestrate+Copyright : (c) Adrian Dawid 2015+License : BSD3+Maintainer : adriandwd@gmail.com+Stability : stable++This library implements most parts of the Orchestrate.io API in Haskell. It provides a convenient way of accessing these parts of the REST API:++ * Validate API Keys++ * List Key/Values++ * Create Key/Value++ * Create Key/Value with server-generated key++ * Update Key/Value++ * Retrieve Value for Key++ * Delete Collection(s)++ * Query Collection++NOTE: You can also use 'Network.Curl', if you need one of the currently unsupported API functions, but you would have to parse all response bodies yourself if you do that.++=How to use this library?+Using this library is pretty straightforward, just import the Orchestrate module like this:++@+import qualified Orchestrate as DB+@++Once you have it, you can use all the supported API functions(Check out the documentation on 'Orchestrate.REST' for detailed information).+One important thing you need to remeber when working with this library is, that it uses 'Data.Aeson' under the hood, so if you want to+store your Haskell types in an Orchestrate database, 'Data.Aseon' must know how convert them into JSON. The best way to achive that, is+to use the 'DeriveGeneric' Langauge extension.++Here is an example of how to store a haskell value in an Orchestrate Collection:++@+\{\-\# LANGUAGE DeriveGeneric \#\-\}+module Main where++import GHC.Generics+import Data.Aeson+import qualified Database.Orchestrate as DB++data SherlockHolmesCase = SherlockHolmesCase {+ title :: String,+ typeOfCrime :: String,+ shortStory :: Bool,+ index :: Integer,+ solved :: Bool+} deriving (Show,Read,Generic,Eq)++\{\-This automatically creates both a JSON parser and a JSON encoder based on the data type SherlockHolmesCase, if you however+want the property names in JSON and haskell to differ, you will have to write your own parser and you own encoder.+ Please check out the 'Data.Aeson' documentation for information on how to do that.\-\}+instance FromJSON SherlockHolmesCase+instance ToJSON SherlockHolmesCase++main :: IO ()+main = do+ -- Create an application record. Oh, BTW, in case you planned to store your api-key in the source code, it is a very very bad idea.+ let dbApplcation = DB.createStdApplication \"your.orchestrate.application.name\" \"one-of-your-api-keys\"+ -- Create an collection record. This collection does not have to exist, if it does not, it will be created as soon as you try to store something in it.+ let dbCollection = DB.createStdCollection \"TheCasebookOfSherlockHolmes\"+ let aStudyInScarlet = SherlockHolmesCase {+ title = \"A Study in Scarlet\",+ typeOfCrime = \"Murder\",+ shortStory = False,+ index = 0,+ solved = True+ }+ -- Store a haskell value in the collection we created earlier. The \"withOutKey\" lets the server generate the key, so no key must be specified.+ didItWork <- DB.orchestrateCollectionPutWithoutKey dbApplcation dbCollection aStudyInScarlet+ if didItWork+ then putStrLn "Invictus maneo!"+ else putStrLn "Something is rotten in the state of Denmark!"+@++If this application does say \"Invictus maneo!\", you should get this result if you LIST the \"TheCasebookOfSherlockHolmes\" collection:++@+"count" : 1,+ "results" : [ {+ "path" : {+ "collection" : "TheCasebookOfSherlockHolmes ",+ "kind" : "item",+ "key" : "0b8b506cf0204ce9",+ "ref" : "--------------",+ "reftime" : --------------+ },+ "value" : {+ "solved" : true,+ "shortStory" : false,+ "title" : "A Study in Scarlet ",+ "index" : 0,+ "typeOfCrime" : "Murder "+ },+ "reftime" : --------------+ } ]+@++If you don't get that result, you either have run the example more than once, it did not return "Invictus maneo", or something really strange is going on.+If you are fairly certain, that something strange is going on, and it is most likely my fault and you might want to open an issue at github.++=How to use the "cabal test" command+Testing applications which depend on some REST api is not easy, and therfore the cabal tests will fail by default. They just will not be able to authenticate, if you+want to run the tests on your local machine, please create a file called "examples/config.txt" and fill it according to this scheme:++@+("a valid api key","something funny(it will be used as a name for the test collection)")+@++If you are asking yourself now, if there really is no way of checking that this library works, before installing it, there is nothing to worry about.+You should consult the travis-ci server(<https://travis-ci.org/dwd31415/Haskell-OrchestrateDB>) on the state of the project, it does not only check wether or not ther library can be built, but also runs the tests with an valid api key.+-}+module Database.Orchestrate(Database.Orchestrate.Types.OrchestrateCollection(..),+ Database.Orchestrate.Types.OrchestrateApplication(..),+ Database.Orchestrate.REST.validateApplication,+ Database.Orchestrate.REST.orchestrateCollectionGet,+ Database.Orchestrate.REST.orchestrateCollectionPut,+ Database.Orchestrate.REST.orchestrateCollectionPutWithoutKey,+ Database.Orchestrate.REST.orchestrateCollectionDelete,+ Database.Orchestrate.REST.orchestrateCollectionDeleteKey,+ Database.Orchestrate.REST.orchestrateCollectionSearch,+ Database.Orchestrate.REST.orchestrateCollectionSearchWithOffset,+ Database.Orchestrate.REST.orchestrateCollectionList,+ createStdCollection,+ createStdApplication) where++import qualified Database.Orchestrate.REST+import qualified Database.Orchestrate.Types++createStdApplication :: String -> String -> Database.Orchestrate.Types.OrchestrateApplication+-- ^Creates an application record with the std.(\"https://api.orchestrate.io/v0\") enpoint.+createStdApplication name api_key =+ Database.Orchestrate.Types.OrchestrateApplication {+ Database.Orchestrate.Types.applicationName = name,+ Database.Orchestrate.Types.apiKey = api_key,+ Database.Orchestrate.Types.httpsEndpoint = "https://api.orchestrate.io/v0"+ }++createStdCollection :: String -> Database.Orchestrate.Types.OrchestrateCollection+-- ^Creates an collection record.+createStdCollection name = Database.Orchestrate.Types.OrchestrateCollection {+ Database.Orchestrate.Types.collectionName = name+}
+ Database/Orchestrate/REST.hs view
@@ -0,0 +1,386 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}++{-|+Module : Orchestrate.REST+Description : All functions which interact with the Orchestrate.io REST API.+Copyright : (c) Adrian Dawid 2015+License : BSD3+Maintainer : adriandwd@gmail.com+Stability : stable++This module conatins all the functions which interact with the Orchestrate.io REST API.+Right now these actions are supported:++ * Validate API Keys++ * List Key/Values++ * Create Key/Value++ * Create Key/Value with server-generated key++ * Update Key/Value++ * Retrieve Value for Key++ * Delete Collection(s)++ * Query Collection++-}+module Database.Orchestrate.REST+ (+ validateApplication,+ orchestrateCollectionPutWithoutKey,+ orchestrateCollectionDelete,+ orchestrateCollectionGet,+ orchestrateCollectionPut,+ orchestrateCollectionDeleteKey,+ orchestrateCollectionSearch,+ orchestrateCollectionSearchWithOffset,+ orchestrateCollectionList+ ) where++import Network.HTTP.Conduit+import Network.HTTP.Types.Status++import qualified Control.Exception.Lifted as X+import qualified Data.ByteString.Char8 as B+import qualified Data.ByteString.Lazy as BSLazy++import Data.Aeson+import Data.Aeson.Types+import Data.Maybe+import Database.Orchestrate.Types++validateApplication :: OrchestrateApplication -> IO Bool+-- ^The 'validateApplication' function validates your API key,+-- by making an authenticated HEAD request to the endpoint specified in the 'OrchestrateApplication' record.+-- The function returns False when the key is invalid or no connection with the endpoint could be established.+validateApplication application = do+ let api_key = apiKey application+ if api_key == ""+ then return False+ else do+ let url = httpsEndpoint application+ case parseUrl url of+ Nothing -> return False+ Just unsecuredRequest -> withManager $ \manager -> do+ let request = applyBasicAuth (B.pack $ apiKey application) "" unsecuredRequest+ let reqHead = request {+ method = "HEAD",+ secure = True }+ resOrException <- X.try (http reqHead manager)+ case resOrException of+ Right res -> if responseStatus res == ok200+ then return True+ else return False+ Left (_::X.SomeException) -> return False++orchestrateCollectionList :: OrchestrateApplication -> OrchestrateCollection -> Integer -> IO (Maybe [Object])+-- ^ The 'orchestrateCollectionList' function lists the contents of the specified collection, by making GET request to the \/$collection?limit=$limit endpoint.+-- For more information check out the Orchestrate.io API docs: <https://orchestrate.io/docs/apiref#keyvalue-list>.+-- If connecting to the api fails, or the api key stored in the application record is invlaid, 'Nothing' is returned.+-- Otherwise an array of the type 'Object'(see the documentation of 'Data.Aeson' for more information) is returned, it+-- contains the values from the HTTP response(see <https://orchestrate.io/docs/apiref#keyvalue-list> for an example of how the response looks like in JSON).+--+-- = Example:+-- @+-- let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"+-- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"+-- dbContents <- DB.orchestrateCollectionList dbApplication dbCollection 10+-- @+orchestrateCollectionList application collection limit = do+ let api_key = apiKey application+ if api_key == ""+ then return Nothing+ else do+ let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "?limit=" ++ show limit+ case parseUrl url of+ Nothing -> return Nothing+ Just unsecuredRequest -> withManager $ \manager -> do+ let request = unsecuredRequest {+ method = "GET",+ secure = True }+ let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request+ resOrException <- X.try (httpLbs reqHead manager)+ case resOrException of+ Right res -> if responseStatus res == ok200+ then do+ let resBody = responseBody res+ let results = fromMaybe [] (parseListResponseBody resBody)+ return $ Just results++ else return Nothing+ Left (_::X.SomeException) -> return Nothing++-- KEY/VALUE++orchestrateCollectionPutWithoutKey :: ToJSON obj => OrchestrateApplication -> OrchestrateCollection -> obj -> IO Bool+-- ^ The 'orchestrateCollectionPutWithoutKey' function stores a Haskell value(with a 'ToJSON' instance) in an Orchestrate.io database.+-- It does so by making a POST request to the \/$collection endpoint(Offical API docs:<https://orchestrate.io/docs/apiref#keyvalue-post>).+-- This function does not need a user specified key, because it uses a server-generated key, if you want to know the key+-- use 'orchestrateCollectionPut' instead of this function.+--+-- = Example:+-- @+-- data TestRecord = TestRecord+-- { string :: String+-- , number :: Int+-- } deriving (Show,Read,Generic,Eq)+--+-- instance FromJSON TestRecord+-- instance ToJSON TestRecord+--+-- let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"+-- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"+-- let testRecord = TestRecord {string = "You may delay, but time will not!",number = 903}+-- _ <- DB.orchestrateCollectionPutWithoutKey dbApplication dbCollection testRecord+-- @+orchestrateCollectionPutWithoutKey application collection object = do+ let objAsJson = encode object+ let api_key = apiKey application+ if api_key == ""+ then return False+ else do+ let url = httpsEndpoint application ++ "/" ++ collectionName collection+ case parseUrl url of+ Nothing -> return False+ Just unsecuredRequest -> withManager $ \manager -> do+ let request = unsecuredRequest {+ method = "POST",+ secure = True,+ requestHeaders = [("Content-Type", "application/json")],+ requestBody = RequestBodyBS $ BSLazy.toStrict objAsJson }+ let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request+ resOrException <- X.try (http reqHead manager)+ case resOrException of+ Right res -> if responseStatus res == status201+ then return True+ else return False+ Left (_::X.SomeException) -> return False++orchestrateCollectionPut :: ToJSON obj => OrchestrateApplication -> OrchestrateCollection -> String -> obj -> IO Bool+-- ^ The 'orchestrateCollectionPut' function stores a Haskell value(with a 'ToJSON' instance) in an Orchestrate.io database.+-- It does so by making a PUT request to the \/$collection\/$key endpoint(Offical API docs:<https://orchestrate.io/docs/apiref#keyvalue-put>).+-- In order to upload a Haskell Value to the database, it must have an instance of 'ToJSON' because this+-- client library uses 'Data.Aeson' to convert Haskel Values to JSON, which is required by Orchestrate.io.+--+-- = Example:+-- @+-- data TestRecord = TestRecord+-- { string :: String+-- , number :: Int+-- } deriving (Show,Read,Generic,Eq)+--+-- instance FromJSON TestRecord+-- instance ToJSON TestRecord+--+-- let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"+-- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"+-- let testRecord = TestRecord {string = "You may delay, but time will not!",number = 903}+-- _ <- DB.orchestrateCollectionPutWithoutKey dbApplication dbCollection "KEY" testRecord+-- @+orchestrateCollectionPut application collection key object = do+ let objAsJson = encode object+ let api_key = apiKey application+ if api_key == ""+ then return False+ else do+ let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "/" ++ key+ case parseUrl url of+ Nothing -> return False+ Just unsecuredRequest -> withManager $ \manager -> do+ let request = unsecuredRequest {+ method = "PUT",+ secure = True,+ requestHeaders = [("Content-Type", "application/json")],+ requestBody = RequestBodyBS $ BSLazy.toStrict objAsJson }+ let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request+ resOrException <- X.try (http reqHead manager)+ case resOrException of+ Right res -> if responseStatus res == status201+ then return True+ else return False+ Left (_::X.SomeException) -> return False++orchestrateCollectionGet :: FromJSON res => OrchestrateApplication -> OrchestrateCollection -> String -> IO (Maybe res)+-- ^ The 'orchestrateCollectionGet' function request a value from an Orchestrate.io database, and tries to convert it to the specified Haskell type,+-- if either gettings the value from the database or converting it to the Haskell type fails 'Nothing' is returned.+-- The value is requested by making a GET request to the \/$collection\/$key endpoint(Offical documentation:<https://orchestrate.io/docs/apiref#keyvalue-get>)+--+-- = Example:+-- @+-- data TestRecord = TestRecord+-- { string :: String+-- , number :: Int+-- } deriving (Show,Read,Generic,Eq)+--+-- instance FromJSON TestRecord+-- instance ToJSON TestRecord+--+-- let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"+-- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"+-- let testRecord = TestRecord {string = "You may delay, but time will not!",number = 903}+-- dbValue <- DB.orchestrateCollectionGet dbApplication dbCollection "KEY" :: IO (Maybe TestRecord)+-- @+orchestrateCollectionGet application collection key = do+ let api_key = apiKey application+ if api_key == ""+ then return Nothing+ else do+ let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "/" ++ key+ case parseUrl url of+ Nothing -> return Nothing+ Just unsecuredRequest -> withManager $ \manager -> do+ let request = unsecuredRequest {+ method = "GET",+ secure = True }+ let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request+ resOrException <- X.try (httpLbs reqHead manager)+ case resOrException of+ Right res -> if responseStatus res == ok200+ then do+ let resBody = responseBody res+ let jsonObject = decode resBody+ return jsonObject+ else return Nothing+ Left (_::X.SomeException) -> return Nothing++orchestrateCollectionDeleteKey :: OrchestrateApplication -> OrchestrateCollection -> String -> IO Bool+-- ^ The 'orchestrateCollectionDeleteKey' function deletes a value from an Orchestrate.io database.+-- This is done by making a DELETE request to the \/$collection\/$key endpoint(Offical documentation:<https://orchestrate.io/docs/apiref#keyvalue-delete>)+--+-- = Example:+-- @+-- data TestRecord = TestRecord+-- { string :: String+-- , number :: Int+-- } deriving (Show,Read,Generic,Eq)+--+-- instance FromJSON TestRecord+-- instance ToJSON TestRecord+--+-- let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"+-- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"+-- _ <- DB.orchestrateCollectionKey dbApplication dbCollection "KEY"+-- @+orchestrateCollectionDeleteKey application collection key = do+ let api_key = apiKey application+ if api_key == ""+ then return False+ else do+ let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "/" ++ key ++ "?force=true"+ case parseUrl url of+ Nothing -> return False+ Just unsecuredRequest -> withManager $ \manager -> do+ let request = unsecuredRequest {+ method = "DELETE",+ secure = True }+ let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request+ resOrException <- X.try (http reqHead manager)+ case resOrException of+ Right res -> if responseStatus res == status204+ then return True+ else return False+ Left (_::X.SomeException) -> return False++orchestrateCollectionDelete :: OrchestrateApplication -> OrchestrateCollection -> IO Bool+-- ^ The 'orchestrateCollectionDelete' function deletes a collection from an Orchestrate.io application.+-- This is done by making a DELETE request to the \/$collection endpoint(Offical documentation:<https://orchestrate.io/docs/apiref#collections-delete>)+--+-- = Example:+-- @+-- let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"+-- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"+-- _ <- DB.orchestrateCollectionDelete dbApplication dbCollection+-- @+orchestrateCollectionDelete application collection = do+ let api_key = apiKey application+ if api_key == ""+ then return False+ else do+ let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "?force=true"+ case parseUrl url of+ Nothing -> return False+ Just unsecuredRequest -> withManager $ \manager -> do+ let request = unsecuredRequest {+ method = "DELETE",+ secure = True }+ let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request+ resOrException <- X.try (http reqHead manager)+ case resOrException of+ Right res -> if responseStatus res == status204+ then return True+ else return False+ Left (_::X.SomeException) -> return False++-- SEARCH++orchestrateCollectionSearch :: OrchestrateApplication -> OrchestrateCollection -> String -> IO (Maybe([Object],Bool))+-- ^ Please see 'orchestrateCollectionSearchWithOffset' for more information. This function just calls it without an offset and with a limit of 10.+orchestrateCollectionSearch application collection query = orchestrateCollectionSearchWithOffset application collection query 0 10+++orchestrateCollectionSearchWithOffset :: OrchestrateApplication -> OrchestrateCollection -> String -> Integer -> Integer -> IO (Maybe([Object],Bool))+-- ^ The 'orchestrateCollectionSearchWithOffset' function searches for the query in the database and returns an array+-- of the type \"'Maybe' ['Object']\". Nothing is returned when establishing a connection or authenticating failed. The function uses the+-- SEARCH method of the Orchestrate.io API (Offical documentation:<https://orchestrate.io/docs/apiref#search-collection>)+-- , but automatically parsers the response. It returns a tupel of the type ('Maybe'(['Object'],'Bool')), the boolean indicates wether or not+-- more results are availble on the server. If that is true, the function should be called again with an increased offset, until (Just _,False) is returned.+--+-- = Example:+-- @+-- dbSearchResults query num =+-- let results = DB.orchestrateCollectionSearchWithOffset query num (num+10)+-- let currentResults = fromJust $ fst results+-- if snd results+-- then currentResults:(dbSearchResults query (num+10))+-- else currentResults+--+-- let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"+-- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"+-- let completeDBSearchResults = dbSearchResults "QUERY" 0+-- @+orchestrateCollectionSearchWithOffset application collection query offset limit = do+ let api_key = apiKey application+ if api_key == ""+ then return Nothing+ else do+ let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "?query=" ++ query ++ "&limit=" ++ show limit ++ "&offset=" ++ show offset+ case parseUrl url of+ Nothing -> return Nothing+ Just unsecuredRequest -> withManager $ \manager -> do+ let request = unsecuredRequest {+ method = "GET",+ secure = True }+ let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request+ resOrException <- X.try (httpLbs reqHead manager)+ case resOrException of+ Right res -> if responseStatus res == ok200+ then do+ let resBody = responseBody res+ let (results,count) = fromMaybe ([],0) (parseQueryResponseBody resBody)+ return $ Just (results,count > (fromIntegral (length results) + offset))++ else return Nothing+ Left (_::X.SomeException) -> return Nothing+++-- HELPERS++parseListResponseBody :: BSLazy.ByteString -> Maybe [Object]+parseListResponseBody resBodyRaw = do+ result <- decode resBodyRaw+ results <- flip parseMaybe result $ \obj -> obj .: "results" :: Parser [OrchestrateListResult]+ return $ resultValuesAsList results++parseQueryResponseBody :: BSLazy.ByteString -> Maybe ([Object],Integer)+parseQueryResponseBody resBodyRaw = do+ result <- decode resBodyRaw+ (count,results) <- flip parseMaybe result $ \obj -> do+ count <- obj .: "total_count" :: Parser Integer+ results <- obj .: "results" :: Parser [OrchestrateQueryResult]+ return (count,results)+ return (resultValuesAsList results,count)
+ Database/Orchestrate/Types.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeFamilies #-}++{-|+Module : Orchestrate.Types+Description : Haskell types for various JSON patterns.+Copyright : (c) Adrian Dawid 2015+License : BSD3+Maintainer : adriandwd@gmail.com+Stability : stable++This module conatins Haskell types which represent all necessary JSON patterns used by the Orchestrate.io API.+-}+module Database.Orchestrate.Types+ ( OrchestrateApplication(..),+ OrchestrateCollection(..),+ OrchestrateQueryResult(..),+ OrchestratePath (..),+ OrchestrateListResult (..),+ resultValuesAsList) where++import Control.Applicative ((<$>), (<*>))+import Control.Monad+import Data.Aeson+import GHC.Generics++{-|+A data type, that represents an Orchestrate application. It stores an api key (generated online) and a https-endpoint.+-}+data OrchestrateApplication = OrchestrateApplication {+ applicationName :: String,+ apiKey :: String,+ httpsEndpoint :: String+}++{-|+Represents a collection inside an OrchestrateApplication, it stores all data necessary to access it.+-}+data OrchestrateCollection = OrchestrateCollection {+ collectionName :: String+}++-- |TypeClass for OrchestrateQueryResult and OrchestrateListResult, it makes it possible to have one function('resultValuesAsList'), that extractes+-- the values from both of those types.+class OrchestrateIntermediateResult a where+ resultValuesAsList :: [a] -> [Object]++-- |Represents a path, as it is returned by the LIST and SEARCH function.+-- Right now it is not actually used, but it might be useful in the future.+data OrchestratePath = OrchestratePath {+ orchestratePathCollection :: String,+ orchestratePathKind :: String,+ orchestratePathKey :: String,+ orchestratePathRef :: String,+ orchestratePathReftime :: Integer+} deriving (Show,Read,Generic)++instance FromJSON OrchestratePath where+ parseJSON (Object v) = OrchestratePath <$>+ v .: "collection" <*>+ v .: "kind" <*>+ v .: "key" <*>+ v .: "ref" <*>+ v .: "reftime"+ -- A non-Object value is of the wrong type, so fail.+ parseJSON _ = mzero+instance ToJSON OrchestratePath++-- | Haskell type for the JSON scheme, that is returned by the SEARCH function.+data OrchestrateQueryResult = OrchestrateQueryResult {+ orchestrateQueryResultPath :: OrchestratePath,+ orchestrateQueryResultValue :: Object,+ orchestrateQueryResultScore :: Double,+ orchestrateQueryResultReftimeQueryResult :: Integer+} deriving (Show,Generic)++instance FromJSON OrchestrateQueryResult where+ parseJSON (Object v) = OrchestrateQueryResult <$>+ v .: "path" <*>+ v .: "value" <*>+ v .: "score" <*>+ v .: "reftime"+ -- A non-Object value is of the wrong type, so fail.+ parseJSON _ = mzero+instance ToJSON OrchestrateQueryResult+instance OrchestrateIntermediateResult OrchestrateQueryResult where+ resultValuesAsList = map orchestrateQueryResultValue+++-- | Haskell type for the JSON scheme, that is returned by the LIST function.+data OrchestrateListResult = OrchestrateListResult {+ orchestrateListResultPath :: OrchestratePath,+ orchestrateListResultValue :: Object,+ orchestrateListResultReftimeQueryResult :: Integer+} deriving (Show,Generic)++instance FromJSON OrchestrateListResult where+ parseJSON (Object v) = OrchestrateListResult <$>+ v .: "path" <*>+ v .: "value" <*>+ v .: "reftime"+ -- A non-Object value is of the wrong type, so fail.+ parseJSON _ = mzero+instance ToJSON OrchestrateListResult+instance OrchestrateIntermediateResult OrchestrateListResult where+ resultValuesAsList = map orchestrateListResultValue
OrchestrateDB.cabal view
@@ -10,7 +10,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 1.0.0.0+version: 1.0.0.1 -- A short (one-line) description of the package. synopsis: Unofficial Haskell Client Library for the Orchestrate.io API@@ -52,20 +52,20 @@ library -- Modules exported by the library.- exposed-modules: Orchestrate+ exposed-modules: Database.Orchestrate, Database.Orchestrate.REST, Database.Orchestrate.Types -- Modules included in this library but not exported.- other-modules: Orchestrate.REST, Orchestrate.Types+ -- other-modules: -- LANGUAGE extensions used by modules in this package. -- other-extensions: -- Other library packages from which modules are imported.- build-depends: base >=4.7 && <4.8, HTTP >= 4000, http-conduit >= 2.1, http-types >= 0.8, bytestring >= 0.10+ build-depends: base >=4.7 && <4.9, HTTP >= 4000, http-conduit >= 2.1, http-types >= 0.8, bytestring >= 0.10 ,lifted-base >= 0.2, aeson -- Directories containing source files.- hs-source-dirs: src+ -- hs-source-dirs: src -- Base language which the package is written in. default-language: Haskell2010@@ -73,16 +73,16 @@ Test-Suite tests-example1 type: exitcode-stdio-1.0 main-is: Test.hs- hs-source-dirs: src,examples+ hs-source-dirs: examples,. default-language: Haskell2010- build-depends: base >=4.4 && <4.8, HTTP >= 4000, http-conduit >= 2.1, http-types >= 0.8, bytestring >= 0.10+ build-depends: base >=4.7 && <4.9, HTTP >= 4000, http-conduit >= 2.1, http-types >= 0.8, bytestring >= 0.10 ,lifted-base >= 0.2, aeson, random Test-Suite test-sherlock type: exitcode-stdio-1.0 main-is: Test2.hs- hs-source-dirs: src,examples+ hs-source-dirs: examples,. default-language: Haskell2010- build-depends: base >=4.4 && <4.8, HTTP >= 4000, http-conduit >= 2.1, http-types >= 0.8, bytestring >= 0.10+ build-depends: base >=4.7 && <4.9, HTTP >= 4000, http-conduit >= 2.1, http-types >= 0.8, bytestring >= 0.10 ,lifted-base >= 0.2, aeson, random
README.md view
@@ -1,5 +1,6 @@-# Haskell-OrchestrateDB [](https://travis-ci.org/dwd31415/Haskell-OrchestrateDB)-Unofficial Haskell Client Library for the Orchestrate.io API+# Haskell-OrchestrateDB ++[](https://travis-ci.org/dwd31415/Haskell-OrchestrateDB) [](http://hackage.haskell.org/package/OrchestrateDB) []() [](http://hackage.haskell.org/package/OrchestrateDB) # How to install this package?
examples/Test.hs view
@@ -10,7 +10,7 @@ import Data.Aeson.Types import Data.Maybe import GHC.Generics-import qualified Orchestrate as DB+import qualified Database.Orchestrate as DB import System.Exit (exitFailure) import System.Random
examples/Test2.hs view
@@ -3,7 +3,7 @@ import GHC.Generics import Data.Aeson-import qualified Orchestrate as DB+import qualified Database.Orchestrate as DB data SherlockHolmesCase = SherlockHolmesCase { title :: String,
− src/Orchestrate.hs
@@ -1,151 +0,0 @@-{-|-Module : Orchestrate-Copyright : (c) Adrian Dawid 2015-License : BSD3-Maintainer : adriandwd@gmail.com-Stability : stable--This library implements most parts of the Orchestrate.io API in Haskell. It provides a convenient way of accessing these parts of the REST API:-- * Validate API Keys-- * List Key/Values-- * Create Key/Value-- * Create Key/Value with server-generated key-- * Update Key/Value-- * Retrieve Value for Key-- * Delete Collection(s)-- * Query Collection--NOTE: You can also use 'Network.Curl', if you need one of the currently unsupported API functions, but you would have to parse all response bodies yourself if you do that.--=How to use this library?-Using this library is pretty straightforward, just import the Orchestrate module like this:--@-import qualified Orchestrate as DB-@--Once you have it, you can use all the supported API functions(Check out the documentation on 'Orchestrate.REST' for detailed information).-One important thing you need to remeber when working with this library is, that it uses 'Data.Aeson' under the hood, so if you want to-store your Haskell types in an Orchestrate database, 'Data.Aseon' must know how convert them into JSON. The best way to achive that, is-to use the 'DeriveGeneric' Langauge extension.--Here is an example of how to store a haskell value in an Orchestrate Collection:--@-\{\-\# LANGUAGE DeriveGeneric \#\-\}-module Main where--import GHC.Generics-import Data.Aeson-import qualified Orchestrate as DB--data SherlockHolmesCase = SherlockHolmesCase {- title :: String,- typeOfCrime :: String,- shortStory :: Bool,- index :: Integer,- solved :: Bool-} deriving (Show,Read,Generic,Eq)--\{\-This automatically creates both a JSON parser and a JSON encoder based on the data type SherlockHolmesCase, if you however-want the property names in JSON and haskell to differ, you will have to write your own parser and you own encoder.- Please check out the 'Data.Aeson' documentation for information on how to do that.\-\}-instance FromJSON SherlockHolmesCase-instance ToJSON SherlockHolmesCase--main :: IO ()-main = do- -- Create an application record. Oh, BTW, in case you planned to store your api-key in the source code, it is a very very bad idea.- let dbApplcation = DB.createStdApplication \"your.orchestrate.application.name\" \"one-of-your-api-keys\"- -- Create an collection record. This collection does not have to exist, if it does not, it will be created as soon as you try to store something in it.- let dbCollection = DB.createStdCollection \"TheCasebookOfSherlockHolmes\"- let aStudyInScarlet = SherlockHolmesCase {- title = \"A Study in Scarlet\",- typeOfCrime = \"Murder\",- shortStory = False,- index = 0,- solved = True- }- -- Store a haskell value in the collection we created earlier. The \"withOutKey\" lets the server generate the key, so no key must be specified.- didItWork <- DB.orchestrateCollectionPutWithoutKey dbApplcation dbCollection aStudyInScarlet- if didItWork- then putStrLn "Invictus maneo!"- else putStrLn "Something is rotten in the state of Denmark!"-@--If this application does say \"Invictus maneo!\", you should get this result if you LIST the \"TheCasebookOfSherlockHolmes\" collection:--@-"count" : 1,- "results" : [ {- "path" : {- "collection" : "TheCasebookOfSherlockHolmes ",- "kind" : "item",- "key" : "0b8b506cf0204ce9",- "ref" : "--------------",- "reftime" : --------------- },- "value" : {- "solved" : true,- "shortStory" : false,- "title" : "A Study in Scarlet ",- "index" : 0,- "typeOfCrime" : "Murder "- },- "reftime" : --------------- } ]-@--If you don't get that result, you either have run the example more than once, it did not return "Invictus maneo", or something really strange is going on.-If you are fairly certain, that something strange is going on, and it is most likely my fault and you might want to open an issue at github.--=How to use the "cabal test" command-Testing applications which depend on some REST api is not easy, and therfore the cabal tests will fail by default. They just will not be able to authenticate, if you-want to run the tests on your local machine, please create a file called "examples/config.txt" and fill it according to this scheme:--@-("a valid api key","something funny(it will be used as a name for the test collection)")-@--If you are asking yourself now, if there really is no way of checking that this library works, before installing it, there is nothing to worry about.-You should consult the travis-ci server(<https://travis-ci.org/dwd31415/Haskell-OrchestrateDB>) on the state of the project, it does not only check wether or not ther library can be built, but also runs the tests with an valid api key.--}-module Orchestrate(Orchestrate.Types.OrchestrateCollection(..),- Orchestrate.Types.OrchestrateApplication(..),- Orchestrate.REST.validateApplication,- Orchestrate.REST.orchestrateCollectionGet,- Orchestrate.REST.orchestrateCollectionPut,- Orchestrate.REST.orchestrateCollectionPutWithoutKey,- Orchestrate.REST.orchestrateCollectionDelete,- Orchestrate.REST.orchestrateCollectionDeleteKey,- Orchestrate.REST.orchestrateCollectionSearch,- Orchestrate.REST.orchestrateCollectionSearchWithOffset,- Orchestrate.REST.orchestrateCollectionList,- createStdCollection,- createStdApplication) where--import qualified Orchestrate.REST-import qualified Orchestrate.Types--createStdApplication :: String -> String -> Orchestrate.Types.OrchestrateApplication--- ^Creates an application record with the std.(\"https://api.orchestrate.io/v0\") enpoint.-createStdApplication name api_key =- Orchestrate.Types.OrchestrateApplication {- Orchestrate.Types.applicationName = name,- Orchestrate.Types.apiKey = api_key,- Orchestrate.Types.httpsEndpoint = "https://api.orchestrate.io/v0"- }--createStdCollection :: String -> Orchestrate.Types.OrchestrateCollection--- ^Creates an collection record.-createStdCollection name = Orchestrate.Types.OrchestrateCollection {- Orchestrate.Types.collectionName = name-}
− src/Orchestrate/REST.hs
@@ -1,386 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}--{-|-Module : Orchestrate.REST-Description : All functions which interact with the Orchestrate.io REST API.-Copyright : (c) Adrian Dawid 2015-License : BSD3-Maintainer : adriandwd@gmail.com-Stability : stable--This module conatins all the functions which interact with the Orchestrate.io REST API.-Right now these actions are supported:-- * Validate API Keys-- * List Key/Values-- * Create Key/Value-- * Create Key/Value with server-generated key-- * Update Key/Value-- * Retrieve Value for Key-- * Delete Collection(s)-- * Query Collection---}-module Orchestrate.REST- (- validateApplication,- orchestrateCollectionPutWithoutKey,- orchestrateCollectionDelete,- orchestrateCollectionGet,- orchestrateCollectionPut,- orchestrateCollectionDeleteKey,- orchestrateCollectionSearch,- orchestrateCollectionSearchWithOffset,- orchestrateCollectionList- ) where--import Network.HTTP.Conduit-import Network.HTTP.Types.Status--import qualified Control.Exception.Lifted as X-import qualified Data.ByteString.Char8 as B-import qualified Data.ByteString.Lazy as BSLazy--import Data.Aeson-import Data.Aeson.Types-import Data.Maybe-import Orchestrate.Types--validateApplication :: OrchestrateApplication -> IO Bool--- ^The 'validateApplication' function validates your API key,--- by making an authenticated HEAD request to the endpoint specified in the 'OrchestrateApplication' record.--- The function returns False when the key is invalid or no connection with the endpoint could be established.-validateApplication application = do- let api_key = apiKey application- if api_key == ""- then return False- else do- let url = httpsEndpoint application- case parseUrl url of- Nothing -> return False- Just unsecuredRequest -> withManager $ \manager -> do- let request = applyBasicAuth (B.pack $ apiKey application) "" unsecuredRequest- let reqHead = request {- method = "HEAD",- secure = True }- resOrException <- X.try (http reqHead manager)- case resOrException of- Right res -> if responseStatus res == ok200- then return True- else return False- Left (_::X.SomeException) -> return False--orchestrateCollectionList :: OrchestrateApplication -> OrchestrateCollection -> Integer -> IO (Maybe [Object])--- ^ The 'orchestrateCollectionList' function lists the contents of the specified collection, by making GET request to the \/$collection?limit=$limit endpoint.--- For more information check out the Orchestrate.io API docs: <https://orchestrate.io/docs/apiref#keyvalue-list>.--- If connecting to the api fails, or the api key stored in the application record is invlaid, 'Nothing' is returned.--- Otherwise an array of the type 'Object'(see the documentation of 'Data.Aeson' for more information) is returned, it--- contains the values from the HTTP response(see <https://orchestrate.io/docs/apiref#keyvalue-list> for an example of how the response looks like in JSON).------ = Example:--- @--- let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"--- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"--- dbContents <- DB.orchestrateCollectionList dbApplication dbCollection 10--- @-orchestrateCollectionList application collection limit = do- let api_key = apiKey application- if api_key == ""- then return Nothing- else do- let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "?limit=" ++ show limit- case parseUrl url of- Nothing -> return Nothing- Just unsecuredRequest -> withManager $ \manager -> do- let request = unsecuredRequest {- method = "GET",- secure = True }- let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request- resOrException <- X.try (httpLbs reqHead manager)- case resOrException of- Right res -> if responseStatus res == ok200- then do- let resBody = responseBody res- let results = fromMaybe [] (parseListResponseBody resBody)- return $ Just results-- else return Nothing- Left (_::X.SomeException) -> return Nothing---- KEY/VALUE--orchestrateCollectionPutWithoutKey :: ToJSON obj => OrchestrateApplication -> OrchestrateCollection -> obj -> IO Bool--- ^ The 'orchestrateCollectionPutWithoutKey' function stores a Haskell value(with a 'ToJSON' instance) in an Orchestrate.io database.--- It does so by making a POST request to the \/$collection endpoint(Offical API docs:<https://orchestrate.io/docs/apiref#keyvalue-post>).--- This function does not need a user specified key, because it uses a server-generated key, if you want to know the key--- use 'orchestrateCollectionPut' instead of this function.------ = Example:--- @--- data TestRecord = TestRecord--- { string :: String--- , number :: Int--- } deriving (Show,Read,Generic,Eq)------ instance FromJSON TestRecord--- instance ToJSON TestRecord------ let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"--- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"--- let testRecord = TestRecord {string = "You may delay, but time will not!",number = 903}--- _ <- DB.orchestrateCollectionPutWithoutKey dbApplication dbCollection testRecord--- @-orchestrateCollectionPutWithoutKey application collection object = do- let objAsJson = encode object- let api_key = apiKey application- if api_key == ""- then return False- else do- let url = httpsEndpoint application ++ "/" ++ collectionName collection- case parseUrl url of- Nothing -> return False- Just unsecuredRequest -> withManager $ \manager -> do- let request = unsecuredRequest {- method = "POST",- secure = True,- requestHeaders = [("Content-Type", "application/json")],- requestBody = RequestBodyBS $ BSLazy.toStrict objAsJson }- let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request- resOrException <- X.try (http reqHead manager)- case resOrException of- Right res -> if responseStatus res == status201- then return True- else return False- Left (_::X.SomeException) -> return False--orchestrateCollectionPut :: ToJSON obj => OrchestrateApplication -> OrchestrateCollection -> String -> obj -> IO Bool--- ^ The 'orchestrateCollectionPut' function stores a Haskell value(with a 'ToJSON' instance) in an Orchestrate.io database.--- It does so by making a PUT request to the \/$collection\/$key endpoint(Offical API docs:<https://orchestrate.io/docs/apiref#keyvalue-put>).--- In order to upload a Haskell Value to the database, it must have an instance of 'ToJSON' because this--- client library uses 'Data.Aeson' to convert Haskel Values to JSON, which is required by Orchestrate.io.------ = Example:--- @--- data TestRecord = TestRecord--- { string :: String--- , number :: Int--- } deriving (Show,Read,Generic,Eq)------ instance FromJSON TestRecord--- instance ToJSON TestRecord------ let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"--- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"--- let testRecord = TestRecord {string = "You may delay, but time will not!",number = 903}--- _ <- DB.orchestrateCollectionPutWithoutKey dbApplication dbCollection "KEY" testRecord--- @-orchestrateCollectionPut application collection key object = do- let objAsJson = encode object- let api_key = apiKey application- if api_key == ""- then return False- else do- let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "/" ++ key- case parseUrl url of- Nothing -> return False- Just unsecuredRequest -> withManager $ \manager -> do- let request = unsecuredRequest {- method = "PUT",- secure = True,- requestHeaders = [("Content-Type", "application/json")],- requestBody = RequestBodyBS $ BSLazy.toStrict objAsJson }- let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request- resOrException <- X.try (http reqHead manager)- case resOrException of- Right res -> if responseStatus res == status201- then return True- else return False- Left (_::X.SomeException) -> return False--orchestrateCollectionGet :: FromJSON res => OrchestrateApplication -> OrchestrateCollection -> String -> IO (Maybe res)--- ^ The 'orchestrateCollectionGet' function request a value from an Orchestrate.io database, and tries to convert it to the specified Haskell type,--- if either gettings the value from the database or converting it to the Haskell type fails 'Nothing' is returned.--- The value is requested by making a GET request to the \/$collection\/$key endpoint(Offical documentation:<https://orchestrate.io/docs/apiref#keyvalue-get>)------ = Example:--- @--- data TestRecord = TestRecord--- { string :: String--- , number :: Int--- } deriving (Show,Read,Generic,Eq)------ instance FromJSON TestRecord--- instance ToJSON TestRecord------ let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"--- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"--- let testRecord = TestRecord {string = "You may delay, but time will not!",number = 903}--- dbValue <- DB.orchestrateCollectionGet dbApplication dbCollection "KEY" :: IO (Maybe TestRecord)--- @-orchestrateCollectionGet application collection key = do- let api_key = apiKey application- if api_key == ""- then return Nothing- else do- let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "/" ++ key- case parseUrl url of- Nothing -> return Nothing- Just unsecuredRequest -> withManager $ \manager -> do- let request = unsecuredRequest {- method = "GET",- secure = True }- let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request- resOrException <- X.try (httpLbs reqHead manager)- case resOrException of- Right res -> if responseStatus res == ok200- then do- let resBody = responseBody res- let jsonObject = decode resBody- return jsonObject- else return Nothing- Left (_::X.SomeException) -> return Nothing--orchestrateCollectionDeleteKey :: OrchestrateApplication -> OrchestrateCollection -> String -> IO Bool--- ^ The 'orchestrateCollectionDeleteKey' function deletes a value from an Orchestrate.io database.--- This is done by making a DELETE request to the \/$collection\/$key endpoint(Offical documentation:<https://orchestrate.io/docs/apiref#keyvalue-delete>)------ = Example:--- @--- data TestRecord = TestRecord--- { string :: String--- , number :: Int--- } deriving (Show,Read,Generic,Eq)------ instance FromJSON TestRecord--- instance ToJSON TestRecord------ let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"--- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"--- _ <- DB.orchestrateCollectionKey dbApplication dbCollection "KEY"--- @-orchestrateCollectionDeleteKey application collection key = do- let api_key = apiKey application- if api_key == ""- then return False- else do- let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "/" ++ key ++ "?force=true"- case parseUrl url of- Nothing -> return False- Just unsecuredRequest -> withManager $ \manager -> do- let request = unsecuredRequest {- method = "DELETE",- secure = True }- let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request- resOrException <- X.try (http reqHead manager)- case resOrException of- Right res -> if responseStatus res == status204- then return True- else return False- Left (_::X.SomeException) -> return False--orchestrateCollectionDelete :: OrchestrateApplication -> OrchestrateCollection -> IO Bool--- ^ The 'orchestrateCollectionDelete' function deletes a collection from an Orchestrate.io application.--- This is done by making a DELETE request to the \/$collection endpoint(Offical documentation:<https://orchestrate.io/docs/apiref#collections-delete>)------ = Example:--- @--- let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"--- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"--- _ <- DB.orchestrateCollectionDelete dbApplication dbCollection--- @-orchestrateCollectionDelete application collection = do- let api_key = apiKey application- if api_key == ""- then return False- else do- let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "?force=true"- case parseUrl url of- Nothing -> return False- Just unsecuredRequest -> withManager $ \manager -> do- let request = unsecuredRequest {- method = "DELETE",- secure = True }- let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request- resOrException <- X.try (http reqHead manager)- case resOrException of- Right res -> if responseStatus res == status204- then return True- else return False- Left (_::X.SomeException) -> return False---- SEARCH--orchestrateCollectionSearch :: OrchestrateApplication -> OrchestrateCollection -> String -> IO (Maybe([Object],Bool))--- ^ Please see 'orchestrateCollectionSearchWithOffset' for more information. This function just calls it without an offset and with a limit of 10.-orchestrateCollectionSearch application collection query = orchestrateCollectionSearchWithOffset application collection query 0 10---orchestrateCollectionSearchWithOffset :: OrchestrateApplication -> OrchestrateCollection -> String -> Integer -> Integer -> IO (Maybe([Object],Bool))--- ^ The 'orchestrateCollectionSearchWithOffset' function searches for the query in the database and returns an array--- of the type \"'Maybe' ['Object']\". Nothing is returned when establishing a connection or authenticating failed. The function uses the--- SEARCH method of the Orchestrate.io API (Offical documentation:<https://orchestrate.io/docs/apiref#search-collection>)--- , but automatically parsers the response. It returns a tupel of the type ('Maybe'(['Object'],'Bool')), the boolean indicates wether or not--- more results are availble on the server. If that is true, the function should be called again with an increased offset, until (Just _,False) is returned.------ = Example:--- @--- dbSearchResults query num =--- let results = DB.orchestrateCollectionSearchWithOffset query num (num+10)--- let currentResults = fromJust $ fst results--- if snd results--- then currentResults:(dbSearchResults query (num+10))--- else currentResults------ let dbApplication = DB.createStdApplication \"APPLICATION_NAME\" \"API_KEY\"--- let dbCollection = DB.createStdCollection \"COLLECTION_NAME\"--- let completeDBSearchResults = dbSearchResults "QUERY" 0--- @-orchestrateCollectionSearchWithOffset application collection query offset limit = do- let api_key = apiKey application- if api_key == ""- then return Nothing- else do- let url = httpsEndpoint application ++ "/" ++ collectionName collection ++ "?query=" ++ query ++ "&limit=" ++ show limit ++ "&offset=" ++ show offset- case parseUrl url of- Nothing -> return Nothing- Just unsecuredRequest -> withManager $ \manager -> do- let request = unsecuredRequest {- method = "GET",- secure = True }- let reqHead = applyBasicAuth (B.pack $ apiKey application) "" request- resOrException <- X.try (httpLbs reqHead manager)- case resOrException of- Right res -> if responseStatus res == ok200- then do- let resBody = responseBody res- let (results,count) = fromMaybe ([],0) (parseQueryResponseBody resBody)- return $ Just (results,count > (fromIntegral (length results) + offset))-- else return Nothing- Left (_::X.SomeException) -> return Nothing----- HELPERS--parseListResponseBody :: BSLazy.ByteString -> Maybe [Object]-parseListResponseBody resBodyRaw = do- result <- decode resBodyRaw- results <- flip parseMaybe result $ \obj -> obj .: "results" :: Parser [OrchestrateListResult]- return $ resultValuesAsList results--parseQueryResponseBody :: BSLazy.ByteString -> Maybe ([Object],Integer)-parseQueryResponseBody resBodyRaw = do- result <- decode resBodyRaw- (count,results) <- flip parseMaybe result $ \obj -> do- count <- obj .: "total_count" :: Parser Integer- results <- obj .: "results" :: Parser [OrchestrateQueryResult]- return (count,results)- return (resultValuesAsList results,count)
− src/Orchestrate/Types.hs
@@ -1,108 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TypeFamilies #-}--{-|-Module : Orchestrate.Types-Description : Haskell types for various JSON patterns.-Copyright : (c) Adrian Dawid 2015-License : BSD3-Maintainer : adriandwd@gmail.com-Stability : stable--This module conatins Haskell types which represent all necessary JSON patterns used by the Orchestrate.io API.--}-module Orchestrate.Types- ( OrchestrateApplication(..),- OrchestrateCollection(..),- OrchestrateQueryResult(..),- OrchestratePath (..),- OrchestrateListResult (..),- resultValuesAsList) where--import Control.Applicative ((<$>), (<*>))-import Control.Monad-import Data.Aeson-import GHC.Generics--{-|-A data type, that represents an Orchestrate application. It stores an api key (generated online) and a https-endpoint.--}-data OrchestrateApplication = OrchestrateApplication {- applicationName :: String,- apiKey :: String,- httpsEndpoint :: String-}--{-|-Represents a collection inside an OrchestrateApplication, it stores all data necessary to access it.--}-data OrchestrateCollection = OrchestrateCollection {- collectionName :: String-}---- |TypeClass for OrchestrateQueryResult and OrchestrateListResult, it makes it possible to have one function('resultValuesAsList'), that extractes--- the values from both of those types.-class OrchestrateIntermediateResult a where- resultValuesAsList :: [a] -> [Object]---- |Represents a path, as it is returned by the LIST and SEARCH function.--- Right now it is not actually used, but it might be useful in the future.-data OrchestratePath = OrchestratePath {- orchestratePathCollection :: String,- orchestratePathKind :: String,- orchestratePathKey :: String,- orchestratePathRef :: String,- orchestratePathReftime :: Integer-} deriving (Show,Read,Generic)--instance FromJSON OrchestratePath where- parseJSON (Object v) = OrchestratePath <$>- v .: "collection" <*>- v .: "kind" <*>- v .: "key" <*>- v .: "ref" <*>- v .: "reftime"- -- A non-Object value is of the wrong type, so fail.- parseJSON _ = mzero-instance ToJSON OrchestratePath---- | Haskell type for the JSON scheme, that is returned by the SEARCH function.-data OrchestrateQueryResult = OrchestrateQueryResult {- orchestrateQueryResultPath :: OrchestratePath,- orchestrateQueryResultValue :: Object,- orchestrateQueryResultScore :: Double,- orchestrateQueryResultReftimeQueryResult :: Integer-} deriving (Show,Generic)--instance FromJSON OrchestrateQueryResult where- parseJSON (Object v) = OrchestrateQueryResult <$>- v .: "path" <*>- v .: "value" <*>- v .: "score" <*>- v .: "reftime"- -- A non-Object value is of the wrong type, so fail.- parseJSON _ = mzero-instance ToJSON OrchestrateQueryResult-instance OrchestrateIntermediateResult OrchestrateQueryResult where- resultValuesAsList = map orchestrateQueryResultValue----- | Haskell type for the JSON scheme, that is returned by the LIST function.-data OrchestrateListResult = OrchestrateListResult {- orchestrateListResultPath :: OrchestratePath,- orchestrateListResultValue :: Object,- orchestrateListResultReftimeQueryResult :: Integer-} deriving (Show,Generic)--instance FromJSON OrchestrateListResult where- parseJSON (Object v) = OrchestrateListResult <$>- v .: "path" <*>- v .: "value" <*>- v .: "reftime"- -- A non-Object value is of the wrong type, so fail.- parseJSON _ = mzero-instance ToJSON OrchestrateListResult-instance OrchestrateIntermediateResult OrchestrateListResult where- resultValuesAsList = map orchestrateListResultValue