@@ -0,0 +1,2341 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# LANGUAGE OverloadedStrings #-}
5+{-# LANGUAGE RecordWildCards #-}
6+{-# LANGUAGE UndecidableInstances #-}
7+
8+module Database.V1.Bloodhound.Internal.Client where
9+
10+import Bloodhound.Import
11+
12+import Control.Applicative as A
13+import qualified Data.HashMap.Strict as HM
14+import Data.Text (Text)
15+import qualified Data.Text as T
16+import qualified Data.Traversable as DT
17+import qualified Data.Vector as V
18+import qualified Data.Version as Vers
19+import GHC.Enum
20+import Network.HTTP.Client
21+import qualified Text.ParserCombinators.ReadP as RP
22+import Text.Read (Read (..))
23+import qualified Text.Read as TR
24+
25+import Database.V1.Bloodhound.Internal.Newtypes
26+import Database.V1.Bloodhound.Internal.Query
27+import Database.V1.Bloodhound.Internal.StringlyTyped
28+
29+
30+{-| Common environment for Elasticsearch calls. Connections will be
31+ pipelined according to the provided HTTP connection manager.
32+-}
33+data BHEnv = BHEnv { bhServer :: Server
34+ , bhManager :: Manager
35+ , bhRequestHook :: Request -> IO Request
36+ -- ^ Low-level hook that is run before every request is sent. Used to implement custom authentication strategies. Defaults to 'return' with 'mkBHEnv'.
37+ }
38+
39+instance (Functor m, Applicative m, MonadIO m) => MonadBH (ReaderT BHEnv m) where
40+ getBHEnv = ask
41+
42+{-| 'Server' is used with the client functions to point at the ES instance
43+-}
44+newtype Server = Server Text deriving (Eq, Show, FromJSON)
45+
46+{-| All API calls to Elasticsearch operate within
47+ MonadBH
48+ . The idea is that it can be easily embedded in your
49+ own monad transformer stack. A default instance for a ReaderT and
50+ alias 'BH' is provided for the simple case.
51+-}
52+class (Functor m, A.Applicative m, MonadIO m) => MonadBH m where
53+ getBHEnv :: m BHEnv
54+
55+-- $setup
56+-- >>> :set -XOverloadedStrings
57+-- >>> import Data.Aeson
58+-- >>> import Database.V1.Bloodhound
59+-- >>> let testServer = (Server "http://localhost:9200")
60+-- >>> let testIndex = IndexName "twitter"
61+-- >>> let testMapping = MappingName "tweet"
62+-- >>> let defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
63+
64+-- defaultIndexSettings is exported by Database.Bloodhound as well
65+-- no trailing slashes in servers, library handles building the path.
66+
67+-- | Create a 'BHEnv' with all optional fields defaulted. HTTP hook
68+-- will be a noop. You can use the exported fields to customize it further, e.g.:
69+--
70+-- >> (mkBHEnv myServer myManager) { bhRequestHook = customHook }
71+mkBHEnv :: Server -> Manager -> BHEnv
72+mkBHEnv s m = BHEnv s m return
73+
74+newtype BH m a = BH {
75+ unBH :: ReaderT BHEnv m a
76+ } deriving ( Functor
77+ , A.Applicative
78+ , Monad
79+ , MonadIO
80+ , MonadState s
81+ , MonadWriter w
82+ , MonadError e
83+ , Alternative
84+ , MonadPlus
85+ , MonadFix
86+ , MonadThrow
87+ , MonadCatch
88+ , MonadMask)
89+
90+instance MonadTrans BH where
91+ lift = BH . lift
92+
93+instance (MonadReader r m) => MonadReader r (BH m) where
94+ ask = lift ask
95+ local f (BH (ReaderT m)) = BH $ ReaderT $ \r ->
96+ local f (m r)
97+
98+instance (Functor m, Applicative m, MonadIO m) => MonadBH (BH m) where
99+ getBHEnv = BH getBHEnv
100+
101+runBH :: BHEnv -> BH m a -> m a
102+runBH e f = runReaderT (unBH f) e
103+
104+{-| 'Version' is embedded in 'Status' -}
105+data Version = Version { number :: VersionNumber
106+ , build_hash :: BuildHash
107+ , build_timestamp :: UTCTime
108+ , build_snapshot :: Bool
109+ , lucene_version :: VersionNumber } deriving (Eq, Show)
110+
111+instance ToJSON Version where
112+ toJSON Version {..} = object ["number" .= number
113+ ,"build_hash" .= build_hash
114+ ,"build_timestamp" .= build_timestamp
115+ ,"build_snapshot" .= build_snapshot
116+ ,"lucene_version" .= lucene_version]
117+
118+instance FromJSON Version where
119+ parseJSON = withObject "Version" parse
120+ where parse o = Version
121+ <$> o .: "number"
122+ <*> o .: "build_hash"
123+ <*> o .: "build_timestamp"
124+ <*> o .: "build_snapshot"
125+ <*> o .: "lucene_version"
126+
127+-- | Traditional software versioning number
128+newtype VersionNumber = VersionNumber { versionNumber :: Vers.Version
129+ } deriving (Eq, Show, Ord)
130+
131+{-| 'Status' is a data type for describing the JSON body returned by
132+ Elasticsearch when you query its status. This was deprecated in 1.2.0.
133+
134+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-status.html#indices-status>
135+-}
136+
137+data Status = Status { ok :: Maybe Bool
138+ , status :: Int
139+ , name :: Text
140+ , version :: Version
141+ , tagline :: Text } deriving (Eq, Show)
142+
143+instance FromJSON Status where
144+ parseJSON (Object v) = Status <$>
145+ v .:? "ok" <*>
146+ (v .:? "status" .!= 200) <*>
147+ v .: "name" <*>
148+ v .: "version" <*>
149+ v .: "tagline"
150+ parseJSON _ = empty
151+
152+{-| 'IndexSettings' is used to configure the shards and replicas when you create
153+ an Elasticsearch Index.
154+
155+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html>
156+-}
157+
158+data IndexSettings =
159+ IndexSettings { indexShards :: ShardCount
160+ , indexReplicas :: ReplicaCount } deriving (Eq, Show)
161+
162+{-| 'defaultIndexSettings' is an 'IndexSettings' with 3 shards and 2 replicas. -}
163+defaultIndexSettings :: IndexSettings
164+defaultIndexSettings = IndexSettings (ShardCount 3) (ReplicaCount 2)
165+
166+
167+{-| 'IndexOptimizationSettings' is used to configure index optimization. See
168+ <https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-optimize.html>
169+ for more info.
170+-}
171+data IndexOptimizationSettings =
172+ IndexOptimizationSettings { maxNumSegments :: Maybe Int
173+ -- ^ Number of segments to optimize to. 1 will fully optimize the index. If omitted, the default behavior is to only optimize if the server deems it necessary.
174+ , onlyExpungeDeletes :: Bool
175+ -- ^ Should the optimize process only expunge segments with deletes in them? If the purpose of the optimization is to free disk space, this should be set to True.
176+ , flushAfterOptimize :: Bool
177+ -- ^ Should a flush be performed after the optimize.
178+ } deriving (Eq, Show)
179+
180+
181+{-| 'defaultIndexOptimizationSettings' implements the default settings that
182+ Elasticsearch uses for index optimization. 'maxNumSegments' is Nothing,
183+ 'onlyExpungeDeletes' is False, and flushAfterOptimize is True.
184+-}
185+defaultIndexOptimizationSettings :: IndexOptimizationSettings
186+defaultIndexOptimizationSettings = IndexOptimizationSettings Nothing False True
187+
188+{-| 'UpdatableIndexSetting' are settings which may be updated after an index is created.
189+
190+ <https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-update-settings.html>
191+-}
192+data UpdatableIndexSetting = NumberOfReplicas ReplicaCount
193+ -- ^ The number of replicas each shard has.
194+ | AutoExpandReplicas ReplicaBounds
195+ | BlocksReadOnly Bool
196+ -- ^ Set to True to have the index read only. False to allow writes and metadata changes.
197+ | BlocksRead Bool
198+ -- ^ Set to True to disable read operations against the index.
199+ | BlocksWrite Bool
200+ -- ^ Set to True to disable write operations against the index.
201+ | BlocksMetaData Bool
202+ -- ^ Set to True to disable metadata operations against the index.
203+ | RefreshInterval NominalDiffTime
204+ -- ^ The async refresh interval of a shard
205+ | IndexConcurrency Int
206+ | FailOnMergeFailure Bool
207+ | TranslogFlushThresholdOps Int
208+ -- ^ When to flush on operations.
209+ | TranslogFlushThresholdSize Bytes
210+ -- ^ When to flush based on translog (bytes) size.
211+ | TranslogFlushThresholdPeriod NominalDiffTime
212+ -- ^ When to flush based on a period of not flushing.
213+ | TranslogDisableFlush Bool
214+ -- ^ Disables flushing. Note, should be set for a short interval and then enabled.
215+ | CacheFilterMaxSize (Maybe Bytes)
216+ -- ^ The maximum size of filter cache (per segment in shard).
217+ | CacheFilterExpire (Maybe NominalDiffTime)
218+ -- ^ The expire after access time for filter cache.
219+ | GatewaySnapshotInterval NominalDiffTime
220+ -- ^ The gateway snapshot interval (only applies to shared gateways).
221+ | RoutingAllocationInclude (NonEmpty NodeAttrFilter)
222+ -- ^ A node matching any rule will be allowed to host shards from the index.
223+ | RoutingAllocationExclude (NonEmpty NodeAttrFilter)
224+ -- ^ A node matching any rule will NOT be allowed to host shards from the index.
225+ | RoutingAllocationRequire (NonEmpty NodeAttrFilter)
226+ -- ^ Only nodes matching all rules will be allowed to host shards from the index.
227+ | RoutingAllocationEnable AllocationPolicy
228+ -- ^ Enables shard allocation for a specific index.
229+ | RoutingAllocationShardsPerNode ShardCount
230+ -- ^ Controls the total number of shards (replicas and primaries) allowed to be allocated on a single node.
231+ | RecoveryInitialShards InitialShardCount
232+ -- ^ When using local gateway a particular shard is recovered only if there can be allocated quorum shards in the cluster.
233+ | GCDeletes NominalDiffTime
234+ | TTLDisablePurge Bool
235+ -- ^ Disables temporarily the purge of expired docs.
236+ | TranslogFSType FSType
237+ | IndexCompoundFormat CompoundFormat
238+ | IndexCompoundOnFlush Bool
239+ | WarmerEnabled Bool
240+ deriving (Eq, Show)
241+
242+data AllocationPolicy = AllocAll
243+ -- ^ Allows shard allocation for all shards.
244+ | AllocPrimaries
245+ -- ^ Allows shard allocation only for primary shards.
246+ | AllocNewPrimaries
247+ -- ^ Allows shard allocation only for primary shards for new indices.
248+ | AllocNone
249+ -- ^ No shard allocation is allowed
250+ deriving (Eq, Show)
251+
252+data ReplicaBounds = ReplicasBounded Int Int
253+ | ReplicasLowerBounded Int
254+ | ReplicasUnbounded
255+ deriving (Eq, Show)
256+
257+-- | A measure of bytes used for various configurations. You may want
258+-- to use smart constructors like 'gigabytes' for larger values.
259+--
260+-- >>> gigabytes 9
261+-- Bytes 9000000000
262+--
263+-- >>> megabytes 9
264+-- Bytes 9000000
265+--
266+-- >>> kilobytes 9
267+-- Bytes 9000
268+newtype Bytes = Bytes Int deriving (Eq, Show, Ord, ToJSON, FromJSON)
269+
270+gigabytes :: Int -> Bytes
271+gigabytes n = megabytes (1000 * n)
272+
273+
274+megabytes :: Int -> Bytes
275+megabytes n = kilobytes (1000 * n)
276+
277+
278+kilobytes :: Int -> Bytes
279+kilobytes n = Bytes (1000 * n)
280+
281+data Interval = Year
282+ | Quarter
283+ | Month
284+ | Week
285+ | Day
286+ | Hour
287+ | Minute
288+ | Second
289+ | FractionalInterval Float TimeInterval deriving (Eq, Show)
290+
291+data TimeInterval = Weeks
292+ | Days
293+ | Hours
294+ | Minutes
295+ | Seconds deriving Eq
296+
297+instance Show TimeInterval where
298+ show Weeks = "w"
299+ show Days = "d"
300+ show Hours = "h"
301+ show Minutes = "m"
302+ show Seconds = "s"
303+
304+instance Read TimeInterval where
305+ readPrec = f =<< TR.get
306+ where
307+ f 'w' = return Weeks
308+ f 'd' = return Days
309+ f 'h' = return Hours
310+ f 'm' = return Minutes
311+ f 's' = return Seconds
312+ f _ = fail "TimeInterval expected one of w, d, h, m, s"
313+
314+-- | Typically a 7 character hex string.
315+newtype BuildHash = BuildHash { buildHash :: Text }
316+ deriving (Eq, Ord, Show, FromJSON, ToJSON)
317+
318+data NodeAttrFilter = NodeAttrFilter
319+ { nodeAttrFilterName :: NodeAttrName
320+ , nodeAttrFilterValues :: NonEmpty Text }
321+ deriving (Eq, Ord, Show)
322+
323+newtype NodeAttrName = NodeAttrName Text deriving (Eq, Ord, Show)
324+
325+data InitialShardCount = QuorumShards
326+ | QuorumMinus1Shards
327+ | FullShards
328+ | FullMinus1Shards
329+ | ExplicitShards Int
330+ deriving (Eq, Show)
331+
332+instance FromJSON InitialShardCount where
333+ parseJSON v = withText "InitialShardCount" parseText v
334+ <|> ExplicitShards <$> parseJSON v
335+ where parseText "quorum" = pure QuorumShards
336+ parseText "quorum-1" = pure QuorumMinus1Shards
337+ parseText "full" = pure FullShards
338+ parseText "full-1" = pure FullMinus1Shards
339+ parseText _ = mzero
340+
341+instance ToJSON InitialShardCount where
342+ toJSON QuorumShards = String "quorum"
343+ toJSON QuorumMinus1Shards = String "quorum-1"
344+ toJSON FullShards = String "full"
345+ toJSON FullMinus1Shards = String "full-1"
346+ toJSON (ExplicitShards x) = toJSON x
347+
348+data FSType = FSSimple
349+ | FSBuffered deriving (Eq, Show)
350+
351+instance ToJSON FSType where
352+ toJSON FSSimple = "simple"
353+ toJSON FSBuffered = "buffered"
354+
355+instance FromJSON FSType where
356+ parseJSON = withText "FSType" parse
357+ where parse "simple" = pure FSSimple
358+ parse "buffered" = pure FSBuffered
359+ parse t = fail ("Invalid FSType: " <> show t)
360+
361+data CompoundFormat = CompoundFileFormat Bool
362+ | MergeSegmentVsTotalIndex Double
363+ -- ^ percentage between 0 and 1 where 0 is false, 1 is true
364+ deriving (Eq, Show)
365+
366+attrFilterJSON :: NonEmpty NodeAttrFilter -> Value
367+attrFilterJSON fs = object [ n .= T.intercalate "," (toList vs)
368+ | NodeAttrFilter (NodeAttrName n) vs <- toList fs]
369+
370+parseAttrFilter :: Value -> Parser (NonEmpty NodeAttrFilter)
371+parseAttrFilter = withObject "NonEmpty NodeAttrFilter" parse
372+ where parse o = case HM.toList o of
373+ [] -> fail "Expected non-empty list of NodeAttrFilters"
374+ x:xs -> DT.mapM (uncurry parse') (x :| xs)
375+ parse' n = withText "Text" $ \t ->
376+ case T.splitOn "," t of
377+ fv:fvs -> return (NodeAttrFilter (NodeAttrName n) (fv :| fvs))
378+ [] -> fail "Expected non-empty list of filter values"
379+
380+newtype NominalDiffTimeJSON = NominalDiffTimeJSON { ndtJSON :: NominalDiffTime }
381+
382+data IndexSettingsSummary = IndexSettingsSummary { sSummaryIndexName :: IndexName
383+ , sSummaryFixedSettings :: IndexSettings
384+ , sSummaryUpdateable :: [UpdatableIndexSetting]}
385+ deriving (Eq, Show)
386+
387+parseSettings :: Object -> Parser [UpdatableIndexSetting]
388+parseSettings o = do
389+ o' <- o .: "index"
390+ -- slice the index object into singleton hashmaps and try to parse each
391+ parses <- forM (HM.toList o') $ \(k, v) -> do
392+ -- blocks are now nested into the "index" key, which is not how they're serialized
393+ let atRoot = Object (HM.singleton k v)
394+ let atIndex = Object (HM.singleton "index" atRoot)
395+ optional (parseJSON atRoot <|> parseJSON atIndex)
396+ return (catMaybes parses)
397+
398+{-| 'OpenCloseIndex' is a sum type for opening and closing indices.
399+
400+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-open-close.html>
401+-}
402+data OpenCloseIndex = OpenIndex | CloseIndex deriving (Eq, Show)
403+
404+data FieldType = GeoPointType
405+ | GeoShapeType
406+ | FloatType
407+ | IntegerType
408+ | LongType
409+ | ShortType
410+ | ByteType deriving (Eq, Show)
411+
412+data FieldDefinition =
413+ FieldDefinition { fieldType :: FieldType } deriving (Eq, Show)
414+
415+{-| An 'IndexTemplate' defines a template that will automatically be
416+ applied to new indices created. The templates include both
417+ 'IndexSettings' and mappings, and a simple 'TemplatePattern' that
418+ controls if the template will be applied to the index created.
419+ Specify mappings as follows: @[toJSON TweetMapping, ...]@
420+
421+ https://www.elastic.co/guide/en/elasticsearch/reference/1.7/indices-templates.html
422+-}
423+data IndexTemplate =
424+ IndexTemplate { templatePattern :: TemplatePattern
425+ , templateSettings :: Maybe IndexSettings
426+ , templateMappings :: [Value]
427+ }
428+
429+data MappingField =
430+ MappingField { mappingFieldName :: FieldName
431+ , fieldDefinition :: FieldDefinition } deriving (Eq, Show)
432+
433+{-| Support for type reification of 'Mapping's is currently incomplete, for
434+ now the mapping API verbiage expects a 'ToJSON'able blob.
435+
436+ Indexes have mappings, mappings are schemas for the documents contained in the
437+ index. I'd recommend having only one mapping per index, always having a mapping,
438+ and keeping different kinds of documents separated if possible.
439+-}
440+data Mapping = Mapping { typeName :: TypeName
441+ , mappingFields :: [MappingField] } deriving (Eq, Show)
442+
443+{-| 'BulkOperation' is a sum type for expressing the four kinds of bulk
444+ operation index, create, delete, and update. 'BulkIndex' behaves like an
445+ "upsert", 'BulkCreate' will fail if a document already exists at the DocId.
446+
447+ <http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-bulk.html#docs-bulk>
448+-}
449+data BulkOperation =
450+ BulkIndex IndexName MappingName DocId Value
451+ | BulkCreate IndexName MappingName DocId Value
452+ | BulkDelete IndexName MappingName DocId
453+ | BulkUpdate IndexName MappingName DocId Value deriving (Eq, Show)
454+
455+{-| 'EsResult' describes the standard wrapper JSON document that you see in
456+ successful Elasticsearch lookups or lookups that couldn't find the document.
457+-}
458+data EsResult a = EsResult { _index :: Text
459+ , _type :: Text
460+ , _id :: Text
461+ , foundResult :: Maybe (EsResultFound a)} deriving (Eq, Show)
462+
463+instance (FromJSON a) => FromJSON (EsResult a) where
464+ parseJSON jsonVal@(Object v) = do
465+ found <- v .:? "found" .!= False
466+ fr <- if found
467+ then parseJSON jsonVal
468+ else return Nothing
469+ EsResult <$> v .: "_index" <*>
470+ v .: "_type" <*>
471+ v .: "_id" <*>
472+ pure fr
473+ parseJSON _ = empty
474+
475+{-| 'EsResultFound' contains the document and its metadata inside of an
476+ 'EsResult' when the document was successfully found.
477+-}
478+data EsResultFound a = EsResultFound { _version :: DocVersion
479+ , _source :: a } deriving (Eq, Show)
480+
481+instance (FromJSON a) => FromJSON (EsResultFound a) where
482+ parseJSON (Object v) = EsResultFound <$>
483+ v .: "_version" <*>
484+ v .: "_source"
485+ parseJSON _ = empty
486+
487+{-| 'EsError' is the generic type that will be returned when there was a
488+ problem. If you can't parse the expected response, its a good idea to
489+ try parsing this.
490+-}
491+data EsError = EsError { errorStatus :: Int
492+ , errorMessage :: Text } deriving (Eq, Show)
493+
494+{-| 'EsProtocolException' will be thrown if Bloodhound cannot parse a response
495+returned by the Elasticsearch server. If you encounter this error, please
496+verify that your domain data types and FromJSON instances are working properly
497+(for example, the 'a' of '[Hit a]' in 'SearchResult.searchHits.hits'). If you're
498+sure that your mappings are correct, then this error may be an indication of an
499+incompatibility between Bloodhound and Elasticsearch. Please open a bug report
500+and be sure to include the exception body.
501+-}
502+data EsProtocolException = EsProtocolException { esProtoExBody :: LByteString }
503+ deriving (Eq, Show)
504+
505+instance Exception EsProtocolException
506+
507+data IndexAlias = IndexAlias { srcIndex :: IndexName
508+ , indexAlias :: IndexAliasName } deriving (Eq, Show)
509+
510+{-| 'DocVersion' is an integer version number for a document between 1
511+and 9.2e+18 used for <<https://www.elastic.co/guide/en/elasticsearch/guide/current/optimistic-concurrency-control.html optimistic concurrency control>>.
512+-}
513+newtype DocVersion = DocVersion {
514+ docVersionNumber :: Int
515+ } deriving (Eq, Show, Ord, ToJSON)
516+
517+instance FromJSON DocVersion where
518+ parseJSON v = do
519+ i <- parseJSON v
520+ maybe (fail "DocVersion out of range") return $ mkDocVersion i
521+
522+-- | Smart constructor for in-range doc version
523+mkDocVersion :: Int -> Maybe DocVersion
524+mkDocVersion i
525+ | i >= (docVersionNumber minBound) && i <= (docVersionNumber maxBound) =
526+ Just $ DocVersion i
527+ | otherwise = Nothing
528+
529+data IndexAliasAction = AddAlias IndexAlias IndexAliasCreate
530+ | RemoveAlias IndexAlias deriving (Show, Eq)
531+
532+data IndexAliasCreate = IndexAliasCreate { aliasCreateRouting :: Maybe AliasRouting
533+ , aliasCreateFilter :: Maybe Filter}
534+ deriving (Show, Eq)
535+
536+data AliasRouting = AllAliasRouting RoutingValue
537+ | GranularAliasRouting (Maybe SearchAliasRouting) (Maybe IndexAliasRouting)
538+ deriving (Show, Eq)
539+
540+newtype SearchAliasRouting = SearchAliasRouting (NonEmpty RoutingValue) deriving (Show, Eq)
541+
542+newtype IndexAliasRouting = IndexAliasRouting RoutingValue deriving (Show, Eq, ToJSON, FromJSON)
543+
544+newtype RoutingValue = RoutingValue { routingValue :: Text } deriving (Show, Eq, ToJSON, FromJSON)
545+
546+newtype IndexAliasesSummary = IndexAliasesSummary { indexAliasesSummary :: [IndexAliasSummary] } deriving (Show, Eq)
547+
548+{-| 'IndexAliasSummary' is a summary of an index alias configured for a server. -}
549+data IndexAliasSummary = IndexAliasSummary { indexAliasSummaryAlias :: IndexAlias
550+ , indexAliasSummaryCreate :: IndexAliasCreate} deriving (Show, Eq)
551+
552+{-| 'ExternalDocVersion' is a convenience wrapper if your code uses its
553+own version numbers instead of ones from ES.
554+-}
555+newtype ExternalDocVersion = ExternalDocVersion DocVersion
556+ deriving (Eq, Show, Ord, Bounded, Enum, ToJSON)
557+
558+{-| 'VersionControl' is specified when indexing documents as a
559+optimistic concurrency control.
560+-}
561+data VersionControl = NoVersionControl
562+ -- ^ Don't send a version. This is a pure overwrite.
563+ | InternalVersion DocVersion
564+ -- ^ Use the default ES versioning scheme. Only
565+ -- index the document if the version is the same
566+ -- as the one specified. Only applicable to
567+ -- updates, as you should be getting Version from
568+ -- a search result.
569+ | ExternalGT ExternalDocVersion
570+ -- ^ Use your own version numbering. Only index
571+ -- the document if the version is strictly higher
572+ -- OR the document doesn't exist. The given
573+ -- version will be used as the new version number
574+ -- for the stored document. N.B. All updates must
575+ -- increment this number, meaning there is some
576+ -- global, external ordering of updates.
577+ | ExternalGTE ExternalDocVersion
578+ -- ^ Use your own version numbering. Only index
579+ -- the document if the version is equal or higher
580+ -- than the stored version. Will succeed if there
581+ -- is no existing document. The given version will
582+ -- be used as the new version number for the
583+ -- stored document. Use with care, as this could
584+ -- result in data loss.
585+ | ForceVersion ExternalDocVersion
586+ -- ^ The document will always be indexed and the
587+ -- given version will be the new version. This is
588+ -- typically used for correcting errors. Use with
589+ -- care, as this could result in data loss.
590+ deriving (Show, Eq, Ord)
591+
592+{-| 'DocumentParent' is used to specify a parent document.
593+-}
594+newtype DocumentParent = DocumentParent DocId
595+ deriving (Eq, Show)
596+
597+{-| 'IndexDocumentSettings' are special settings supplied when indexing
598+a document. For the best backwards compatiblity when new fields are
599+added, you should probably prefer to start with 'defaultIndexDocumentSettings'
600+-}
601+data IndexDocumentSettings =
602+ IndexDocumentSettings { idsVersionControl :: VersionControl
603+ , idsParent :: Maybe DocumentParent
604+ } deriving (Eq, Show)
605+
606+{-| Reasonable default settings. Chooses no version control and no parent.
607+-}
608+defaultIndexDocumentSettings :: IndexDocumentSettings
609+defaultIndexDocumentSettings = IndexDocumentSettings NoVersionControl Nothing
610+
611+{-| 'IndexSelection' is used for APIs which take a single index, a list of
612+ indexes, or the special @_all@ index.
613+-}
614+--TODO: this does not fully support <https://www.elastic.co/guide/en/elasticsearch/reference/1.7/multi-index.html multi-index syntax>. It wouldn't be too hard to implement but you'd have to add the optional parameters (ignore_unavailable, allow_no_indices, expand_wildcards) to any APIs using it. Also would be a breaking API.
615+data IndexSelection = IndexList (NonEmpty IndexName)
616+ | AllIndexes deriving (Eq, Show)
617+
618+{-| 'NodeSelection' is used for most cluster APIs. See <https://www.elastic.co/guide/en/elasticsearch/reference/current/cluster.html#cluster-nodes here> for more details.
619+-}
620+data NodeSelection = LocalNode
621+ -- ^ Whatever node receives this request
622+ | NodeList (NonEmpty NodeSelector)
623+ | AllNodes deriving (Eq, Show)
624+
625+
626+-- | An exact match or pattern to identify a node. Note that All of
627+-- these options support wildcarding, so your node name, server, attr
628+-- name can all contain * characters to be a fuzzy match.
629+data NodeSelector = NodeByName NodeName
630+ | NodeByFullNodeId FullNodeId
631+ | NodeByHost Server
632+ -- ^ e.g. 10.0.0.1 or even 10.0.0.*
633+ | NodeByAttribute NodeAttrName Text
634+ -- ^ NodeAttrName can be a pattern, e.g. rack*. The value can too.
635+ deriving (Eq, Show)
636+
637+-- | A human-readable node name that is supplied by the user in the
638+-- node config or automatically generated by Elasticsearch.
639+newtype NodeName = NodeName { nodeName :: Text }
640+ deriving (Eq, Ord, Show, FromJSON)
641+
642+-- | Unique, automatically-generated name assigned to nodes that are
643+-- usually returned in node-oriented APIs.
644+newtype FullNodeId = FullNodeId { fullNodeId :: Text }
645+ deriving (Eq, Ord, Show, FromJSON)
646+
647+-- | Username type used for HTTP Basic authentication. See 'basicAuthHook'.
648+newtype EsUsername = EsUsername { esUsername :: Text } deriving (Show, Eq)
649+
650+-- | Password type used for HTTP Basic authentication. See 'basicAuthHook'.
651+newtype EsPassword = EsPassword { esPassword :: Text } deriving (Show, Eq)
652+
653+data SnapshotRepoSelection = SnapshotRepoList (NonEmpty SnapshotRepoPattern)
654+ | AllSnapshotRepos deriving (Eq, Show)
655+
656+
657+-- | Either specifies an exact repo name or one with globs in it,
658+-- e.g. @RepoPattern "foo*"@ __NOTE__: Patterns are not supported on ES < 1.7
659+data SnapshotRepoPattern = ExactRepo SnapshotRepoName
660+ | RepoPattern Text
661+ deriving (Eq, Show)
662+
663+-- | The unique name of a snapshot repository.
664+newtype SnapshotRepoName = SnapshotRepoName { snapshotRepoName :: Text }
665+ deriving (Eq, Ord, Show, ToJSON, FromJSON)
666+
667+
668+-- | A generic representation of a snapshot repo. This is what gets
669+-- sent to and parsed from the server. For repo types enabled by
670+-- plugins that aren't exported by this library, consider making a
671+-- custom type which implements 'SnapshotRepo'. If it is a common repo
672+-- type, consider submitting a pull request to have it included in the
673+-- library proper
674+data GenericSnapshotRepo = GenericSnapshotRepo {
675+ gSnapshotRepoName :: SnapshotRepoName
676+ , gSnapshotRepoType :: SnapshotRepoType
677+ , gSnapshotRepoSettings :: GenericSnapshotRepoSettings
678+ } deriving (Eq, Show)
679+
680+-- | Law: fromGSnapshotRepo (toGSnapshotRepo r) == Right r
681+class SnapshotRepo r where
682+ toGSnapshotRepo :: r -> GenericSnapshotRepo
683+ fromGSnapshotRepo :: GenericSnapshotRepo -> Either SnapshotRepoConversionError r
684+
685+data SnapshotRepoConversionError = RepoTypeMismatch SnapshotRepoType SnapshotRepoType
686+ -- ^ Expected type and actual type
687+ | OtherRepoConversionError Text
688+ deriving (Show, Eq)
689+
690+instance SnapshotRepo GenericSnapshotRepo where
691+ toGSnapshotRepo = id
692+ fromGSnapshotRepo = Right
693+
694+
695+newtype SnapshotRepoType = SnapshotRepoType { snapshotRepoType :: Text }
696+ deriving (Eq, Ord, Show, ToJSON, FromJSON)
697+
698+
699+-- | Opaque representation of snapshot repo settings. Instances of
700+-- 'SnapshotRepo' will produce this.
701+newtype GenericSnapshotRepoSettings = GenericSnapshotRepoSettings { gSnapshotRepoSettingsObject :: Object }
702+ deriving (Eq, Show, ToJSON)
703+
704+
705+ -- Regardless of whether you send strongly typed json, my version of
706+ -- ES sends back stringly typed json in the settings, e.g. booleans
707+ -- as strings, so we'll try to convert them.
708+instance FromJSON GenericSnapshotRepoSettings where
709+ parseJSON = fmap (GenericSnapshotRepoSettings . fmap unStringlyTypeJSON). parseJSON
710+
711+-- | The result of running 'verifySnapshotRepo'.
712+newtype SnapshotVerification = SnapshotVerification { snapshotNodeVerifications :: [SnapshotNodeVerification] }
713+ deriving (Eq, Show)
714+
715+
716+instance FromJSON SnapshotVerification where
717+ parseJSON = withObject "SnapshotVerification" parse
718+ where
719+ parse o = do
720+ o2 <- o .: "nodes"
721+ SnapshotVerification <$> mapM (uncurry parse') (HM.toList o2)
722+ parse' rawFullId = withObject "SnapshotNodeVerification" $ \o ->
723+ SnapshotNodeVerification (FullNodeId rawFullId) <$> o .: "name"
724+
725+
726+-- | A node that has verified a snapshot
727+data SnapshotNodeVerification = SnapshotNodeVerification {
728+ snvFullId :: FullNodeId
729+ , snvNodeName :: NodeName
730+ } deriving (Eq, Show)
731+
732+newtype ClusterName = ClusterName { clusterName :: Text }
733+ deriving (Eq, Ord, Show, FromJSON)
734+
735+data NodesInfo = NodesInfo {
736+ nodesInfo :: [NodeInfo]
737+ , nodesClusterName :: ClusterName
738+ } deriving (Eq, Show)
739+
740+instance FromJSON NodesInfo where
741+ parseJSON = withObject "NodesInfo" parse
742+ where
743+ parse o = do
744+ nodes <- o .: "nodes"
745+ infos <- forM (HM.toList nodes) $ \(fullNID, v) -> do
746+ node <- parseJSON v
747+ parseNodeInfo (FullNodeId fullNID) node
748+ cn <- o .: "cluster_name"
749+ return (NodesInfo infos cn)
750+
751+data NodesStats = NodesStats {
752+ nodesStats :: [NodeStats]
753+ , nodesStatsClusterName :: ClusterName
754+ } deriving (Eq, Show)
755+
756+data NodeStats = NodeStats {
757+ nodeStatsName :: NodeName
758+ , nodeStatsFullId :: FullNodeId
759+ , nodeStatsBreakersStats :: Maybe NodeBreakersStats
760+ , nodeStatsHTTP :: NodeHTTPStats
761+ , nodeStatsTransport :: NodeTransportStats
762+ , nodeStatsFS :: NodeFSStats
763+ , nodeStatsNetwork :: NodeNetworkStats
764+ , nodeStatsThreadPool :: NodeThreadPoolsStats
765+ , nodeStatsJVM :: NodeJVMStats
766+ , nodeStatsProcess :: NodeProcessStats
767+ , nodeStatsOS :: NodeOSStats
768+ , nodeStatsIndices :: NodeIndicesStats
769+ } deriving (Eq, Show)
770+
771+data NodeBreakersStats = NodeBreakersStats {
772+ nodeStatsParentBreaker :: NodeBreakerStats
773+ , nodeStatsRequestBreaker :: NodeBreakerStats
774+ , nodeStatsFieldDataBreaker :: NodeBreakerStats
775+ } deriving (Eq, Show)
776+
777+data NodeBreakerStats = NodeBreakerStats {
778+ nodeBreakersTripped :: Int
779+ , nodeBreakersOverhead :: Double
780+ , nodeBreakersEstSize :: Bytes
781+ , nodeBreakersLimitSize :: Bytes
782+ } deriving (Eq, Show)
783+
784+data NodeHTTPStats = NodeHTTPStats {
785+ nodeHTTPTotalOpened :: Int
786+ , nodeHTTPCurrentOpen :: Int
787+ } deriving (Eq, Show)
788+
789+data NodeTransportStats = NodeTransportStats {
790+ nodeTransportTXSize :: Bytes
791+ , nodeTransportCount :: Int
792+ , nodeTransportRXSize :: Bytes
793+ , nodeTransportRXCount :: Int
794+ , nodeTransportServerOpen :: Int
795+ } deriving (Eq, Show)
796+
797+data NodeFSStats = NodeFSStats {
798+ nodeFSDataPaths :: [NodeDataPathStats]
799+ , nodeFSTotal :: NodeFSTotalStats
800+ , nodeFSTimestamp :: UTCTime
801+ } deriving (Eq, Show)
802+
803+data NodeDataPathStats = NodeDataPathStats {
804+ nodeDataPathDiskServiceTime :: Maybe Double
805+ , nodeDataPathDiskQueue :: Maybe Double
806+ , nodeDataPathIOSize :: Maybe Bytes
807+ , nodeDataPathWriteSize :: Maybe Bytes
808+ , nodeDataPathReadSize :: Maybe Bytes
809+ , nodeDataPathIOOps :: Maybe Int
810+ , nodeDataPathWrites :: Maybe Int
811+ , nodeDataPathReads :: Maybe Int
812+ , nodeDataPathAvailable :: Bytes
813+ , nodeDataPathFree :: Bytes
814+ , nodeDataPathTotal :: Bytes
815+ , nodeDataPathType :: Maybe Text
816+ , nodeDataPathDevice :: Text
817+ , nodeDataPathMount :: Text
818+ , nodeDataPathPath :: Text
819+ } deriving (Eq, Show)
820+
821+data NodeFSTotalStats = NodeFSTotalStats {
822+ nodeFSTotalDiskServiceTime :: Maybe Double
823+ , nodeFSTotalDiskQueue :: Maybe Double
824+ , nodeFSTotalIOSize :: Maybe Bytes
825+ , nodeFSTotalWriteSize :: Maybe Bytes
826+ , nodeFSTotalReadSize :: Maybe Bytes
827+ , nodeFSTotalIOOps :: Maybe Int
828+ , nodeFSTotalWrites :: Maybe Int
829+ , nodeFSTotalReads :: Maybe Int
830+ , nodeFSTotalAvailable :: Bytes
831+ , nodeFSTotalFree :: Bytes
832+ , nodeFSTotalTotal :: Bytes
833+ } deriving (Eq, Show)
834+
835+data NodeNetworkStats = NodeNetworkStats {
836+ nodeNetTCPOutRSTs :: Int
837+ , nodeNetTCPInErrs :: Int
838+ , nodeNetTCPAttemptFails :: Int
839+ , nodeNetTCPEstabResets :: Int
840+ , nodeNetTCPRetransSegs :: Int
841+ , nodeNetTCPOutSegs :: Int
842+ , nodeNetTCPInSegs :: Int
843+ , nodeNetTCPCurrEstab :: Int
844+ , nodeNetTCPPassiveOpens :: Int
845+ , nodeNetTCPActiveOpens :: Int
846+ } deriving (Eq, Show)
847+
848+data NodeThreadPoolsStats = NodeThreadPoolsStats {
849+ nodeThreadPoolsStatsSnapshot :: NodeThreadPoolStats
850+ , nodeThreadPoolsStatsBulk :: NodeThreadPoolStats
851+ , nodeThreadPoolsStatsMerge :: NodeThreadPoolStats
852+ , nodeThreadPoolsStatsGet :: NodeThreadPoolStats
853+ , nodeThreadPoolsStatsManagement :: NodeThreadPoolStats
854+ , nodeThreadPoolsStatsFetchShardStore :: Maybe NodeThreadPoolStats
855+ , nodeThreadPoolsStatsOptimize :: NodeThreadPoolStats
856+ , nodeThreadPoolsStatsFlush :: NodeThreadPoolStats
857+ , nodeThreadPoolsStatsSearch :: NodeThreadPoolStats
858+ , nodeThreadPoolsStatsWarmer :: NodeThreadPoolStats
859+ , nodeThreadPoolsStatsGeneric :: NodeThreadPoolStats
860+ , nodeThreadPoolsStatsSuggest :: NodeThreadPoolStats
861+ , nodeThreadPoolsStatsRefresh :: NodeThreadPoolStats
862+ , nodeThreadPoolsStatsIndex :: NodeThreadPoolStats
863+ , nodeThreadPoolsStatsListener :: Maybe NodeThreadPoolStats
864+ , nodeThreadPoolsStatsFetchShardStarted :: Maybe NodeThreadPoolStats
865+ , nodeThreadPoolsStatsPercolate :: NodeThreadPoolStats
866+ } deriving (Eq, Show)
867+
868+data NodeThreadPoolStats = NodeThreadPoolStats {
869+ nodeThreadPoolCompleted :: Int
870+ , nodeThreadPoolLargest :: Int
871+ , nodeThreadPoolRejected :: Int
872+ , nodeThreadPoolActive :: Int
873+ , nodeThreadPoolQueue :: Int
874+ , nodeThreadPoolThreads :: Int
875+ } deriving (Eq, Show)
876+
877+data NodeJVMStats = NodeJVMStats {
878+ nodeJVMStatsMappedBufferPool :: JVMBufferPoolStats
879+ , nodeJVMStatsDirectBufferPool :: JVMBufferPoolStats
880+ , nodeJVMStatsGCOldCollector :: JVMGCStats
881+ , nodeJVMStatsGCYoungCollector :: JVMGCStats
882+ , nodeJVMStatsPeakThreadsCount :: Int
883+ , nodeJVMStatsThreadsCount :: Int
884+ , nodeJVMStatsOldPool :: JVMPoolStats
885+ , nodeJVMStatsSurvivorPool :: JVMPoolStats
886+ , nodeJVMStatsYoungPool :: JVMPoolStats
887+ , nodeJVMStatsNonHeapCommitted :: Bytes
888+ , nodeJVMStatsNonHeapUsed :: Bytes
889+ , nodeJVMStatsHeapMax :: Bytes
890+ , nodeJVMStatsHeapCommitted :: Bytes
891+ , nodeJVMStatsHeapUsedPercent :: Int
892+ , nodeJVMStatsHeapUsed :: Bytes
893+ , nodeJVMStatsUptime :: NominalDiffTime
894+ , nodeJVMStatsTimestamp :: UTCTime
895+ } deriving (Eq, Show)
896+
897+data JVMBufferPoolStats = JVMBufferPoolStats {
898+ jvmBufferPoolStatsTotalCapacity :: Bytes
899+ , jvmBufferPoolStatsUsed :: Bytes
900+ , jvmBufferPoolStatsCount :: Int
901+ } deriving (Eq, Show)
902+
903+data JVMGCStats = JVMGCStats {
904+ jvmGCStatsCollectionTime :: NominalDiffTime
905+ , jvmGCStatsCollectionCount :: Int
906+ } deriving (Eq, Show)
907+
908+data JVMPoolStats = JVMPoolStats {
909+ jvmPoolStatsPeakMax :: Bytes
910+ , jvmPoolStatsPeakUsed :: Bytes
911+ , jvmPoolStatsMax :: Bytes
912+ , jvmPoolStatsUsed :: Bytes
913+ } deriving (Eq, Show)
914+
915+data NodeProcessStats = NodeProcessStats {
916+ nodeProcessMemTotalVirtual :: Bytes
917+ , nodeProcessMemShare :: Bytes
918+ , nodeProcessMemResident :: Bytes
919+ , nodeProcessCPUTotal :: NominalDiffTime
920+ , nodeProcessCPUUser :: NominalDiffTime
921+ , nodeProcessCPUSys :: NominalDiffTime
922+ , nodeProcessCPUPercent :: Int
923+ , nodeProcessOpenFDs :: Int
924+ , nodeProcessTimestamp :: UTCTime
925+ } deriving (Eq, Show)
926+
927+data NodeOSStats = NodeOSStats {
928+ nodeOSSwapFree :: Bytes
929+ , nodeOSSwapUsed :: Bytes
930+ , nodeOSMemActualUsed :: Bytes
931+ , nodeOSMemActualFree :: Bytes
932+ , nodeOSMemUsedPercent :: Int
933+ , nodeOSMemFreePercent :: Int
934+ , nodeOSMemUsed :: Bytes
935+ , nodeOSMemFree :: Bytes
936+ , nodeOSCPUStolen :: Int
937+ , nodeOSCPUUsage :: Int
938+ , nodeOSCPUIdle :: Int
939+ , nodeOSCPUUser :: Int
940+ , nodeOSCPUSys :: Int
941+ , nodeOSLoad :: Maybe LoadAvgs
942+ , nodeOSUptime :: NominalDiffTime
943+ , nodeOSTimestamp :: UTCTime
944+ } deriving (Eq, Show)
945+
946+data LoadAvgs = LoadAvgs {
947+ loadAvg1Min :: Double
948+ , loadAvg5Min :: Double
949+ , loadAvg15Min :: Double
950+ } deriving (Eq, Show)
951+
952+data NodeIndicesStats = NodeIndicesStats {
953+ nodeIndicesStatsRecoveryThrottleTime :: Maybe NominalDiffTime
954+ , nodeIndicesStatsRecoveryCurrentAsTarget :: Maybe Int
955+ , nodeIndicesStatsRecoveryCurrentAsSource :: Maybe Int
956+ , nodeIndicesStatsQueryCacheMisses :: Maybe Int
957+ , nodeIndicesStatsQueryCacheHits :: Maybe Int
958+ , nodeIndicesStatsQueryCacheEvictions :: Maybe Int
959+ , nodeIndicesStatsQueryCacheSize :: Maybe Bytes
960+ , nodeIndicesStatsSuggestCurrent :: Int
961+ , nodeIndicesStatsSuggestTime :: NominalDiffTime
962+ , nodeIndicesStatsSuggestTotal :: Int
963+ , nodeIndicesStatsTranslogSize :: Bytes
964+ , nodeIndicesStatsTranslogOps :: Int
965+ , nodeIndicesStatsSegFixedBitSetMemory :: Maybe Bytes
966+ , nodeIndicesStatsSegVersionMapMemory :: Bytes
967+ , nodeIndicesStatsSegIndexWriterMaxMemory :: Maybe Bytes
968+ , nodeIndicesStatsSegIndexWriterMemory :: Bytes
969+ , nodeIndicesStatsSegMemory :: Bytes
970+ , nodeIndicesStatsSegCount :: Int
971+ , nodeIndicesStatsCompletionSize :: Bytes
972+ , nodeIndicesStatsPercolateQueries :: Int
973+ , nodeIndicesStatsPercolateMemory :: Bytes
974+ , nodeIndicesStatsPercolateCurrent :: Int
975+ , nodeIndicesStatsPercolateTime :: NominalDiffTime
976+ , nodeIndicesStatsPercolateTotal :: Int
977+ , nodeIndicesStatsFieldDataEvictions :: Int
978+ , nodeIndicesStatsFieldDataMemory :: Bytes
979+ , nodeIndicesStatsIDCacheMemory :: Bytes
980+ , nodeIndicesStatsFilterCacheEvictions :: Int
981+ , nodeIndicesStatsFilterCacheMemory :: Bytes
982+ , nodeIndicesStatsWarmerTotalTime :: NominalDiffTime
983+ , nodeIndicesStatsWarmerTotal :: Int
984+ , nodeIndicesStatsWarmerCurrent :: Int
985+ , nodeIndicesStatsFlushTotalTime :: NominalDiffTime
986+ , nodeIndicesStatsFlushTotal :: Int
987+ , nodeIndicesStatsRefreshTotalTime :: NominalDiffTime
988+ , nodeIndicesStatsRefreshTotal :: Int
989+ , nodeIndicesStatsMergesTotalSize :: Bytes
990+ , nodeIndicesStatsMergesTotalDocs :: Int
991+ , nodeIndicesStatsMergesTotalTime :: NominalDiffTime
992+ , nodeIndicesStatsMergesTotal :: Int
993+ , nodeIndicesStatsMergesCurrentSize :: Bytes
994+ , nodeIndicesStatsMergesCurrentDocs :: Int
995+ , nodeIndicesStatsMergesCurrent :: Int
996+ , nodeIndicesStatsSearchFetchCurrent :: Int
997+ , nodeIndicesStatsSearchFetchTime :: NominalDiffTime
998+ , nodeIndicesStatsSearchFetchTotal :: Int
999+ , nodeIndicesStatsSearchQueryCurrent :: Int
1000+ , nodeIndicesStatsSearchQueryTime :: NominalDiffTime
1001+ , nodeIndicesStatsSearchQueryTotal :: Int
1002+ , nodeIndicesStatsSearchOpenContexts :: Int
1003+ , nodeIndicesStatsGetCurrent :: Int
1004+ , nodeIndicesStatsGetMissingTime :: NominalDiffTime
1005+ , nodeIndicesStatsGetMissingTotal :: Int
1006+ , nodeIndicesStatsGetExistsTime :: NominalDiffTime
1007+ , nodeIndicesStatsGetExistsTotal :: Int
1008+ , nodeIndicesStatsGetTime :: NominalDiffTime
1009+ , nodeIndicesStatsGetTotal :: Int
1010+ , nodeIndicesStatsIndexingThrottleTime :: Maybe NominalDiffTime
1011+ , nodeIndicesStatsIndexingIsThrottled :: Maybe Bool
1012+ , nodeIndicesStatsIndexingNoopUpdateTotal :: Maybe Int
1013+ , nodeIndicesStatsIndexingDeleteCurrent :: Int
1014+ , nodeIndicesStatsIndexingDeleteTime :: NominalDiffTime
1015+ , nodeIndicesStatsIndexingDeleteTotal :: Int
1016+ , nodeIndicesStatsIndexingIndexCurrent :: Int
1017+ , nodeIndicesStatsIndexingIndexTime :: NominalDiffTime
1018+ , nodeIndicesStatsIndexingTotal :: Int
1019+ , nodeIndicesStatsStoreThrottleTime :: NominalDiffTime
1020+ , nodeIndicesStatsStoreSize :: Bytes
1021+ , nodeIndicesStatsDocsDeleted :: Int
1022+ , nodeIndicesStatsDocsCount :: Int
1023+ } deriving (Eq, Show)
1024+
1025+data NodeInfo = NodeInfo {
1026+ nodeInfoHTTPAddress :: EsAddress
1027+ , nodeInfoBuild :: BuildHash
1028+ , nodeInfoESVersion :: VersionNumber
1029+ , nodeInfoIP :: Server
1030+ , nodeInfoHost :: Server
1031+ , nodeInfoTransportAddress :: EsAddress
1032+ , nodeInfoName :: NodeName
1033+ , nodeInfoFullId :: FullNodeId
1034+ , nodeInfoPlugins :: [NodePluginInfo]
1035+ , nodeInfoHTTP :: NodeHTTPInfo
1036+ , nodeInfoTransport :: NodeTransportInfo
1037+ , nodeInfoNetwork :: NodeNetworkInfo
1038+ , nodeInfoThreadPool :: NodeThreadPoolsInfo
1039+ , nodeInfoJVM :: NodeJVMInfo
1040+ , nodeInfoProcess :: NodeProcessInfo
1041+ , nodeInfoOS :: NodeOSInfo
1042+ , nodeInfoSettings :: Object
1043+ -- ^ The members of the settings objects are not consistent,
1044+ -- dependent on plugins, etc.
1045+ } deriving (Eq, Show)
1046+
1047+data NodePluginInfo = NodePluginInfo {
1048+ nodePluginSite :: Bool
1049+ -- ^ Is this a site plugin?
1050+ , nodePluginJVM :: Bool
1051+ -- ^ Is this plugin running on the JVM
1052+ , nodePluginDescription :: Text
1053+ , nodePluginVersion :: MaybeNA VersionNumber
1054+ , nodePluginName :: PluginName
1055+ } deriving (Eq, Show)
1056+
1057+data NodeHTTPInfo = NodeHTTPInfo {
1058+ nodeHTTPMaxContentLength :: Bytes
1059+ , nodeHTTPTransportAddress :: BoundTransportAddress
1060+ } deriving (Eq, Show)
1061+
1062+data NodeTransportInfo = NodeTransportInfo {
1063+ nodeTransportProfiles :: [BoundTransportAddress]
1064+ , nodeTransportAddress :: BoundTransportAddress
1065+ } deriving (Eq, Show)
1066+
1067+data BoundTransportAddress = BoundTransportAddress {
1068+ publishAddress :: EsAddress
1069+ , boundAddress :: EsAddress
1070+ } deriving (Eq, Show)
1071+
1072+data NodeNetworkInfo = NodeNetworkInfo {
1073+ nodeNetworkPrimaryInterface :: NodeNetworkInterface
1074+ , nodeNetworkRefreshInterval :: NominalDiffTime
1075+ } deriving (Eq, Show)
1076+
1077+newtype MacAddress = MacAddress { macAddress :: Text }
1078+ deriving (Eq, Ord, Show, FromJSON)
1079+
1080+newtype NetworkInterfaceName = NetworkInterfaceName { networkInterfaceName :: Text }
1081+ deriving (Eq, Ord, Show, FromJSON)
1082+
1083+data NodeNetworkInterface = NodeNetworkInterface {
1084+ nodeNetIfaceMacAddress :: MacAddress
1085+ , nodeNetIfaceName :: NetworkInterfaceName
1086+ , nodeNetIfaceAddress :: Server
1087+ } deriving (Eq, Show)
1088+
1089+data NodeThreadPoolsInfo = NodeThreadPoolsInfo {
1090+ nodeThreadPoolsRefresh :: NodeThreadPoolInfo
1091+ , nodeThreadPoolsManagement :: NodeThreadPoolInfo
1092+ , nodeThreadPoolsPercolate :: NodeThreadPoolInfo
1093+ , nodeThreadPoolsListener :: Maybe NodeThreadPoolInfo
1094+ , nodeThreadPoolsFetchShardStarted :: Maybe NodeThreadPoolInfo
1095+ , nodeThreadPoolsSearch :: NodeThreadPoolInfo
1096+ , nodeThreadPoolsFlush :: NodeThreadPoolInfo
1097+ , nodeThreadPoolsWarmer :: NodeThreadPoolInfo
1098+ , nodeThreadPoolsOptimize :: NodeThreadPoolInfo
1099+ , nodeThreadPoolsBulk :: NodeThreadPoolInfo
1100+ , nodeThreadPoolsSuggest :: NodeThreadPoolInfo
1101+ , nodeThreadPoolsMerge :: NodeThreadPoolInfo
1102+ , nodeThreadPoolsSnapshot :: NodeThreadPoolInfo
1103+ , nodeThreadPoolsGet :: NodeThreadPoolInfo
1104+ , nodeThreadPoolsFetchShardStore :: Maybe NodeThreadPoolInfo
1105+ , nodeThreadPoolsIndex :: NodeThreadPoolInfo
1106+ , nodeThreadPoolsGeneric :: NodeThreadPoolInfo
1107+ } deriving (Eq, Show)
1108+
1109+data NodeThreadPoolInfo = NodeThreadPoolInfo {
1110+ nodeThreadPoolQueueSize :: ThreadPoolSize
1111+ , nodeThreadPoolKeepalive :: Maybe NominalDiffTime
1112+ , nodeThreadPoolMin :: Maybe Int
1113+ , nodeThreadPoolMax :: Maybe Int
1114+ , nodeThreadPoolType :: ThreadPoolType
1115+ } deriving (Eq, Show)
1116+
1117+data ThreadPoolSize = ThreadPoolBounded Int
1118+ | ThreadPoolUnbounded
1119+ deriving (Eq, Show)
1120+
1121+data ThreadPoolType = ThreadPoolScaling
1122+ | ThreadPoolFixed
1123+ | ThreadPoolCached
1124+ deriving (Eq, Show)
1125+
1126+data NodeJVMInfo = NodeJVMInfo {
1127+ nodeJVMInfoMemoryPools :: [JVMMemoryPool]
1128+ , nodeJVMInfoMemoryPoolsGCCollectors :: [JVMGCCollector]
1129+ , nodeJVMInfoMemoryInfo :: JVMMemoryInfo
1130+ , nodeJVMInfoStartTime :: UTCTime
1131+ , nodeJVMInfoVMVendor :: Text
1132+ , nodeJVMVMVersion :: VersionNumber
1133+ -- ^ JVM doesn't seme to follow normal version conventions
1134+ , nodeJVMVMName :: Text
1135+ , nodeJVMVersion :: VersionNumber
1136+ , nodeJVMPID :: PID
1137+ } deriving (Eq, Show)
1138+
1139+-- | Handles quirks in the way JVM versions are rendered (1.7.0_101 -> 1.7.0.101)
1140+newtype JVMVersion = JVMVersion { unJVMVersion :: VersionNumber }
1141+
1142+data JVMMemoryInfo = JVMMemoryInfo {
1143+ jvmMemoryInfoDirectMax :: Bytes
1144+ , jvmMemoryInfoNonHeapMax :: Bytes
1145+ , jvmMemoryInfoNonHeapInit :: Bytes
1146+ , jvmMemoryInfoHeapMax :: Bytes
1147+ , jvmMemoryInfoHeapInit :: Bytes
1148+ } deriving (Eq, Show)
1149+
1150+newtype JVMMemoryPool = JVMMemoryPool {
1151+ jvmMemoryPool :: Text
1152+ } deriving (Eq, Show, FromJSON)
1153+
1154+newtype JVMGCCollector = JVMGCCollector {
1155+ jvmGCCollector :: Text
1156+ } deriving (Eq, Show, FromJSON)
1157+
1158+newtype PID = PID {
1159+ pid :: Int
1160+ } deriving (Eq, Show, FromJSON)
1161+
1162+data NodeOSInfo = NodeOSInfo {
1163+ nodeOSSwap :: Bytes
1164+ , nodeOSMem :: Bytes
1165+ , nodeOSCPUInfo :: CPUInfo
1166+ , nodeOSAvailableProcessors :: Int
1167+ , nodeOSRefreshInterval :: NominalDiffTime
1168+ } deriving (Eq, Show)
1169+
1170+data CPUInfo = CPUInfo {
1171+ cpuCacheSize :: Bytes
1172+ , cpuCoresPerSocket :: Int
1173+ , cpuTotalSockets :: Int
1174+ , cpuTotalCores :: Int
1175+ , cpuMHZ :: Int
1176+ , cpuModel :: Text
1177+ , cpuVendor :: Text
1178+ } deriving (Eq, Show)
1179+
1180+data NodeProcessInfo = NodeProcessInfo {
1181+ nodeProcessMLockAll :: Bool
1182+ -- ^ See <https://www.elastic.co/guide/en/elasticsearch/reference/current/setup-configuration.html>
1183+ , nodeProcessMaxFileDescriptors :: Int
1184+ , nodeProcessId :: PID
1185+ , nodeProcessRefreshInterval :: NominalDiffTime
1186+ } deriving (Eq, Show)
1187+
1188+-- | A quirky address format used throughout Elasticsearch. An example
1189+-- would be inet[/1.1.1.1:9200]. inet may be a placeholder for a
1190+-- <https://en.wikipedia.org/wiki/Fully_qualified_domain_name FQDN>.
1191+newtype EsAddress = EsAddress { esAddress :: Text }
1192+ deriving (Eq, Ord, Show, FromJSON)
1193+
1194+newtype PluginName = PluginName { pluginName :: Text }
1195+ deriving (Eq, Ord, Show, FromJSON)
1196+
1197+data ShardResult =
1198+ ShardResult { shardTotal :: Int
1199+ , shardsSuccessful :: Int
1200+ , shardsFailed :: Int } deriving (Eq, Show)
1201+
1202+data SnapshotState = SnapshotInit
1203+ | SnapshotStarted
1204+ | SnapshotSuccess
1205+ | SnapshotFailed
1206+ | SnapshotAborted
1207+ | SnapshotMissing
1208+ | SnapshotWaiting
1209+ deriving (Show, Eq)
1210+
1211+instance FromJSON SnapshotState where
1212+ parseJSON = withText "SnapshotState" parse
1213+ where
1214+ parse "INIT" = return SnapshotInit
1215+ parse "STARTED" = return SnapshotStarted
1216+ parse "SUCCESS" = return SnapshotSuccess
1217+ parse "FAILED" = return SnapshotFailed
1218+ parse "ABORTED" = return SnapshotAborted
1219+ parse "MISSING" = return SnapshotMissing
1220+ parse "WAITING" = return SnapshotWaiting
1221+ parse t = fail ("Invalid snapshot state " <> T.unpack t)
1222+
1223+data SnapshotRestoreSettings = SnapshotRestoreSettings {
1224+ snapRestoreWaitForCompletion :: Bool
1225+ -- ^ Should the API call return immediately after initializing
1226+ -- the restore or wait until completed? Note that if this is
1227+ -- enabled, it could wait a long time, so you should adjust your
1228+ -- 'ManagerSettings' accordingly to set long timeouts or
1229+ -- explicitly handle timeouts.
1230+ , snapRestoreIndices :: Maybe IndexSelection
1231+ -- ^ Nothing will restore all indices in the snapshot. Just [] is
1232+ -- permissable and will essentially be a no-op restore.
1233+ , snapRestoreIgnoreUnavailable :: Bool
1234+ -- ^ If set to True, any indices that do not exist will be ignored
1235+ -- during snapshot rather than failing the restore.
1236+ , snapRestoreIncludeGlobalState :: Bool
1237+ -- ^ If set to false, will ignore any global state in the snapshot
1238+ -- and will not restore it.
1239+ , snapRestoreRenamePattern :: Maybe RestoreRenamePattern
1240+ -- ^ A regex pattern for matching indices. Used with
1241+ -- 'snapRestoreRenameReplacement', the restore can reference the
1242+ -- matched index and create a new index name upon restore.
1243+ , snapRestoreRenameReplacement :: Maybe (NonEmpty RestoreRenameToken)
1244+ -- ^ Expression of how index renames should be constructed.
1245+ , snapRestorePartial :: Bool
1246+ -- ^ If some indices fail to restore, should the process proceed?
1247+ , snapRestoreIncludeAliases :: Bool
1248+ -- ^ Should the restore also restore the aliases captured in the
1249+ -- snapshot.
1250+ , snapRestoreIndexSettingsOverrides :: Maybe RestoreIndexSettings
1251+ -- ^ Settings to apply during the restore process. __NOTE:__ This
1252+ -- option is not supported in ES < 1.5 and should be set to
1253+ -- Nothing in that case.
1254+ , snapRestoreIgnoreIndexSettings :: Maybe (NonEmpty Text)
1255+ -- ^ This type could be more rich but it isn't clear which
1256+ -- settings are allowed to be ignored during restore, so we're
1257+ -- going with including this feature in a basic form rather than
1258+ -- omitting it. One example here would be
1259+ -- "index.refresh_interval". Any setting specified here will
1260+ -- revert back to the server default during the restore process.
1261+ } deriving (Eq, Show)
1262+
1263+-- | Regex-stype pattern, e.g. "index_(.+)" to match index names
1264+newtype RestoreRenamePattern = RestoreRenamePattern { rrPattern :: Text }
1265+ deriving (Show, Eq, Ord, ToJSON)
1266+
1267+
1268+-- | A single token in a index renaming scheme for a restore. These
1269+-- are concatenated into a string before being sent to
1270+-- Elasticsearch. Check out these Java
1271+-- <https://docs.oracle.com/javase/7/docs/api/java/util/regex/Matcher.html docs> to find out more if you're into that sort of thing.
1272+data RestoreRenameToken = RRTLit Text
1273+ -- ^ Just a literal string of characters
1274+ | RRSubWholeMatch
1275+ -- ^ Equivalent to $0. The entire matched pattern, not any subgroup
1276+ | RRSubGroup RRGroupRefNum
1277+ -- ^ A specific reference to a group number
1278+ deriving (Show, Eq)
1279+
1280+
1281+-- | A group number for regex matching. Only values from 1-9 are
1282+-- supported. Construct with 'mkRRGroupRefNum'
1283+newtype RRGroupRefNum = RRGroupRefNum { rrGroupRefNum :: Int }
1284+ deriving (Show, Eq, Ord)
1285+
1286+instance Bounded RRGroupRefNum where
1287+ minBound = RRGroupRefNum 1
1288+ maxBound = RRGroupRefNum 9
1289+
1290+
1291+-- | Only allows valid group number references (1-9).
1292+mkRRGroupRefNum :: Int -> Maybe RRGroupRefNum
1293+mkRRGroupRefNum i
1294+ | i >= (rrGroupRefNum minBound) && i <= (rrGroupRefNum maxBound) =
1295+ Just $ RRGroupRefNum i
1296+ | otherwise = Nothing
1297+
1298+-- | Index settings that can be overridden. The docs only mention you
1299+-- can update number of replicas, but there may be more. You
1300+-- definitely cannot override shard count.
1301+data RestoreIndexSettings = RestoreIndexSettings {
1302+ restoreOverrideReplicas :: Maybe ReplicaCount
1303+ } deriving (Show, Eq)
1304+
1305+instance ToJSON RestoreIndexSettings where
1306+ toJSON RestoreIndexSettings {..} = object prs
1307+ where
1308+ prs = catMaybes [("index.number_of_replicas" .=) <$> restoreOverrideReplicas]
1309+
1310+data SnapshotRepoUpdateSettings = SnapshotRepoUpdateSettings {
1311+ repoUpdateVerify :: Bool
1312+ -- ^ After creation/update, synchronously check that nodes can
1313+ -- write to this repo. Defaults to True. You may use False if you
1314+ -- need a faster response and plan on verifying manually later
1315+ -- with 'verifySnapshotRepo'.
1316+ } deriving (Eq, Show)
1317+
1318+-- | Reasonable defaults for repo creation/update
1319+--
1320+-- * repoUpdateVerify True
1321+defaultSnapshotRepoUpdateSettings :: SnapshotRepoUpdateSettings
1322+defaultSnapshotRepoUpdateSettings = SnapshotRepoUpdateSettings True
1323+
1324+-- | A filesystem-based snapshot repo that ships with
1325+-- Elasticsearch. This is an instance of 'SnapshotRepo' so it can be
1326+-- used with 'updateSnapshotRepo'
1327+data FsSnapshotRepo = FsSnapshotRepo {
1328+ fsrName :: SnapshotRepoName
1329+ , fsrLocation :: FilePath
1330+ , fsrCompressMetadata :: Bool
1331+ , fsrChunkSize :: Maybe Bytes
1332+ -- ^ Size by which to split large files during snapshotting.
1333+ , fsrMaxRestoreBytesPerSec :: Maybe Bytes
1334+ -- ^ Throttle node restore rate. If not supplied, defaults to 40mb/sec
1335+ , fsrMaxSnapshotBytesPerSec :: Maybe Bytes
1336+ -- ^ Throttle node snapshot rate. If not supplied, defaults to 40mb/sec
1337+ } deriving (Eq, Show)
1338+
1339+instance SnapshotRepo FsSnapshotRepo where
1340+ toGSnapshotRepo FsSnapshotRepo {..} =
1341+ GenericSnapshotRepo fsrName fsRepoType (GenericSnapshotRepoSettings settings)
1342+ where
1343+ Object settings = object $ [ "location" .= fsrLocation
1344+ , "compress" .= fsrCompressMetadata
1345+ ] ++ optionalPairs
1346+ optionalPairs = catMaybes [ ("chunk_size" .=) <$> fsrChunkSize
1347+ , ("max_restore_bytes_per_sec" .=) <$> fsrMaxRestoreBytesPerSec
1348+ , ("max_snapshot_bytes_per_sec" .=) <$> fsrMaxSnapshotBytesPerSec
1349+ ]
1350+ fromGSnapshotRepo GenericSnapshotRepo {..}
1351+ | gSnapshotRepoType == fsRepoType = do
1352+ let o = gSnapshotRepoSettingsObject gSnapshotRepoSettings
1353+ parseRepo $ do
1354+ FsSnapshotRepo gSnapshotRepoName <$> o .: "location"
1355+ <*> o .:? "compress" .!= False
1356+ <*> o .:? "chunk_size"
1357+ <*> o .:? "max_restore_bytes_per_sec"
1358+ <*> o .:? "max_snapshot_bytes_per_sec"
1359+ | otherwise = Left (RepoTypeMismatch fsRepoType gSnapshotRepoType)
1360+
1361+data SnapshotCreateSettings = SnapshotCreateSettings {
1362+ snapWaitForCompletion :: Bool
1363+ -- ^ Should the API call return immediately after initializing
1364+ -- the snapshot or wait until completed? Note that if this is
1365+ -- enabled it could wait a long time, so you should adjust your
1366+ -- 'ManagerSettings' accordingly to set long timeouts or
1367+ -- explicitly handle timeouts.
1368+ , snapIndices :: Maybe IndexSelection
1369+ -- ^ Nothing will snapshot all indices. Just [] is permissable and
1370+ -- will essentially be a no-op snapshot.
1371+ , snapIgnoreUnavailable :: Bool
1372+ -- ^ If set to True, any matched indices that don't exist will be
1373+ -- ignored. Otherwise it will be an error and fail.
1374+ , snapIncludeGlobalState :: Bool
1375+ , snapPartial :: Bool
1376+ -- ^ If some indices failed to snapshot (e.g. if not all primary
1377+ -- shards are available), should the process proceed?
1378+ } deriving (Eq, Show)
1379+
1380+-- | Reasonable defaults for snapshot creation
1381+--
1382+-- * snapWaitForCompletion False
1383+-- * snapIndices Nothing
1384+-- * snapIgnoreUnavailable False
1385+-- * snapIncludeGlobalState True
1386+-- * snapPartial False
1387+defaultSnapshotCreateSettings :: SnapshotCreateSettings
1388+defaultSnapshotCreateSettings = SnapshotCreateSettings {
1389+ snapWaitForCompletion = False
1390+ , snapIndices = Nothing
1391+ , snapIgnoreUnavailable = False
1392+ , snapIncludeGlobalState = True
1393+ , snapPartial = False
1394+ }
1395+
1396+
1397+data SnapshotSelection = SnapshotList (NonEmpty SnapshotPattern)
1398+ | AllSnapshots deriving (Eq, Show)
1399+
1400+
1401+-- | Either specifies an exact snapshot name or one with globs in it,
1402+-- e.g. @SnapPattern "foo*"@ __NOTE__: Patterns are not supported on
1403+-- ES < 1.7
1404+data SnapshotPattern = ExactSnap SnapshotName
1405+ | SnapPattern Text
1406+ deriving (Eq, Show)
1407+
1408+
1409+-- | General information about the state of a snapshot. Has some
1410+-- redundancies with 'SnapshotStatus'
1411+data SnapshotInfo = SnapshotInfo {
1412+ snapInfoShards :: ShardResult
1413+ , snapInfoFailures :: [SnapshotShardFailure]
1414+ , snapInfoDuration :: NominalDiffTime
1415+ , snapInfoEndTime :: UTCTime
1416+ , snapInfoStartTime :: UTCTime
1417+ , snapInfoState :: SnapshotState
1418+ , snapInfoIndices :: [IndexName]
1419+ , snapInfoName :: SnapshotName
1420+ } deriving (Eq, Show)
1421+
1422+
1423+instance FromJSON SnapshotInfo where
1424+ parseJSON = withObject "SnapshotInfo" parse
1425+ where
1426+ parse o = SnapshotInfo <$> o .: "shards"
1427+ <*> o .: "failures"
1428+ <*> (unMS <$> o .: "duration_in_millis")
1429+ <*> (posixMS <$> o .: "end_time_in_millis")
1430+ <*> (posixMS <$> o .: "start_time_in_millis")
1431+ <*> o .: "state"
1432+ <*> o .: "indices"
1433+ <*> o .: "snapshot"
1434+
1435+data SnapshotShardFailure = SnapshotShardFailure {
1436+ snapShardFailureIndex :: IndexName
1437+ , snapShardFailureNodeId :: Maybe NodeName -- I'm not 100% sure this isn't actually 'FullNodeId'
1438+ , snapShardFailureReason :: Text
1439+ , snapShardFailureShardId :: ShardId
1440+ } deriving (Eq, Show)
1441+
1442+
1443+instance FromJSON SnapshotShardFailure where
1444+ parseJSON = withObject "SnapshotShardFailure" parse
1445+ where
1446+ parse o = SnapshotShardFailure <$> o .: "index"
1447+ <*> o .:? "node_id"
1448+ <*> o .: "reason"
1449+ <*> o .: "shard_id"
1450+
1451+parseRepo :: Parser a -> Either SnapshotRepoConversionError a
1452+parseRepo parser = case parseEither (const parser) () of
1453+ Left e -> Left (OtherRepoConversionError (T.pack e))
1454+ Right a -> Right a
1455+
1456+parseNodeStats :: FullNodeId -> Object -> Parser NodeStats
1457+parseNodeStats fnid o = do
1458+ NodeStats <$> o .: "name"
1459+ <*> pure fnid
1460+ <*> o .:? "breakers"
1461+ <*> o .: "http"
1462+ <*> o .: "transport"
1463+ <*> o .: "fs"
1464+ <*> o .: "network"
1465+ <*> o .: "thread_pool"
1466+ <*> o .: "jvm"
1467+ <*> o .: "process"
1468+ <*> o .: "os"
1469+ <*> o .: "indices"
1470+
1471+parseNodeInfo :: FullNodeId -> Object -> Parser NodeInfo
1472+parseNodeInfo nid o =
1473+ NodeInfo <$> o .: "http_address"
1474+ <*> o .: "build"
1475+ <*> o .: "version"
1476+ <*> o .: "ip"
1477+ <*> o .: "host"
1478+ <*> o .: "transport_address"
1479+ <*> o .: "name"
1480+ <*> pure nid
1481+ <*> o .: "plugins"
1482+ <*> o .: "http"
1483+ <*> o .: "transport"
1484+ <*> o .: "network"
1485+ <*> o .: "thread_pool"
1486+ <*> o .: "jvm"
1487+ <*> o .: "process"
1488+ <*> o .: "os"
1489+ <*> o .: "settings"
1490+
1491+-- | Reasonable defaults for snapshot restores
1492+--
1493+-- * snapRestoreWaitForCompletion False
1494+-- * snapRestoreIndices Nothing
1495+-- * snapRestoreIgnoreUnavailable False
1496+-- * snapRestoreIncludeGlobalState True
1497+-- * snapRestoreRenamePattern Nothing
1498+-- * snapRestoreRenameReplacement Nothing
1499+-- * snapRestorePartial False
1500+-- * snapRestoreIncludeAliases True
1501+-- * snapRestoreIndexSettingsOverrides Nothing
1502+-- * snapRestoreIgnoreIndexSettings Nothing
1503+defaultSnapshotRestoreSettings :: SnapshotRestoreSettings
1504+defaultSnapshotRestoreSettings = SnapshotRestoreSettings {
1505+ snapRestoreWaitForCompletion = False
1506+ , snapRestoreIndices = Nothing
1507+ , snapRestoreIgnoreUnavailable = False
1508+ , snapRestoreIncludeGlobalState = True
1509+ , snapRestoreRenamePattern = Nothing
1510+ , snapRestoreRenameReplacement = Nothing
1511+ , snapRestorePartial = False
1512+ , snapRestoreIncludeAliases = True
1513+ , snapRestoreIndexSettingsOverrides = Nothing
1514+ , snapRestoreIgnoreIndexSettings = Nothing
1515+ }
1516+
1517+fsRepoType :: SnapshotRepoType
1518+fsRepoType = SnapshotRepoType "fs"
1519+
1520+{-| 'Reply' and 'Method' are type synonyms from 'Network.HTTP.Types.Method.Method' -}
1521+type Reply = Network.HTTP.Client.Response LByteString
1522+
1523+instance FromJSON IndexSettingsSummary where
1524+ parseJSON = withObject "IndexSettingsSummary" parse
1525+ where parse o = case HM.toList o of
1526+ [(ixn, v@(Object o'))] -> IndexSettingsSummary (IndexName ixn)
1527+ <$> parseJSON v
1528+ <*> (fmap (filter (not . redundant)) . parseSettings =<< o' .: "settings")
1529+ _ -> fail "Expected single-key object with index name"
1530+ redundant (NumberOfReplicas _) = True
1531+ redundant _ = False
1532+
1533+instance ToJSON VersionNumber where
1534+ toJSON = toJSON . Vers.showVersion . versionNumber
1535+
1536+instance FromJSON VersionNumber where
1537+ parseJSON = withText "VersionNumber" (parse . T.unpack)
1538+ where
1539+ parse s = case filter (null . snd)(RP.readP_to_S Vers.parseVersion s) of
1540+ [(v, _)] -> pure (VersionNumber v)
1541+ [] -> fail ("Invalid version string " ++ s)
1542+ xs -> fail ("Ambiguous version string " ++ s ++ " (" ++ intercalate ", " (Vers.showVersion . fst <$> xs) ++ ")")
1543+
1544+instance ToJSON Interval where
1545+ toJSON Year = "year"
1546+ toJSON Quarter = "quarter"
1547+ toJSON Month = "month"
1548+ toJSON Week = "week"
1549+ toJSON Day = "day"
1550+ toJSON Hour = "hour"
1551+ toJSON Minute = "minute"
1552+ toJSON Second = "second"
1553+ toJSON (FractionalInterval fraction interval) = toJSON $ show fraction ++ show interval
1554+
1555+parseStringInterval :: (Monad m) => String -> m NominalDiffTime
1556+parseStringInterval s = case span isNumber s of
1557+ ("", _) -> fail "Invalid interval"
1558+ (nS, unitS) -> case (readMay nS, readMay unitS) of
1559+ (Just n, Just unit) -> return (fromInteger (n * unitNDT unit))
1560+ (Nothing, _) -> fail "Invalid interval number"
1561+ (_, Nothing) -> fail "Invalid interval unit"
1562+ where
1563+ unitNDT Seconds = 1
1564+ unitNDT Minutes = 60
1565+ unitNDT Hours = 60 * 60
1566+ unitNDT Days = 24 * 60 * 60
1567+ unitNDT Weeks = 7 * 24 * 60 * 60
1568+
1569+instance ToJSON IndexSettings where
1570+ toJSON (IndexSettings s r) = object ["settings" .=
1571+ object ["index" .=
1572+ object ["number_of_shards" .= s, "number_of_replicas" .= r]
1573+ ]
1574+ ]
1575+
1576+instance FromJSON IndexSettings where
1577+ parseJSON = withObject "IndexSettings" parse
1578+ where parse o = do s <- o .: "settings"
1579+ i <- s .: "index"
1580+ IndexSettings <$> i .: "number_of_shards"
1581+ <*> i .: "number_of_replicas"
1582+
1583+instance Bounded DocVersion where
1584+ minBound = DocVersion 1
1585+ maxBound = DocVersion 9200000000000000000 -- 9.2e+18
1586+
1587+instance Enum DocVersion where
1588+ succ x
1589+ | x /= maxBound = DocVersion (succ $ docVersionNumber x)
1590+ | otherwise = succError "DocVersion"
1591+ pred x
1592+ | x /= minBound = DocVersion (pred $ docVersionNumber x)
1593+ | otherwise = predError "DocVersion"
1594+ toEnum i =
1595+ fromMaybe (error $ show i ++ " out of DocVersion range") $ mkDocVersion i
1596+ fromEnum = docVersionNumber
1597+ enumFrom = boundedEnumFrom
1598+ enumFromThen = boundedEnumFromThen
1599+
1600+instance FromJSON ShardResult where
1601+ parseJSON (Object v) = ShardResult <$>
1602+ v .: "total" <*>
1603+ v .: "successful" <*>
1604+ v .: "failed"
1605+ parseJSON _ = empty
1606+
1607+instance FromJSON NodeBreakerStats where
1608+ parseJSON = withObject "NodeBreakerStats" parse
1609+ where
1610+ parse o = NodeBreakerStats <$> o .: "tripped"
1611+ <*> o .: "overhead"
1612+ <*> o .: "estimated_size_in_bytes"
1613+ <*> o .: "limit_size_in_bytes"
1614+
1615+instance FromJSON NodeHTTPStats where
1616+ parseJSON = withObject "NodeHTTPStats" parse
1617+ where
1618+ parse o = NodeHTTPStats <$> o .: "total_opened"
1619+ <*> o .: "current_open"
1620+
1621+instance FromJSON NodeTransportStats where
1622+ parseJSON = withObject "NodeTransportStats" parse
1623+ where
1624+ parse o = NodeTransportStats <$> o .: "tx_size_in_bytes"
1625+ <*> o .: "tx_count"
1626+ <*> o .: "rx_size_in_bytes"
1627+ <*> o .: "rx_count"
1628+ <*> o .: "server_open"
1629+
1630+instance FromJSON NodeFSStats where
1631+ parseJSON = withObject "NodeFSStats" parse
1632+ where
1633+ parse o = NodeFSStats <$> o .: "data"
1634+ <*> o .: "total"
1635+ <*> (posixMS <$> o .: "timestamp")
1636+
1637+instance FromJSON NodeDataPathStats where
1638+ parseJSON = withObject "NodeDataPathStats" parse
1639+ where
1640+ parse o =
1641+ NodeDataPathStats <$> (fmap unStringlyTypedDouble <$> o .:? "disk_service_time")
1642+ <*> (fmap unStringlyTypedDouble <$> o .:? "disk_queue")
1643+ <*> o .:? "disk_io_size_in_bytes"
1644+ <*> o .:? "disk_write_size_in_bytes"
1645+ <*> o .:? "disk_read_size_in_bytes"
1646+ <*> o .:? "disk_io_op"
1647+ <*> o .:? "disk_writes"
1648+ <*> o .:? "disk_reads"
1649+ <*> o .: "available_in_bytes"
1650+ <*> o .: "free_in_bytes"
1651+ <*> o .: "total_in_bytes"
1652+ <*> o .:? "type"
1653+ <*> o .: "dev"
1654+ <*> o .: "mount"
1655+ <*> o .: "path"
1656+
1657+instance FromJSON NodeNetworkStats where
1658+ parseJSON = withObject "NodeNetworkStats" parse
1659+ where
1660+ parse o = do
1661+ tcp <- o .: "tcp"
1662+ NodeNetworkStats <$> tcp .: "out_rsts"
1663+ <*> tcp .: "in_errs"
1664+ <*> tcp .: "attempt_fails"
1665+ <*> tcp .: "estab_resets"
1666+ <*> tcp .: "retrans_segs"
1667+ <*> tcp .: "out_segs"
1668+ <*> tcp .: "in_segs"
1669+ <*> tcp .: "curr_estab"
1670+ <*> tcp .: "passive_opens"
1671+ <*> tcp .: "active_opens"
1672+
1673+instance FromJSON NodeThreadPoolsStats where
1674+ parseJSON = withObject "NodeThreadPoolsStats" parse
1675+ where
1676+ parse o = NodeThreadPoolsStats <$> o .: "snapshot"
1677+ <*> o .: "bulk"
1678+ <*> o .: "merge"
1679+ <*> o .: "get"
1680+ <*> o .: "management"
1681+ <*> o .:? "fetch_shard_store"
1682+ <*> o .: "optimize"
1683+ <*> o .: "flush"
1684+ <*> o .: "search"
1685+ <*> o .: "warmer"
1686+ <*> o .: "generic"
1687+ <*> o .: "suggest"
1688+ <*> o .: "refresh"
1689+ <*> o .: "index"
1690+ <*> o .:? "listener"
1691+ <*> o .:? "fetch_shard_started"
1692+ <*> o .: "percolate"
1693+instance FromJSON NodeThreadPoolStats where
1694+ parseJSON = withObject "NodeThreadPoolStats" parse
1695+ where
1696+ parse o = NodeThreadPoolStats <$> o .: "completed"
1697+ <*> o .: "largest"
1698+ <*> o .: "rejected"
1699+ <*> o .: "active"
1700+ <*> o .: "queue"
1701+ <*> o .: "threads"
1702+
1703+instance FromJSON NodeJVMStats where
1704+ parseJSON = withObject "NodeJVMStats" parse
1705+ where
1706+ parse o = do
1707+ bufferPools <- o .: "buffer_pools"
1708+ mapped <- bufferPools .: "mapped"
1709+ direct <- bufferPools .: "direct"
1710+ gc <- o .: "gc"
1711+ collectors <- gc .: "collectors"
1712+ oldC <- collectors .: "old"
1713+ youngC <- collectors .: "young"
1714+ threads <- o .: "threads"
1715+ mem <- o .: "mem"
1716+ pools <- mem .: "pools"
1717+ oldM <- pools .: "old"
1718+ survivorM <- pools .: "survivor"
1719+ youngM <- pools .: "young"
1720+ NodeJVMStats <$> pure mapped
1721+ <*> pure direct
1722+ <*> pure oldC
1723+ <*> pure youngC
1724+ <*> threads .: "peak_count"
1725+ <*> threads .: "count"
1726+ <*> pure oldM
1727+ <*> pure survivorM
1728+ <*> pure youngM
1729+ <*> mem .: "non_heap_committed_in_bytes"
1730+ <*> mem .: "non_heap_used_in_bytes"
1731+ <*> mem .: "heap_max_in_bytes"
1732+ <*> mem .: "heap_committed_in_bytes"
1733+ <*> mem .: "heap_used_percent"
1734+ <*> mem .: "heap_used_in_bytes"
1735+ <*> (unMS <$> o .: "uptime_in_millis")
1736+ <*> (posixMS <$> o .: "timestamp")
1737+
1738+instance FromJSON JVMBufferPoolStats where
1739+ parseJSON = withObject "JVMBufferPoolStats" parse
1740+ where
1741+ parse o = JVMBufferPoolStats <$> o .: "total_capacity_in_bytes"
1742+ <*> o .: "used_in_bytes"
1743+ <*> o .: "count"
1744+
1745+instance FromJSON JVMGCStats where
1746+ parseJSON = withObject "JVMGCStats" parse
1747+ where
1748+ parse o = JVMGCStats <$> (unMS <$> o .: "collection_time_in_millis")
1749+ <*> o .: "collection_count"
1750+
1751+instance FromJSON JVMPoolStats where
1752+ parseJSON = withObject "JVMPoolStats" parse
1753+ where
1754+ parse o = JVMPoolStats <$> o .: "peak_max_in_bytes"
1755+ <*> o .: "peak_used_in_bytes"
1756+ <*> o .: "max_in_bytes"
1757+ <*> o .: "used_in_bytes"
1758+
1759+instance FromJSON NodeProcessStats where
1760+ parseJSON = withObject "NodeProcessStats" parse
1761+ where
1762+ parse o = do
1763+ mem <- o .: "mem"
1764+ cpu <- o .: "cpu"
1765+ NodeProcessStats <$> mem .: "total_virtual_in_bytes"
1766+ <*> mem .: "share_in_bytes"
1767+ <*> mem .: "resident_in_bytes"
1768+ <*> (unMS <$> cpu .: "total_in_millis")
1769+ <*> (unMS <$> cpu .: "user_in_millis")
1770+ <*> (unMS <$> cpu .: "sys_in_millis")
1771+ <*> cpu .: "percent"
1772+ <*> o .: "open_file_descriptors"
1773+ <*> (posixMS <$> o .: "timestamp")
1774+
1775+instance FromJSON NodeOSStats where
1776+ parseJSON = withObject "NodeOSStats" parse
1777+ where
1778+ parse o = do
1779+ swap <- o .: "swap"
1780+ mem <- o .: "mem"
1781+ cpu <- o .: "cpu"
1782+ load <- o .:? "load_average"
1783+ NodeOSStats <$> swap .: "free_in_bytes"
1784+ <*> swap .: "used_in_bytes"
1785+ <*> mem .: "actual_used_in_bytes"
1786+ <*> mem .: "actual_free_in_bytes"
1787+ <*> mem .: "used_percent"
1788+ <*> mem .: "free_percent"
1789+ <*> mem .: "used_in_bytes"
1790+ <*> mem .: "free_in_bytes"
1791+ <*> cpu .: "stolen"
1792+ <*> cpu .: "usage"
1793+ <*> cpu .: "idle"
1794+ <*> cpu .: "user"
1795+ <*> cpu .: "sys"
1796+ <*> pure load
1797+ <*> (unMS <$> o .: "uptime_in_millis")
1798+ <*> (posixMS <$> o .: "timestamp")
1799+
1800+instance FromJSON LoadAvgs where
1801+ parseJSON = withArray "LoadAvgs" parse
1802+ where
1803+ parse v = case V.toList v of
1804+ [one, five, fifteen] -> LoadAvgs <$> parseJSON one
1805+ <*> parseJSON five
1806+ <*> parseJSON fifteen
1807+ _ -> fail "Expecting a triple of Doubles"
1808+
1809+instance FromJSON NodeIndicesStats where
1810+ parseJSON = withObject "NodeIndicesStats" parse
1811+ where
1812+ parse o = do
1813+ let (.::) mv k = case mv of
1814+ Just v -> Just <$> v .: k
1815+ Nothing -> pure Nothing
1816+ mRecovery <- o .:? "recovery"
1817+ mQueryCache <- o .:? "query_cache"
1818+ suggest <- o .: "suggest"
1819+ translog <- o .: "translog"
1820+ segments <- o .: "segments"
1821+ completion <- o .: "completion"
1822+ percolate <- o .: "percolate"
1823+ fielddata <- o .: "fielddata"
1824+ idCache <- o .: "id_cache"
1825+ filterCache <- o .: "filter_cache"
1826+ warmer <- o .: "warmer"
1827+ flush <- o .: "flush"
1828+ refresh <- o .: "refresh"
1829+ merges <- o .: "merges"
1830+ search <- o .: "search"
1831+ getStats <- o .: "get"
1832+ indexing <- o .: "indexing"
1833+ store <- o .: "store"
1834+ docs <- o .: "docs"
1835+ NodeIndicesStats <$> (fmap unMS <$> mRecovery .:: "throttle_time_in_millis")
1836+ <*> mRecovery .:: "current_as_target"
1837+ <*> mRecovery .:: "current_as_source"
1838+ <*> mQueryCache .:: "miss_count"
1839+ <*> mQueryCache .:: "hit_count"
1840+ <*> mQueryCache .:: "evictions"
1841+ <*> mQueryCache .:: "memory_size_in_bytes"
1842+ <*> suggest .: "current"
1843+ <*> (unMS <$> suggest .: "time_in_millis")
1844+ <*> suggest .: "total"
1845+ <*> translog .: "size_in_bytes"
1846+ <*> translog .: "operations"
1847+ <*> segments .:? "fixed_bit_set_memory_in_bytes"
1848+ <*> segments .: "version_map_memory_in_bytes"
1849+ <*> segments .:? "index_writer_max_memory_in_bytes"
1850+ <*> segments .: "index_writer_memory_in_bytes"
1851+ <*> segments .: "memory_in_bytes"
1852+ <*> segments .: "count"
1853+ <*> completion .: "size_in_bytes"
1854+ <*> percolate .: "queries"
1855+ <*> percolate .: "memory_size_in_bytes"
1856+ <*> percolate .: "current"
1857+ <*> (unMS <$> percolate .: "time_in_millis")
1858+ <*> percolate .: "total"
1859+ <*> fielddata .: "evictions"
1860+ <*> fielddata .: "memory_size_in_bytes"
1861+ <*> idCache .: "memory_size_in_bytes"
1862+ <*> filterCache .: "evictions"
1863+ <*> filterCache .: "memory_size_in_bytes"
1864+ <*> (unMS <$> warmer .: "total_time_in_millis")
1865+ <*> warmer .: "total"
1866+ <*> warmer .: "current"
1867+ <*> (unMS <$> flush .: "total_time_in_millis")
1868+ <*> flush .: "total"
1869+ <*> (unMS <$> refresh .: "total_time_in_millis")
1870+ <*> refresh .: "total"
1871+ <*> merges .: "total_size_in_bytes"
1872+ <*> merges .: "total_docs"
1873+ <*> (unMS <$> merges .: "total_time_in_millis")
1874+ <*> merges .: "total"
1875+ <*> merges .: "current_size_in_bytes"
1876+ <*> merges .: "current_docs"
1877+ <*> merges .: "current"
1878+ <*> search .: "fetch_current"
1879+ <*> (unMS <$> search .: "fetch_time_in_millis")
1880+ <*> search .: "fetch_total"
1881+ <*> search .: "query_current"
1882+ <*> (unMS <$> search .: "query_time_in_millis")
1883+ <*> search .: "query_total"
1884+ <*> search .: "open_contexts"
1885+ <*> getStats .: "current"
1886+ <*> (unMS <$> getStats .: "missing_time_in_millis")
1887+ <*> getStats .: "missing_total"
1888+ <*> (unMS <$> getStats .: "exists_time_in_millis")
1889+ <*> getStats .: "exists_total"
1890+ <*> (unMS <$> getStats .: "time_in_millis")
1891+ <*> getStats .: "total"
1892+ <*> (fmap unMS <$> indexing .:? "throttle_time_in_millis")
1893+ <*> indexing .:? "is_throttled"
1894+ <*> indexing .:? "noop_update_total"
1895+ <*> indexing .: "delete_current"
1896+ <*> (unMS <$> indexing .: "delete_time_in_millis")
1897+ <*> indexing .: "delete_total"
1898+ <*> indexing .: "index_current"
1899+ <*> (unMS <$> indexing .: "index_time_in_millis")
1900+ <*> indexing .: "index_total"
1901+ <*> (unMS <$> store .: "throttle_time_in_millis")
1902+ <*> store .: "size_in_bytes"
1903+ <*> docs .: "deleted"
1904+ <*> docs .: "count"
1905+
1906+instance FromJSON NodeBreakersStats where
1907+ parseJSON = withObject "NodeBreakersStats" parse
1908+ where
1909+ parse o = NodeBreakersStats <$> o .: "parent"
1910+ <*> o .: "request"
1911+ <*> o .: "fielddata"
1912+
1913+instance FromJSON NodePluginInfo where
1914+ parseJSON = withObject "NodePluginInfo" parse
1915+ where
1916+ parse o = NodePluginInfo <$> o .: "site"
1917+ <*> o .: "jvm"
1918+ <*> o .: "description"
1919+ <*> o .: "version"
1920+ <*> o .: "name"
1921+
1922+instance FromJSON NodeHTTPInfo where
1923+ parseJSON = withObject "NodeHTTPInfo" parse
1924+ where
1925+ parse o = NodeHTTPInfo <$> o .: "max_content_length_in_bytes"
1926+ <*> parseJSON (Object o)
1927+
1928+instance FromJSON NodeTransportInfo where
1929+ parseJSON = withObject "NodeTransportInfo" parse
1930+ where
1931+ parse o = NodeTransportInfo <$> (maybe (return mempty) parseProfiles =<< o .:? "profiles")
1932+ <*> parseJSON (Object o)
1933+ parseProfiles (Object o) | HM.null o = return []
1934+ parseProfiles v@(Array _) = parseJSON v
1935+ parseProfiles Null = return []
1936+ parseProfiles _ = fail "Could not parse profiles"
1937+
1938+instance FromJSON NodeNetworkInfo where
1939+ parseJSON = withObject "NodeNetworkInfo" parse
1940+ where
1941+ parse o = NodeNetworkInfo <$> o .: "primary_interface"
1942+ <*> (unMS <$> o .: "refresh_interval_in_millis")
1943+
1944+instance FromJSON NodeNetworkInterface where
1945+ parseJSON = withObject "NodeNetworkInterface" parse
1946+ where
1947+ parse o = NodeNetworkInterface <$> o .: "mac_address"
1948+ <*> o .: "name"
1949+ <*> o .: "address"
1950+
1951+instance FromJSON NodeJVMInfo where
1952+ parseJSON = withObject "NodeJVMInfo" parse
1953+ where
1954+ parse o = NodeJVMInfo <$> o .: "memory_pools"
1955+ <*> o .: "gc_collectors"
1956+ <*> o .: "mem"
1957+ <*> (posixMS <$> o .: "start_time_in_millis")
1958+ <*> o .: "vm_vendor"
1959+ <*> o .: "vm_version"
1960+ <*> o .: "vm_name"
1961+ <*> (unJVMVersion <$> o .: "version")
1962+ <*> o .: "pid"
1963+
1964+instance FromJSON NodeThreadPoolsInfo where
1965+ parseJSON = withObject "NodeThreadPoolsInfo" parse
1966+ where
1967+ parse o = NodeThreadPoolsInfo <$> o .: "refresh"
1968+ <*> o .: "management"
1969+ <*> o .: "percolate"
1970+ <*> o .:? "listener"
1971+ <*> o .:? "fetch_shard_started"
1972+ <*> o .: "search"
1973+ <*> o .: "flush"
1974+ <*> o .: "warmer"
1975+ <*> o .: "optimize"
1976+ <*> o .: "bulk"
1977+ <*> o .: "suggest"
1978+ <*> o .: "merge"
1979+ <*> o .: "snapshot"
1980+ <*> o .: "get"
1981+ <*> o .:? "fetch_shard_store"
1982+ <*> o .: "index"
1983+ <*> o .: "generic"
1984+
1985+instance FromJSON NodeProcessInfo where
1986+ parseJSON = withObject "NodeProcessInfo" parse
1987+ where
1988+ parse o = NodeProcessInfo <$> o .: "mlockall"
1989+ <*> o .: "max_file_descriptors"
1990+ <*> o .: "id"
1991+ <*> (unMS <$> o .: "refresh_interval_in_millis")
1992+
1993+instance FromJSON NodeOSInfo where
1994+ parseJSON = withObject "NodeOSInfo" parse
1995+ where
1996+ parse o = do
1997+ swap <- o .: "swap"
1998+ mem <- o .: "mem"
1999+ NodeOSInfo <$> swap .: "total_in_bytes"
2000+ <*> mem .: "total_in_bytes"
2001+ <*> o .: "cpu"
2002+ <*> o .: "available_processors"
2003+ <*> (unMS <$> o .: "refresh_interval_in_millis")
2004+
2005+instance FromJSON ReplicaBounds where
2006+ parseJSON v = withText "ReplicaBounds" parseText v
2007+ <|> withBool "ReplicaBounds" parseBool v
2008+ where parseText t = case T.splitOn "-" t of
2009+ [a, "all"] -> ReplicasLowerBounded <$> parseReadText a
2010+ [a, b] -> ReplicasBounded <$> parseReadText a
2011+ <*> parseReadText b
2012+ _ -> fail ("Could not parse ReplicaBounds: " <> show t)
2013+ parseBool False = pure ReplicasUnbounded
2014+ parseBool _ = fail "ReplicasUnbounded cannot be represented with True"
2015+
2016+instance FromJSON NominalDiffTimeJSON where
2017+ parseJSON = withText "NominalDiffTime" parse
2018+ where parse t = case T.takeEnd 1 t of
2019+ "s" -> NominalDiffTimeJSON . fromInteger <$> parseReadText (T.dropEnd 1 t)
2020+ _ -> fail "Invalid or missing NominalDiffTime unit (expected s)"
2021+
2022+instance FromJSON AllocationPolicy where
2023+ parseJSON = withText "AllocationPolicy" parse
2024+ where parse "all" = pure AllocAll
2025+ parse "primaries" = pure AllocPrimaries
2026+ parse "new_primaries" = pure AllocNewPrimaries
2027+ parse "none" = pure AllocNone
2028+ parse t = fail ("Invlaid AllocationPolicy: " <> show t)
2029+
2030+instance ToJSON CompoundFormat where
2031+ toJSON (CompoundFileFormat x) = Bool x
2032+ toJSON (MergeSegmentVsTotalIndex x) = toJSON x
2033+
2034+instance FromJSON CompoundFormat where
2035+ parseJSON v = CompoundFileFormat <$> parseJSON v
2036+ <|> MergeSegmentVsTotalIndex <$> parseJSON v
2037+
2038+instance FromJSON NodeFSTotalStats where
2039+ parseJSON = withObject "NodeFSTotalStats" parse
2040+ where
2041+ parse o = NodeFSTotalStats <$> (fmap unStringlyTypedDouble <$> o .:? "disk_service_time")
2042+ <*> (fmap unStringlyTypedDouble <$> o .:? "disk_queue")
2043+ <*> o .:? "disk_io_size_in_bytes"
2044+ <*> o .:? "disk_write_size_in_bytes"
2045+ <*> o .:? "disk_read_size_in_bytes"
2046+ <*> o .:? "disk_io_op"
2047+ <*> o .:? "disk_writes"
2048+ <*> o .:? "disk_reads"
2049+ <*> o .: "available_in_bytes"
2050+ <*> o .: "free_in_bytes"
2051+ <*> o .: "total_in_bytes"
2052+
2053+instance FromJSON BoundTransportAddress where
2054+ parseJSON = withObject "BoundTransportAddress" parse
2055+ where
2056+ parse o = BoundTransportAddress <$> o .: "publish_address"
2057+ <*> o .: "bound_address"
2058+
2059+instance FromJSON JVMMemoryInfo where
2060+ parseJSON = withObject "JVMMemoryInfo" parse
2061+ where
2062+ parse o = JVMMemoryInfo <$> o .: "direct_max_in_bytes"
2063+ <*> o .: "non_heap_max_in_bytes"
2064+ <*> o .: "non_heap_init_in_bytes"
2065+ <*> o .: "heap_max_in_bytes"
2066+ <*> o .: "heap_init_in_bytes"
2067+
2068+instance FromJSON JVMVersion where
2069+ parseJSON (String t) =
2070+ JVMVersion <$> parseJSON (String (T.replace "_" "." t))
2071+ parseJSON v = JVMVersion <$> parseJSON v
2072+
2073+instance FromJSON NodeThreadPoolInfo where
2074+ parseJSON = withObject "NodeThreadPoolInfo" parse
2075+ where
2076+ parse o = do
2077+ ka <- maybe (return Nothing) (fmap Just . parseStringInterval) =<< o .:? "keep_alive"
2078+ NodeThreadPoolInfo <$> (parseJSON . unStringlyTypeJSON =<< o .: "queue_size")
2079+ <*> pure ka
2080+ <*> o .:? "min"
2081+ <*> o .:? "max"
2082+ <*> o .: "type"
2083+
2084+instance FromJSON CPUInfo where
2085+ parseJSON = withObject "CPUInfo" parse
2086+ where
2087+ parse o = CPUInfo <$> o .: "cache_size_in_bytes"
2088+ <*> o .: "cores_per_socket"
2089+ <*> o .: "total_sockets"
2090+ <*> o .: "total_cores"
2091+ <*> o .: "mhz"
2092+ <*> o .: "model"
2093+ <*> o .: "vendor"
2094+
2095+instance FromJSON ThreadPoolSize where
2096+ parseJSON v = parseAsNumber v <|> parseAsString v
2097+ where
2098+ parseAsNumber = parseAsInt <=< parseJSON
2099+ parseAsInt (-1) = return ThreadPoolUnbounded
2100+ parseAsInt n
2101+ | n >= 0 = return (ThreadPoolBounded n)
2102+ | otherwise = fail "Thread pool size must be >= -1."
2103+ parseAsString = withText "ThreadPoolSize" $ \t ->
2104+ case first (readMay . T.unpack) (T.span isNumber t) of
2105+ (Just n, "k") -> return (ThreadPoolBounded (n * 1000))
2106+ (Just n, "") -> return (ThreadPoolBounded n)
2107+ _ -> fail ("Invalid thread pool size " <> T.unpack t)
2108+
2109+instance FromJSON ThreadPoolType where
2110+ parseJSON = withText "ThreadPoolType" parse
2111+ where
2112+ parse "scaling" = return ThreadPoolScaling
2113+ parse "fixed" = return ThreadPoolFixed
2114+ parse "cached" = return ThreadPoolCached
2115+ parse e = fail ("Unexpected thread pool type" <> T.unpack e)
2116+
2117+data Source =
2118+ NoSource
2119+ | SourcePatterns PatternOrPatterns
2120+ | SourceIncludeExclude Include Exclude
2121+ deriving (Show, Eq)
2122+
2123+instance ToJSON Source where
2124+ toJSON NoSource = toJSON False
2125+ toJSON (SourcePatterns patterns) = toJSON patterns
2126+ toJSON (SourceIncludeExclude incl excl) = object [ "include" .= incl, "exclude" .= excl ]
2127+
2128+data PatternOrPatterns =
2129+ PopPattern Pattern
2130+ | PopPatterns [Pattern]
2131+ deriving (Eq, Show)
2132+
2133+data Include = Include [Pattern] deriving (Eq, Show)
2134+data Exclude = Exclude [Pattern] deriving (Eq, Show)
2135+
2136+newtype Pattern = Pattern Text deriving (Eq, Show)
2137+
2138+newtype ScrollId = ScrollId Text deriving (Eq, Show, Ord, ToJSON, FromJSON)
2139+
2140+instance ToJSON PatternOrPatterns where
2141+ toJSON (PopPattern pattern) = toJSON pattern
2142+ toJSON (PopPatterns patterns) = toJSON patterns
2143+
2144+instance ToJSON Include where
2145+ toJSON (Include patterns) = toJSON patterns
2146+
2147+instance ToJSON Exclude where
2148+ toJSON (Exclude patterns) = toJSON patterns
2149+
2150+instance ToJSON Pattern where
2151+ toJSON (Pattern pattern) = toJSON pattern
2152+
2153+instance FromJSON NodesStats where
2154+ parseJSON = withObject "NodesStats" parse
2155+ where
2156+ parse o = do
2157+ nodes <- o .: "nodes"
2158+ stats <- forM (HM.toList nodes) $ \(fullNID, v) -> do
2159+ node <- parseJSON v
2160+ parseNodeStats (FullNodeId fullNID) node
2161+ cn <- o .: "cluster_name"
2162+ return (NodesStats stats cn)
2163+
2164+instance ToJSON UpdatableIndexSetting where
2165+ toJSON (NumberOfReplicas x) = oPath ("index" :| ["number_of_replicas"]) x
2166+ toJSON (AutoExpandReplicas x) = oPath ("index" :| ["auto_expand_replicas"]) x
2167+ toJSON (RefreshInterval x) = oPath ("index" :| ["refresh_interval"]) (NominalDiffTimeJSON x)
2168+ toJSON (IndexConcurrency x) = oPath ("index" :| ["concurrency"]) x
2169+ toJSON (FailOnMergeFailure x) = oPath ("index" :| ["fail_on_merge_failure"]) x
2170+ toJSON (TranslogFlushThresholdOps x) = oPath ("index" :| ["translog", "flush_threshold_ops"]) x
2171+ toJSON (TranslogFlushThresholdSize x) = oPath ("index" :| ["translog", "flush_threshold_size"]) x
2172+ toJSON (TranslogFlushThresholdPeriod x) = oPath ("index" :| ["translog", "flush_threshold_period"]) (NominalDiffTimeJSON x)
2173+ toJSON (TranslogDisableFlush x) = oPath ("index" :| ["translog", "disable_flush"]) x
2174+ toJSON (CacheFilterMaxSize x) = oPath ("index" :| ["cache", "filter", "max_size"]) x
2175+ toJSON (CacheFilterExpire x) = oPath ("index" :| ["cache", "filter", "expire"]) (NominalDiffTimeJSON <$> x)
2176+ toJSON (GatewaySnapshotInterval x) = oPath ("index" :| ["gateway", "snapshot_interval"]) (NominalDiffTimeJSON x)
2177+ toJSON (RoutingAllocationInclude fs) = oPath ("index" :| ["routing", "allocation", "include"]) (attrFilterJSON fs)
2178+ toJSON (RoutingAllocationExclude fs) = oPath ("index" :| ["routing", "allocation", "exclude"]) (attrFilterJSON fs)
2179+ toJSON (RoutingAllocationRequire fs) = oPath ("index" :| ["routing", "allocation", "require"]) (attrFilterJSON fs)
2180+ toJSON (RoutingAllocationEnable x) = oPath ("index" :| ["routing", "allocation", "enable"]) x
2181+ toJSON (RoutingAllocationShardsPerNode x) = oPath ("index" :| ["routing", "allocation", "total_shards_per_node"]) x
2182+ toJSON (RecoveryInitialShards x) = oPath ("index" :| ["recovery", "initial_shards"]) x
2183+ toJSON (GCDeletes x) = oPath ("index" :| ["gc_deletes"]) (NominalDiffTimeJSON x)
2184+ toJSON (TTLDisablePurge x) = oPath ("index" :| ["ttl", "disable_purge"]) x
2185+ toJSON (TranslogFSType x) = oPath ("index" :| ["translog", "fs", "type"]) x
2186+ toJSON (IndexCompoundFormat x) = oPath ("index" :| ["compound_format"]) x
2187+ toJSON (IndexCompoundOnFlush x) = oPath ("index" :| ["compound_on_flush"]) x
2188+ toJSON (WarmerEnabled x) = oPath ("index" :| ["warmer", "enabled"]) x
2189+ toJSON (BlocksReadOnly x) = oPath ("blocks" :| ["read_only"]) x
2190+ toJSON (BlocksRead x) = oPath ("blocks" :| ["read"]) x
2191+ toJSON (BlocksWrite x) = oPath ("blocks" :| ["write"]) x
2192+ toJSON (BlocksMetaData x) = oPath ("blocks" :| ["metadata"]) x
2193+
2194+instance FromJSON UpdatableIndexSetting where
2195+ parseJSON = withObject "UpdatableIndexSetting" parse
2196+ where parse o = numberOfReplicas `taggedAt` ["index", "number_of_replicas"]
2197+ <|> autoExpandReplicas `taggedAt` ["index", "auto_expand_replicas"]
2198+ <|> refreshInterval `taggedAt` ["index", "refresh_interval"]
2199+ <|> indexConcurrency `taggedAt` ["index", "concurrency"]
2200+ <|> failOnMergeFailure `taggedAt` ["index", "fail_on_merge_failure"]
2201+ <|> translogFlushThresholdOps `taggedAt` ["index", "translog", "flush_threshold_ops"]
2202+ <|> translogFlushThresholdSize `taggedAt` ["index", "translog", "flush_threshold_size"]
2203+ <|> translogFlushThresholdPeriod `taggedAt` ["index", "translog", "flush_threshold_period"]
2204+ <|> translogDisableFlush `taggedAt` ["index", "translog", "disable_flush"]
2205+ <|> cacheFilterMaxSize `taggedAt` ["index", "cache", "filter", "max_size"]
2206+ <|> cacheFilterExpire `taggedAt` ["index", "cache", "filter", "expire"]
2207+ <|> gatewaySnapshotInterval `taggedAt` ["index", "gateway", "snapshot_interval"]
2208+ <|> routingAllocationInclude `taggedAt` ["index", "routing", "allocation", "include"]
2209+ <|> routingAllocationExclude `taggedAt` ["index", "routing", "allocation", "exclude"]
2210+ <|> routingAllocationRequire `taggedAt` ["index", "routing", "allocation", "require"]
2211+ <|> routingAllocationEnable `taggedAt` ["index", "routing", "allocation", "enable"]
2212+ <|> routingAllocationShardsPerNode `taggedAt` ["index", "routing", "allocation", "total_shards_per_node"]
2213+ <|> recoveryInitialShards `taggedAt` ["index", "recovery", "initial_shards"]
2214+ <|> gcDeletes `taggedAt` ["index", "gc_deletes"]
2215+ <|> ttlDisablePurge `taggedAt` ["index", "ttl", "disable_purge"]
2216+ <|> translogFSType `taggedAt` ["index", "translog", "fs", "type"]
2217+ <|> compoundFormat `taggedAt` ["index", "compound_format"]
2218+ <|> compoundOnFlush `taggedAt` ["index", "compound_on_flush"]
2219+ <|> warmerEnabled `taggedAt` ["index", "warmer", "enabled"]
2220+ <|> blocksReadOnly `taggedAt` ["blocks", "read_only"]
2221+ <|> blocksRead `taggedAt` ["blocks", "read"]
2222+ <|> blocksWrite `taggedAt` ["blocks", "write"]
2223+ <|> blocksMetaData `taggedAt` ["blocks", "metadata"]
2224+ where taggedAt f ks = taggedAt' f (Object o) ks
2225+ taggedAt' f v [] = f =<< (parseJSON v <|> (parseJSON (unStringlyTypeJSON v)))
2226+ taggedAt' f v (k:ks) = withObject "Object" (\o -> do v' <- o .: k
2227+ taggedAt' f v' ks) v
2228+ numberOfReplicas = pure . NumberOfReplicas
2229+ autoExpandReplicas = pure . AutoExpandReplicas
2230+ refreshInterval = pure . RefreshInterval . ndtJSON
2231+ indexConcurrency = pure . IndexConcurrency
2232+ failOnMergeFailure = pure . FailOnMergeFailure
2233+ translogFlushThresholdOps = pure . TranslogFlushThresholdOps
2234+ translogFlushThresholdSize = pure . TranslogFlushThresholdSize
2235+ translogFlushThresholdPeriod = pure . TranslogFlushThresholdPeriod . ndtJSON
2236+ translogDisableFlush = pure . TranslogDisableFlush
2237+ cacheFilterMaxSize = pure . CacheFilterMaxSize
2238+ cacheFilterExpire = pure . CacheFilterExpire . fmap ndtJSON
2239+ gatewaySnapshotInterval = pure . GatewaySnapshotInterval . ndtJSON
2240+ routingAllocationInclude = fmap RoutingAllocationInclude . parseAttrFilter
2241+ routingAllocationExclude = fmap RoutingAllocationExclude . parseAttrFilter
2242+ routingAllocationRequire = fmap RoutingAllocationRequire . parseAttrFilter
2243+ routingAllocationEnable = pure . RoutingAllocationEnable
2244+ routingAllocationShardsPerNode = pure . RoutingAllocationShardsPerNode
2245+ recoveryInitialShards = pure . RecoveryInitialShards
2246+ gcDeletes = pure . GCDeletes . ndtJSON
2247+ ttlDisablePurge = pure . TTLDisablePurge
2248+ translogFSType = pure . TranslogFSType
2249+ compoundFormat = pure . IndexCompoundFormat
2250+ compoundOnFlush = pure . IndexCompoundOnFlush
2251+ warmerEnabled = pure . WarmerEnabled
2252+ blocksReadOnly = pure . BlocksReadOnly
2253+ blocksRead = pure . BlocksRead
2254+ blocksWrite = pure . BlocksWrite
2255+ blocksMetaData = pure . BlocksMetaData
2256+
2257+instance ToJSON ReplicaBounds where
2258+ toJSON (ReplicasBounded a b) = String (showText a <> "-" <> showText b)
2259+ toJSON (ReplicasLowerBounded a) = String (showText a <> "-all")
2260+ toJSON ReplicasUnbounded = Bool False
2261+
2262+instance ToJSON NominalDiffTimeJSON where
2263+ toJSON (NominalDiffTimeJSON t) = String (showText (round t :: Integer) <> "s")
2264+
2265+instance ToJSON AllocationPolicy where
2266+ toJSON AllocAll = String "all"
2267+ toJSON AllocPrimaries = String "primaries"
2268+ toJSON AllocNewPrimaries = String "new_primaries"
2269+ toJSON AllocNone = String "none"
2270+
2271+instance ToJSON IndexTemplate where
2272+ toJSON (IndexTemplate p s m) = merge
2273+ (object [ "template" .= p
2274+ , "mappings" .= foldl' merge (object []) m
2275+ ])
2276+ (toJSON s)
2277+ where
2278+ merge (Object o1) (Object o2) = toJSON $ HM.union o1 o2
2279+ merge o Null = o
2280+ merge _ _ = undefined
2281+
2282+instance FromJSON IndexAliasesSummary where
2283+ parseJSON = withObject "IndexAliasesSummary" parse
2284+ where parse o = IndexAliasesSummary . mconcat <$> mapM (uncurry go) (HM.toList o)
2285+ go ixn = withObject "index aliases" $ \ia -> do
2286+ aliases <- ia .:? "aliases" .!= mempty
2287+ forM (HM.toList aliases) $ \(aName, v) -> do
2288+ let indexAlias = IndexAlias (IndexName ixn) (IndexAliasName (IndexName aName))
2289+ IndexAliasSummary indexAlias <$> parseJSON v
2290+
2291+instance ToJSON IndexAliasAction where
2292+ toJSON (AddAlias ia opts) = object ["add" .= (iaObj <> optsObj)]
2293+ where Object iaObj = toJSON ia
2294+ Object optsObj = toJSON opts
2295+ toJSON (RemoveAlias ia) = object ["remove" .= iaObj]
2296+ where Object iaObj = toJSON ia
2297+
2298+instance ToJSON IndexAlias where
2299+ toJSON IndexAlias {..} = object ["index" .= srcIndex
2300+ , "alias" .= indexAlias
2301+ ]
2302+
2303+instance FromJSON AliasRouting where
2304+ parseJSON = withObject "AliasRouting" parse
2305+ where parse o = parseAll o <|> parseGranular o
2306+ parseAll o = AllAliasRouting <$> o .: "routing"
2307+ parseGranular o = do
2308+ sr <- o .:? "search_routing"
2309+ ir <- o .:? "index_routing"
2310+ if isNothing sr && isNothing ir
2311+ then fail "Both search_routing and index_routing can't be blank"
2312+ else return (GranularAliasRouting sr ir)
2313+
2314+instance FromJSON IndexAliasCreate where
2315+ parseJSON v = withObject "IndexAliasCreate" parse v
2316+ where parse o = IndexAliasCreate <$> optional (parseJSON v)
2317+ <*> o .:? "filter"
2318+
2319+instance ToJSON IndexAliasCreate where
2320+ toJSON IndexAliasCreate {..} = Object (filterObj <> routingObj)
2321+ where filterObj = maybe mempty (HM.singleton "filter" . toJSON) aliasCreateFilter
2322+ Object routingObj = maybe (Object mempty) toJSON aliasCreateRouting
2323+
2324+instance ToJSON AliasRouting where
2325+ toJSON (AllAliasRouting v) = object ["routing" .= v]
2326+ toJSON (GranularAliasRouting srch idx) = object (catMaybes prs)
2327+ where prs = [("search_routing" .=) <$> srch
2328+ ,("index_routing" .=) <$> idx]
2329+
2330+instance ToJSON SearchAliasRouting where
2331+ toJSON (SearchAliasRouting rvs) = toJSON (T.intercalate "," (routingValue <$> toList rvs))
2332+
2333+instance FromJSON SearchAliasRouting where
2334+ parseJSON = withText "SearchAliasRouting" parse
2335+ where parse t = SearchAliasRouting <$> parseNEJSON (String <$> T.splitOn "," t)
2336+
2337+instance FromJSON EsError where
2338+ parseJSON (Object v) = EsError <$>
2339+ v .: "status" <*>
2340+ (v .: "error" <|> (v .: "error" >>= (.: "reason")))
2341+ parseJSON _ = empty