packages feed

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 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 [![Build Status](https://travis-ci.org/dwd31415/Haskell-OrchestrateDB.svg?branch=master)](https://travis-ci.org/dwd31415/Haskell-OrchestrateDB)-Unofficial Haskell Client Library for the Orchestrate.io API+# Haskell-OrchestrateDB  ++[![Build Status](https://travis-ci.org/dwd31415/Haskell-OrchestrateDB.svg?branch=master)](https://travis-ci.org/dwd31415/Haskell-OrchestrateDB)  [![Hackage](https://img.shields.io/hackage/v/OrchestrateDB.svg)](http://hackage.haskell.org/package/OrchestrateDB)  [![GitHub license](https://img.shields.io/github/license/dwd31415/Haskell-OrchestrateDB.svg)]()  [![Hackage-Deps](https://img.shields.io/hackage-deps/v/OrchestrateDB.svg)](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