git.iko.soy/typeable/bloodhound
on master help sign in
WIP adding manager and server url state
commit279e169fdf5499a335ec630be1580c4ea7e7d479 authorMichael Xavier <michael@michaelxavier.net> dateTue 3 Mar 2015, 09:21 (12 years ago) parent20ed65b
4 files changed+244−177
bloodhound.cabal+4−1 src/Database/Bloodhound/Client.hs+94−79 src/Database/Bloodhound/Types.hs+16 tests/tests.hs+130−97
bloodhound.cabal+4−1
@@ -35,6 +35,8 @@ library
3535 semigroups >= 0.15 && <0.17,
3636 time >= 1.4 && <1.6,
3737 text >= 0.11 && <1.3,
38+ mtl >= 1.0 && <2.3,
39+ transformers >= 0.2 && <0.5,
3840 http-types >= 0.8 && <0.9,
3941 vector >= 0.10.9 && <0.11
4042 default-language: Haskell2010
@@ -56,7 +58,8 @@ test-suite tests
5658 semigroups,
5759 QuickCheck,
5860 vector,
59− unordered-containers >= 0.2.5.0 && <0.3
61+ unordered-containers >= 0.2.5.0 && <0.3,
62+ mtl
6063 default-language: Haskell2010
6164
6265test-suite doctests
src/Database/Bloodhound/Client.hs+94−79
@@ -49,6 +49,9 @@ module Database.Bloodhound.Client
4949 )
5050 where
5151
52+import Control.Applicative
53+import Control.Monad
54+import Control.Monad.IO.Class
5255import Data.Aeson
5356import Data.ByteString.Lazy.Builder
5457import qualified Data.ByteString.Lazy.Char8 as L
@@ -133,29 +136,39 @@ mkReplicaCount n
133136emptyBody :: L.ByteString
134137emptyBody = L.pack ""
135138
136−dispatch :: Method -> String -> Maybe L.ByteString
137− -> IO Reply
139+dispatch :: MonadBloodhound m => Method -> String -> Maybe L.ByteString
140+ -> m Reply
138141dispatch dMethod url body = do
139− initReq <- parseUrl url
142+ initReq <- liftIO $ parseUrl url
140143 let reqBody = RequestBodyLBS $ fromMaybe emptyBody body
141144 let req = initReq { method = dMethod
142145 , requestBody = reqBody
143146 , checkStatus = \_ _ _ -> Nothing}
144− withManager defaultManagerSettings $ httpLbs req
147+ mgr <- bhManager <$> getBHEnv
148+ liftIO $ httpLbs req mgr
149+
150+joinPath' :: [String] -> String
151+joinPath' = intercalate "/"
152+
153+joinPath :: MonadBloodhound m => [String] -> m String
154+joinPath ps = do
155+ Server s <- bhServer <$> getBHEnv
156+ return $ joinPath' (s:ps)
157+
158+bindM2 :: (Applicative m, Monad m) => (a -> b -> m c) -> m a -> m b -> m c
159+bindM2 f ma mb = join (f <$> ma <*> mb)
145160
146−joinPath :: [String] -> String
147−joinPath = intercalate "/"
148161
149162-- Shortcut functions for HTTP methods
150−delete :: String -> IO Reply
163+delete :: MonadBloodhound m => String -> m Reply
151164delete = flip (dispatch NHTM.methodDelete) Nothing
152−get :: String -> IO Reply
165+get :: MonadBloodhound m => String -> m Reply
153166get = flip (dispatch NHTM.methodGet) Nothing
154−head :: String -> IO Reply
167+head :: MonadBloodhound m => String -> m Reply
155168head = flip (dispatch NHTM.methodHead) Nothing
156−put :: String -> Maybe L.ByteString -> IO Reply
169+put :: MonadBloodhound m => String -> Maybe L.ByteString -> m Reply
157170put = dispatch NHTM.methodPost
158−post :: String -> Maybe L.ByteString -> IO Reply
171+post :: MonadBloodhound m => String -> Maybe L.ByteString -> m Reply
159172post = dispatch NHTM.methodPost
160173
161174-- indexDocument s ix name doc = put (root </> s </> ix </> name </> doc) (Just encode doc)
@@ -167,10 +180,12 @@ post = dispatch NHTM.methodPost
167180-- >>> serverStatus <- getStatus testServer
168181-- >>> fmap status (serverStatus)
169182-- Just 200
170−getStatus :: Server -> IO (Maybe Status)
171−getStatus (Server server) = do
172− request <- parseUrl $ joinPath [server]
173− response <- withManager defaultManagerSettings $ httpLbs request
183+getStatus :: MonadBloodhound m => m (Maybe Status)
184+getStatus = do
185+ url <- joinPath []
186+ request <- liftIO $ parseUrl url
187+ mgr <- bhManager <$> getBHEnv
188+ response <- liftIO $ httpLbs request mgr
174189 return $ decode (responseBody response)
175190
176191-- | 'createIndex' will create an index given a 'Server', 'IndexSettings', and an 'IndexName'.
@@ -180,10 +195,10 @@ getStatus (Server server) = do
180195-- True
181196-- >>> indexExists testServer (IndexName "didimakeanindex")
182197-- True
183−createIndex :: Server -> IndexSettings -> IndexName -> IO Reply
184−createIndex (Server server) indexSettings (IndexName indexName) =
185− put url body
186− where url = joinPath [server, indexName]
198+createIndex :: MonadBloodhound m => IndexSettings -> IndexName -> m Reply
199+createIndex indexSettings (IndexName indexName) =
200+ bindM2 put url (return body)
201+ where url = joinPath [indexName]
187202 body = Just $ encode indexSettings
188203
189204-- | 'deleteIndex' will delete an index given a 'Server', and an 'IndexName'.
@@ -194,9 +209,9 @@ createIndex (Server server) indexSettings (IndexName indexName) =
194209-- True
195210-- >>> indexExists testServer testIndex
196211-- False
197−deleteIndex :: Server -> IndexName -> IO Reply
198−deleteIndex (Server server) (IndexName indexName) =
199− delete $ joinPath [server, indexName]
212+deleteIndex :: MonadBloodhound m => IndexName -> m Reply
213+deleteIndex (IndexName indexName) =
214+ delete =<< joinPath [indexName]
200215
201216statusCodeIs :: Int -> Reply -> Bool
202217statusCodeIs n resp = NHTS.statusCode (responseStatus resp) == n
@@ -204,7 +219,7 @@ statusCodeIs n resp = NHTS.statusCode (responseStatus resp) == n
204219respIsTwoHunna :: Reply -> Bool
205220respIsTwoHunna = statusCodeIs 200
206221
207−existentialQuery :: String -> IO (Reply, Bool)
222+existentialQuery :: MonadBloodhound m => String -> m (Reply, Bool)
208223existentialQuery url = do
209224 reply <- head url
210225 return (reply, respIsTwoHunna reply)
@@ -213,45 +228,44 @@ existentialQuery url = do
213228-- in IO
214229--
215230-- >>> exists <- indexExists testServer testIndex
216−indexExists :: Server -> IndexName -> IO Bool
217−indexExists (Server server) (IndexName indexName) = do
218− (_, exists) <- existentialQuery url
231+indexExists :: MonadBloodhound m => IndexName -> m Bool
232+indexExists (IndexName indexName) = do
233+ (_, exists) <- existentialQuery =<< joinPath [indexName]
219234 return exists
220− where url = joinPath [server, indexName]
221235
222236-- | 'refreshIndex' will force a refresh on an index. You must
223237-- do this if you want to read what you wrote.
224238--
225239-- >>> _ <- createIndex testServer defaultIndexSettings testIndex
226240-- >>> _ <- refreshIndex testServer testIndex
227−refreshIndex :: Server -> IndexName -> IO Reply
228−refreshIndex (Server server) (IndexName indexName) =
229− post url Nothing
230− where url = joinPath [server, indexName, "_refresh"]
241+refreshIndex :: MonadBloodhound m => IndexName -> m Reply
242+refreshIndex (IndexName indexName) =
243+ bindM2 post url (return Nothing)
244+ where url = joinPath [indexName, "_refresh"]
231245
232246stringifyOCIndex :: OpenCloseIndex -> String
233247stringifyOCIndex oci = case oci of
234248 OpenIndex -> "_open"
235249 CloseIndex -> "_close"
236250
237−openOrCloseIndexes :: OpenCloseIndex -> Server -> IndexName -> IO Reply
238−openOrCloseIndexes oci (Server server) (IndexName indexName) =
239− post url Nothing
251+openOrCloseIndexes :: MonadBloodhound m => OpenCloseIndex -> IndexName -> m Reply
252+openOrCloseIndexes oci (IndexName indexName) =
253+ bindM2 post url (return Nothing)
240254 where ociString = stringifyOCIndex oci
241− url = joinPath [server, indexName, ociString]
255+ url = joinPath [indexName, ociString]
242256
243257-- | 'openIndex' opens an index given a 'Server' and an 'IndexName'. Explained in further detail at
244258-- <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html>
245259--
246260-- >>> reply <- openIndex testServer testIndex
247−openIndex :: Server -> IndexName -> IO Reply
261+openIndex :: MonadBloodhound m => IndexName -> m Reply
248262openIndex = openOrCloseIndexes OpenIndex
249263
250264-- | 'closeIndex' closes an index given a 'Server' and an 'IndexName'. Explained in further detail at
251265-- <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html>
252266--
253267-- >>> reply <- closeIndex testServer testIndex
254−closeIndex :: Server -> IndexName -> IO Reply
268+closeIndex :: MonadBloodhound m => IndexName -> m Reply
255269closeIndex = openOrCloseIndexes CloseIndex
256270
257271-- | 'putMapping' is an HTTP PUT and has upsert semantics. Mappings are schemas
@@ -261,11 +275,11 @@ closeIndex = openOrCloseIndexes CloseIndex
261275-- >>> resp <- putMapping testServer testIndex testMapping TweetMapping
262276-- >>> print resp
263277-- Response {responseStatus = Status {statusCode = 200, statusMessage = "OK"}, responseVersion = HTTP/1.1, responseHeaders = [("Content-Type","application/json; charset=UTF-8"),("Content-Length","21")], responseBody = "{\"acknowledged\":true}", responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose}
264−putMapping :: ToJSON a => Server -> IndexName
265− -> MappingName -> a -> IO Reply
266−putMapping (Server server) (IndexName indexName) (MappingName mappingName) mapping =
267− put url body
268− where url = joinPath [server, indexName, mappingName, "_mapping"]
278+putMapping :: (MonadBloodhound m, ToJSON a) => IndexName
279+ -> MappingName -> a -> m Reply
280+putMapping (IndexName indexName) (MappingName mappingName) mapping =
281+ bindM2 put url (return body)
282+ where url = joinPath [indexName, mappingName, "_mapping"]
269283 body = Just $ encode mapping
270284
271285-- | 'deleteMapping' is an HTTP DELETE and deletes a mapping for a given index.
@@ -276,10 +290,10 @@ putMapping (Server server) (IndexName indexName) (MappingName mappingName) mappi
276290-- >>> resp <- deleteMapping testServer testIndex testMapping
277291-- >>> print resp
278292-- Response {responseStatus = Status {statusCode = 200, statusMessage = "OK"}, responseVersion = HTTP/1.1, responseHeaders = [("Content-Type","application/json; charset=UTF-8"),("Content-Length","21")], responseBody = "{\"acknowledged\":true}", responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose}
279−deleteMapping :: Server -> IndexName -> MappingName -> IO Reply
280−deleteMapping (Server server) (IndexName indexName)
293+deleteMapping :: MonadBloodhound m => IndexName -> MappingName -> m Reply
294+deleteMapping (IndexName indexName)
281295 (MappingName mappingName) =
282− delete $ joinPath [server, indexName, mappingName, "_mapping"]
296+ delete =<< joinPath [indexName, mappingName, "_mapping"]
283297
284298-- | 'indexDocument' is the primary way to save a single document in
285299-- Elasticsearch. The document itself is simply something we can
@@ -289,22 +303,22 @@ deleteMapping (Server server) (IndexName indexName)
289303-- >>> resp <- indexDocument testServer testIndex testMapping exampleTweet (DocId "1")
290304-- >>> print resp
291305-- Response {responseStatus = Status {statusCode = 201, statusMessage = "Created"}, responseVersion = HTTP/1.1, responseHeaders = [("Content-Type","application/json; charset=UTF-8"),("Content-Length","74")], responseBody = "{\"_index\":\"twitter\",\"_type\":\"tweet\",\"_id\":\"1\",\"_version\":1,\"created\":true}", responseCookieJar = CJ {expose = []}, responseClose' = ResponseClose}
292−indexDocument :: ToJSON doc => Server -> IndexName -> MappingName
293− -> doc -> DocId -> IO Reply
294−indexDocument (Server server) (IndexName indexName)
306+indexDocument :: (ToJSON doc, MonadBloodhound m) => IndexName -> MappingName
307+ -> doc -> DocId -> m Reply
308+indexDocument (IndexName indexName)
295309 (MappingName mappingName) document (DocId docId) =
296− put url body
297− where url = joinPath [server, indexName, mappingName, docId]
310+ bindM2 put url (return body)
311+ where url = joinPath [indexName, mappingName, docId]
298312 body = Just (encode document)
299313
300314-- | 'deleteDocument' is the primary way to delete a single document.
301315--
302316-- >>> _ <- deleteDocument testServer testIndex testMapping (DocId "1")
303−deleteDocument :: Server -> IndexName -> MappingName
304− -> DocId -> IO Reply
305−deleteDocument (Server server) (IndexName indexName)
317+deleteDocument :: MonadBloodhound m => IndexName -> MappingName
318+ -> DocId -> m Reply
319+deleteDocument (IndexName indexName)
306320 (MappingName mappingName) (DocId docId) =
307− delete $ joinPath [server, indexName, mappingName, docId]
321+ delete =<< joinPath [indexName, mappingName, docId]
308322
309323-- | 'bulk' uses
310324-- <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-bulk.html Elasticsearch's bulk API>
@@ -316,10 +330,10 @@ deleteDocument (Server server) (IndexName indexName)
316330-- >>> let stream = V.fromList [BulkIndex testIndex testMapping (DocId "2") (toJSON (BulkTest "blah"))]
317331-- >>> _ <- bulk testServer stream
318332-- >>> _ <- refreshIndex testServer testIndex
319−bulk :: Server -> V.Vector BulkOperation -> IO Reply
320−bulk (Server server) bulkOps = post url body where
321− url = joinPath [server, "_bulk"]
322− body = Just $ encodeBulkOperations bulkOps
333+bulk :: MonadBloodhound m => V.Vector BulkOperation -> m Reply
334+bulk bulkOps = bindM2 post url (return body)
335+ where url = joinPath ["_bulk"]
336+ body = Just $ encodeBulkOperations bulkOps
323337
324338-- | 'encodeBulkOperations' is a convenience function for dumping a vector of 'BulkOperation'
325339-- into an 'L.ByteString'
@@ -380,25 +394,25 @@ encodeBulkOperation (BulkUpdate (IndexName indexName)
380394-- The 'DocId' is the primary key for your Elasticsearch document.
381395--
382396-- >>> yourDoc <- getDocument testServer testIndex testMapping (DocId "1")
383−getDocument :: Server -> IndexName -> MappingName
384− -> DocId -> IO Reply
385−getDocument (Server server) (IndexName indexName)
397+getDocument :: MonadBloodhound m => IndexName -> MappingName
398+ -> DocId -> m Reply
399+getDocument (IndexName indexName)
386400 (MappingName mappingName) (DocId docId) =
387− get $ joinPath [server, indexName, mappingName, docId]
401+ get =<< joinPath [indexName, mappingName, docId]
388402
389403-- | 'documentExists' enables you to check if a document exists. Returns 'Bool'
390404-- in IO
391405--
392406-- >>> exists <- documentExists testServer testIndex testMapping (DocId "1")
393−documentExists :: Server -> IndexName -> MappingName
394− -> DocId -> IO Bool
395−documentExists (Server server) (IndexName indexName)
407+documentExists :: MonadBloodhound m => IndexName -> MappingName
408+ -> DocId -> m Bool
409+documentExists (IndexName indexName)
396410 (MappingName mappingName) (DocId docId) = do
397− (_, exists) <- existentialQuery url
398− return exists where
399− url = joinPath [server, indexName, mappingName, docId]
411+ (_, exists) <- existentialQuery =<< url
412+ return exists
413+ where url = joinPath [indexName, mappingName, docId]
400414
401−dispatchSearch :: String -> Search -> IO Reply
415+dispatchSearch :: MonadBloodhound m => String -> Search -> m Reply
402416dispatchSearch url search = post url (Just (encode search))
403417
404418-- | 'searchAll', given a 'Search', will perform that search against all indexes
@@ -407,9 +421,9 @@ dispatchSearch url search = post url (Just (encode search))
407421-- >>> let query = TermQuery (Term "user" "bitemyapp") Nothing
408422-- >>> let search = mkSearch (Just query) Nothing
409423-- >>> reply <- searchAll testServer search
410−searchAll :: Server -> Search -> IO Reply
411−searchAll (Server server) = dispatchSearch url where
412− url = joinPath [server, "_search"]
424+searchAll :: MonadBloodhound m => Search -> m Reply
425+searchAll = bindM2 dispatchSearch url . return
426+ where url = joinPath ["_search"]
413427
414428-- | 'searchByIndex', given a 'Search' and an 'IndexName', will perform that search
415429-- against all mappings within an index on an Elasticsearch server.
@@ -417,9 +431,9 @@ searchAll (Server server) = dispatchSearch url where
417431-- >>> let query = TermQuery (Term "user" "bitemyapp") Nothing
418432-- >>> let search = mkSearch (Just query) Nothing
419433-- >>> reply <- searchByIndex testServer testIndex search
420−searchByIndex :: Server -> IndexName -> Search -> IO Reply
421−searchByIndex (Server server) (IndexName indexName) = dispatchSearch url where
422− url = joinPath [server, indexName, "_search"]
434+searchByIndex :: MonadBloodhound m => IndexName -> Search -> m Reply
435+searchByIndex (IndexName indexName) = bindM2 dispatchSearch url . return
436+ where url = joinPath [indexName, "_search"]
423437
424438-- | 'searchByType', given a 'Search', 'IndexName', and 'MappingName', will perform that
425439-- search against a specific mapping within an index on an Elasticsearch server.
@@ -427,10 +441,11 @@ searchByIndex (Server server) (IndexName indexName) = dispatchSearch url where
427441-- >>> let query = TermQuery (Term "user" "bitemyapp") Nothing
428442-- >>> let search = mkSearch (Just query) Nothing
429443-- >>> reply <- searchByType testServer testIndex testMapping search
430−searchByType :: Server -> IndexName -> MappingName -> Search -> IO Reply
431−searchByType (Server server) (IndexName indexName)
432− (MappingName mappingName) = dispatchSearch url where
433− url = joinPath [server, indexName, mappingName, "_search"]
444+searchByType :: MonadBloodhound m => IndexName -> MappingName -> Search
445+ -> m Reply
446+searchByType (IndexName indexName)
447+ (MappingName mappingName) = bindM2 dispatchSearch url . return
448+ where url = joinPath [indexName, mappingName, "_search"]
434449
435450-- | 'mkSearch' is a helper function for defaulting additional fields of a 'Search'
436451-- to Nothing in case you only care about your 'Query' and 'Filter'. Use record update
src/Database/Bloodhound/Types.hs+16
@@ -1,6 +1,8 @@
11{-# LANGUAGE DeriveGeneric #-}
22{-# LANGUAGE OverloadedStrings #-}
33{-# LANGUAGE RecordWildCards #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE FlexibleContexts #-}
46
57-------------------------------------------------------------------------------
68-- |
@@ -39,6 +41,8 @@ module Database.Bloodhound.Types
3941 , toTerms
4042 , toDateHistogram
4143 , omitNulls
44+ , BHEnv(..)
45+ , MonadBloodhound(..)
4246 , Version(..)
4347 , Status(..)
4448 , Existence(..)
@@ -194,6 +198,8 @@ module Database.Bloodhound.Types
194198 ) where
195199
196200import Control.Applicative
201+import Control.Monad.IO.Class
202+import Control.Monad.Reader
197203import Data.Aeson
198204import Data.Aeson.Types (Pair, emptyObject, parseMaybe)
199205import qualified Data.ByteString.Lazy.Char8 as L
@@ -222,6 +228,16 @@ import Database.Bloodhound.Types.Class
222228-- defaultIndexSettings is exported by Database.Bloodhound as well
223229-- no trailing slashes in servers, library handles building the path.
224230
231+data BHEnv = BHEnv { bhServer :: Server
232+ , bhManager :: Manager
233+ }
234+
235+class (Functor m, Applicative m, MonadIO m) => MonadBloodhound m where
236+ getBHEnv :: m BHEnv
237+
238+instance (Functor m, Applicative m, MonadIO m) => MonadBloodhound (ReaderT BHEnv m) where
239+ getBHEnv = ask
240+
225241{-| 'Version' is embedded in 'Status' -}
226242data Version = Version { number :: Text
227243 , build_hash :: Text
tests/tests.hs+130−97
@@ -6,6 +6,7 @@ module Main where
66
77import Control.Applicative
88import Control.Monad
9+import Control.Monad.Reader
910import Data.Aeson
1011import qualified Data.HashMap.Strict as HM
1112import Data.List (nub)
@@ -34,15 +35,23 @@ testIndex = IndexName "bloodhound-tests-twitter-1"
3435testMapping :: MappingName
3536testMapping = MappingName "tweet"
3637
38+
39+--TODO: similar convenience function in Client
40+-- withTestEnv :: MonadIO m => Reader m a -> IO a
41+withTestEnv f = withManager defaultManagerSettings $ \mgr -> do
42+ let env = BHEnv { bhServer = testServer
43+ , bhManager = mgr }
44+ runReaderT f env
45+
3746validateStatus :: Response body -> Int -> Expectation
3847validateStatus resp expected =
3948 (NHTS.statusCode $ responseStatus resp)
4049 `shouldBe` (expected :: Int)
4150
42−createExampleIndex :: IO Reply
43−createExampleIndex = createIndex testServer defaultIndexSettings testIndex
44−deleteExampleIndex :: IO Reply
45−deleteExampleIndex = deleteIndex testServer testIndex
51+-- createExampleIndex :: IO Reply
52+createExampleIndex = createIndex defaultIndexSettings testIndex
53+-- deleteExampleIndex :: IO Reply
54+deleteExampleIndex = deleteIndex testIndex
4655
4756data ServerVersion = ServerVersion Int Int Int deriving (Show, Eq, Ord)
4857
@@ -70,18 +79,16 @@ mkServerVersion [majorVer, minorVer, patchVer] =
7079 Just (ServerVersion majorVer minorVer patchVer)
7180mkServerVersion _ = Nothing
7281
73−getServerVersion :: Server -> IO (Maybe ServerVersion)
74−getServerVersion s = liftM extractVersion (getStatus s)
82+-- getServerVersion :: IO (Maybe ServerVersion)
83+getServerVersion = liftM extractVersion (withTestEnv getStatus)
7584 where
7685 version' = T.splitOn "." . number . version
7786 toInt = read . T.unpack
7887 parseVersion v = map toInt (version' v)
7988 extractVersion = join . liftM (mkServerVersion . parseVersion)
8089
81−
82−
8390testServerBranch :: IO (Maybe ServerVersion)
84−testServerBranch = getServerVersion testServer >>= \v -> return $ liftM serverBranch v
91+testServerBranch = getServerVersion >>= \v -> return $ liftM serverBranch v
8592
8693atleast :: ServerVersion -> IO Bool
8794atleast v = testServerBranch >>= \x -> return $ x >= Just (serverBranch v)
@@ -136,60 +143,63 @@ otherTweet = Tweet { user = "notmyapp"
136143 , age = 1000
137144 , location = Location 40.12 (-71.34) }
138145
139−insertData :: IO ()
146+insertData :: ReaderT BHEnv IO ()
140147insertData = do
141148 _ <- deleteExampleIndex
142149 _ <- createExampleIndex
143− _ <- putMapping testServer testIndex testMapping TweetMapping
144− _ <- indexDocument testServer testIndex testMapping exampleTweet (DocId "1")
145− _ <- refreshIndex testServer testIndex
150+ _ <- putMapping testIndex testMapping TweetMapping
151+ _ <- indexDocument testIndex testMapping exampleTweet (DocId "1")
152+ _ <- refreshIndex testIndex
146153 return ()
147154
148−insertOther :: IO ()
155+-- insertOther :: IO ()
149156insertOther = do
150− _ <- indexDocument testServer testIndex testMapping otherTweet (DocId "2")
151− _ <- refreshIndex testServer testIndex
157+ _ <- indexDocument testIndex testMapping otherTweet (DocId "2")
158+ _ <- refreshIndex testIndex
152159 return ()
153160
154−searchTweet :: Search -> IO (Either String Tweet)
161+-- searchTweet :: Search -> IO (Either String Tweet)
155162searchTweet search = do
156− reply <- searchByIndex testServer testIndex search
163+ reply <- searchByIndex testIndex search
157164 let result = eitherDecode (responseBody reply) :: Either String (SearchResult Tweet)
158165 let myTweet = fmap (hitSource . head . hits . searchHits) result
159166 return myTweet
160167
161−searchExpectNoResults :: Search -> IO ()
168+-- searchExpectNoResults :: Search -> IO ()
162169searchExpectNoResults search = do
163− reply <- searchByIndex testServer testIndex search
170+ reply <- searchByIndex testIndex search
164171 let result = eitherDecode (responseBody reply) :: Either String (SearchResult Tweet)
165172 let emptyHits = fmap (hits . searchHits) result
166− emptyHits `shouldBe` Right []
173+ liftIO $
174+ emptyHits `shouldBe` Right []
167175
168−searchExpectAggs :: Search -> IO ()
176+-- searchExpectAggs :: Search -> IO ()
169177searchExpectAggs search = do
170− reply <- searchAll testServer search
178+ reply <- searchAll search
171179 let isEmpty x = return (M.null x)
172180 let result = decode (responseBody reply) :: Maybe (SearchResult Tweet)
173− (result >>= aggregations >>= isEmpty) `shouldBe` Just False
181+ liftIO $
182+ (result >>= aggregations >>= isEmpty) `shouldBe` Just False
174183
175−searchValidBucketAgg :: (BucketAggregation a, FromJSON a, Show a) => Search -> Text -> (Text -> AggregationResults -> Maybe (Bucket a)) -> IO ()
184+-- searchValidBucketAgg :: (BucketAggregation a, FromJSON a, Show a) => Search -> Text -> (Text -> AggregationResults -> Maybe (Bucket a)) -> IO ()
176185searchValidBucketAgg search aggKey extractor = do
177− reply <- searchAll testServer search
186+ reply <- searchAll search
178187 let bucketDocs = docCount . head . buckets
179188 let result = decode (responseBody reply) :: Maybe (SearchResult Tweet)
180189 let count = result >>= aggregations >>= extractor aggKey >>= \x -> return (bucketDocs x)
181− count `shouldBe` Just 1
190+ liftIO $
191+ count `shouldBe` Just 1
182192
183−searchTermsAggHint :: [ExecutionHint] -> IO ()
193+-- searchTermsAggHint :: [ExecutionHint] -> IO ()
184194searchTermsAggHint hints = do
185195 let terms hint = TermsAgg $ (mkTermsAggregation "user") { termExecutionHint = Just hint }
186196 let search hint = mkAggregateSearch Nothing $ mkAggregations "users" $ terms hint
187197 forM_ hints $ searchExpectAggs . search
188198 forM_ hints (\x -> searchValidBucketAgg (search x) "users" toTerms)
189199
190−searchTweetHighlight :: Search -> IO (Either String (Maybe HitHighlight))
200+-- searchTweetHighlight :: Search -> IO (Either String (Maybe HitHighlight))
191201searchTweetHighlight search = do
192− reply <- searchByIndex testServer testIndex search
202+ reply <- searchByIndex testIndex search
193203 let result = eitherDecode (responseBody reply) :: Either String (SearchResult Tweet)
194204 let myHighlight = fmap (hitHighlight . head . hits . searchHits) result
195205 return myHighlight
@@ -223,26 +233,27 @@ main :: IO ()
223233main = hspec $ do
224234
225235 describe "index create/delete API" $ do
226− it "creates and then deletes the requested index" $ do
236+ it "creates and then deletes the requested index" $ withTestEnv $ do
227237 -- priming state.
228238 _ <- deleteExampleIndex
229239 resp <- createExampleIndex
230240 deleteResp <- deleteExampleIndex
231− validateStatus resp 200
232− validateStatus deleteResp 200
241+ liftIO $ do
242+ validateStatus resp 200
243+ validateStatus deleteResp 200
233244
234245
235246 describe "document API" $ do
236− it "indexes, gets, and then deletes the generated document" $ do
247+ it "indexes, gets, and then deletes the generated document" $ withTestEnv $ do
237248 _ <- insertData
238− docInserted <- getDocument testServer testIndex testMapping (DocId "1")
249+ docInserted <- getDocument testIndex testMapping (DocId "1")
239250 let newTweet = eitherDecode
240251 (responseBody docInserted) :: Either String (EsResult Tweet)
241− fmap _source newTweet `shouldBe` Right exampleTweet
252+ liftIO $ (fmap _source newTweet `shouldBe` Right exampleTweet)
242253
243254
244255 describe "bulk API" $ do
245− it "inserts all documents we request" $ do
256+ it "inserts all documents we request" $ withTestEnv $ do
246257 _ <- insertData
247258 let firstTest = BulkTest "blah"
248259 let secondTest = BulkTest "bloo"
@@ -251,49 +262,54 @@ main = hspec $ do
251262 let secondDoc = BulkCreate testIndex
252263 testMapping (DocId "3") (toJSON secondTest)
253264 let stream = V.fromList [firstDoc, secondDoc]
254− _ <- bulk testServer stream
255− _ <- refreshIndex testServer testIndex
256− fDoc <- getDocument testServer testIndex testMapping (DocId "2")
257− sDoc <- getDocument testServer testIndex testMapping (DocId "3")
265+ _ <- bulk stream
266+ _ <- refreshIndex testIndex
267+ fDoc <- getDocument testIndex testMapping (DocId "2")
268+ sDoc <- getDocument testIndex testMapping (DocId "3")
258269 let maybeFirst = eitherDecode $ responseBody fDoc :: Either String (EsResult BulkTest)
259270 let maybeSecond = eitherDecode $ responseBody sDoc :: Either String (EsResult BulkTest)
260− fmap _source maybeFirst `shouldBe` Right firstTest
261− fmap _source maybeSecond `shouldBe` Right secondTest
271+ liftIO $ do
272+ fmap _source maybeFirst `shouldBe` Right firstTest
273+ fmap _source maybeSecond `shouldBe` Right secondTest
262274
263275
264276 describe "query API" $ do
265− it "returns document for term query and identity filter" $ do
277+ it "returns document for term query and identity filter" $ withTestEnv $ do
266278 _ <- insertData
267279 let query = TermQuery (Term "user" "bitemyapp") Nothing
268280 let filter = IdentityFilter <&&> IdentityFilter
269281 let search = mkSearch (Just query) (Just filter)
270282 myTweet <- searchTweet search
271− myTweet `shouldBe` Right exampleTweet
283+ liftIO $
284+ myTweet `shouldBe` Right exampleTweet
272285
273− it "returns document for terms query and identity filter" $ do
286+ it "returns document for terms query and identity filter" $ withTestEnv $ do
274287 _ <- insertData
275288 let query = TermsQuery (NE.fromList [(Term "user" "bitemyapp")])
276289 let filter = IdentityFilter <&&> IdentityFilter
277290 let search = mkSearch (Just query) (Just filter)
278291 myTweet <- searchTweet search
279− myTweet `shouldBe` Right exampleTweet
292+ liftIO $
293+ myTweet `shouldBe` Right exampleTweet
280294
281− it "returns document for match query" $ do
295+ it "returns document for match query" $ withTestEnv $ do
282296 _ <- insertData
283297 let query = QueryMatchQuery $ mkMatchQuery (FieldName "user") (QueryString "bitemyapp")
284298 let search = mkSearch (Just query) Nothing
285299 myTweet <- searchTweet search
286− myTweet `shouldBe` Right exampleTweet
300+ liftIO $
301+ myTweet `shouldBe` Right exampleTweet
287302
288− it "returns document for multi-match query" $ do
303+ it "returns document for multi-match query" $ withTestEnv $ do
289304 _ <- insertData
290305 let fields = [FieldName "user", FieldName "message"]
291306 let query = QueryMultiMatchQuery $ mkMultiMatchQuery fields (QueryString "bitemyapp")
292307 let search = mkSearch (Just query) Nothing
293308 myTweet <- searchTweet search
294− myTweet `shouldBe` Right exampleTweet
309+ liftIO $
310+ myTweet `shouldBe` Right exampleTweet
295311
296− it "returns document for bool query" $ do
312+ it "returns document for bool query" $ withTestEnv $ do
297313 _ <- insertData
298314 let innerQuery = QueryMatchQuery $
299315 mkMatchQuery (FieldName "user") (QueryString "bitemyapp")
@@ -301,18 +317,20 @@ main = hspec $ do
301317 mkBoolQuery [innerQuery] [] []
302318 let search = mkSearch (Just query) Nothing
303319 myTweet <- searchTweet search
304− myTweet `shouldBe` Right exampleTweet
320+ liftIO $
321+ myTweet `shouldBe` Right exampleTweet
305322
306− it "returns document for boosting query" $ do
323+ it "returns document for boosting query" $ withTestEnv $ do
307324 _ <- insertData
308325 let posQuery = QueryMatchQuery $ mkMatchQuery (FieldName "user") (QueryString "bitemyapp")
309326 let negQuery = QueryMatchQuery $ mkMatchQuery (FieldName "user") (QueryString "notmyapp")
310327 let query = QueryBoostingQuery $ BoostingQuery posQuery negQuery (Boost 0.2)
311328 let search = mkSearch (Just query) Nothing
312329 myTweet <- searchTweet search
313− myTweet `shouldBe` Right exampleTweet
330+ liftIO $
331+ myTweet `shouldBe` Right exampleTweet
314332
315− it "returns document for common terms query" $ do
333+ it "returns document for common terms query" $ withTestEnv $ do
316334 _ <- insertData
317335 let query = QueryCommonTermsQuery $
318336 CommonTermsQuery (FieldName "user")
@@ -321,55 +339,61 @@ main = hspec $ do
321339 Or Or Nothing Nothing Nothing Nothing
322340 let search = mkSearch (Just query) Nothing
323341 myTweet <- searchTweet search
324− myTweet `shouldBe` Right exampleTweet
342+ liftIO $
343+ myTweet `shouldBe` Right exampleTweet
325344
326345
327346 describe "sorting" $ do
328− it "returns documents in the right order" $ do
347+ it "returns documents in the right order" $ withTestEnv $ do
329348 _ <- insertData
330349 _ <- insertOther
331350 let sortSpec = DefaultSortSpec $ mkSort (FieldName "age") Ascending
332351 let search = Search Nothing
333352 (Just IdentityFilter) (Just [sortSpec]) Nothing Nothing
334353 False 0 10
335− reply <- searchByIndex testServer testIndex search
354+ reply <- searchByIndex testIndex search
336355 let result = eitherDecode (responseBody reply) :: Either String (SearchResult Tweet)
337356 let myTweet = fmap (hitSource . head . hits . searchHits) result
338− myTweet `shouldBe` Right otherTweet
357+ liftIO $
358+ myTweet `shouldBe` Right otherTweet
339359
340360
341361 describe "filtering API" $ do
342− it "returns document for composed boolmatch and identity" $ do
362+ it "returns document for composed boolmatch and identity" $ withTestEnv $ do
343363 _ <- insertData
344364 let queryFilter = BoolFilter (MustMatch (Term "user" "bitemyapp") False)
345365 <&&> IdentityFilter
346366 let search = mkSearch Nothing (Just queryFilter)
347367 myTweet <- searchTweet search
348− myTweet `shouldBe` Right exampleTweet
368+ liftIO $
369+ myTweet `shouldBe` Right exampleTweet
349370
350− it "returns document for term filter" $ do
371+ it "returns document for term filter" $ withTestEnv $ do
351372 _ <- insertData
352373 let termFilter = TermFilter (Term "user" "bitemyapp") False
353374 let search = mkSearch Nothing (Just termFilter)
354375 myTweet <- searchTweet search
355− myTweet `shouldBe` Right exampleTweet
376+ liftIO $
377+ myTweet `shouldBe` Right exampleTweet
356378
357− it "returns document for existential filter" $ do
379+ it "returns document for existential filter" $ withTestEnv $ do
358380 _ <- insertData
359381 let search = mkSearch Nothing (Just (ExistsFilter (FieldName "user")))
360382 myTweet <- searchTweet search
361− myTweet `shouldBe` Right exampleTweet
383+ liftIO $
384+ myTweet `shouldBe` Right exampleTweet
362385
363− it "returns document for geo boundingbox filter" $ do
386+ it "returns document for geo boundingbox filter" $ withTestEnv $ do
364387 _ <- insertData
365388 let box = GeoBoundingBox (LatLon 40.73 (-74.1)) (LatLon 40.10 (-71.12))
366389 let bbConstraint = GeoBoundingBoxConstraint (FieldName "tweet.location") box False GeoFilterMemory
367390 let geoFilter = GeoBoundingBoxFilter bbConstraint
368391 let search = mkSearch Nothing (Just geoFilter)
369392 myTweet <- searchTweet search
370− myTweet `shouldBe` Right exampleTweet
393+ liftIO $
394+ myTweet `shouldBe` Right exampleTweet
371395
372− it "doesn't return document for nonsensical boundingbox filter" $ do
396+ it "doesn't return document for nonsensical boundingbox filter" $ withTestEnv $ do
373397 _ <- insertData
374398 let box = GeoBoundingBox (LatLon 0.73 (-4.1)) (LatLon 0.10 (-1.12))
375399 let bbConstraint = GeoBoundingBoxConstraint (FieldName "tweet.location") box False GeoFilterMemory
@@ -377,7 +401,7 @@ main = hspec $ do
377401 let search = mkSearch Nothing (Just geoFilter)
378402 searchExpectNoResults search
379403
380− it "returns document for geo distance filter" $ do
404+ it "returns document for geo distance filter" $ withTestEnv $ do
381405 _ <- insertData
382406 let geoPoint = GeoPoint (FieldName "tweet.location") (LatLon 40.12 (-71.34))
383407 let distance = Distance 10.0 Miles
@@ -385,18 +409,20 @@ main = hspec $ do
385409 let geoFilter = GeoDistanceFilter geoPoint distance SloppyArc optimizeBbox False
386410 let search = mkSearch Nothing (Just geoFilter)
387411 myTweet <- searchTweet search
388− myTweet `shouldBe` Right exampleTweet
412+ liftIO $
413+ myTweet `shouldBe` Right exampleTweet
389414
390− it "returns document for geo distance range filter" $ do
415+ it "returns document for geo distance range filter" $ withTestEnv $ do
391416 _ <- insertData
392417 let geoPoint = GeoPoint (FieldName "tweet.location") (LatLon 40.12 (-71.34))
393418 let distanceRange = DistanceRange (Distance 0.0 Miles) (Distance 10.0 Miles)
394419 let geoFilter = GeoDistanceRangeFilter geoPoint distanceRange
395420 let search = mkSearch Nothing (Just geoFilter)
396421 myTweet <- searchTweet search
397− myTweet `shouldBe` Right exampleTweet
422+ liftIO $
423+ myTweet `shouldBe` Right exampleTweet
398424
399− it "doesn't return document for wild geo distance range filter" $ do
425+ it "doesn't return document for wild geo distance range filter" $ withTestEnv $ do
400426 _ <- insertData
401427 let geoPoint = GeoPoint (FieldName "tweet.location") (LatLon 40.12 (-71.34))
402428 let distanceRange = DistanceRange (Distance 100.0 Miles) (Distance 1000.0 Miles)
@@ -404,7 +430,7 @@ main = hspec $ do
404430 let search = mkSearch Nothing (Just geoFilter)
405431 searchExpectNoResults search
406432
407− it "returns document for geo polygon filter" $ do
433+ it "returns document for geo polygon filter" $ withTestEnv $ do
408434 _ <- insertData
409435 let points = [LatLon 40.0 (-70.00),
410436 LatLon 40.0 (-72.00),
@@ -413,9 +439,10 @@ main = hspec $ do
413439 let geoFilter = GeoPolygonFilter (FieldName "tweet.location") points
414440 let search = mkSearch Nothing (Just geoFilter)
415441 myTweet <- searchTweet search
416− myTweet `shouldBe` Right exampleTweet
442+ liftIO $
443+ myTweet `shouldBe` Right exampleTweet
417444
418− it "doesn't return document for bad geo polygon filter" $ do
445+ it "doesn't return document for bad geo polygon filter" $ withTestEnv $ do
419446 _ <- insertData
420447 let points = [LatLon 40.0 (-70.00),
421448 LatLon 40.0 (-71.00),
@@ -425,23 +452,25 @@ main = hspec $ do
425452 let search = mkSearch Nothing (Just geoFilter)
426453 searchExpectNoResults search
427454
428− it "returns document for ids filter" $ do
455+ it "returns document for ids filter" $ withTestEnv $ do
429456 _ <- insertData
430457 let filter = IdsFilter (MappingName "tweet") [DocId "1"]
431458 let search = mkSearch Nothing (Just filter)
432459 myTweet <- searchTweet search
433− myTweet `shouldBe` Right exampleTweet
460+ liftIO $
461+ myTweet `shouldBe` Right exampleTweet
434462
435− it "returns document for Double range filter" $ do
463+ it "returns document for Double range filter" $ withTestEnv $ do
436464 _ <- insertData
437465 let filter = RangeFilter (FieldName "age")
438466 (RangeDoubleGtLt (GreaterThan 1000.0) (LessThan 100000.0))
439467 RangeExecutionIndex False
440468 let search = mkSearch Nothing (Just filter)
441469 myTweet <- searchTweet search
442− myTweet `shouldBe` Right exampleTweet
470+ liftIO $
471+ myTweet `shouldBe` Right exampleTweet
443472
444− it "returns document for UTCTime date filter" $ do
473+ it "returns document for UTCTime date filter" $ withTestEnv $ do
445474 _ <- insertData
446475 let filter = RangeFilter (FieldName "postDate")
447476 (RangeDateGtLt
@@ -454,18 +483,20 @@ main = hspec $ do
454483 RangeExecutionIndex False
455484 let search = mkSearch Nothing (Just filter)
456485 myTweet <- searchTweet search
457− myTweet `shouldBe` Right exampleTweet
486+ liftIO $
487+ myTweet `shouldBe` Right exampleTweet
458488
459489
460− it "returns document for regexp filter" $ do
490+ it "returns document for regexp filter" $ withTestEnv $ do
461491 _ <- insertData
462492 let filter = RegexpFilter (FieldName "user") (Regexp "bite.*app")
463493 AllRegexpFlags (CacheName "test") False (CacheKey "key")
464494 let search = mkSearch Nothing (Just filter)
465495 myTweet <- searchTweet search
466− myTweet `shouldBe` Right exampleTweet
496+ liftIO $
497+ myTweet `shouldBe` Right exampleTweet
467498
468− it "doesn't return document for non-matching regexp filter" $ do
499+ it "doesn't return document for non-matching regexp filter" $ withTestEnv $ do
469500 _ <- insertData
470501 let filter = RegexpFilter (FieldName "user")
471502 (Regexp "boy") AllRegexpFlags
@@ -474,40 +505,40 @@ main = hspec $ do
474505 searchExpectNoResults search
475506
476507 describe "Aggregation API" $ do
477− it "returns term aggregation results" $ do
508+ it "returns term aggregation results" $ withTestEnv $ do
478509 _ <- insertData
479510 let terms = TermsAgg $ mkTermsAggregation "user"
480511 let search = mkAggregateSearch Nothing $ mkAggregations "users" terms
481512 searchExpectAggs search
482513 searchValidBucketAgg search "users" toTerms
483514
484− it "can give collection hint parameters to term aggregations" $ when' (atleast es13) $ do
515+ it "can give collection hint parameters to term aggregations" $ when' (atleast es13) $ withTestEnv $ do
485516 _ <- insertData
486517 let terms = TermsAgg $ (mkTermsAggregation "user") { termCollectMode = Just BreadthFirst }
487518 let search = mkAggregateSearch Nothing $ mkAggregations "users" terms
488519 searchExpectAggs search
489520 searchValidBucketAgg search "users" toTerms
490521
491− it "can give execution hint paramters to term aggregations" $ when' (atmost es11) $ do
522+ it "can give execution hint paramters to term aggregations" $ when' (atmost es11) $ withTestEnv $ do
492523 _ <- insertData
493524 searchTermsAggHint [Map, Ordinals]
494525
495− it "can give execution hint paramters to term aggregations" $ when' (is es12) $ do
526+ it "can give execution hint paramters to term aggregations" $ when' (is es12) $ withTestEnv $ do
496527 _ <- insertData
497528 searchTermsAggHint [GlobalOrdinals, GlobalOrdinalsHash, GlobalOrdinalsLowCardinality, Map, Ordinals]
498529
499− it "can give execution hint paramters to term aggregations" $ when' (atleast es12) $ do
530+ it "can give execution hint paramters to term aggregations" $ when' (atleast es12) $ withTestEnv $ do
500531 _ <- insertData
501532 searchTermsAggHint [GlobalOrdinals, GlobalOrdinalsHash, GlobalOrdinalsLowCardinality, Map]
502533
503− it "returns date histogram aggregation results" $ do
534+ it "returns date histogram aggregation results" $ withTestEnv $ do
504535 _ <- insertData
505536 let histogram = DateHistogramAgg $ mkDateHistogram (FieldName "postDate") Minute
506537 let search = mkAggregateSearch Nothing (mkAggregations "byDate" histogram)
507538 searchExpectAggs search
508539 searchValidBucketAgg search "byDate" toDateHistogram
509540
510− it "returns date histogram using fractional date" $ do
541+ it "returns date histogram using fractional date" $ withTestEnv $ do
511542 _ <- insertData
512543 let periods = [Year, Quarter, Month, Week, Day, Hour, Minute, Second]
513544 let fractionals = map (FractionalInterval 1.5) [Weeks, Days, Hours, Minutes, Seconds]
@@ -521,7 +552,7 @@ main = hspec $ do
521552
522553 describe "Highlights API" $ do
523554
524− it "returns highlight from query when there should be one" $ do
555+ it "returns highlight from query when there should be one" $ withTestEnv $ do
525556 _ <- insertData
526557 _ <- insertOther
527558 let query = QueryMatchQuery $ mkMatchQuery (FieldName "_all") (QueryString "haskell")
@@ -529,9 +560,10 @@ main = hspec $ do
529560
530561 let search = mkHighlightSearch (Just query) testHighlight
531562 myHighlight <- searchTweetHighlight search
532− myHighlight `shouldBe` Right (Just (M.fromList [("message",["Use <em>haskell</em>!"])]))
563+ liftIO $
564+ myHighlight `shouldBe` Right (Just (M.fromList [("message",["Use <em>haskell</em>!"])]))
533565
534− it "doesn't return highlight from a query when it shouldn't" $ do
566+ it "doesn't return highlight from a query when it shouldn't" $ withTestEnv $ do
535567 _ <- insertData
536568 _ <- insertOther
537569 let query = QueryMatchQuery $ mkMatchQuery (FieldName "_all") (QueryString "haskell")
@@ -539,7 +571,8 @@ main = hspec $ do
539571
540572 let search = mkHighlightSearch (Just query) testHighlight
541573 myHighlight <- searchTweetHighlight search
542− myHighlight `shouldBe` Right Nothing
574+ liftIO $
575+ myHighlight `shouldBe` Right Nothing
543576
544577
545578