packages feed

influxdb 0.10.0 → 1.0.0

raw patch · 24 files changed

+1807/−2358 lines, 24 filesdep +clockdep +foldldep +http-typesdep −dlistdep −exceptionsdep −ghc-primdep ~aesondep ~basedep ~bytestringnew-component:exe:influx-write-udpPVP ok

version bump matches the API change (PVP)

Dependencies added: clock, foldl, http-types, lens, optional-args, unordered-containers

Dependencies removed: dlist, exceptions, ghc-prim, network-uri, retry, tagged, template-haskell

Dependency ranges changed: aeson, base, bytestring, containers, http-client, mtl, network, scientific, time, vector

API changes (from Hackage documentation)

- Database.InfluxDB: (.!=) :: Parser (Maybe a) -> a -> Parser a
- Database.InfluxDB: (.:) :: FromValue a => Vector Value -> Column -> ValueParser a
- Database.InfluxDB: (.:?) :: FromValue a => Vector Value -> Column -> ValueParser (Maybe a)
- Database.InfluxDB: Admin :: Text -> Admin
- Database.InfluxDB: Bool :: !Bool -> Value
- Database.InfluxDB: Config :: !Credentials -> !(IORef ServerPool) -> !Manager -> Config
- Database.InfluxDB: Credentials :: !Text -> !Text -> Credentials
- Database.InfluxDB: Database :: Text -> Database
- Database.InfluxDB: DatabaseRequest :: [ShardSpaceRequest] -> [Text] -> DatabaseRequest
- Database.InfluxDB: Done :: Stream m a
- Database.InfluxDB: Float :: !Double -> Value
- Database.InfluxDB: Int :: !Int64 -> Value
- Database.InfluxDB: JsonDecodeError :: String -> InfluxException
- Database.InfluxDB: MicrosecondsPrecision :: TimePrecision
- Database.InfluxDB: MillisecondsPrecision :: TimePrecision
- Database.InfluxDB: Null :: Value
- Database.InfluxDB: Ping :: Text -> Ping
- Database.InfluxDB: SecondsPrecision :: TimePrecision
- Database.InfluxDB: Series :: {-# UNPACK #-} !Text -> {-# UNPACK #-} !SeriesData -> Series
- Database.InfluxDB: SeriesData :: Vector Column -> [Vector Value] -> SeriesData
- Database.InfluxDB: SeriesDecodeError :: String -> InfluxException
- Database.InfluxDB: Server :: !Text -> !Int -> !Bool -> Server
- Database.InfluxDB: ShardSpace :: Maybe Text -> Text -> Text -> Text -> Text -> Word32 -> Word32 -> ShardSpace
- Database.InfluxDB: ShardSpaceRequest :: Text -> Text -> Text -> Text -> Word32 -> Word32 -> ShardSpaceRequest
- Database.InfluxDB: String :: !Text -> Value
- Database.InfluxDB: User :: Text -> Bool -> User
- Database.InfluxDB: Yield :: a -> (m (Stream m a)) -> Stream m a
- Database.InfluxDB: [adminName] :: Admin -> Text
- Database.InfluxDB: [configCreds] :: Config -> !Credentials
- Database.InfluxDB: [configHttpManager] :: Config -> !Manager
- Database.InfluxDB: [configServerPool] :: Config -> !(IORef ServerPool)
- Database.InfluxDB: [credsPassword] :: Credentials -> !Text
- Database.InfluxDB: [credsUser] :: Credentials -> !Text
- Database.InfluxDB: [databaseName] :: Database -> Text
- Database.InfluxDB: [databaseRequestContinuousQueries] :: DatabaseRequest -> [Text]
- Database.InfluxDB: [databaseRequestSpaces] :: DatabaseRequest -> [ShardSpaceRequest]
- Database.InfluxDB: [pingStatus] :: Ping -> Text
- Database.InfluxDB: [seriesDataColumns] :: SeriesData -> Vector Column
- Database.InfluxDB: [seriesDataPoints] :: SeriesData -> [Vector Value]
- Database.InfluxDB: [seriesData] :: Series -> {-# UNPACK #-} !SeriesData
- Database.InfluxDB: [seriesName] :: Series -> {-# UNPACK #-} !Text
- Database.InfluxDB: [serverHost] :: Server -> !Text
- Database.InfluxDB: [serverPort] :: Server -> !Int
- Database.InfluxDB: [serverSsl] :: Server -> !Bool
- Database.InfluxDB: [shardSpaceDatabase] :: ShardSpace -> Maybe Text
- Database.InfluxDB: [shardSpaceName] :: ShardSpace -> Text
- Database.InfluxDB: [shardSpaceRegex] :: ShardSpace -> Text
- Database.InfluxDB: [shardSpaceReplicationFactor] :: ShardSpace -> Word32
- Database.InfluxDB: [shardSpaceRequestName] :: ShardSpaceRequest -> Text
- Database.InfluxDB: [shardSpaceRequestRegex] :: ShardSpaceRequest -> Text
- Database.InfluxDB: [shardSpaceRequestReplicationFactor] :: ShardSpaceRequest -> Word32
- Database.InfluxDB: [shardSpaceRequestRetentionPolicy] :: ShardSpaceRequest -> Text
- Database.InfluxDB: [shardSpaceRequestShardDuration] :: ShardSpaceRequest -> Text
- Database.InfluxDB: [shardSpaceRequestSplit] :: ShardSpaceRequest -> Word32
- Database.InfluxDB: [shardSpaceRetentionPolicy] :: ShardSpace -> Text
- Database.InfluxDB: [shardSpaceShardDuration] :: ShardSpace -> Text
- Database.InfluxDB: [shardSpaceSplit] :: ShardSpace -> Word32
- Database.InfluxDB: [userIsAdmin] :: User -> Bool
- Database.InfluxDB: [userName] :: User -> Text
- Database.InfluxDB: addClusterAdmin :: Config -> Text -> Text -> IO Admin
- Database.InfluxDB: addDatabaseUser :: Config -> Text -> Text -> Text -> IO ()
- Database.InfluxDB: authenticateClusterAdmin :: Config -> IO ()
- Database.InfluxDB: authenticateDatabaseUser :: Config -> Text -> IO ()
- Database.InfluxDB: class FromSeries a
- Database.InfluxDB: class FromSeriesData a
- Database.InfluxDB: class FromValue a
- Database.InfluxDB: class ToSeriesData a
- Database.InfluxDB: class ToValue a
- Database.InfluxDB: configureDatabase :: Config -> Text -> DatabaseRequest -> IO ()
- Database.InfluxDB: createDatabase :: Config -> Text -> IO ()
- Database.InfluxDB: createShardSpace :: Config -> Text -> ShardSpaceRequest -> IO ()
- Database.InfluxDB: data Config
- Database.InfluxDB: data DatabaseRequest
- Database.InfluxDB: data PointT p m a
- Database.InfluxDB: data Series
- Database.InfluxDB: data SeriesData
- Database.InfluxDB: data SeriesT m a
- Database.InfluxDB: data ServerPool
- Database.InfluxDB: data ShardSpace
- Database.InfluxDB: data ShardSpaceRequest
- Database.InfluxDB: data Stream m a
- Database.InfluxDB: data TimePrecision
- Database.InfluxDB: data User
- Database.InfluxDB: data Value
- Database.InfluxDB: deleteClusterAdmin :: Config -> Admin -> IO ()
- Database.InfluxDB: deleteDatabaseUser :: Config -> Text -> Text -> IO ()
- Database.InfluxDB: deleteSeries :: Config -> Text -> Text -> IO ()
- Database.InfluxDB: dropDatabase :: Config -> Text -> IO ()
- Database.InfluxDB: dropShardSpace :: Config -> Text -> Text -> IO ()
- Database.InfluxDB: fromSeries :: FromSeries a => Series -> Either String a
- Database.InfluxDB: fromSeriesData :: FromSeriesData a => SeriesData -> Either String [a]
- Database.InfluxDB: fromValue :: FromValue a => Value -> Either String a
- Database.InfluxDB: grantAdminPrivilegeTo :: Config -> Text -> Text -> IO ()
- Database.InfluxDB: isInSync :: Config -> IO Bool
- Database.InfluxDB: listClusterAdmins :: Config -> IO [Admin]
- Database.InfluxDB: listDatabaseUsers :: Config -> Text -> IO [User]
- Database.InfluxDB: listDatabases :: Config -> IO [Database]
- Database.InfluxDB: listShardSpaces :: Config -> IO [ShardSpace]
- Database.InfluxDB: newServerPool :: Server -> [Server] -> IO (IORef ServerPool)
- Database.InfluxDB: newServerPoolWithRetryPolicy :: RetryPolicy -> Server -> [Server] -> IO (IORef ServerPool)
- Database.InfluxDB: newtype Admin
- Database.InfluxDB: newtype Database
- Database.InfluxDB: newtype Ping
- Database.InfluxDB: parseSeries :: FromSeries a => Series -> Parser a
- Database.InfluxDB: parseSeriesData :: FromSeriesData a => Vector Column -> Vector Value -> Parser a
- Database.InfluxDB: parseValue :: FromValue a => Value -> Parser a
- Database.InfluxDB: ping :: Config -> IO Ping
- Database.InfluxDB: post :: Config -> Text -> SeriesT IO a -> IO a
- Database.InfluxDB: postWithPrecision :: Config -> Text -> TimePrecision -> SeriesT IO a -> IO a
- Database.InfluxDB: revokeAdminPrivilegeFrom :: Config -> Text -> Text -> IO ()
- Database.InfluxDB: rootCreds :: Credentials
- Database.InfluxDB: seriesColumns :: Series -> Vector Column
- Database.InfluxDB: seriesPoints :: Series -> [Vector Value]
- Database.InfluxDB: toSeriesColumns :: ToSeriesData a => Proxy a -> Vector Column
- Database.InfluxDB: toSeriesPoints :: ToSeriesData a => a -> Vector Value
- Database.InfluxDB: toValue :: ToValue a => a -> Value
- Database.InfluxDB: typeMismatch :: String -> Value -> Parser a
- Database.InfluxDB: updateClusterAdminPassword :: Config -> Admin -> Text -> IO ()
- Database.InfluxDB: updateDatabaseUserPassword :: Config -> Text -> Text -> Text -> IO ()
- Database.InfluxDB: withSeries :: forall m a. (Monad m, ToSeriesData a) => Text -> PointT a m () -> SeriesT m ()
- Database.InfluxDB: withValues :: (Vector Value -> ValueParser a) -> Vector Column -> Vector Value -> Parser a
- Database.InfluxDB: writePoints :: (Monad m, ToSeriesData a) => a -> PointT a m ()
- Database.InfluxDB: writeSeries :: (Monad m, ToSeriesData a) => Text -> a -> SeriesT m ()
- Database.InfluxDB: writeSeriesData :: Monad m => Text -> SeriesData -> SeriesT m ()
- Database.InfluxDB.Decode: (.!=) :: Parser (Maybe a) -> a -> Parser a
- Database.InfluxDB.Decode: (.:) :: FromValue a => Vector Value -> Column -> ValueParser a
- Database.InfluxDB.Decode: (.:?) :: FromValue a => Vector Value -> Column -> ValueParser (Maybe a)
- Database.InfluxDB.Decode: class FromSeries a
- Database.InfluxDB.Decode: class FromSeriesData a
- Database.InfluxDB.Decode: class FromValue a
- Database.InfluxDB.Decode: data Parser a
- Database.InfluxDB.Decode: data ValueParser a
- Database.InfluxDB.Decode: fromSeries :: FromSeries a => Series -> Either String a
- Database.InfluxDB.Decode: fromSeriesData :: FromSeriesData a => SeriesData -> Either String [a]
- Database.InfluxDB.Decode: fromSeriesData_ :: FromSeriesData a => SeriesData -> [a]
- Database.InfluxDB.Decode: fromValue :: FromValue a => Value -> Either String a
- Database.InfluxDB.Decode: instance Control.Monad.Reader.Class.MonadReader Database.InfluxDB.Decode.ColumnIndex Database.InfluxDB.Decode.ValueParser
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromSeries Database.InfluxDB.Types.Series
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromSeries Database.InfluxDB.Types.SeriesData
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromSeriesData Database.InfluxDB.Types.SeriesData
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue Data.Text.Internal.Lazy.Text
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue Data.Text.Internal.Text
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue Database.InfluxDB.Types.Value
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue GHC.Base.String
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue GHC.Int.Int16
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue GHC.Int.Int32
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue GHC.Int.Int64
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue GHC.Int.Int8
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue GHC.Types.Bool
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue GHC.Types.Double
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue GHC.Types.Int
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue GHC.Word.Word16
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue GHC.Word.Word32
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue GHC.Word.Word8
- Database.InfluxDB.Decode: instance Database.InfluxDB.Decode.FromValue a => Database.InfluxDB.Decode.FromValue (GHC.Base.Maybe a)
- Database.InfluxDB.Decode: instance GHC.Base.Applicative Database.InfluxDB.Decode.Parser
- Database.InfluxDB.Decode: instance GHC.Base.Applicative Database.InfluxDB.Decode.ValueParser
- Database.InfluxDB.Decode: instance GHC.Base.Functor Database.InfluxDB.Decode.Parser
- Database.InfluxDB.Decode: instance GHC.Base.Functor Database.InfluxDB.Decode.ValueParser
- Database.InfluxDB.Decode: instance GHC.Base.Monad Database.InfluxDB.Decode.Parser
- Database.InfluxDB.Decode: instance GHC.Base.Monad Database.InfluxDB.Decode.ValueParser
- Database.InfluxDB.Decode: parseSeries :: FromSeries a => Series -> Parser a
- Database.InfluxDB.Decode: parseSeriesData :: FromSeriesData a => Vector Column -> Vector Value -> Parser a
- Database.InfluxDB.Decode: parseValue :: FromValue a => Value -> Parser a
- Database.InfluxDB.Decode: typeMismatch :: String -> Value -> Parser a
- Database.InfluxDB.Decode: withValues :: (Vector Value -> ValueParser a) -> Vector Column -> Vector Value -> Parser a
- Database.InfluxDB.Encode: class ToSeries a
- Database.InfluxDB.Encode: class ToSeriesData a
- Database.InfluxDB.Encode: class ToValue a
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue Data.Text.Internal.Lazy.Text
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue Data.Text.Internal.Text
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue Database.InfluxDB.Types.Value
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue GHC.Base.String
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue GHC.Int.Int16
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue GHC.Int.Int32
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue GHC.Int.Int64
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue GHC.Int.Int8
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue GHC.Types.Bool
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue GHC.Types.Double
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue GHC.Types.Int
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue GHC.Word.Word16
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue GHC.Word.Word32
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue GHC.Word.Word8
- Database.InfluxDB.Encode: instance Database.InfluxDB.Encode.ToValue a => Database.InfluxDB.Encode.ToValue (GHC.Base.Maybe a)
- Database.InfluxDB.Encode: toSeries :: ToSeries a => a -> Series
- Database.InfluxDB.Encode: toSeriesColumns :: ToSeriesData a => Proxy a -> Vector Column
- Database.InfluxDB.Encode: toSeriesData :: forall a. ToSeriesData a => a -> SeriesData
- Database.InfluxDB.Encode: toSeriesPoints :: ToSeriesData a => a -> Vector Value
- Database.InfluxDB.Encode: toValue :: ToValue a => a -> Value
- Database.InfluxDB.Http: Config :: !Credentials -> !(IORef ServerPool) -> !Manager -> Config
- Database.InfluxDB.Http: Credentials :: !Text -> !Text -> Credentials
- Database.InfluxDB.Http: DatabaseRequest :: [ShardSpaceRequest] -> [Text] -> DatabaseRequest
- Database.InfluxDB.Http: Done :: Stream m a
- Database.InfluxDB.Http: MicrosecondsPrecision :: TimePrecision
- Database.InfluxDB.Http: MillisecondsPrecision :: TimePrecision
- Database.InfluxDB.Http: SecondsPrecision :: TimePrecision
- Database.InfluxDB.Http: Server :: !Text -> !Int -> !Bool -> Server
- Database.InfluxDB.Http: ShardSpaceRequest :: Text -> Text -> Text -> Text -> Word32 -> Word32 -> ShardSpaceRequest
- Database.InfluxDB.Http: Yield :: a -> (m (Stream m a)) -> Stream m a
- Database.InfluxDB.Http: [configCreds] :: Config -> !Credentials
- Database.InfluxDB.Http: [configHttpManager] :: Config -> !Manager
- Database.InfluxDB.Http: [configServerPool] :: Config -> !(IORef ServerPool)
- Database.InfluxDB.Http: [credsPassword] :: Credentials -> !Text
- Database.InfluxDB.Http: [credsUser] :: Credentials -> !Text
- Database.InfluxDB.Http: [databaseRequestContinuousQueries] :: DatabaseRequest -> [Text]
- Database.InfluxDB.Http: [databaseRequestSpaces] :: DatabaseRequest -> [ShardSpaceRequest]
- Database.InfluxDB.Http: [serverHost] :: Server -> !Text
- Database.InfluxDB.Http: [serverPort] :: Server -> !Int
- Database.InfluxDB.Http: [serverSsl] :: Server -> !Bool
- Database.InfluxDB.Http: [shardSpaceRequestName] :: ShardSpaceRequest -> Text
- Database.InfluxDB.Http: [shardSpaceRequestRegex] :: ShardSpaceRequest -> Text
- Database.InfluxDB.Http: [shardSpaceRequestReplicationFactor] :: ShardSpaceRequest -> Word32
- Database.InfluxDB.Http: [shardSpaceRequestRetentionPolicy] :: ShardSpaceRequest -> Text
- Database.InfluxDB.Http: [shardSpaceRequestShardDuration] :: ShardSpaceRequest -> Text
- Database.InfluxDB.Http: [shardSpaceRequestSplit] :: ShardSpaceRequest -> Word32
- Database.InfluxDB.Http: addClusterAdmin :: Config -> Text -> Text -> IO Admin
- Database.InfluxDB.Http: addDatabaseUser :: Config -> Text -> Text -> Text -> IO ()
- Database.InfluxDB.Http: authenticateClusterAdmin :: Config -> IO ()
- Database.InfluxDB.Http: authenticateDatabaseUser :: Config -> Text -> IO ()
- Database.InfluxDB.Http: configureDatabase :: Config -> Text -> DatabaseRequest -> IO ()
- Database.InfluxDB.Http: createDatabase :: Config -> Text -> IO ()
- Database.InfluxDB.Http: createShardSpace :: Config -> Text -> ShardSpaceRequest -> IO ()
- Database.InfluxDB.Http: data Config
- Database.InfluxDB.Http: data Credentials
- Database.InfluxDB.Http: data DatabaseRequest
- Database.InfluxDB.Http: data PointT p m a
- Database.InfluxDB.Http: data SeriesT m a
- Database.InfluxDB.Http: data Server
- Database.InfluxDB.Http: data ShardSpaceRequest
- Database.InfluxDB.Http: data Stream m a
- Database.InfluxDB.Http: data TimePrecision
- Database.InfluxDB.Http: deleteClusterAdmin :: Config -> Admin -> IO ()
- Database.InfluxDB.Http: deleteDatabaseUser :: Config -> Text -> Text -> IO ()
- Database.InfluxDB.Http: deleteSeries :: Config -> Text -> Text -> IO ()
- Database.InfluxDB.Http: dropDatabase :: Config -> Text -> IO ()
- Database.InfluxDB.Http: dropShardSpace :: Config -> Text -> Text -> IO ()
- Database.InfluxDB.Http: grantAdminPrivilegeTo :: Config -> Text -> Text -> IO ()
- Database.InfluxDB.Http: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Database.InfluxDB.Http.PointT p m)
- Database.InfluxDB.Http: instance Control.Monad.IO.Class.MonadIO m => Control.Monad.IO.Class.MonadIO (Database.InfluxDB.Http.SeriesT m)
- Database.InfluxDB.Http: instance Control.Monad.Trans.Class.MonadTrans (Database.InfluxDB.Http.PointT p)
- Database.InfluxDB.Http: instance Control.Monad.Trans.Class.MonadTrans Database.InfluxDB.Http.SeriesT
- Database.InfluxDB.Http: instance Data.Aeson.Types.Class.ToJSON Database.InfluxDB.Http.DatabaseRequest
- Database.InfluxDB.Http: instance Data.Aeson.Types.Class.ToJSON Database.InfluxDB.Http.ShardSpaceRequest
- Database.InfluxDB.Http: instance GHC.Base.Applicative m => GHC.Base.Applicative (Database.InfluxDB.Http.PointT p m)
- Database.InfluxDB.Http: instance GHC.Base.Applicative m => GHC.Base.Applicative (Database.InfluxDB.Http.SeriesT m)
- Database.InfluxDB.Http: instance GHC.Base.Functor m => GHC.Base.Functor (Database.InfluxDB.Http.PointT p m)
- Database.InfluxDB.Http: instance GHC.Base.Functor m => GHC.Base.Functor (Database.InfluxDB.Http.SeriesT m)
- Database.InfluxDB.Http: instance GHC.Base.Monad m => Control.Monad.Writer.Class.MonadWriter (Data.DList.DList (Data.Vector.Vector Database.InfluxDB.Types.Value)) (Database.InfluxDB.Http.PointT p m)
- Database.InfluxDB.Http: instance GHC.Base.Monad m => Control.Monad.Writer.Class.MonadWriter (Data.DList.DList Database.InfluxDB.Types.Series) (Database.InfluxDB.Http.SeriesT m)
- Database.InfluxDB.Http: instance GHC.Base.Monad m => GHC.Base.Monad (Database.InfluxDB.Http.PointT p m)
- Database.InfluxDB.Http: instance GHC.Base.Monad m => GHC.Base.Monad (Database.InfluxDB.Http.SeriesT m)
- Database.InfluxDB.Http: instance GHC.Show.Show Database.InfluxDB.Http.DatabaseRequest
- Database.InfluxDB.Http: instance GHC.Show.Show Database.InfluxDB.Http.ShardSpaceRequest
- Database.InfluxDB.Http: isInSync :: Config -> IO Bool
- Database.InfluxDB.Http: listClusterAdmins :: Config -> IO [Admin]
- Database.InfluxDB.Http: listDatabaseUsers :: Config -> Text -> IO [User]
- Database.InfluxDB.Http: listDatabases :: Config -> IO [Database]
- Database.InfluxDB.Http: listShardSpaces :: Config -> IO [ShardSpace]
- Database.InfluxDB.Http: localServer :: Server
- Database.InfluxDB.Http: ping :: Config -> IO Ping
- Database.InfluxDB.Http: post :: Config -> Text -> SeriesT IO a -> IO a
- Database.InfluxDB.Http: postWithPrecision :: Config -> Text -> TimePrecision -> SeriesT IO a -> IO a
- Database.InfluxDB.Http: query :: FromSeries a => Config -> Text -> Text -> IO [a]
- Database.InfluxDB.Http: queryChunked :: FromSeries a => Config -> Text -> Text -> (Stream IO a -> IO b) -> IO b
- Database.InfluxDB.Http: revokeAdminPrivilegeFrom :: Config -> Text -> Text -> IO ()
- Database.InfluxDB.Http: rootCreds :: Credentials
- Database.InfluxDB.Http: updateClusterAdminPassword :: Config -> Admin -> Text -> IO ()
- Database.InfluxDB.Http: updateDatabaseUserPassword :: Config -> Text -> Text -> Text -> IO ()
- Database.InfluxDB.Http: withSeries :: forall m a. (Monad m, ToSeriesData a) => Text -> PointT a m () -> SeriesT m ()
- Database.InfluxDB.Http: writePoints :: (Monad m, ToSeriesData a) => a -> PointT a m ()
- Database.InfluxDB.Http: writeSeries :: (Monad m, ToSeriesData a) => Text -> a -> SeriesT m ()
- Database.InfluxDB.Http: writeSeriesData :: Monad m => Text -> SeriesData -> SeriesT m ()
- Database.InfluxDB.Lens: credentials :: Lens' Config Credentials
- Database.InfluxDB.Lens: host :: Lens' Server Text
- Database.InfluxDB.Lens: httpManager :: Lens' Config Manager
- Database.InfluxDB.Lens: password :: Lens' Credentials Text
- Database.InfluxDB.Lens: port :: Lens' Server Int
- Database.InfluxDB.Lens: ssl :: Lens' Server Bool
- Database.InfluxDB.Lens: type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
- Database.InfluxDB.Lens: type Lens' s a = Lens s s a a
- Database.InfluxDB.Lens: user :: Lens' Credentials Text
- Database.InfluxDB.Stream: Done :: Stream m a
- Database.InfluxDB.Stream: Yield :: a -> (m (Stream m a)) -> Stream m a
- Database.InfluxDB.Stream: data Stream m a
- Database.InfluxDB.Stream: fold :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
- Database.InfluxDB.Stream: fold' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
- Database.InfluxDB.Stream: mapM :: Monad m => (a -> m b) -> Stream m a -> m (Stream m b)
- Database.InfluxDB.TH: Options :: (String -> String) -> Options
- Database.InfluxDB.TH: [fieldLabelModifier] :: Options -> String -> String
- Database.InfluxDB.TH: data Options
- Database.InfluxDB.TH: defaultOptions :: Options
- Database.InfluxDB.TH: deriveFromSeriesData :: Options -> Name -> Q [Dec]
- Database.InfluxDB.TH: deriveSeriesData :: Options -> Name -> Q [Dec]
- Database.InfluxDB.TH: deriveToSeriesData :: Options -> Name -> Q [Dec]
- Database.InfluxDB.TH: stripPrefixLower :: String -> String -> String
- Database.InfluxDB.TH: stripPrefixSnake :: String -> String -> String
- Database.InfluxDB.Types: Admin :: Text -> Admin
- Database.InfluxDB.Types: Bool :: !Bool -> Value
- Database.InfluxDB.Types: Float :: !Double -> Value
- Database.InfluxDB.Types: Int :: !Int64 -> Value
- Database.InfluxDB.Types: JsonDecodeError :: String -> InfluxException
- Database.InfluxDB.Types: Null :: Value
- Database.InfluxDB.Types: Ping :: Text -> Ping
- Database.InfluxDB.Types: Series :: {-# UNPACK #-} !Text -> {-# UNPACK #-} !SeriesData -> Series
- Database.InfluxDB.Types: SeriesData :: Vector Column -> [Vector Value] -> SeriesData
- Database.InfluxDB.Types: SeriesDecodeError :: String -> InfluxException
- Database.InfluxDB.Types: ShardSpace :: Maybe Text -> Text -> Text -> Text -> Text -> Word32 -> Word32 -> ShardSpace
- Database.InfluxDB.Types: String :: !Text -> Value
- Database.InfluxDB.Types: User :: Text -> Bool -> User
- Database.InfluxDB.Types: [adminName] :: Admin -> Text
- Database.InfluxDB.Types: [credsPassword] :: Credentials -> !Text
- Database.InfluxDB.Types: [credsUser] :: Credentials -> !Text
- Database.InfluxDB.Types: [pingStatus] :: Ping -> Text
- Database.InfluxDB.Types: [seriesDataColumns] :: SeriesData -> Vector Column
- Database.InfluxDB.Types: [seriesDataPoints] :: SeriesData -> [Vector Value]
- Database.InfluxDB.Types: [seriesData] :: Series -> {-# UNPACK #-} !SeriesData
- Database.InfluxDB.Types: [seriesName] :: Series -> {-# UNPACK #-} !Text
- Database.InfluxDB.Types: [serverHost] :: Server -> !Text
- Database.InfluxDB.Types: [serverPort] :: Server -> !Int
- Database.InfluxDB.Types: [serverSsl] :: Server -> !Bool
- Database.InfluxDB.Types: [shardSpaceDatabase] :: ShardSpace -> Maybe Text
- Database.InfluxDB.Types: [shardSpaceName] :: ShardSpace -> Text
- Database.InfluxDB.Types: [shardSpaceRegex] :: ShardSpace -> Text
- Database.InfluxDB.Types: [shardSpaceReplicationFactor] :: ShardSpace -> Word32
- Database.InfluxDB.Types: [shardSpaceRetentionPolicy] :: ShardSpace -> Text
- Database.InfluxDB.Types: [shardSpaceShardDuration] :: ShardSpace -> Text
- Database.InfluxDB.Types: [shardSpaceSplit] :: ShardSpace -> Word32
- Database.InfluxDB.Types: [userIsAdmin] :: User -> Bool
- Database.InfluxDB.Types: [userName] :: User -> Text
- Database.InfluxDB.Types: activeServer :: IORef ServerPool -> IO Server
- Database.InfluxDB.Types: data Series
- Database.InfluxDB.Types: data SeriesData
- Database.InfluxDB.Types: data ServerPool
- Database.InfluxDB.Types: data ShardSpace
- Database.InfluxDB.Types: data User
- Database.InfluxDB.Types: data Value
- Database.InfluxDB.Types: failover :: IORef ServerPool -> IO ()
- Database.InfluxDB.Types: instance Data.Aeson.Types.Class.FromJSON Database.InfluxDB.Types.Admin
- Database.InfluxDB.Types: instance Data.Aeson.Types.Class.FromJSON Database.InfluxDB.Types.Database
- Database.InfluxDB.Types: instance Data.Aeson.Types.Class.FromJSON Database.InfluxDB.Types.Ping
- Database.InfluxDB.Types: instance Data.Aeson.Types.Class.FromJSON Database.InfluxDB.Types.Series
- Database.InfluxDB.Types: instance Data.Aeson.Types.Class.FromJSON Database.InfluxDB.Types.ShardSpace
- Database.InfluxDB.Types: instance Data.Aeson.Types.Class.FromJSON Database.InfluxDB.Types.User
- Database.InfluxDB.Types: instance Data.Aeson.Types.Class.FromJSON Database.InfluxDB.Types.Value
- Database.InfluxDB.Types: instance Data.Aeson.Types.Class.ToJSON Database.InfluxDB.Types.Series
- Database.InfluxDB.Types: instance Data.Aeson.Types.Class.ToJSON Database.InfluxDB.Types.Value
- Database.InfluxDB.Types: instance Data.Data.Data Database.InfluxDB.Types.Value
- Database.InfluxDB.Types: instance GHC.Classes.Eq Database.InfluxDB.Types.SeriesData
- Database.InfluxDB.Types: instance GHC.Classes.Eq Database.InfluxDB.Types.Value
- Database.InfluxDB.Types: instance GHC.Generics.Generic Database.InfluxDB.Types.Admin
- Database.InfluxDB.Types: instance GHC.Generics.Generic Database.InfluxDB.Types.Credentials
- Database.InfluxDB.Types: instance GHC.Generics.Generic Database.InfluxDB.Types.Database
- Database.InfluxDB.Types: instance GHC.Generics.Generic Database.InfluxDB.Types.Ping
- Database.InfluxDB.Types: instance GHC.Generics.Generic Database.InfluxDB.Types.Series
- Database.InfluxDB.Types: instance GHC.Generics.Generic Database.InfluxDB.Types.SeriesData
- Database.InfluxDB.Types: instance GHC.Generics.Generic Database.InfluxDB.Types.ShardSpace
- Database.InfluxDB.Types: instance GHC.Generics.Generic Database.InfluxDB.Types.User
- Database.InfluxDB.Types: instance GHC.Generics.Generic Database.InfluxDB.Types.Value
- Database.InfluxDB.Types: instance GHC.Show.Show Database.InfluxDB.Types.Admin
- Database.InfluxDB.Types: instance GHC.Show.Show Database.InfluxDB.Types.Credentials
- Database.InfluxDB.Types: instance GHC.Show.Show Database.InfluxDB.Types.Ping
- Database.InfluxDB.Types: instance GHC.Show.Show Database.InfluxDB.Types.SeriesData
- Database.InfluxDB.Types: instance GHC.Show.Show Database.InfluxDB.Types.ShardSpace
- Database.InfluxDB.Types: instance GHC.Show.Show Database.InfluxDB.Types.User
- Database.InfluxDB.Types: instance GHC.Show.Show Database.InfluxDB.Types.Value
- Database.InfluxDB.Types: jsonDecodeError :: String -> IO a
- Database.InfluxDB.Types: newServerPool :: Server -> [Server] -> IO (IORef ServerPool)
- Database.InfluxDB.Types: newServerPoolWithRetryPolicy :: RetryPolicy -> Server -> [Server] -> IO (IORef ServerPool)
- Database.InfluxDB.Types: newtype Admin
- Database.InfluxDB.Types: newtype Ping
- Database.InfluxDB.Types: seriesColumns :: Series -> Vector Column
- Database.InfluxDB.Types: seriesDecodeError :: String -> IO a
- Database.InfluxDB.Types: seriesPoints :: Series -> [Vector Value]
- Database.InfluxDB.Types: serverRetryPolicy :: ServerPool -> RetryPolicy
- Database.InfluxDB.Types: type Column = Text
- Database.InfluxDB.Types: type Interface = Text
+ Database.InfluxDB: (%) :: Format b c -> Format a b -> Format a c
+ Database.InfluxDB: BadRequest :: String -> Request -> InfluxException
+ Database.InfluxDB: FieldBool :: !Bool -> FieldValue
+ Database.InfluxDB: FieldFloat :: !Double -> FieldValue
+ Database.InfluxDB: FieldInt :: !Int64 -> FieldValue
+ Database.InfluxDB: FieldNull :: FieldValue
+ Database.InfluxDB: FieldString :: !Text -> FieldValue
+ Database.InfluxDB: IllformedJSON :: String -> ByteString -> InfluxException
+ Database.InfluxDB: Line :: !Key -> !(Map Key Text) -> !(Map Key FieldValue) -> !(Maybe time) -> Line time
+ Database.InfluxDB: ServerError :: String -> InfluxException
+ Database.InfluxDB: [Hour] :: Precision ty
+ Database.InfluxDB: [Microsecond] :: Precision ty
+ Database.InfluxDB: [Millisecond] :: Precision ty
+ Database.InfluxDB: [Minute] :: Precision ty
+ Database.InfluxDB: [Nanosecond] :: Precision ty
+ Database.InfluxDB: [RFC3339] :: Precision QueryRequest
+ Database.InfluxDB: [Second] :: Precision ty
+ Database.InfluxDB: authentication :: HasCredentials a => Lens' a (Maybe Credentials)
+ Database.InfluxDB: class HasDatabase a
+ Database.InfluxDB: class HasManager a
+ Database.InfluxDB: class HasPrecision (ty :: RequestType) a | a -> ty
+ Database.InfluxDB: class HasServer a
+ Database.InfluxDB: class QueryResults a
+ Database.InfluxDB: class Timestamp time
+ Database.InfluxDB: data Database
+ Database.InfluxDB: data FieldValue
+ Database.InfluxDB: data Key
+ Database.InfluxDB: data Line time
+ Database.InfluxDB: data Precision (ty :: RequestType)
+ Database.InfluxDB: data Query
+ Database.InfluxDB: data QueryParams
+ Database.InfluxDB: data WriteParams
+ Database.InfluxDB: database :: HasDatabase a => Lens' a Database
+ Database.InfluxDB: fieldSet :: Lens' (Line time) (Map Key FieldValue)
+ Database.InfluxDB: formatQuery :: Format Query r -> r
+ Database.InfluxDB: getField :: Text -> Vector Text -> Array -> Parser Value
+ Database.InfluxDB: getTag :: Monad m => Text -> HashMap Text Text -> m Text
+ Database.InfluxDB: host :: Lens' Server Text
+ Database.InfluxDB: manage :: QueryParams -> Query -> IO ()
+ Database.InfluxDB: manager :: HasManager a => Lens' a (Either ManagerSettings Manager)
+ Database.InfluxDB: measurement :: Lens' (Line time) Key
+ Database.InfluxDB: parseFieldValue :: Value -> Parser FieldValue
+ Database.InfluxDB: parseKey :: Key -> Vector Text -> Array -> Parser Key
+ Database.InfluxDB: parseResults :: QueryResults a => Precision QueryRequest -> Value -> Parser (Vector a)
+ Database.InfluxDB: parseResultsWith :: (Maybe Text -> HashMap Text Text -> Vector Text -> Array -> Parser a) -> Value -> Parser (Vector a)
+ Database.InfluxDB: parseTimestamp :: Precision ty -> Value -> Parser POSIXTime
+ Database.InfluxDB: password :: Lens' Credentials Text
+ Database.InfluxDB: port :: Lens' Server Int
+ Database.InfluxDB: precision :: HasPrecision ty a => Lens' a (Precision ty)
+ Database.InfluxDB: precisionName :: Precision ty -> Text
+ Database.InfluxDB: precisionScale :: Fractional a => Precision ty -> a
+ Database.InfluxDB: queryParams :: Database -> QueryParams
+ Database.InfluxDB: retentionPolicy :: Lens' WriteParams (Maybe Key)
+ Database.InfluxDB: roundTo :: Timestamp time => Precision WriteRequest -> time -> Int64
+ Database.InfluxDB: scaleTo :: Timestamp time => Precision WriteRequest -> time -> Int64
+ Database.InfluxDB: server :: HasServer a => Lens' a Server
+ Database.InfluxDB: ssl :: Lens' Server Bool
+ Database.InfluxDB: tagSet :: Lens' (Line time) (Map Key Text)
+ Database.InfluxDB: timestamp :: Lens' (Line time) (Maybe time)
+ Database.InfluxDB: user :: Lens' Credentials Text
+ Database.InfluxDB: write :: Timestamp time => WriteParams -> Line time -> IO ()
+ Database.InfluxDB: writeBatch :: (Timestamp time, Traversable f) => WriteParams -> f (Line time) -> IO ()
+ Database.InfluxDB: writeByteString :: WriteParams -> ByteString -> IO ()
+ Database.InfluxDB: writeParams :: Database -> WriteParams
+ Database.InfluxDB.Format: (%) :: Format b c -> Format a b -> Format a c
+ Database.InfluxDB.Format: data Format a b
+ Database.InfluxDB.Format: data Query
+ Database.InfluxDB.Format: database :: Format r (Database -> r)
+ Database.InfluxDB.Format: decimal :: Integral a => Format r (a -> r)
+ Database.InfluxDB.Format: fieldVal :: Format r (FieldValue -> r)
+ Database.InfluxDB.Format: formatDatabase :: Format Database r -> r
+ Database.InfluxDB.Format: formatKey :: Format Key r -> r
+ Database.InfluxDB.Format: formatQuery :: Format Query r -> r
+ Database.InfluxDB.Format: fromQuery :: Query -> ByteString
+ Database.InfluxDB.Format: instance Control.Category.Category Database.InfluxDB.Format.Format
+ Database.InfluxDB.Format: instance a ~ b => Data.String.IsString (Database.InfluxDB.Format.Format a b)
+ Database.InfluxDB.Format: key :: Format r (Key -> r)
+ Database.InfluxDB.Format: keys :: Format r ([Key] -> r)
+ Database.InfluxDB.Format: makeFormat :: (a -> Builder) -> Format r (a -> r)
+ Database.InfluxDB.Format: realFloat :: RealFloat a => Format r (a -> r)
+ Database.InfluxDB.Format: text :: Format r (Text -> r)
+ Database.InfluxDB.Format: time :: FormatTime time => Format r (time -> r)
+ Database.InfluxDB.JSON: Decoder :: (Parser a -> Parser b) -> (Parser (Vector b) -> Parser (Vector a)) -> Decoder a
+ Database.InfluxDB.JSON: [decodeEach] :: Decoder a -> Parser a -> Parser b
+ Database.InfluxDB.JSON: [decodeFold] :: Decoder a -> Parser (Vector b) -> Parser (Vector a)
+ Database.InfluxDB.JSON: data Decoder a
+ Database.InfluxDB.JSON: getField :: Text -> Vector Text -> Array -> Parser Value
+ Database.InfluxDB.JSON: getTag :: Monad m => Text -> HashMap Text Text -> m Text
+ Database.InfluxDB.JSON: lenientDecoder :: Decoder a
+ Database.InfluxDB.JSON: parseErrorObject :: Value -> Parser a
+ Database.InfluxDB.JSON: parseFieldValue :: Value -> Parser FieldValue
+ Database.InfluxDB.JSON: parsePOSIXTime :: Precision ty -> Value -> Parser POSIXTime
+ Database.InfluxDB.JSON: parseRFC3339 :: ParseTime time => Value -> Parser time
+ Database.InfluxDB.JSON: parseResultsObject :: Value -> Parser (Vector Value)
+ Database.InfluxDB.JSON: parseResultsWith :: (Maybe Text -> HashMap Text Text -> Vector Text -> Array -> Parser a) -> Value -> Parser (Vector a)
+ Database.InfluxDB.JSON: parseResultsWithDecoder :: Decoder a -> (Maybe Text -> HashMap Text Text -> Vector Text -> Array -> Parser a) -> Value -> Parser (Vector a)
+ Database.InfluxDB.JSON: parseSeriesBody :: Value -> Parser (Maybe Text, HashMap Text Text, Vector Text, Array)
+ Database.InfluxDB.JSON: parseSeriesObject :: Value -> Parser (Vector Value)
+ Database.InfluxDB.JSON: parseTimestamp :: Precision ty -> Value -> Parser POSIXTime
+ Database.InfluxDB.JSON: strictDecoder :: Decoder a
+ Database.InfluxDB.Line: Line :: !Key -> !(Map Key Text) -> !(Map Key FieldValue) -> !(Maybe time) -> Line time
+ Database.InfluxDB.Line: buildLine :: (time -> Int64) -> Line time -> Builder
+ Database.InfluxDB.Line: buildLines :: Traversable f => (time -> Int64) -> f (Line time) -> Builder
+ Database.InfluxDB.Line: data Line time
+ Database.InfluxDB.Line: encodeLine :: (time -> Int64) -> Line time -> ByteString
+ Database.InfluxDB.Line: encodeLines :: Traversable f => (time -> Int64) -> f (Line time) -> ByteString
+ Database.InfluxDB.Line: fieldSet :: Lens' (Line time) (Map Key FieldValue)
+ Database.InfluxDB.Line: measurement :: Lens' (Line time) Key
+ Database.InfluxDB.Line: tagSet :: Lens' (Line time) (Map Key Text)
+ Database.InfluxDB.Line: timestamp :: Lens' (Line time) (Maybe time)
+ Database.InfluxDB.Manage: data ShowQuery
+ Database.InfluxDB.Manage: data ShowSeries
+ Database.InfluxDB.Manage: database :: HasDatabase a => Lens' a Database
+ Database.InfluxDB.Manage: duration :: Lens' ShowQuery NominalDiffTime
+ Database.InfluxDB.Manage: instance Database.InfluxDB.Query.QueryResults Database.InfluxDB.Manage.ShowQuery
+ Database.InfluxDB.Manage: instance Database.InfluxDB.Query.QueryResults Database.InfluxDB.Manage.ShowSeries
+ Database.InfluxDB.Manage: instance Database.InfluxDB.Types.HasDatabase Database.InfluxDB.Manage.ShowQuery
+ Database.InfluxDB.Manage: instance GHC.Show.Show Database.InfluxDB.Manage.ShowQuery
+ Database.InfluxDB.Manage: instance GHC.Show.Show Database.InfluxDB.Manage.ShowSeries
+ Database.InfluxDB.Manage: key :: Lens' ShowSeries Key
+ Database.InfluxDB.Manage: manage :: QueryParams -> Query -> IO ()
+ Database.InfluxDB.Manage: qid :: Lens' ShowQuery Int
+ Database.InfluxDB.Manage: queryText :: Lens' ShowQuery Query
+ Database.InfluxDB.Ping: PingParams :: !Server -> !(Either ManagerSettings Manager) -> !(Maybe Int) -> PingParams
+ Database.InfluxDB.Ping: PingResult :: !TimeSpec -> !ByteString -> PingResult
+ Database.InfluxDB.Ping: [_influxdbVersion] :: PingResult -> !ByteString
+ Database.InfluxDB.Ping: [_manager] :: PingParams -> !(Either ManagerSettings Manager)
+ Database.InfluxDB.Ping: [_roundtripTime] :: PingResult -> !TimeSpec
+ Database.InfluxDB.Ping: [_server] :: PingParams -> !Server
+ Database.InfluxDB.Ping: [_waitForLeader] :: PingParams -> !(Maybe Int)
+ Database.InfluxDB.Ping: data PingParams
+ Database.InfluxDB.Ping: data PingResult
+ Database.InfluxDB.Ping: influxdbVersion :: Lens' PingResult ByteString
+ Database.InfluxDB.Ping: instance Database.InfluxDB.Types.HasManager Database.InfluxDB.Ping.PingParams
+ Database.InfluxDB.Ping: instance Database.InfluxDB.Types.HasServer Database.InfluxDB.Ping.PingParams
+ Database.InfluxDB.Ping: instance GHC.Classes.Eq Database.InfluxDB.Ping.PingResult
+ Database.InfluxDB.Ping: instance GHC.Classes.Ord Database.InfluxDB.Ping.PingResult
+ Database.InfluxDB.Ping: instance GHC.Show.Show Database.InfluxDB.Ping.PingResult
+ Database.InfluxDB.Ping: manager :: HasManager a => Lens' a (Either ManagerSettings Manager)
+ Database.InfluxDB.Ping: ping :: PingParams -> IO PingResult
+ Database.InfluxDB.Ping: pingParams :: PingParams
+ Database.InfluxDB.Ping: roundtripTime :: Lens' PingResult TimeSpec
+ Database.InfluxDB.Ping: server :: HasServer a => Lens' a Server
+ Database.InfluxDB.Ping: waitForLeader :: Lens' PingParams (Maybe Int)
+ Database.InfluxDB.Query: class QueryResults a
+ Database.InfluxDB.Query: data Query
+ Database.InfluxDB.Query: data QueryParams
+ Database.InfluxDB.Query: database :: HasDatabase a => Lens' a Database
+ Database.InfluxDB.Query: instance (a ~ Data.Aeson.Types.Internal.Value, b ~ Data.Aeson.Types.Internal.Value) => Database.InfluxDB.Query.QueryResults (a, b)
+ Database.InfluxDB.Query: instance (a ~ Data.Aeson.Types.Internal.Value, b ~ Data.Aeson.Types.Internal.Value, c ~ Data.Aeson.Types.Internal.Value) => Database.InfluxDB.Query.QueryResults (a, b, c)
+ Database.InfluxDB.Query: instance (a ~ Data.Aeson.Types.Internal.Value, b ~ Data.Aeson.Types.Internal.Value, c ~ Data.Aeson.Types.Internal.Value, d ~ Data.Aeson.Types.Internal.Value) => Database.InfluxDB.Query.QueryResults (a, b, c, d)
+ Database.InfluxDB.Query: instance (a ~ Data.Aeson.Types.Internal.Value, b ~ Data.Aeson.Types.Internal.Value, c ~ Data.Aeson.Types.Internal.Value, d ~ Data.Aeson.Types.Internal.Value, e ~ Data.Aeson.Types.Internal.Value) => Database.InfluxDB.Query.QueryResults (a, b, c, d, e)
+ Database.InfluxDB.Query: instance (a ~ Data.Aeson.Types.Internal.Value, b ~ Data.Aeson.Types.Internal.Value, c ~ Data.Aeson.Types.Internal.Value, d ~ Data.Aeson.Types.Internal.Value, e ~ Data.Aeson.Types.Internal.Value, f ~ Data.Aeson.Types.Internal.Value) => Database.InfluxDB.Query.QueryResults (a, b, c, d, e, f)
+ Database.InfluxDB.Query: instance (a ~ Data.Aeson.Types.Internal.Value, b ~ Data.Aeson.Types.Internal.Value, c ~ Data.Aeson.Types.Internal.Value, d ~ Data.Aeson.Types.Internal.Value, e ~ Data.Aeson.Types.Internal.Value, f ~ Data.Aeson.Types.Internal.Value, g ~ Data.Aeson.Types.Internal.Value) => Database.InfluxDB.Query.QueryResults (a, b, c, d, e, f, g)
+ Database.InfluxDB.Query: instance (a ~ Data.Aeson.Types.Internal.Value, b ~ Data.Aeson.Types.Internal.Value, c ~ Data.Aeson.Types.Internal.Value, d ~ Data.Aeson.Types.Internal.Value, e ~ Data.Aeson.Types.Internal.Value, f ~ Data.Aeson.Types.Internal.Value, g ~ Data.Aeson.Types.Internal.Value, h ~ Data.Aeson.Types.Internal.Value) => Database.InfluxDB.Query.QueryResults (a, b, c, d, e, f, g, h)
+ Database.InfluxDB.Query: instance Database.InfluxDB.Query.QueryResults Data.Void.Void
+ Database.InfluxDB.Query: instance Database.InfluxDB.Types.HasCredentials Database.InfluxDB.Query.QueryParams
+ Database.InfluxDB.Query: instance Database.InfluxDB.Types.HasDatabase Database.InfluxDB.Query.QueryParams
+ Database.InfluxDB.Query: instance Database.InfluxDB.Types.HasManager Database.InfluxDB.Query.QueryParams
+ Database.InfluxDB.Query: instance Database.InfluxDB.Types.HasPrecision 'Database.InfluxDB.Types.QueryRequest Database.InfluxDB.Query.QueryParams
+ Database.InfluxDB.Query: instance Database.InfluxDB.Types.HasServer Database.InfluxDB.Query.QueryParams
+ Database.InfluxDB.Query: manager :: HasManager a => Lens' a (Either ManagerSettings Manager)
+ Database.InfluxDB.Query: parseKey :: Key -> Vector Text -> Array -> Parser Key
+ Database.InfluxDB.Query: parseResults :: QueryResults a => Precision QueryRequest -> Value -> Parser (Vector a)
+ Database.InfluxDB.Query: parseResultsWith :: (Maybe Text -> HashMap Text Text -> Vector Text -> Array -> Parser a) -> Value -> Parser (Vector a)
+ Database.InfluxDB.Query: precision :: HasPrecision ty a => Lens' a (Precision ty)
+ Database.InfluxDB.Query: query :: QueryResults a => QueryParams -> Query -> IO (Vector a)
+ Database.InfluxDB.Query: queryChunked :: QueryResults a => QueryParams -> Optional Int -> Query -> FoldM IO (Vector a) r -> IO r
+ Database.InfluxDB.Query: queryParams :: Database -> QueryParams
+ Database.InfluxDB.Query: server :: HasServer a => Lens' a Server
+ Database.InfluxDB.Query: withQueryResponse :: QueryParams -> Maybe (Optional Int) -> Query -> (Request -> Response BodyReader -> IO r) -> IO r
+ Database.InfluxDB.Types: BadRequest :: String -> Request -> InfluxException
+ Database.InfluxDB.Types: FieldBool :: !Bool -> FieldValue
+ Database.InfluxDB.Types: FieldFloat :: !Double -> FieldValue
+ Database.InfluxDB.Types: FieldInt :: !Int64 -> FieldValue
+ Database.InfluxDB.Types: FieldNull :: FieldValue
+ Database.InfluxDB.Types: FieldString :: !Text -> FieldValue
+ Database.InfluxDB.Types: IllformedJSON :: String -> ByteString -> InfluxException
+ Database.InfluxDB.Types: Key :: Text -> Key
+ Database.InfluxDB.Types: Query :: Text -> Query
+ Database.InfluxDB.Types: QueryRequest :: RequestType
+ Database.InfluxDB.Types: ServerError :: String -> InfluxException
+ Database.InfluxDB.Types: WriteRequest :: RequestType
+ Database.InfluxDB.Types: [Hour] :: Precision ty
+ Database.InfluxDB.Types: [Microsecond] :: Precision ty
+ Database.InfluxDB.Types: [Millisecond] :: Precision ty
+ Database.InfluxDB.Types: [Minute] :: Precision ty
+ Database.InfluxDB.Types: [Nanosecond] :: Precision ty
+ Database.InfluxDB.Types: [RFC3339] :: Precision QueryRequest
+ Database.InfluxDB.Types: [Second] :: Precision ty
+ Database.InfluxDB.Types: [_host] :: Server -> !Text
+ Database.InfluxDB.Types: [_password] :: Credentials -> !Text
+ Database.InfluxDB.Types: [_port] :: Server -> !Int
+ Database.InfluxDB.Types: [_ssl] :: Server -> !Bool
+ Database.InfluxDB.Types: [_user] :: Credentials -> !Text
+ Database.InfluxDB.Types: authentication :: HasCredentials a => Lens' a (Maybe Credentials)
+ Database.InfluxDB.Types: class HasCredentials a
+ Database.InfluxDB.Types: class HasDatabase a
+ Database.InfluxDB.Types: class HasManager a
+ Database.InfluxDB.Types: class HasPrecision (ty :: RequestType) a | a -> ty
+ Database.InfluxDB.Types: class HasServer a
+ Database.InfluxDB.Types: class Timestamp time
+ Database.InfluxDB.Types: data FieldValue
+ Database.InfluxDB.Types: data Precision (ty :: RequestType)
+ Database.InfluxDB.Types: data RequestType
+ Database.InfluxDB.Types: database :: HasDatabase a => Lens' a Database
+ Database.InfluxDB.Types: fromNonEmptyString :: String -> String -> Text
+ Database.InfluxDB.Types: host :: Lens' Server Text
+ Database.InfluxDB.Types: instance Data.Data.Data Database.InfluxDB.Types.FieldValue
+ Database.InfluxDB.Types: instance Data.String.IsString Database.InfluxDB.Types.Database
+ Database.InfluxDB.Types: instance Data.String.IsString Database.InfluxDB.Types.FieldValue
+ Database.InfluxDB.Types: instance Data.String.IsString Database.InfluxDB.Types.Key
+ Database.InfluxDB.Types: instance Data.String.IsString Database.InfluxDB.Types.Query
+ Database.InfluxDB.Types: instance Database.InfluxDB.Types.Timestamp Data.Time.Clock.UTC.NominalDiffTime
+ Database.InfluxDB.Types: instance Database.InfluxDB.Types.Timestamp Data.Time.Clock.UTC.UTCTime
+ Database.InfluxDB.Types: instance GHC.Classes.Eq Database.InfluxDB.Types.Database
+ Database.InfluxDB.Types: instance GHC.Classes.Eq Database.InfluxDB.Types.FieldValue
+ Database.InfluxDB.Types: instance GHC.Classes.Eq Database.InfluxDB.Types.Key
+ Database.InfluxDB.Types: instance GHC.Classes.Eq Database.InfluxDB.Types.Server
+ Database.InfluxDB.Types: instance GHC.Classes.Ord Database.InfluxDB.Types.Database
+ Database.InfluxDB.Types: instance GHC.Classes.Ord Database.InfluxDB.Types.Key
+ Database.InfluxDB.Types: instance GHC.Generics.Generic Database.InfluxDB.Types.FieldValue
+ Database.InfluxDB.Types: instance GHC.Show.Show (Database.InfluxDB.Types.Precision a)
+ Database.InfluxDB.Types: instance GHC.Show.Show Database.InfluxDB.Types.FieldValue
+ Database.InfluxDB.Types: instance GHC.Show.Show Database.InfluxDB.Types.Key
+ Database.InfluxDB.Types: instance GHC.Show.Show Database.InfluxDB.Types.Query
+ Database.InfluxDB.Types: instance GHC.Show.Show Database.InfluxDB.Types.RequestType
+ Database.InfluxDB.Types: localServer :: Server
+ Database.InfluxDB.Types: manager :: HasManager a => Lens' a (Either ManagerSettings Manager)
+ Database.InfluxDB.Types: newtype Key
+ Database.InfluxDB.Types: newtype Query
+ Database.InfluxDB.Types: password :: Lens' Credentials Text
+ Database.InfluxDB.Types: port :: Lens' Server Int
+ Database.InfluxDB.Types: precision :: HasPrecision ty a => Lens' a (Precision ty)
+ Database.InfluxDB.Types: precisionName :: Precision ty -> Text
+ Database.InfluxDB.Types: precisionScale :: Fractional a => Precision ty -> a
+ Database.InfluxDB.Types: roundAt :: RealFrac a => a -> a -> a
+ Database.InfluxDB.Types: roundTo :: Timestamp time => Precision WriteRequest -> time -> Int64
+ Database.InfluxDB.Types: scaleTo :: Timestamp time => Precision WriteRequest -> time -> Int64
+ Database.InfluxDB.Types: server :: HasServer a => Lens' a Server
+ Database.InfluxDB.Types: ssl :: Lens' Server Bool
+ Database.InfluxDB.Types: user :: Lens' Credentials Text
+ Database.InfluxDB.Write: data WriteParams
+ Database.InfluxDB.Write: database :: HasDatabase a => Lens' a Database
+ Database.InfluxDB.Write: instance Database.InfluxDB.Types.HasCredentials Database.InfluxDB.Write.WriteParams
+ Database.InfluxDB.Write: instance Database.InfluxDB.Types.HasDatabase Database.InfluxDB.Write.WriteParams
+ Database.InfluxDB.Write: instance Database.InfluxDB.Types.HasManager Database.InfluxDB.Write.WriteParams
+ Database.InfluxDB.Write: instance Database.InfluxDB.Types.HasPrecision 'Database.InfluxDB.Types.WriteRequest Database.InfluxDB.Write.WriteParams
+ Database.InfluxDB.Write: instance Database.InfluxDB.Types.HasServer Database.InfluxDB.Write.WriteParams
+ Database.InfluxDB.Write: manager :: HasManager a => Lens' a (Either ManagerSettings Manager)
+ Database.InfluxDB.Write: precision :: HasPrecision ty a => Lens' a (Precision ty)
+ Database.InfluxDB.Write: retentionPolicy :: Lens' WriteParams (Maybe Key)
+ Database.InfluxDB.Write: server :: HasServer a => Lens' a Server
+ Database.InfluxDB.Write: write :: Timestamp time => WriteParams -> Line time -> IO ()
+ Database.InfluxDB.Write: writeBatch :: (Timestamp time, Traversable f) => WriteParams -> f (Line time) -> IO ()
+ Database.InfluxDB.Write: writeByteString :: WriteParams -> ByteString -> IO ()
+ Database.InfluxDB.Write: writeParams :: Database -> WriteParams
+ Database.InfluxDB.Write.UDP: data WriteParams
+ Database.InfluxDB.Write.UDP: instance Database.InfluxDB.Types.HasPrecision 'Database.InfluxDB.Types.WriteRequest Database.InfluxDB.Write.UDP.WriteParams
+ Database.InfluxDB.Write.UDP: precision :: HasPrecision ty a => Lens' a (Precision ty)
+ Database.InfluxDB.Write.UDP: sockAddr :: Lens' WriteParams SockAddr
+ Database.InfluxDB.Write.UDP: socket :: Lens' WriteParams Socket
+ Database.InfluxDB.Write.UDP: write :: Timestamp time => WriteParams -> Line time -> IO ()
+ Database.InfluxDB.Write.UDP: writeBatch :: (Timestamp time, Traversable f) => WriteParams -> f (Line time) -> IO ()
+ Database.InfluxDB.Write.UDP: writeByteString :: WriteParams -> ByteString -> IO ()
+ Database.InfluxDB.Write.UDP: writeParams :: Socket -> SockAddr -> WriteParams
- Database.InfluxDB: query :: FromSeries a => Config -> Text -> Text -> IO [a]
+ Database.InfluxDB: query :: QueryResults a => QueryParams -> Query -> IO (Vector a)
- Database.InfluxDB: queryChunked :: FromSeries a => Config -> Text -> Text -> (Stream IO a -> IO b) -> IO b
+ Database.InfluxDB: queryChunked :: QueryResults a => QueryParams -> Optional Int -> Query -> FoldM IO (Vector a) r -> IO r

Files

CHANGELOG.md view
@@ -1,3 +1,11 @@+## TBD++## v1.0.0 - 2017-03-03++The library was completely rewritten and support for older InfluxDB has been dropped.++* Support for InfluxDB 1.2+ ## v0.10.0 - 2016-05-17  * Fix a typo in a Haddock comment (#28)
README.md view
@@ -1,10 +1,11 @@ Haskell client library for InfluxDB ==========-[![Build Status](https://travis-ci.org/maoe/influxdb-haskell.svg?branch=develop)](https://travis-ci.org/maoe/influxdb-haskell?branch=develop)-[![Coverage Status](https://coveralls.io/repos/maoe/influxdb-haskell/badge.png?branch=develop)](https://coveralls.io/r/maoe/influxdb-haskell?branch=develop)-[![Gitter chat](https://badges.gitter.im/maoe/influxdb-haskell.png)](https://gitter.im/maoe/influxdb-haskell)+[![Build Status](https://travis-ci.org/maoe/influxdb-haskell.svg?branch=master)](https://travis-ci.org/maoe/influxdb-haskell)+[![Hackage](https://img.shields.io/hackage/v/influxdb.svg)](https://hackage.haskell.org/package/influxdb)+[![Hackage-Deps](https://img.shields.io/hackage-deps/v/influxdb.svg)](http://packdeps.haskellers.com/feed?needle=influxdbhttp://packdeps.haskellers.com/feed?needle=influxdb)+[![Gitter](https://badges.gitter.im/maoe/influxdb-haskell.svg)](https://gitter.im/maoe/influxdb-haskell?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -Support for current version of InfluxDB is under development.+Currently this library supports InfluxDB 1.2.  Contact information ----------
examples/random-points.hs view
@@ -1,26 +1,30 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE ViewPatterns #-}-{-# LANGUAGE TemplateHaskell #-}-import Control.Applicative-import Control.Exception as E-import Control.Monad-import Control.Monad.Trans-import Data.Function (fix)-import Data.Time.Clock.POSIX+import Data.Foldable+import Data.Traversable import System.Environment import System.IO-import qualified Data.Text as T+import Text.Printf (printf) +import Control.Lens+import Data.Aeson+import Data.Optional (Optional(Default))+import Data.Time.Clock.POSIX import System.Random.MWC (Variate(..))+import qualified Control.Foldl as L+import qualified Data.Map.Strict as Map+import qualified Data.Text as T import qualified Network.HTTP.Client as HC import qualified System.Random.MWC as MWC  import Database.InfluxDB-import Database.InfluxDB.TH-import qualified Database.InfluxDB.Stream as S+import qualified Database.InfluxDB.Format as F+import qualified Database.InfluxDB.Manage as M  oneWeekInSeconds :: Int oneWeekInSeconds = 7*24*60*60@@ -29,74 +33,60 @@ main = do   [read -> (numPoints :: Int), read -> (batches :: Int)] <- getArgs   hSetBuffering stdout NoBuffering-  HC.withManager managerSettings $ \manager -> do-    config <- newConfig manager+  manager' <- HC.newManager managerSettings -    let db = "ctx"-    dropDatabase config db-      `E.catch`-        -- Ignore exceptions here-        \(_ :: HC.HttpException) -> return ()-    createDatabase config "ctx"-    gen <- MWC.create-    flip fix batches $ \outerLoop !m -> when (m > 0) $ do-      postWithPrecision config db SecondsPrecision $ withSeries "ct1" $-        flip fix numPoints $ \innerLoop !n -> when (n > 0) $ do-          !timestamp <- liftIO $ (-)-            <$> getPOSIXTime-            <*> (fromIntegral <$> uniformR (0, oneWeekInSeconds) gen)-          !value <- liftIO $ uniform gen-          writePoints $ Point value (Time timestamp)-          innerLoop $ n - 1-      outerLoop $ m - 1+  let+    ctx = "ctx"+    ct1 = "ct1"+    qparams = queryParams ctx+      & manager .~ Right manager'+      & precision .~ RFC3339 -    result <- query config db "select count(value) from ct1;"-    case result of-      [] -> putStrLn "Empty series"-      series:_ -> do-        print $ seriesColumns series-        print $ seriesPoints series-    -- Streaming output-    queryChunked config db "select * from ct1;" $ S.fold step ()-  where-    step _ series = do-      case fromSeriesData series of-        Left reason -> hPutStrLn stderr reason-        Right points -> mapM_ print (points :: [Point])-      putStrLn "--"+  M.manage qparams $ F.formatQuery ("DROP DATABASE "%F.database) ctx+  M.manage qparams $ F.formatQuery ("CREATE DATABASE "%F.database) ctx -newConfig :: HC.Manager -> IO Config-newConfig manager = do-  pool <- newServerPool localServer []-  return Config-    { configCreds = rootCreds-    , configServerPool = pool-    , configHttpManager = manager-    }+  let wparams = writeParams ctx & manager .~ Right manager' +  gen <- MWC.create+  for_ [1..batches] $ \_ -> do+    batch <- for [1..numPoints] $ \_ -> do+      !time <- (-)+        <$> getPOSIXTime+        <*> (fromIntegral <$> uniformR (0, oneWeekInSeconds) gen)+      !value <- uniform gen+      return (time, value)+    writeBatch wparams $ flip map batch $ \(time, value) ->+      Line ct1+        (Map.fromList [])+        (Map.fromList [("value", nameToFVal value)])+        (Just time)++  queryChunked qparams Default (F.formatQuery ("SELECT * FROM "%F.key) ct1) $+    L.mapM_ $ traverse_ $ \Row {..} ->+      printf "%s:\t%s\n"+        (show $ posixSecondsToUTCTime rowTime)+        (show rowValue)+ managerSettings :: HC.ManagerSettings managerSettings = HC.defaultManagerSettings-  { HC.managerResponseTimeout = Just $ 60*(10 :: Int)^(6 :: Int)-  } -data Point = Point-  { pointValue :: !Name-  , pointTime :: !Time+data Row = Row+  { rowTime :: POSIXTime+  , rowValue :: Name   } deriving Show -newtype Time = Time POSIXTime-  deriving Show--instance ToValue Time where-  toValue (Time epoch) = toValue $ epochInSeconds epoch-    where-      epochInSeconds :: POSIXTime -> Value-      epochInSeconds = Int . floor--instance FromValue Time where-  parseValue (Int n) = return $ Time $ fromIntegral n-  parseValue (Float d) = return $ Time $ realToFrac d-  parseValue v = typeMismatch "Int or Float" v+instance QueryResults Row where+  parseResults prec = parseResultsWith $ \_ _ columns fields -> do+    rowTime <- getField "time" columns fields >>= parseTimestamp prec+    String name <- getField "value" columns fields+    rowValue <- case name of+      "foo" -> return Foo+      "bar" -> return Bar+      "baz" -> return Baz+      "quu" -> return Quu+      "qux" -> return Qux+      _ -> fail $ "unknown name: " ++ show name+    return Row {..}  data Name   = Foo@@ -106,31 +96,11 @@   | Qux   deriving (Enum, Bounded, Show) -instance ToValue Name where-  toValue Foo = String "foo"-  toValue Bar = String "bar"-  toValue Baz = String "baz"-  toValue Quu = String "quu"-  toValue Qux = String "qux"--instance FromValue Name where-  parseValue (String name) = case name of-    "foo" -> return Foo-    "bar" -> return Bar-    "baz" -> return Baz-    "quu" -> return Quu-    "qux" -> return Qux-    _ -> fail $ "Incorrect string: " ++ T.unpack name-  parseValue v = typeMismatch "String" v+nameToFVal :: Name -> FieldValue+nameToFVal = FieldString . T.toLower . T.pack . show  instance Variate Name where   uniform = uniformR (minBound, maxBound)   uniformR (lower, upper) g = do     name <- uniformR (fromEnum lower, fromEnum upper) g     return $! toEnum name---- Instance deriving--deriveSeriesData defaultOptions-  { fieldLabelModifier = stripPrefixLower "point" }-  ''Point
+ examples/write-udp.hs view
@@ -0,0 +1,36 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedLists #-}+{-# LANGUAGE OverloadedStrings #-}+module Main where+import Control.Exception++import Control.Lens+import Data.Time.Clock+import Network.Socket++import Database.InfluxDB.Types+import Database.InfluxDB.Line+import qualified Database.InfluxDB.Write.UDP as UDP++main :: IO ()+main = bracket (socket AF_INET Datagram defaultProtocol) close $ \sock -> do+  localhost <- inet_addr "127.0.0.1"+  let params = UDP.writeParams sock $ SockAddrInet 8089 localhost+      tags1 =+          [ ("tag1", "A")+          , ("tag2", "B")+          ]+      fields1 =+        [ ("val1", FieldInt 10)+        , ("val2", FieldBool True)+        ]+      fields2 =+        [ ("val1", FieldInt 1)+        , ("val2", FieldBool False)+        ]+  UDP.write params $+    Line "measurement1" tags1 fields1 (Nothing :: Maybe UTCTime)+  now <- getCurrentTime+  UDP.write+    (params & UDP.precision .~ Millisecond)+    (Line "measurement1" tags1 fields2 (Just now))
influxdb.cabal view
@@ -1,5 +1,5 @@ name: influxdb-version: 0.10.0+version: 1.0.0 synopsis: Haskell client library for InfluxDB description: Haskell client library for InfluxDB homepage: https://github.com/maoe/influxdb-haskell@@ -7,10 +7,11 @@ license-file: LICENSE author: Mitsutoshi Aoe maintainer: Mitsutoshi Aoe <maoe@foldr.in>-copyright: Copyright (C) 2014-2015 Mitsutoshi Aoe+copyright: Copyright (C) 2014-2017 Mitsutoshi Aoe category: Database build-type: Simple cabal-version: >= 1.10+tested-with: GHC >= 7.10 && < 8.1  extra-source-files:   README.md@@ -21,80 +22,69 @@   default: False   manual: True -flag aeson-070-  description: Use aeson >= 0.7.0+flag http-client-05   default: True   manual: False -flag network-uri-   description: Get Network.URI from the network-uri package-   default: True- library   exposed-modules:     Database.InfluxDB-    Database.InfluxDB.Decode-    Database.InfluxDB.Encode-    Database.InfluxDB.Http-    Database.InfluxDB.Lens-    -- Database.InfluxDB.Protobuf-    -- Database.InfluxDB.Query-    Database.InfluxDB.Stream+    Database.InfluxDB.Format+    Database.InfluxDB.JSON+    Database.InfluxDB.Line+    Database.InfluxDB.Manage+    Database.InfluxDB.Ping+    Database.InfluxDB.Query     Database.InfluxDB.Types-    Database.InfluxDB.TH+    Database.InfluxDB.Write+    Database.InfluxDB.Write.UDP   other-modules:-    Database.InfluxDB.Types.Internal+    Network.HTTP.Client.Compat   other-extensions:     BangPatterns     CPP-    ConstraintKinds+    DataKinds     DeriveDataTypeable+    DeriveGeneric+    ExistentialQuantification     FlexibleInstances+    FunctionalDependencies+    GADTs     GeneralizedNewtypeDeriving+    KindSignatures+    LambdaCase+    MultiParamTypeClasses     NamedFieldPuns     OverloadedStrings-    RankNTypes     RecordWildCards     ScopedTypeVariables+    StandaloneDeriving     TemplateHaskell-    TypeSynonymInstances     ViewPatterns   ghc-options: -Wall   build-depends:-      base >= 4 && < 5.0+      base >= 4 && < 4.10+    , aeson >= 0.7 && < 1.2     , attoparsec < 0.14-    , bytestring-    , containers-    , data-default-class-    , dlist-    , exceptions >= 0.5 && < 0.9-    , http-client < 0.5-    , mtl < 2.3-    , retry >= 0.7 && < 0.8-    , tagged-    , template-haskell+    , bytestring >= 0.10 && < 0.11+    , clock >= 0.7 && < 0.8+    , containers >= 0.5 && < 0.6+    , foldl < 1.3+    , http-types >= 0.8.6 && < 0.10+    , lens >= 4.9 && < 4.16+    , network >= 2.6 && < 2.7+    , optional-args >= 1.0 && < 1.1+    , scientific >= 0.3.3 && < 0.4     , text < 1.3-    , vector--  if flag(aeson-070)-    build-depends:-        aeson >= 0.7.0 && < 0.12-      , scientific >= 0.2-  else-    build-depends:-        aeson >= 0.6.1.0 && < 0.7.0--  if flag(network-uri)-    build-depends:-      network-uri >= 2.6+    , time >= 1.5 && < 1.9+    , unordered-containers < 0.3+    , vector >= 0.10 && < 0.13+  if flag(http-client-05)+    build-depends: http-client >= 0.5 && < 0.6   else     build-depends:-      network < 2.6--  if impl(ghc < 7.6)-    build-depends:-      ghc-prim-+        data-default-class+      , http-client >= 0.4.10 && < 0.5   hs-source-dirs: src   default-language: Haskell2010 @@ -117,24 +107,42 @@   default-language: Haskell2010  executable influx-random-points-  if flag(examples)-    buildable: True-  else+  if !flag(examples)     buildable: False   hs-source-dirs: examples   main-is: random-points.hs   ghc-options: -Wall   build-depends:-      base+      aeson+    , base     , bytestring+    , containers+    , foldl >= 1.1.3     , http-client     , influxdb-    , mtl+    , lens     , mwc-random+    , optional-args     , text     , time+    , vector   default-language: Haskell2010 +executable influx-write-udp+  if !flag(examples)+    buildable: False+  hs-source-dirs: examples+  main-is: write-udp.hs+  ghc-options: -Wall+  build-depends:+      base+    , containers+    , influxdb+    , lens+    , network+    , time+  default-language: Haskell2010+ source-repository head   type: git   branch: develop@@ -142,5 +150,5 @@  source-repository this   type: git-  tag: v0.10.0+  tag: v1.0.0   location: https://github.com/maoe/influxdb-haskell.git
src/Database/InfluxDB.hs view
@@ -1,94 +1,97 @@+{- |+stability: experimental+portability: GHC+-} module Database.InfluxDB-  (-  -- * Series data types-    Series(..), seriesColumns, seriesPoints-  , SeriesData(..)-  , Value(..)+  ( -- $intro -  -- ** Encoding-  , ToSeriesData(..)-  , ToValue(..)+  -- * Writing data+  -- $write+    write+  , writeBatch+  , writeByteString -  -- ** Decoding-  , FromSeries(..), fromSeries-  , FromSeriesData(..), fromSeriesData-  , FromValue(..), fromValue+  -- ** Write parameters+  , WriteParams+  , writeParams+  , retentionPolicy -  , withValues, (.:), (.:?), (.!=)-  , typeMismatch+  -- ** The Line protocol+  , Line(Line)+  , measurement+  , tagSet+  , fieldSet+  , timestamp -  -- * HTTP API-  -- ** Data types-  , Config(..)-  , Credentials(..), rootCreds-  , TimePrecision(..)-  , Server(..), localServer-  , ServerPool, newServerPool-  , newServerPoolWithRetryPolicy-  , Database(..)-  , User(..)-  , Admin(..)-  , Ping(..)-  , ShardSpace(..)+  , FieldValue(..)+  , Timestamp(..)+  , precisionScale+  , precisionName -  -- *** Exception-  , InfluxException(..)+  -- * Querying data+  , Query+  , query+  , queryChunked -  -- ** Writing Data+  -- * Query constructor+  , formatQuery+  , (%) -  -- *** Updating Points-  , post, postWithPrecision-  , SeriesT, PointT-  , writeSeries-  , writeSeriesData-  , withSeries-  , writePoints+  -- ** Query parameters+  , QueryParams+  , queryParams+  , authentication -  -- *** Deleting Points-  , deleteSeries+  -- ** Parsing results+  , QueryResults(..)+  , parseResultsWith+  , getField+  , getTag+  , parseTimestamp+  , parseFieldValue+  , parseKey -  -- ** Querying Data-  , query-  , Stream(..)-  , queryChunked+  -- * Database management+  , manage -  -- ** Administration & Security-  -- *** Creating and Dropping Databases-  , listDatabases-  , createDatabase-  , dropDatabase+  -- * Common data types and classes+  , Precision(..)+  , Database+  , Key -  , DatabaseRequest(..)-  , configureDatabase+  , Server+  , host+  , port+  , ssl+  , localServer -  -- *** Security-  -- **** Shard spaces-  , ShardSpaceRequest(..)-  , listShardSpaces-  , createShardSpace-  , dropShardSpace+  , Credentials+  , user+  , password -  -- **** Cluster admin-  , listClusterAdmins-  , authenticateClusterAdmin-  , addClusterAdmin-  , updateClusterAdminPassword-  , deleteClusterAdmin-  -- **** Database user-  , listDatabaseUsers-  , authenticateDatabaseUser-  , addDatabaseUser-  , updateDatabaseUserPassword-  , deleteDatabaseUser-  , grantAdminPrivilegeTo-  , revokeAdminPrivilegeFrom+  -- * Exception+  , InfluxException(..) -  -- *** Other API-  , ping-  , isInSync+  , HasServer(..)+  , HasDatabase(..)+  , HasPrecision(..)+  , HasManager(..)   ) where -import Database.InfluxDB.Decode-import Database.InfluxDB.Encode-import Database.InfluxDB.Http+import Database.InfluxDB.Format ((%), formatQuery)+import Database.InfluxDB.JSON+import Database.InfluxDB.Line+import Database.InfluxDB.Manage (manage)+import Database.InfluxDB.Query import Database.InfluxDB.Types+import Database.InfluxDB.Write++{- $write+InfluxDB has two ways to write data into it, via HTTP and UDP. This module+only exports functions for the HTTP API. For UDP, you can use a qualified+import:++@+import qualified "Database.InfluxDB.Write.UDP" as UDP+@+-}
− src/Database/InfluxDB/Decode.hs
@@ -1,232 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE TypeSynonymInstances #-}-module Database.InfluxDB.Decode-  ( FromSeries(..), fromSeries-  , FromSeriesData(..), fromSeriesData, fromSeriesData_-  , withValues, (.:), (.:?), (.!=)-  , FromValue(..), fromValue-  , Parser, ValueParser, typeMismatch-  ) where-import Control.Applicative-import Control.Monad.Reader-import Data.Either (rights)-import Data.Int-import Data.Word-import Data.Map (Map)-import Data.Maybe (fromMaybe)-import Data.Vector (Vector)-import Data.Tuple (swap)-import qualified Data.Map as Map-import qualified Data.Text as T-import qualified Data.Text.Lazy as TL-import qualified Data.Vector as V-import Prelude--import Database.InfluxDB.Types---- | A type that can be converted from a 'Series'.-class FromSeries a where-  parseSeries :: Series -> Parser a--instance FromSeries Series where-  parseSeries = return--instance FromSeries SeriesData where-  parseSeries = return . seriesData---- | Converte a value from a 'Series', failing if the types do not match.-fromSeries :: FromSeries a => Series -> Either String a-fromSeries = runParser . parseSeries---- | A type that can be converted from a 'SeriesData'. A typical implementation--- is as follows.------ > import Control.Applicative ((<$>), (<*>))--- > import qualified Data.Vector as V--- >--- > data Event = Event Text EventType--- > data EventType = Login | Logout--- >--- > instance FromSeriesData Event where--- >   parseSeriesData = withValues $ \values -> Event--- >     <$> values .: "user"--- >     <*> values .: "type"--- >--- > instance FromValue EventType-class FromSeriesData a where-  parseSeriesData :: Vector Column -> Vector Value -> Parser a--instance FromSeriesData SeriesData where-  parseSeriesData columns values = return SeriesData-    { seriesDataColumns = columns-    , seriesDataPoints = [values]-    }---- | Converte a value from a 'SeriesData', failing if the types do not match.-fromSeriesData :: FromSeriesData a => SeriesData -> Either String [a]-fromSeriesData SeriesData {..} = mapM-  (runParser . parseSeriesData seriesDataColumns)-  seriesDataPoints---- | Same as @fromSeriesData@ but ignores parse errors and returns only--- successful data.-fromSeriesData_ :: FromSeriesData a => SeriesData -> [a]-fromSeriesData_ SeriesData {..} = rights $ map-  (runParser . parseSeriesData seriesDataColumns)-  seriesDataPoints---- | Helper function to define 'parseSeriesData' from 'ValueParser's.-withValues-  :: (Vector Value -> ValueParser a)-  -> Vector Column -> Vector Value -> Parser a-withValues f columns values =-  runReaderT m $ Map.fromList $ map swap $ V.toList $ V.indexed columns-  where-    ValueParser m = f values---- | Retrieve the value associated with the given column. The result is 'empty'--- if the column is not present or the value cannot be converted to the desired--- type.-(.:) :: FromValue a => Vector Value -> Column -> ValueParser a-values .: column = do-  found <- asks $ Map.lookup column-  case found of-    Nothing -> liftParser $ parseError $ "No such column: " ++ T.unpack column-    Just idx -> do-      value <- V.indexM values idx-      liftParser $ parseValue value---- | Retrieve the value associated with the given column. The result is--- 'Nothing' if the column is not present or the value cannot be converted to--- the desired type.-(.:?) :: FromValue a => Vector Value -> Column -> ValueParser (Maybe a)-values .:? column = do-  found <- asks $ Map.lookup column-  case found of-    Nothing -> return Nothing-    Just idx ->-      case values V.!? idx of-        Nothing -> return Nothing-        Just value -> liftParser $ parseValue value---- | Helper for use in combination with '.:?' to provide default values for--- optional columns.-(.!=) :: Parser (Maybe a) -> a -> Parser a-p .!= def = fromMaybe def <$> p---- | A type that can be converted from a 'Value'.-class FromValue a where-  parseValue :: Value -> Parser a---- | Converte a value from a 'Value', failing if the types do not match.-fromValue :: FromValue a => Value -> Either String a-fromValue = runParser . parseValue--instance FromValue Value where-  parseValue = return--instance FromValue Bool where-  parseValue (Bool b) = return b-  parseValue v = typeMismatch "Bool" v--instance FromValue a => FromValue (Maybe a) where-  parseValue Null = return Nothing-  parseValue v = Just <$> parseValue v--instance FromValue Int where-  parseValue (Int n) = return $ fromIntegral n-  parseValue v = typeMismatch "Int" v--instance FromValue Int8 where-  parseValue (Int n)-    | n <= fromIntegral (maxBound :: Int8) = return $ fromIntegral n-    | otherwise = parseError $ "Larger than the maximum Int8: " ++ show n-  parseValue v = typeMismatch "Int8" v--instance FromValue Int16 where-  parseValue (Int n)-    | n <= fromIntegral (maxBound :: Int16) = return $ fromIntegral n-    | otherwise = parseError $ "Larger than the maximum Int16: " ++ show n-  parseValue v = typeMismatch "Int16" v--instance FromValue Int32 where-  parseValue (Int n)-    | n <= fromIntegral (maxBound :: Int32) = return $ fromIntegral n-    | otherwise = parseError $ "Larger than the maximum Int32: " ++ show n-  parseValue v = typeMismatch "Int32" v--instance FromValue Int64 where-  parseValue (Int n)-    | n <= fromIntegral (maxBound :: Int64) = return $ fromIntegral n-    | otherwise = parseError $ "Larger than the maximum Int64: " ++ show n-  parseValue v = typeMismatch "Int64" v--instance FromValue Word8 where-  parseValue (Int n)-    | n <= fromIntegral (maxBound :: Word8) = return $ fromIntegral n-    | otherwise = parseError $ "Larger than the maximum Word8: " ++ show n-  parseValue v = typeMismatch "Word8" v--instance FromValue Word16 where-  parseValue (Int n)-    | n <= fromIntegral (maxBound :: Word16) = return $ fromIntegral n-    | otherwise = parseError $ "Larger than the maximum Word16: " ++ show n-  parseValue v = typeMismatch "Word16" v--instance FromValue Word32 where-  parseValue (Int n)-    | n <= fromIntegral (maxBound :: Word32) = return $ fromIntegral n-    | otherwise = parseError $ "Larger than the maximum Word32: " ++ show n-  parseValue v = typeMismatch "Word32" v--instance FromValue Double where-  parseValue (Float d) = return d-  -- If the floating number happens to be a whole number, it must-  -- have encoded as an integer. We should decode it back as a floating-  -- number here.-  parseValue (Int n) = return $ fromIntegral n-  parseValue v = typeMismatch "Float" v--instance FromValue T.Text where-  parseValue (String xs) = return xs-  parseValue v = typeMismatch "Text" v--instance FromValue TL.Text where-  parseValue (String xs) = return $ TL.fromStrict xs-  parseValue v = typeMismatch "lazy Text" v--instance FromValue String where-  parseValue (String xs) = return $ T.unpack xs-  parseValue v = typeMismatch "String" v--typeMismatch-  :: String-  -> Value-  -> Parser a-typeMismatch expected actual = parseError $-  "when expecting a " ++ expected ++-  ", encountered " ++ name ++ " instead"-  where-    name = case actual of-      Int _ -> "Int"-      Float _ -> "Float"-      String _ -> "String"-      Bool _ -> "Bool"-      Null -> "Null"--newtype Parser a = Parser-  { runParser :: Either String a-  } deriving (Functor, Applicative, Monad)--type ColumnIndex = Map Column Int--newtype ValueParser a = ValueParser (ReaderT ColumnIndex Parser a)-  deriving (Functor, Applicative, Monad, MonadReader ColumnIndex)--liftParser :: Parser a -> ValueParser a-liftParser = ValueParser . ReaderT . const--parseError :: String -> Parser a-parseError = Parser . Left
− src/Database/InfluxDB/Encode.hs
@@ -1,95 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeSynonymInstances #-}-module Database.InfluxDB.Encode-  ( ToSeries(..)-  , ToSeriesData(..), toSeriesData-  , ToValue(..)-  ) where-import Data.Int (Int8, Int16, Int32, Int64)-import Data.Proxy-import Data.Vector (Vector)-import Data.Word (Word8, Word16, Word32)-import qualified Data.Text as T-import qualified Data.Text.Lazy as TL--import Database.InfluxDB.Types---- | A type that can be converted to a 'Series'.-class ToSeries a where-  toSeries :: a -> Series---- | A type that can be converted to a 'SeriesData'. A typical implementation is--- as follows.------ > import qualified Data.Vector as V--- >--- > data Event = Event Text EventType--- > data EventType = Login | Logout--- >--- > instance ToSeriesData Event where--- >   toSeriesColumns _ = V.fromList ["user", "type"]--- >   toSeriesPoints (Event user ty) = V.fromList [toValue user, toValue ty]--- >--- > instance ToValue EventType-class ToSeriesData a where-  -- | Column names. You can safely ignore the proxy agument.-  toSeriesColumns :: Proxy a -> Vector Column-  -- | Data points.-  toSeriesPoints :: a -> Vector Value--toSeriesData :: forall a. ToSeriesData a => a -> SeriesData-toSeriesData a = SeriesData-  { seriesDataColumns = toSeriesColumns (Proxy :: Proxy a)-  , seriesDataPoints = [toSeriesPoints a]-  }---- | A type that can be stored in InfluxDB.-class ToValue a where-  toValue :: a -> Value--instance ToValue Value where-  toValue = id--instance ToValue Bool where-  toValue = Bool--instance ToValue a => ToValue (Maybe a) where-  toValue Nothing = Null-  toValue (Just a) = toValue a--instance ToValue Int where-  toValue = Int . fromIntegral--instance ToValue Int8 where-  toValue = Int . fromIntegral--instance ToValue Int16 where-  toValue = Int . fromIntegral--instance ToValue Int32 where-  toValue = Int . fromIntegral--instance ToValue Int64 where-  toValue = Int--instance ToValue Word8 where-  toValue = Int . fromIntegral--instance ToValue Word16 where-  toValue = Int . fromIntegral--instance ToValue Word32 where-  toValue = Int . fromIntegral--instance ToValue Double where-  toValue = Float--instance ToValue T.Text where-  toValue = String--instance ToValue TL.Text where-  toValue = String . TL.toStrict--instance ToValue String where-  toValue = String . T.pack
+ src/Database/InfluxDB/Format.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+module Database.InfluxDB.Format+  ( Query+  , fromQuery++  , Format+  , makeFormat+  , (%)+  , formatQuery+  , formatDatabase+  , formatKey++  , database+  , key+  , keys+  , fieldVal+  , decimal+  , realFloat+  , text+  , time+  ) where+import Control.Category+import Data.Monoid+import Data.String+import Prelude hiding ((.), id)++import Data.Time+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Lazy.Builder as BL+import qualified Data.List as L+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.Builder as TL+import qualified Data.Text.Lazy.Builder.Int as TL+import qualified Data.Text.Lazy.Builder.RealFloat as TL++import Database.InfluxDB.Types hiding (database)++fromQuery :: Query -> B.ByteString+fromQuery (Query q) =+  BL.toStrict $ BL.toLazyByteString $ T.encodeUtf8Builder q++newtype Format a b = Format { runFormat :: (TL.Builder -> a) -> b }++instance Category Format where+  id = Format (\k -> k "")+  fmt1 . fmt2 = Format $ \k ->+    runFormat fmt1 $ \a ->+      runFormat fmt2 $ \b ->+        k (a <> b)++instance a ~ b => IsString (Format a b) where+  fromString xs = Format $ \k -> k $ fromString xs++(%) :: Format b c -> Format a b -> Format a c+(%) = (.)++formatQuery :: Format Query r -> r+formatQuery fmt = runFormat fmt (Query . TL.toStrict . TL.toLazyText)++formatDatabase :: Format Database r -> r+formatDatabase fmt = runFormat fmt (Database . TL.toStrict . TL.toLazyText)++formatKey :: Format Key r -> r+formatKey fmt = runFormat fmt (Key . TL.toStrict . TL.toLazyText)++makeFormat :: (a -> TL.Builder) -> Format r (a -> r)+makeFormat build = Format $ \k a -> k $ build a++database :: Format r (Database -> r)+database = makeFormat $ \(Database name) -> "\"" <> TL.fromText name <> "\""++keyBuilder :: Key -> TL.Builder+keyBuilder (Key name) = "\"" <> TL.fromText name <> "\""++key :: Format r (Key -> r)+key = makeFormat keyBuilder++keys :: Format r ([Key] -> r)+keys = makeFormat $ mconcat . L.intersperse "," . map keyBuilder++fieldVal :: Format r (FieldValue -> r)+fieldVal = makeFormat $ \case+  FieldInt n -> TL.decimal n+  FieldFloat d -> TL.realFloat d+  FieldString s -> "'" <> TL.fromText s <> "'"+  FieldBool b -> if b then "true" else "false"+  FieldNull -> "null"++decimal :: Integral a => Format r (a -> r)+decimal = makeFormat TL.decimal++realFloat :: RealFloat a => Format r (a -> r)+realFloat = makeFormat TL.realFloat++text :: Format r (T.Text -> r)+text = makeFormat TL.fromText++time :: FormatTime time => Format r (time -> r)+time = makeFormat $ \t ->+  "'" <> TL.fromString (formatTime defaultTimeLocale fmt t) <> "'"+  where+    fmt = "%F %X%Q" -- YYYY-MM-DD HH:MM:SS.nnnnnnnnn
− src/Database/InfluxDB/Http.hs
@@ -1,792 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell #-}-module Database.InfluxDB.Http-  ( Config(..)-  , Credentials(..), rootCreds-  , Server(..), localServer-  , TimePrecision(..)--  -- * Writing Data--  -- ** Updating Points-  , post, postWithPrecision-  , SeriesT, PointT-  , writeSeries-  , writeSeriesData-  , withSeries-  , writePoints--  -- ** Deleting Points-  , deleteSeries--  -- * Querying Data-  , query-  , Stream(..)-  , queryChunked--  -- * Administration & Security-  -- ** Creating and Dropping Databases-  , listDatabases-  , createDatabase-  , dropDatabase--  , DatabaseRequest(..)-  , configureDatabase--  -- ** Security-  -- *** Shard spaces-  , ShardSpaceRequest(..)-  , listShardSpaces-  , createShardSpace-  , dropShardSpace--  -- *** Cluster admin-  , listClusterAdmins-  , authenticateClusterAdmin-  , addClusterAdmin-  , updateClusterAdminPassword-  , deleteClusterAdmin-  -- *** Database user-  , listDatabaseUsers-  , authenticateDatabaseUser-  , addDatabaseUser-  , updateDatabaseUserPassword-  , deleteDatabaseUser-  , grantAdminPrivilegeTo-  , revokeAdminPrivilegeFrom--  -- ** Other API-  , ping-  , isInSync-  ) where--import Control.Applicative-import Control.Monad.Identity-import Control.Monad.Writer-import Data.DList (DList)-import Data.IORef-import Data.Proxy-import Data.Text (Text)-import Data.Vector (Vector)-import Data.Word (Word32)-import Network.URI (escapeURIString, isAllowedInURI)-import qualified Data.ByteString as BS-import qualified Data.ByteString.Char8 as BS8-import qualified Data.ByteString.Lazy as BL-import qualified Data.DList as DL-import qualified Data.Text as T-import Text.Printf (printf)-import Prelude--import Control.Monad.Catch (Handler(..))-import Control.Retry (recovering)-import Data.Aeson ((.=))-import Data.Aeson.TH (deriveToJSON)-import Data.Default.Class (Default(def))-import qualified Data.Aeson as A-import qualified Data.Aeson.Encode as AE-import qualified Data.Aeson.Parser as AP-import qualified Data.Aeson.Types as AT-import qualified Data.Attoparsec.ByteString as P-import qualified Data.Attoparsec.ByteString.Lazy as PL-import qualified Network.HTTP.Client as HC--import Database.InfluxDB.Decode-import Database.InfluxDB.Encode-import Database.InfluxDB.Types-import Database.InfluxDB.Types.Internal (stripPrefixOptions)-import Database.InfluxDB.Stream (Stream(..))-import qualified Database.InfluxDB.Stream as S---- | Configurations for HTTP API client.-data Config = Config-  { configCreds :: !Credentials-  , configServerPool :: !(IORef ServerPool)-  , configHttpManager :: !HC.Manager-  }---- | Default credentials.-rootCreds :: Credentials-rootCreds = Credentials-  { credsUser = "root"-  , credsPassword = "root"-  }---- | Default server location.-localServer :: Server-localServer = Server-  { serverHost = "localhost"-  , serverPort = 8086-  , serverSsl = False-  }--data TimePrecision-  = SecondsPrecision-  | MillisecondsPrecision-  | MicrosecondsPrecision--timePrecString :: TimePrecision -> String-timePrecString SecondsPrecision = "s"-timePrecString MillisecondsPrecision = "ms"-timePrecString MicrosecondsPrecision = "u"---------------------------------------------------------------- Writing Data---- | Post a bunch of writes for (possibly multiple) series into a database.-post-  :: Config-  -> Text-  -> SeriesT IO a-  -> IO a-post config databaseName =-  postGeneric config databaseName Nothing---- | Post a bunch of writes for (possibly multiple) series into a database like--- 'post' but with time precision.-postWithPrecision-  :: Config-  -> Text -- ^ Database name-  -> TimePrecision-  -> SeriesT IO a-  -> IO a-postWithPrecision config databaseName timePrec =-  postGeneric config databaseName (Just timePrec)--postGeneric-  :: Config-  -> Text -- ^ Database name-  -> Maybe TimePrecision-  -> SeriesT IO a-  -> IO a-postGeneric Config {..} databaseName timePrec write = do-  (a, series) <- runSeriesT write-  void $ httpLbsWithRetry configServerPool-    (makeRequest series)-    configHttpManager-  return a-  where-    makeRequest series = def-      { HC.method = "POST"-      , HC.requestBody = HC.RequestBodyLBS $ AE.encode series-      , HC.path = escapeString $ printf "/db/%s/series"-          (T.unpack databaseName)-      , HC.queryString = escapeString $ printf "u=%s&p=%s%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-          (maybe-            ""-            (printf "&time_precision=%s" . timePrecString)-            timePrec :: String)-      }-    Credentials {..} = configCreds---- | Monad transformer to batch up multiple writes of series to speed up--- insertions.-newtype SeriesT m a = SeriesT (WriterT (DList Series) m a)-  deriving-    ( Functor, Applicative, Monad, MonadIO, MonadTrans-    , MonadWriter (DList Series)-    )---- | Monad transformer to batch up multiple writes of points to speed up--- insertions.-newtype PointT p m a = PointT (WriterT (DList (Vector Value)) m a)-  deriving-    ( Functor, Applicative, Monad, MonadIO, MonadTrans-    , MonadWriter (DList (Vector Value))-    )--runSeriesT :: Monad m => SeriesT m a -> m (a, [Series])-runSeriesT (SeriesT w) = do-  (a, series) <- runWriterT w-  return (a, DL.toList series)---- | Write a single series data.-writeSeries-  :: (Monad m, ToSeriesData a)-  => Text-  -- ^ Series name-  -> a-  -- ^ Series data-  -> SeriesT m ()-writeSeries name = writeSeriesData name . toSeriesData---- | Write a single series data.-writeSeriesData-  :: Monad m-  => Text-  -- ^ Series name-  -> SeriesData-  -- ^ Series data-  -> SeriesT m ()-writeSeriesData name a = tell . DL.singleton $ Series-  { seriesName = name-  , seriesData = a-  }---- | Write a bunch of data for a single series. Columns for the points don't--- need to be specified because they can be inferred from the type of @a@.-withSeries-  :: forall m a. (Monad m, ToSeriesData a)-  => Text-  -- ^ Series name-  -> PointT a m ()-  -> SeriesT m ()-withSeries name (PointT w) = do-  (_, values) <- lift $ runWriterT w-  tell $ DL.singleton Series-    { seriesName = name-    , seriesData = SeriesData-        { seriesDataColumns = toSeriesColumns (Proxy :: Proxy a)-        , seriesDataPoints = DL.toList values-        }-    }---- | Write a data into a series.-writePoints-  :: (Monad m, ToSeriesData a)-  => a-  -> PointT a m ()-writePoints = tell . DL.singleton . toSeriesPoints--deleteSeries-  :: Config-  -> Text -- ^ Database name-  -> Text -- ^ Series name-  -> IO ()-deleteSeries config databaseName seriesName = runRequest_ config request-  where-    request = def-      { HC.method = "DELETE"-      , HC.path = escapeString $ printf "/db/%s/series/%s"-          (T.unpack databaseName)-          (T.unpack seriesName)-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config---------------------------------------------------------------- Querying Data---- | Query a specified database.------ The query format is specified in the--- <http://influxdb.org/docs/query_language/ InfluxDB Query Language>.-query-  :: FromSeries a-  => Config-  -> Text -- ^ Database name-  -> Text -- ^ Query text-  -> IO [a]-query config databaseName q = do-  xs <- runRequest config request-  case mapM fromSeries xs of-    Left reason -> seriesDecodeError reason-    Right ys -> return ys-  where-    request = def-      { HC.path = escapeString $ printf "/db/%s/series"-          (T.unpack databaseName)-      , HC.queryString = escapeString $ printf "u=%s&p=%s&q=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-          (T.unpack q)-      }-    Credentials {..} = configCreds config---- | Construct streaming output-responseStream :: A.FromJSON a => HC.BodyReader -> IO (Stream IO a)-responseStream body = demandPayload $ \payload ->-  if BS.null payload-    then return Done-    else decode $ parseAsJson payload-  where-    demandPayload k = HC.brRead body >>= k-    decode (P.Done leftover value) = case A.fromJSON value of-      A.Success a -> return $ Yield a $ if BS.null leftover-        then responseStream body-        else decode $ parseAsJson leftover-      A.Error message -> jsonDecodeError message-    decode (P.Partial k) = demandPayload (decode . k)-    decode (P.Fail _ _ message) = jsonDecodeError message-    parseAsJson = P.parse A.json---- | Query a specified database like 'query' but in a streaming fashion.-queryChunked-  :: FromSeries a-  => Config-  -> Text -- ^ Database name-  -> Text -- ^ Query text-  -> (Stream IO a -> IO b)-  -- ^ Action to handle the resulting stream of series-  -> IO b-queryChunked Config {..} databaseName q f =-  withPool configServerPool request $ \request' ->-    HC.withResponse request' configHttpManager $-      responseStream . HC.responseBody >=> S.mapM parse >=> f-  where-    parse series = case fromSeries series of-      Left reason -> seriesDecodeError reason-      Right a -> return a-    request = def-      { HC.path = escapeString $ printf "/db/%s/series"-          (T.unpack databaseName)-      , HC.queryString = escapeString $ printf "u=%s&p=%s&q=%s&chunked=true"-          (T.unpack credsUser)-          (T.unpack credsPassword)-          (T.unpack q)-      }-    Credentials {..} = configCreds---------------------------------------------------------------- Administration & Security---- | List existing databases.-listDatabases :: Config -> IO [Database]-listDatabases config = runRequest config request-  where-    request = def-      { HC.path = "/db"-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config---- | Create a new database. Requires cluster admin privileges.-createDatabase :: Config -> Text -> IO ()-createDatabase config name = runRequest_ config request-  where-    request = def-      { HC.method = "POST"-      , HC.requestBody = HC.RequestBodyLBS $ AE.encode $ A.object-          [ "name" .= name-          ]-      , HC.path = "/db"-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config---- | Drop a database. Requires cluster admin privileges.-dropDatabase-  :: Config-  -> Text -- ^ Database name-  -> IO ()-dropDatabase config databaseName = runRequest_ config request-  where-    request = def-      { HC.method = "DELETE"-      , HC.path = escapeString $ printf "/db/%s"-          (T.unpack databaseName)-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config---data DatabaseRequest = DatabaseRequest-  { databaseRequestSpaces :: [ShardSpaceRequest]-  , databaseRequestContinuousQueries :: [Text]-  } deriving Show--configureDatabase-  :: Config-  -> Text -- ^ Database name-  -> DatabaseRequest-  -> IO ()-configureDatabase config databaseName databaseRequest =-  runRequest_ config request-  where-    request = def-      { HC.method = "POST"-      , HC.requestBody = HC.RequestBodyLBS $ AE.encode databaseRequest-      , HC.path = escapeString $ printf "/cluster/database_configs/%s"-          (T.unpack databaseName)-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config---- | List shard spaces.-listShardSpaces :: Config -> IO [ShardSpace]-listShardSpaces config = runRequest config request-  where-    request = def-      { HC.path = "/cluster/shard_spaces"-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config--data ShardSpaceRequest = ShardSpaceRequest-  { shardSpaceRequestName :: Text-  , shardSpaceRequestRegex :: Text-  , shardSpaceRequestRetentionPolicy :: Text-  , shardSpaceRequestShardDuration :: Text-  , shardSpaceRequestReplicationFactor :: Word32-  , shardSpaceRequestSplit :: Word32-  } deriving Show---- | Create a shard space.-createShardSpace-  :: Config-  -> Text -- ^ Database-  -> ShardSpaceRequest-  -> IO ()-createShardSpace config databaseName shardSpace = runRequest_ config request-  where-    request = def-      { HC.method = "POST"-      , HC.requestBody = HC.RequestBodyLBS $ AE.encode shardSpace-      , HC.path = escapeString $ printf "/cluster/shard_spaces/%s"-          (T.unpack databaseName)-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config--dropShardSpace-  :: Config-  -> Text -- ^ Database name-  -> Text -- ^ Shard space name-  -> IO ()-dropShardSpace config databaseName shardSpaceName = runRequest_ config request-  where-    request = def-      { HC.method = "DELETE"-      , HC.path = escapeString $ printf "/cluster/shard_spaces/%s/%s"-          (T.unpack databaseName)-          (T.unpack shardSpaceName)-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config---- | List cluster administrators.-listClusterAdmins :: Config -> IO [Admin]-listClusterAdmins config = runRequest config request-  where-    request = def-      { HC.path = "/cluster_admins"-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config--authenticateClusterAdmin :: Config -> IO ()-authenticateClusterAdmin config = runRequest_ config request-  where-    request = def-      { HC.path = "/cluster_admins/authenticate"-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config---- | Add a new cluster administrator. Requires cluster admin privilege.-addClusterAdmin-  :: Config-  -> Text -- ^ Admin name-  -> Text -- ^ Password-  -> IO Admin-addClusterAdmin config name password = do-  runRequest_ config request-  return Admin-    { adminName = name-    }-  where-    request = def-      { HC.method = "POST"-      , HC.requestBody = HC.RequestBodyLBS $ AE.encode $ A.object-          [ "name" .= name-          , "password" .= password-          ]-      , HC.path = "/cluster_admins"-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config---- | Update a cluster administrator's password. Requires cluster admin--- privilege.-updateClusterAdminPassword-  :: Config-  -> Admin-  -> Text -- ^ New password-  -> IO ()-updateClusterAdminPassword Config {..} admin password =-  void $ httpLbsWithRetry configServerPool makeRequest configHttpManager-  where-    makeRequest = def-      { HC.method = "POST"-      , HC.requestBody = HC.RequestBodyLBS $ AE.encode $ A.object-          [ "password" .= password-          ]-      , HC.path = escapeString $ printf "/cluster_admins/%s"-          (T.unpack adminName)-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Admin {adminName} = admin-    Credentials {..} = configCreds---- | Delete a cluster administrator. Requires cluster admin privilege.-deleteClusterAdmin-  :: Config-  -> Admin-  -> IO ()-deleteClusterAdmin Config {..} admin =-  void $ httpLbsWithRetry configServerPool makeRequest configHttpManager-  where-    makeRequest = def-      { HC.method = "DELETE"-      , HC.path = escapeString $ printf "/cluster_admins/%s"-          (T.unpack adminName)-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Admin {adminName} = admin-    Credentials {..} = configCreds---- | List database users.-listDatabaseUsers-  :: Config-  -> Text-  -> IO [User]-listDatabaseUsers config@Config {..} database = runRequest config makeRequest-  where-    makeRequest = def-      { HC.path = escapeString $ printf "/db/%s/users"-          (T.unpack database)-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds--authenticateDatabaseUser-  :: Config-  -> Text -- ^ Database name-  -> IO ()-authenticateDatabaseUser Config {..} database =-  void $ httpLbsWithRetry configServerPool makeRequest configHttpManager-  where-    makeRequest = def-      { HC.path = escapeString $ printf "/db/%s/authenticate"-          (T.unpack database)-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds---- | Add an user to the database users.-addDatabaseUser-  :: Config-  -> Text -- ^ Database name-  -> Text -- ^ User name-  -> Text -- ^ Password-  -> IO ()-addDatabaseUser config databaseName name password = runRequest_ config request-  where-    request = def-      { HC.method = "POST"-      , HC.requestBody = HC.RequestBodyLBS $ AE.encode $ A.object-          [ "name" .= name-          , "password" .= password-          ]-      , HC.path = escapeString $ printf "/db/%s/users"-          (T.unpack databaseName)-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds config---- | Delete an user from the database users.-deleteDatabaseUser-  :: Config-  -> Text -- ^ Database name-  -> Text -- ^ User name-  -> IO ()-deleteDatabaseUser config databaseName userName = runRequest_ config request-  where-    request = (makeRequestFromDatabaseUser config databaseName userName)-      { HC.method = "DELETE"-      }---- | Update password for the database user.-updateDatabaseUserPassword-  :: Config-  -> Text -- ^ Database name-  -> Text -- ^ User name-  -> Text -- ^ New password-  -> IO ()-updateDatabaseUserPassword config databaseName userName password =-  runRequest_ config request-  where-    request = (makeRequestFromDatabaseUser config databaseName userName)-      { HC.method = "POST"-      , HC.requestBody = HC.RequestBodyLBS $ AE.encode $ A.object-          [ "password" .= password-          ]-      }---- | Give admin privilege to the user.-grantAdminPrivilegeTo-  :: Config-  -> Text -- ^ Database name-  -> Text -- ^ User name-  -> IO ()-grantAdminPrivilegeTo config databaseName userName = runRequest_ config request-  where-    request = (makeRequestFromDatabaseUser config databaseName userName)-      { HC.method = "POST"-      , HC.requestBody = HC.RequestBodyLBS $ AE.encode $ A.object-          [ "admin" .= True-          ]-      }---- | Remove admin privilege from the user.-revokeAdminPrivilegeFrom-  :: Config-  -> Text -- ^ Database name-  -> Text -- ^ User name-  -> IO ()-revokeAdminPrivilegeFrom config databaseName userName =-  runRequest_ config request-  where-    request = (makeRequestFromDatabaseUser config databaseName userName)-      { HC.method = "POST"-      , HC.requestBody = HC.RequestBodyLBS $ AE.encode $ A.object-          [ "admin" .= False-          ]-      }--makeRequestFromDatabaseUser-  :: Config-  -> Text -- ^ Database name-  -> Text -- ^ User name-  -> HC.Request-makeRequestFromDatabaseUser Config {..} databaseName userName = def-  { HC.path = escapeString $ printf "/db/%s/users/%s"-      (T.unpack databaseName)-      (T.unpack userName)-  , HC.queryString = escapeString $ printf "u=%s&p=%s"-      (T.unpack credsUser)-      (T.unpack credsPassword)-  }-  where-    Credentials {..} = configCreds--ping :: Config -> IO Ping-ping config = runRequest config request-  where-    request = def-      { HC.path = "/ping"-      }--isInSync :: Config -> IO Bool-isInSync Config {..} = do-  response <- httpLbsWithRetry configServerPool makeRequest configHttpManager-  case eitherDecodeBool (HC.responseBody response) of-    Left reason -> jsonDecodeError reason-    Right status -> return status-  where-    makeRequest = def-      { HC.path = "/sync"-      , HC.queryString = escapeString $ printf "u=%s&p=%s"-          (T.unpack credsUser)-          (T.unpack credsPassword)-      }-    Credentials {..} = configCreds-    eitherDecodeBool lbs = do-      val <- PL.eitherResult $ PL.parse AP.value lbs-      AT.parseEither A.parseJSON val---------------------------------------------------------------httpLbsWithRetry-  :: IORef ServerPool-  -> HC.Request-  -> HC.Manager-  -> IO (HC.Response BL.ByteString)-httpLbsWithRetry pool request manager =-  withPool pool request $ \request' ->-    HC.httpLbs request' manager--withPool-  :: IORef ServerPool-  -> HC.Request-  -> (HC.Request -> IO a)-  -> IO a-withPool pool request f = do-  retryPolicy <- serverRetryPolicy <$> readIORef pool-  recovering retryPolicy handlers $ \_ -> do-    server <- activeServer pool-    f $ makeRequest server-  where-    makeRequest Server {..} = request-      { HC.host = escapeText serverHost-      , HC.port = serverPort-      , HC.secure = serverSsl-      }-    handlers =-      [ const $ Handler $ \e -> case e of-        HC.FailedConnectionException {} -> retry-        HC.FailedConnectionException2 {} -> retry-        HC.InternalIOException {} -> retry-        HC.ResponseTimeout {} -> retry-        _ -> return False-      ]-    retry = True <$ failover pool--escapeText :: Text -> BS.ByteString-escapeText = escapeString . T.unpack--escapeString :: String -> BS.ByteString-escapeString = BS8.pack . escapeURIString isAllowedInURI--decodeJsonResponse-  :: A.FromJSON a-  => HC.Response BL.ByteString-  -> IO a-decodeJsonResponse response =-  case A.eitherDecode (HC.responseBody response) of-    Left reason -> jsonDecodeError reason-    Right a -> return a--runRequest :: A.FromJSON a => Config -> HC.Request -> IO a-runRequest Config {..} req = do-  response <- httpLbsWithRetry configServerPool req configHttpManager-  decodeJsonResponse response--runRequest_ :: Config -> HC.Request -> IO ()-runRequest_ Config {..} req =-  void $ httpLbsWithRetry configServerPool req configHttpManager---------------------------------------------------------------- Aeson instances--deriveToJSON (stripPrefixOptions "shardSpaceRequest") ''ShardSpaceRequest-deriveToJSON (stripPrefixOptions "databaseRequest") ''DatabaseRequest
+ src/Database/InfluxDB/JSON.hs view
@@ -0,0 +1,207 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ViewPatterns #-}+module Database.InfluxDB.JSON+  ( parseResultsWith+  , parseResultsWithDecoder+  , Decoder(..)+  , strictDecoder+  , lenientDecoder++  , getField+  , getTag++  , parseTimestamp+  , parsePOSIXTime+  , parseRFC3339+  , parseFieldValue++  , parseResultsObject+  , parseSeriesObject+  , parseSeriesBody+  , parseErrorObject+  ) where+import Control.Applicative+import Control.Exception+import Control.Monad+import Data.Maybe++import Data.Aeson+import Data.HashMap.Strict (HashMap)+import Data.Text (Text)+import Data.Time.Clock.POSIX+import Data.Time.Format+import Data.Vector (Vector)+import qualified Data.Aeson.Types as A+import qualified Data.HashMap.Strict as HashMap+import qualified Data.Scientific as Sci+import qualified Data.Text as T+import qualified Data.Vector as V++import Database.InfluxDB.Types++-- | Parse a JSON response+parseResultsWith+  :: (Maybe Text -> HashMap Text Text -> Vector Text -> Array -> A.Parser a)+  -- ^ A parser that takes+  --+  -- 1. an optional name of the series+  -- 2. a map of tags+  -- 3. an array of field names+  -- 4. an array of values+  --+  -- to construct a value.+  -> Value+  -> A.Parser (Vector a)+parseResultsWith = parseResultsWithDecoder lenientDecoder++-- | Parse a JSON response with specified decoder settings.+parseResultsWithDecoder+  :: Decoder a+  -> (Maybe Text -> HashMap Text Text -> Vector Text -> Array -> A.Parser a)+  -- ^ A parser that takes+  --+  -- 1. an optional name of the series+  -- 2. a map of tags+  -- 3. an array of field names+  -- 4. an array of values+  --+  -- to construct a value.+  -> Value+  -> A.Parser (Vector a)+parseResultsWithDecoder Decoder {..} row val0 = success+  where+    success = do+      results <- parseResultsObject val0++      (join -> series) <- V.forM results $ \val ->+        parseSeriesObject val <|> parseErrorObject val+      values <- V.forM series $ \val -> do+        (name, tags, columns, values) <- parseSeriesBody val+        decodeFold $ V.forM values $ A.withArray "values" $ \fields -> do+          assert (V.length columns == V.length fields) $ return ()+          decodeEach $ row name tags columns fields+      return $! join values++-- | Decoder settings+data Decoder a = forall b. Decoder+  { decodeEach :: A.Parser a -> A.Parser b+  -- ^ How to turn a parser for each element into another. For example, a+  -- failure can be turned into 'Nothing'.+  , decodeFold :: A.Parser (Vector b) -> A.Parser (Vector a)+  -- ^ How to aggregate all results from 'decodeEach' into a vector of results.+  }++-- | Fail immediately if there's any parse failure.+strictDecoder :: Decoder a+strictDecoder = Decoder+  { decodeEach = id+  , decodeFold = id+  }++-- | Ignore parse failures and return successful results.+lenientDecoder :: Decoder a+lenientDecoder = Decoder+  { decodeEach = optional+  , decodeFold = \p -> do+    bs <- p+    return $! V.map fromJust $ V.filter isJust bs+  }++-- | Get a field value from a column name+getField+  :: Text -- ^ Column name+  -> Vector Text -- ^ Columns+  -> Array -- ^ Fields+  -> A.Parser Value+getField column columns fields =+  case V.elemIndex column columns of+    Nothing -> fail $ "getField: no such column " ++ show column+    Just idx -> case V.indexM fields idx of+      Nothing -> fail $ "getField: index out of bound for " ++ show column+      Just field -> return field++-- | Get a tag value from a tag name+getTag+  :: Monad m+  => Text -- ^ Tag name+  -> HashMap Text Text -- ^ Tags+  -> m Text+getTag tag tags = case HashMap.lookup tag tags of+  Nothing -> fail $ "getTag: no such tag " ++ show tag+  Just val -> return val++parseResultsObject :: Value -> A.Parser (Vector A.Value)+parseResultsObject = A.withObject "results" $ \obj -> obj .: "results"++parseSeriesObject :: Value -> A.Parser (Vector A.Value)+parseSeriesObject = A.withObject "series" $ \obj ->+  fromMaybe V.empty <$> obj .:? "series"++parseSeriesBody+  :: Value+  -> A.Parser (Maybe Text, HashMap Text Text, Vector Text, Array)+parseSeriesBody = A.withObject "series" $ \obj -> do+  !name <- obj .:? "name"+  !columns <- obj .: "columns"+  !values <- obj .: "values"+  !tags <- obj .:? "tags" .!= HashMap.empty+  return (name, tags, columns, values)++parseErrorObject :: A.Value -> A.Parser a+parseErrorObject = A.withObject "error" $ \obj -> do+  message <- obj .: "error"+  fail $ T.unpack message++-- | Parse either a POSIX timestamp or RFC3339 formatted timestamp.+parseTimestamp :: Precision ty -> A.Value -> A.Parser POSIXTime+parseTimestamp prec val = case prec of+  RFC3339 -> utcTimeToPOSIXSeconds <$!> parseRFC3339 val+  _ -> parsePOSIXTime prec val++-- | Parse an integer POSIX timestamp in given time precision.+parsePOSIXTime :: Precision ty -> A.Value -> A.Parser POSIXTime+parsePOSIXTime prec val = case prec of+  RFC3339 -> A.typeMismatch err val+  _ -> A.withScientific err+    (\s -> case timestampToUTC s of+      Nothing -> A.typeMismatch err val+      Just !utc -> return utc)+    val+  where+    err = "POSIX timestamp in " ++ T.unpack (precisionName prec)+    timestampToUTC s = do+      n <- Sci.toBoundedInteger s+      return $! fromIntegral (n :: Int) * precisionScale prec++-- | Parse a RFC3339-formatted timestamp.+--+-- Note that this parser is slow as it converts a 'T.Text' input to a 'String'+-- before parsing.+parseRFC3339 :: ParseTime time => A.Value -> A.Parser time+parseRFC3339 val = A.withText err+  (\text -> maybe (A.typeMismatch err val) (return $!) $+    parseTimeM True defaultTimeLocale fmt $ T.unpack text)+  val+  where+    fmt, err :: String+    fmt = "%FT%X%QZ"+    err = "RFC3339-formatted timestamp"++parseFieldValue :: A.Value -> A.Parser FieldValue+parseFieldValue val = case val of+  A.Number sci ->+    return $! either FieldFloat FieldInt $ Sci.floatingOrInteger sci+  A.String txt ->+    return $! FieldString txt+  A.Bool b ->+    return $! FieldBool b+  A.Null ->+    return FieldNull+  _ -> fail "parseFieldValue: expected a flat data structure"
− src/Database/InfluxDB/Lens.hs
@@ -1,66 +0,0 @@-{-# LANGUAGE RankNTypes #-}-module Database.InfluxDB.Lens-  ( Lens, Lens'--  -- * Lenses for 'Config'-  , credentials-  , httpManager--  -- * Lenses for 'Credentials'-  , user, password--  -- * Lenses for 'Server'-  , host, port, ssl-  ) where-import Control.Applicative-import Data.Text (Text)-import Prelude--import Network.HTTP.Client (Manager)--import Database.InfluxDB.Http--type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t-type Lens' s a = Lens s s a a---- | User credentials for authentication-credentials :: Lens' Config Credentials-credentials f r = set <$> f (configCreds r)-  where-    set c = r { configCreds = c }---- | An instance of 'Manager' from @http-client@ package-httpManager :: Lens' Config Manager-httpManager f c = set <$> f (configHttpManager c)-  where-    set m = c { configHttpManager = m }---- | User name to be used for authentication-user :: Lens' Credentials Text-user f c = set <$> f (credsUser c)-  where-    set u = c { credsUser = u }---- | Password to be used for authentication-password :: Lens' Credentials Text-password f s = set <$> f (credsPassword s)-  where-    set p = s { credsPassword = p }---- | Host name or IP address of an InfluxDB-host :: Lens' Server Text-host f s = set <$> f (serverHost s)-  where-    set h = s { serverHost = h }---- | Port number to be used to connect to an InfluxDB-port :: Lens' Server Int-port f s = set <$> f (serverPort s)-  where-    set p = s { serverPort = p }---- | Whether or not to enable SSL connection-ssl :: Lens' Server Bool-ssl f s = set <$> f (serverSsl s)-  where-    set s' = s { serverSsl = s' }
+ src/Database/InfluxDB/Line.hs view
@@ -0,0 +1,124 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+module Database.InfluxDB.Line+  ( Line(Line)+  , measurement+  , tagSet+  , fieldSet+  , timestamp++  , buildLine+  , buildLines+  , encodeLine+  , encodeLines+  ) where+import Data.List (intersperse)+import Data.Int (Int64)+import Data.Monoid++import Control.Lens+import Data.Map (Map)+import Data.Text (Text)+import qualified Data.ByteString.Builder as B+import qualified Data.ByteString.Lazy as L+import qualified Data.Map.Strict as Map+import qualified Data.Text as T+import qualified Data.Text.Encoding as TE++import Database.InfluxDB.Types++-- | Placeholder for the Line Protocol+--+-- See https://docs.influxdata.com/influxdb/v1.0/write_protocols/line_protocol_tutorial/ for the+-- concrete syntax.+data Line time = Line+  { _measurement :: !Key+  -- ^ Measurement name+  , _tagSet :: !(Map Key Text)+  -- ^ Set of tags (optional)+  , _fieldSet :: !(Map Key FieldValue)+  -- ^ Set of fields+  --+  -- It shouldn't be empty.+  , _timestamp :: !(Maybe time)+  -- ^ Timestamp (optional)+  }++encodeLine+  :: (time -> Int64)+  -> Line time+  -> L.ByteString+encodeLine toTimestamp = B.toLazyByteString . buildLine toTimestamp++encodeLines+  :: Traversable f+  => (time -> Int64)+  -> f (Line time)+  -> L.ByteString+encodeLines toTimestamp = B.toLazyByteString . buildLines toTimestamp++buildLine+  :: (time -> Int64)+  -> Line time+  -> B.Builder+buildLine toTimestamp Line {..} =+  key <> " " <> fields <> maybe "" (" " <>) timestamp+  where+    measurement = buildKey _measurement+    tags = buildMap TE.encodeUtf8Builder _tagSet+    key = if Map.null _tagSet+      then measurement+      else measurement <> "," <> tags+    fields = buildMap buildFieldValue _fieldSet+    timestamp = B.int64Dec . toTimestamp <$> _timestamp+    buildMap encodeVal =+      mconcat . intersperse "," . map encodeKeyVal . Map.toList+      where+        encodeKeyVal (name, val) = mconcat+          [ buildKey name+          , "="+          , encodeVal val+          ]++buildKey :: Key -> B.Builder+buildKey = TE.encodeUtf8Builder . escapeKey++escapeKey :: Key -> Text+escapeKey (Key text) = T.replace " " "\\ " $ T.replace "," "\\," text++buildFieldValue :: FieldValue -> B.Builder+buildFieldValue = \case+  FieldInt i -> B.int64Dec i <> "i"+  FieldFloat d -> B.doubleDec d+  FieldString t -> "\"" <> TE.encodeUtf8Builder t <> "\""+  FieldBool b -> if b then "true" else "false"+  FieldNull -> "null"++buildLines+  :: Traversable f+  => (time -> Int64)+  -> f (Line time)+  -> B.Builder+buildLines toTimestamp = foldMap ((<> "\n") . buildLine toTimestamp)++makeLensesWith (lensRules & generateSignatures .~ False) ''Line++-- | Name of the measurement that you want to write your data to.+measurement :: Lens' (Line time) Key++-- | Tag(s) that you want to include with your data point. Tags are optional in+-- the Line Protocol, so you can set it 'empty'.+tagSet :: Lens' (Line time) (Map Key Text)++-- | Field(s) for your data point. Every data point requires at least one field+-- in the Line Protocol, so it shouldn't be 'empty'.+fieldSet :: Lens' (Line time) (Map Key FieldValue)++-- | Timestamp for your data point. You can put whatever type of timestamp that+-- is an instance of the 'Timestamp' class.+timestamp :: Lens' (Line time) (Maybe time)
+ src/Database/InfluxDB/Manage.hs view
@@ -0,0 +1,158 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+module Database.InfluxDB.Manage+  ( manage++  , ShowQuery+  , qid+  , queryText+  , Types.database+  , duration++  , ShowSeries+  , key+  ) where+import Control.Applicative+import Control.Exception+import Control.Monad++import Control.Lens+import Data.Aeson+import Data.Scientific (toBoundedInteger)+import Data.Text (Text)+import Data.Time.Clock+import Data.Void+import qualified Data.Aeson.Types as A+import qualified Data.Attoparsec.Combinator as AC+import qualified Data.Attoparsec.Text as AT+import qualified Data.Text.Encoding as TE+import qualified Data.Vector as V+import qualified Network.HTTP.Types as HT++import Database.InfluxDB.Types as Types+import Database.InfluxDB.Query hiding (query)+import qualified Database.InfluxDB.Format as F+import qualified Network.HTTP.Client.Compat as HC++-- | Send a database management query to InfluxDB.+manage :: QueryParams -> Query -> IO ()+manage params q = do+  manager' <- either HC.newManager return $ params^.manager+  response <- HC.httpLbs request manager'+  let body = HC.responseBody response+  case eitherDecode' body of+    Left message -> do+      throwIO $ IllformedJSON message body+    Right val -> case A.parse (parseResults (params^.precision)) val of+      A.Success (_ :: V.Vector Void) -> return ()+      A.Error message -> do+        let status = HC.responseStatus response+        when (HT.statusIsServerError status) $+          throwIO $ ServerError message+        when (HT.statusIsClientError status) $+          throwIO $ BadRequest message request+        fail $ "BUG: " ++ message ++ " in Database.InfluxDB.Manage.manage"+  where+    request = HC.setQueryString qs $ manageRequest params+    qs =+      [ ("q", Just $ F.fromQuery q)+      ]++manageRequest :: QueryParams -> HC.Request+manageRequest params = HC.defaultRequest+  { HC.host = TE.encodeUtf8 $ params^.server.host+  , HC.port = fromIntegral $ params^.server.port+  , HC.secure = params^.server.ssl+  , HC.method = "POST"+  , HC.path = "/query"+  }+  where+    Server {..} = params^.server++data ShowQuery = ShowQuery+  { _qid :: !Int+  , _queryText :: !Query+  , _database :: !Database+  , _duration :: !NominalDiffTime+  } deriving Show++instance QueryResults ShowQuery where+  parseResults _ = parseResultsWith $ \_ _ columns fields ->+    maybe (fail "parseResults: parse error") return $ do+      Number (toBoundedInteger -> Just _qid) <-+        V.elemIndex "qid" columns >>= V.indexM fields+      String (F.formatQuery F.text -> _queryText) <-+        V.elemIndex "query" columns >>= V.indexM fields+      String (F.formatDatabase F.text -> _database) <-+        V.elemIndex "database" columns >>= V.indexM fields+      String (parseDuration -> Right _duration) <-+        V.elemIndex "duration" columns >>= V.indexM fields+      return ShowQuery {..}++parseDuration :: Text -> Either String NominalDiffTime+parseDuration = AT.parseOnly $ sum <$!> durations+  where+    durations = some $ (*)+      <$> fmap fromIntegral int+      <*> unit+      where+        int :: AT.Parser Int+        int = AT.decimal+    unit = AC.choice+      [ 10^^(-6 :: Int) <$ AT.char 'u'+      , 1 <$ AT.char 's'+      , 60 <$ AT.char 'm'+      , 3600 <$ AT.char 'h'+      ]++newtype ShowSeries = ShowSeries+  { _key :: Key+  } deriving Show++instance QueryResults ShowSeries where+  parseResults _ = parseResultsWith $ \_ _ columns fields ->+    ShowSeries <$> parseKey "key" columns fields++makeLensesWith (lensRules & generateSignatures .~ False) ''ShowQuery++-- | Query ID+--+-- >>> v <- query (queryParams "_internal") "SHOW QUERIES" :: IO (V.Vector ShowQuery)+-- >>> v ^.. each.qid+-- [149250]+qid :: Lens' ShowQuery Int++-- | Query text+--+-- >>> v <- query (queryParams "_internal") "SHOW QUERIES" :: IO (V.Vector ShowQuery)+-- >>> v ^.. each.queryText+-- ["SHOW QUERIES"]+queryText :: Lens' ShowQuery Query++database :: Lens' ShowQuery Database++-- |+-- >>> v <- query (queryParams "_internal") "SHOW QUERIES" :: IO (V.Vector ShowQuery)+-- >>> v ^.. each.database+-- ["_internal"]+instance HasDatabase ShowQuery where+  database = Database.InfluxDB.Manage.database++-- | Duration of the query+--+-- >>> v <- query (queryParams "_internal") "SHOW QUERIES" :: IO (V.Vector ShowQuery)+-- >>> v ^.. each.duration+-- [0.06062s]+duration :: Lens' ShowQuery NominalDiffTime++makeLensesWith (lensRules & generateSignatures .~ False) ''ShowSeries++-- | Series name+--+-- >>> v <- query (queryParams "_internal") "SHOW SERIES" :: IO (V.Vector ShowSeries)+-- >>> length $ v ^.. each.key+-- 755+key :: Lens' ShowSeries Key
+ src/Database/InfluxDB/Ping.hs view
@@ -0,0 +1,101 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TemplateHaskell #-}++module Database.InfluxDB.Ping+  (+  -- * Ping interface+    ping++  -- * Ping parameters+  , PingParams(..)+  , pingParams+  , Types.server+  , Types.manager+  , waitForLeader++  -- * Ping result+  , PingResult(..)+  , roundtripTime+  , influxdbVersion+  ) where++import Control.Lens+import qualified Data.ByteString as BS+import qualified Data.Text.Encoding as TE+import qualified Network.HTTP.Client.Compat as HC+import System.Clock++import Database.InfluxDB.Types as Types+++-- Ping requests do not require authentication+-- | The full set of parameters for the ping API+data PingParams = PingParams+  { _server :: !Server+  , _manager :: !(Either HC.ManagerSettings HC.Manager)+  -- ^ HTTP connection manager+  , _waitForLeader :: !(Maybe Int)+  -- ^ the number of seconds to wait+  }++makeLensesWith (lensRules & generateSignatures .~ False) ''PingParams++server :: Lens' PingParams Server++instance HasServer PingParams where+  server = Database.InfluxDB.Ping.server++manager :: Lens' PingParams (Either HC.ManagerSettings HC.Manager)++instance HasManager PingParams where+  manager = Database.InfluxDB.Ping.manager++-- | The number of seconds to wait before returning a response+waitForLeader :: Lens' PingParams (Maybe Int)++pingParams :: PingParams+pingParams =+  PingParams+  { _server = localServer+  , _manager = Left HC.defaultManagerSettings+  , _waitForLeader = Nothing+  }++pingRequest :: PingParams -> HC.Request+pingRequest PingParams {..} = HC.defaultRequest+  { HC.host = TE.encodeUtf8 _host+  , HC.port = fromIntegral _port+  , HC.secure = _ssl+  , HC.method = "GET"+  , HC.path = "/ping"+  }+  where+    Server {..} = _server++data PingResult = PingResult+  { _roundtripTime :: !TimeSpec+  , _influxdbVersion :: !BS.ByteString+  } deriving (Show, Eq, Ord)++makeLensesWith (lensRules & generateSignatures .~ False) ''PingResult++-- | Roundtrip time of the ping+roundtripTime :: Lens' PingResult TimeSpec++-- | Version string returned by the InfluxDB header+influxdbVersion :: Lens' PingResult BS.ByteString++ping :: PingParams -> IO PingResult+ping params = do+  manager' <- either HC.newManager return $ _manager params+  startTime <- getTime'+  HC.withResponse request manager' (\response -> do+    endTime <- getTime'+    let headers = HC.responseHeaders response+    case lookup "X-Influxdb-Version" headers of+      Just version -> pure (PingResult (diffTimeSpec endTime startTime) version)+      Nothing -> error "A response by influxdb should always contain a version header.")+  where+    request = pingRequest params+    getTime' = getTime Monotonic
+ src/Database/InfluxDB/Query.hs view
@@ -0,0 +1,367 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ViewPatterns #-}+module Database.InfluxDB.Query+  (+  -- * Query interface+    Query+  , query+  , queryChunked++  -- * Query parameters+  , QueryParams+  , queryParams+  , Types.server+  , Types.database+  , Types.precision+  , Types.manager++  -- * Parsing results+  , QueryResults(..)+  , parseResultsWith+  , parseKey++  -- * Low-level functions+  , withQueryResponse+  ) where+import Control.Exception+import Control.Monad+import Text.Printf++import Control.Lens+import Data.Aeson+import Data.Optional (Optional(..), optional)+import Data.Text (Text)+import Data.Vector (Vector)+import Data.Void+import qualified Control.Foldl as L+import qualified Data.Aeson.Parser as A+import qualified Data.Aeson.Types as A+import qualified Data.Attoparsec.ByteString as AB+import qualified Data.ByteString as B+import qualified Data.ByteString.Builder as BB+import qualified Data.ByteString.Lazy as BL+import qualified Data.Text.Encoding as TE+import qualified Data.Text as T+import qualified Data.Vector as V+import qualified Network.HTTP.Types as HT++import Database.InfluxDB.JSON+import Database.InfluxDB.Types as Types+import qualified Database.InfluxDB.Format as F+import qualified Network.HTTP.Client.Compat as HC++class QueryResults a where+  parseResults+    :: Precision 'QueryRequest+    -> Value+    -> A.Parser (Vector a)++instance QueryResults Void where+  parseResults _ = A.withObject "error" $ \obj -> obj .:? "error"+    >>= maybe (pure V.empty) (withText "error" $ fail . T.unpack)++instance (a ~ Value, b ~ Value) => QueryResults (a, b) where+  parseResults _ = parseResultsWith $ \_ _ _ fields ->+    maybe (fail $ "invalid fields: " ++ show fields) return $ do+      a <- fields V.!? 0+      b <- fields V.!? 1+      return (a, b)++instance (a ~ Value, b ~ Value, c ~ Value)+  => QueryResults (a, b, c) where+    parseResults _ = parseResultsWith $ \_ _ _ fields ->+      maybe (fail $ "invalid fields: " ++ show fields) return $ do+        a <- fields V.!? 0+        b <- fields V.!? 1+        c <- fields V.!? 2+        return (a, b, c)++instance (a ~ Value, b ~ Value, c ~ Value, d ~ Value)+  => QueryResults (a, b, c, d) where+    parseResults _ = parseResultsWith $ \_ _ _ fields ->+      maybe (fail $ "invalid fields: " ++ show fields) return $ do+        a <- fields V.!? 0+        b <- fields V.!? 1+        c <- fields V.!? 2+        d <- fields V.!? 3+        return (a, b, c, d)++instance (a ~ Value, b ~ Value, c ~ Value, d ~ Value, e ~ Value)+  => QueryResults (a, b, c, d, e) where+    parseResults _ = parseResultsWith $ \_ _ _ fields ->+      maybe (fail $ "invalid fields: " ++ show fields) return $ do+        a <- fields V.!? 0+        b <- fields V.!? 1+        c <- fields V.!? 2+        d <- fields V.!? 3+        e <- fields V.!? 4+        return (a, b, c, d, e)++instance (a ~ Value, b ~ Value, c ~ Value, d ~ Value, e ~ Value, f ~ Value)+  => QueryResults (a, b, c, d, e, f) where+    parseResults _ = parseResultsWith $ \_ _ _ fields ->+      maybe (fail $ "invalid fields: " ++ show fields) return $ do+        a <- fields V.!? 0+        b <- fields V.!? 1+        c <- fields V.!? 2+        d <- fields V.!? 3+        e <- fields V.!? 4+        f <- fields V.!? 5+        return (a, b, c, d, e, f)++instance+  ( a ~ Value, b ~ Value, c ~ Value, d ~ Value, e ~ Value, f ~ Value+  , g ~ Value )+  => QueryResults (a, b, c, d, e, f, g) where+    parseResults _ = parseResultsWith $ \_ _ _ fields ->+      maybe (fail $ "invalid fields: " ++ show fields) return $ do+        a <- fields V.!? 0+        b <- fields V.!? 1+        c <- fields V.!? 2+        d <- fields V.!? 3+        e <- fields V.!? 4+        f <- fields V.!? 5+        g <- fields V.!? 6+        return (a, b, c, d, e, f, g)++instance+  ( a ~ Value, b ~ Value, c ~ Value, d ~ Value, e ~ Value, f ~ Value+  , g ~ Value, h ~ Value )+  => QueryResults (a, b, c, d, e, f, g, h) where+    parseResults _ = parseResultsWith $ \_ _ _ fields ->+      maybe (fail $ "invalid fields: " ++ show fields) return $ do+        a <- fields V.!? 0+        b <- fields V.!? 1+        c <- fields V.!? 2+        d <- fields V.!? 3+        e <- fields V.!? 4+        f <- fields V.!? 5+        g <- fields V.!? 6+        h <- fields V.!? 7+        return (a, b, c, d, e, f, g, h)++parseKey :: Key -> Vector Text -> Array -> A.Parser Key+parseKey (Key name) columns fields = do+  case V.elemIndex name columns >>= V.indexM fields of+    Just (String (F.formatKey F.text -> key)) -> return key+    _ -> fail $ printf "parseKey: %s not found in columns" $ show name++-- | The full set of parameters for the query API+data QueryParams = QueryParams+  { _server :: !Server+  , _database :: !Database+  , _precision :: !(Precision 'QueryRequest)+  -- ^ Timestamp precision+  --+  -- InfluxDB uses nanosecond precision if nothing is specified.+  , _authentication :: !(Maybe Credentials)+  -- ^ No authentication by default+  , _manager :: !(Either HC.ManagerSettings HC.Manager)+  -- ^ HTTP connection manager+  }++-- | Smart constructor for 'QueryParams'+--+-- Default parameters:+--+--   ['L.server'] 'localServer'+--   ['L.precision'] 'RFC3339'+--   ['authentication'] 'Nothing'+--   ['L.manager'] @'Left' 'HC.defaultManagerSettings'@+queryParams :: Database -> QueryParams+queryParams _database = QueryParams+  { _server = localServer+  , _precision = RFC3339+  , _authentication = Nothing+  , _manager = Left HC.defaultManagerSettings+  , ..+  }++-- | Query data from InfluxDB.+--+-- It may throw 'InfluxException'.+query :: QueryResults a => QueryParams -> Query -> IO (Vector a)+query params q = withQueryResponse params Nothing q go+  where+    go request response = do+      chunks <- HC.brConsume $ HC.responseBody response+      let body = BL.fromChunks chunks+      case eitherDecode' body of+        Left message -> throwIO $ IllformedJSON message body+        Right val -> case A.parse (parseResults (_precision params)) val of+          A.Success vec -> return vec+          A.Error message ->+            errorQuery request response message++setPrecision+  :: Precision 'QueryRequest+  -> [(B.ByteString, Maybe B.ByteString)]+  -> [(B.ByteString, Maybe B.ByteString)]+setPrecision prec qs = maybe qs (\p -> ("epoch", Just p):qs) $+  precisionParam prec++precisionParam :: Precision 'QueryRequest -> Maybe B.ByteString+precisionParam = \case+  Nanosecond -> return "ns"+  Microsecond -> return "u"+  Millisecond -> return "ms"+  Second -> return "s"+  Minute -> return "m"+  Hour -> return "h"+  RFC3339 -> Nothing++-- | Same as 'query' but it instructs InfluxDB to stream chunked responses+-- rather than returning a huge JSON object. This can be lot more efficient than+-- 'query' if the result is huge.+--+-- It may throw 'InfluxException'.+queryChunked+  :: QueryResults a+  => QueryParams+  -> Optional Int+  -- ^ Chunk size+  --+  -- By 'Default', InfluxDB chunks responses by series or by every 10,000+  -- points, whichever occurs first. If it set to a 'Specific' value, InfluxDB+  -- chunks responses by series or by that number of points.+  -> Query+  -> L.FoldM IO (Vector a) r+  -> IO r+queryChunked params chunkSize q (L.FoldM step initialize extract) =+  withQueryResponse params (Just chunkSize) q go+  where+    go request response = do+      x0 <- initialize+      chunk0 <- HC.responseBody response+      x <- loop x0 k0 chunk0+      extract x+      where+        k0 = AB.parse A.json+        loop x k chunk+          | B.null chunk = return x+          | otherwise = case k chunk of+            AB.Fail unconsumed _contexts message ->+              throwIO $ IllformedJSON message $ BL.fromStrict unconsumed+            AB.Partial k' -> do+              chunk' <- HC.responseBody response+              loop x k' chunk'+            AB.Done leftover val ->+              case A.parse (parseResults (_precision params)) val of+                A.Success vec -> do+                  x' <- step x vec+                  loop x' k0 leftover+                A.Error message ->+                  errorQuery request response message++withQueryResponse+  :: QueryParams+  -> Maybe (Optional Int)+  -- ^ Chunk size+  --+  -- By 'Nothing', InfluxDB returns all matching data points at once.+  -- By @'Just' 'Default'@, InfluxDB chunks responses by series or by every+  -- 10,000 points, whichever occurs first. If it set to a 'Specific' value,+  -- InfluxDB chunks responses by series or by that number of points.+  -> Query+  -> (HC.Request -> HC.Response HC.BodyReader -> IO r)+  -> IO r+withQueryResponse params chunkSize q f = do+    manager' <- either HC.newManager return $ _manager params+    HC.withResponse request manager' (f request)+  where+    request =+      HC.setQueryString (setPrecision (_precision params) queryString) $+        queryRequest params+    queryString = addChunkedParam+      [ ("q", Just $ F.fromQuery q)+      , ("db", Just db)+      ]+      where+        !db = TE.encodeUtf8 $ databaseName $ _database params+    addChunkedParam ps = case chunkSize of+      Nothing -> ps+      Just size ->+        let !chunked = optional "true" (decodeChunkSize . max 1) size+        in ("chunked", Just chunked) : ps+      where+        decodeChunkSize = BL.toStrict . BB.toLazyByteString . BB.intDec+++queryRequest :: QueryParams -> HC.Request+queryRequest QueryParams {..} = HC.defaultRequest+  { HC.host = TE.encodeUtf8 _host+  , HC.port = fromIntegral _port+  , HC.secure = _ssl+  , HC.method = "GET"+  , HC.path = "/query"+  }+  where+    Server {..} = _server++errorQuery :: HC.Request -> HC.Response body -> String -> IO a+errorQuery request response message = do+  let status = HC.responseStatus response+  when (HT.statusIsServerError status) $+    throwIO $ ServerError message+  when (HT.statusIsClientError status) $+    throwIO $ BadRequest message request+  fail $ "BUG: " ++ message ++ " in Database.InfluxDB.Query.query - "+    ++ show request++makeLensesWith (lensRules & generateSignatures .~ False) ''QueryParams++server :: Lens' QueryParams Server++-- |+-- >>> let p = queryParams "foo"+-- >>> p ^. server.host+-- "localhost"+instance HasServer QueryParams where+  server = Database.InfluxDB.Query.server++database :: Lens' QueryParams Database++-- |+-- >>> let p = queryParams "foo"+-- >>> p ^. database+-- "foo"+instance HasDatabase QueryParams where+  database = Database.InfluxDB.Query.database++precision :: Lens' QueryParams (Precision 'QueryRequest)++-- | Returning JSON responses contain timestamps in the specified+-- precision/format.+--+-- >>> let p = queryParams "foo"+-- >>> p ^. precision+-- Nanosecond+instance HasPrecision 'QueryRequest QueryParams where+  precision = Database.InfluxDB.Query.precision++manager :: Lens' QueryParams (Either HC.ManagerSettings HC.Manager)++-- |+-- >>> let p = queryParams "foo"+-- >>> p & manager .~ Left HC.defaultManagerSettings+instance HasManager QueryParams where+  manager = Database.InfluxDB.Query.manager++-- | Authentication info for the query+--+-- >>> let p = queryParams "foo"+-- >>> p ^. authentication+-- Nothing+authentication :: Lens' QueryParams (Maybe Credentials)++instance HasCredentials QueryParams where+  authentication = Database.InfluxDB.Query.authentication
− src/Database/InfluxDB/Stream.hs
@@ -1,41 +0,0 @@-{-# LANGUAGE BangPatterns #-}-module Database.InfluxDB.Stream where-import Prelude hiding (mapM)---- | Effectful stream-data Stream m a-  = Yield a (m (Stream m a))-  -- ^ Yield a value. The stream will be continued.-  | Done-  -- ^ The end of the stream.---- | Map each element of a stream to a monadic action, evaluate these actions--- from left to right, and collect the results as a stream.-mapM :: Monad m => (a -> m b) -> Stream m a -> m (Stream m b)-mapM _ Done = return Done-mapM f (Yield a mb) = do-  a' <- f a-  b <- mb-  return $ Yield a' (mapM f b)---- | Monadic left fold for 'Stream'.-fold :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b-fold f = loop-  where-    loop z stream = case stream of-      Done -> return z-      Yield a nextStream -> do-        b <- f z a-        stream' <- nextStream-        loop b stream'---- | Strict version of 'fold'.-fold' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b-fold' f = loop-  where-    loop z stream = case stream of-      Done -> return z-      Yield a nextStream -> do-        !b <- f z a-        stream' <- nextStream-        loop b stream'
− src/Database/InfluxDB/TH.hs
@@ -1,141 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE TemplateHaskell #-}--#if __GLASGOW_HASKELL__ == 704-{-# LANGUAGE ConstraintKinds #-}-#endif--module Database.InfluxDB.TH-  ( Options(..), defaultOptions-  , deriveSeriesData-  , deriveToSeriesData-  , deriveFromSeriesData--  , stripPrefixLower-  , stripPrefixSnake-  ) where-import Control.Applicative-import Language.Haskell.TH-import Language.Haskell.TH.Syntax (VarStrictType)-import Prelude--import qualified Data.Vector as V--import Database.InfluxDB.Decode-import Database.InfluxDB.Encode-import Database.InfluxDB.Types.Internal--data Options = Options-  { fieldLabelModifier :: String -> String-  }--defaultOptions :: Options-defaultOptions = Options-  { fieldLabelModifier = id-  }--deriveSeriesData :: Options -> Name -> Q [Dec]-deriveSeriesData opts name = (++)-  <$> deriveToSeriesData opts name-  <*> deriveFromSeriesData opts name--deriveToSeriesData :: Options -> Name -> Q [Dec]-deriveToSeriesData opts name = do-  info <- reify name-  case info of-    TyConI dec -> pure <$> deriveWith toSeriesDataBody opts dec-    _ -> fail $ "Expected a type constructor, but got " ++ show info--deriveFromSeriesData :: Options -> Name -> Q [Dec]-deriveFromSeriesData opts name = do-  info <- reify name-  case info of-    TyConI dec -> pure <$> deriveWith fromSeriesDataBody opts dec-    _ -> fail $ "Expected a type constructor, but got " ++ show info--deriveWith-  :: (Options -> Name -> [TyVarBndr] -> Con -> Q Dec)-  -> Options -> Dec -> Q Dec-deriveWith f opts dec = case dec of-#if MIN_VERSION_template_haskell(2, 11, 0)-  DataD _ tyName tyVars _ [con] _ -> f opts tyName tyVars con-  NewtypeD _ tyName tyVars _ con _ -> f opts tyName tyVars con-#else-  DataD _ tyName tyVars [con] _ -> f opts tyName tyVars con-  NewtypeD _ tyName tyVars con _ -> f opts tyName tyVars con-#endif-  _ -> fail $ "Expected a data or newtype declaration, but got " ++ show dec--toSeriesDataBody :: Options -> Name -> [TyVarBndr] -> Con -> Q Dec-toSeriesDataBody opts tyName tyVars con = do-  case con of-    RecC conName vars -> InstanceD-#if MIN_VERSION_template_haskell(2, 11, 0)-        Nothing-#endif-      <$> mapM tyVarToPred tyVars-      <*> [t| ToSeriesData $(conT tyName) |]-      <*> deriveDecs conName vars-    _ -> fail $ "Expected a record, but got " ++ show con-  where-    tyVarToPred tv = case tv of-#if MIN_VERSION_template_haskell(2, 10, 0)-      PlainTV name -> conT ''FromValue `appT` varT name-      KindedTV name _ -> conT ''FromValue `appT` varT name-#else-      PlainTV name -> classP ''FromValue [varT name]-      KindedTV name _ -> classP ''FromValue [varT name]-#endif-    deriveDecs _conName vars = do-      a <- newName "a"-      sequence-        [ funD 'toSeriesColumns-          [ clause [wildP]-            (normalB [| V.fromList $(listE columns) |]) []-          ]-        , funD 'toSeriesPoints-          [ clause [varP a]-            (normalB [| V.fromList $(listE $ map (applyToValue a) vars) |]) []-          ]-        ]-      where-        applyToValue a (name, _, _) = [| toValue ($(varE name) $(varE a)) |]-        columns = map (varStrictTypeToColumn opts) vars--fromSeriesDataBody :: Options -> Name -> [TyVarBndr] -> Con -> Q Dec-fromSeriesDataBody opts tyName tyVars con = do-  case con of-    RecC conName vars -> instanceD-      (mapM tyVarToPred tyVars)-      [t| FromSeriesData $(conT tyName) |]-      [deriveDec conName vars]-    _ -> fail $ "Expected a record, but got " ++ show con-  where-    tyVarToPred tv = case tv of-#if MIN_VERSION_template_haskell(2, 10, 0)-      PlainTV name -> conT ''FromValue `appT` varT name-      KindedTV name _ -> conT ''FromValue `appT` varT name-#else-      PlainTV name -> classP ''FromValue [varT name]-      KindedTV name _ -> classP ''FromValue [varT name]-#endif-    deriveDec conName vars = funD 'parseSeriesData-      [ clause [] (normalB deriveBody) []-      ]-      where-        deriveBody = do-          values <- newName "values"-          appE (varE 'withValues) $ lamE [varP values] $-              foldl (go values) [| pure $(conE conName) |] columns-          where-            go :: Name -> Q Exp -> Q Exp -> Q Exp-            go values expQ col = [| $expQ <*> $(varE values) .: $col |]-            columns = map (varStrictTypeToColumn opts) vars--varStrictTypeToColumn :: Options -> VarStrictType -> Q Exp-varStrictTypeToColumn opts = column opts . f-  where-    f (var, _, _) = var--column :: Options -> Name -> Q Exp-column opts = litE . stringL . fieldLabelModifier opts . nameBase
src/Database/InfluxDB/Types.hs view
@@ -1,302 +1,225 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TemplateHaskell #-}-module Database.InfluxDB.Types-  ( -- * Series, columns and data points-    Series(..)-  , seriesColumns-  , seriesPoints-  , SeriesData(..)-  , Column-  , Value(..)--  -- * Data types for HTTP API-  , Credentials(..)-  , Server(..)-  , Database(..)-  , User(..)-  , Admin(..)-  , Ping(..)-  , Interface-  , ShardSpace(..)--  -- * Server pool-  , ServerPool-  , serverRetryPolicy-  , newServerPool-  , newServerPoolWithRetryPolicy-  , activeServer-  , failover--  -- * Exceptions-  , InfluxException(..)-  , jsonDecodeError-  , seriesDecodeError-  ) where--import Control.Applicative (empty)-import Control.Exception (Exception, throwIO)+module Database.InfluxDB.Types where+import Control.Exception import Data.Data (Data)-import Data.IORef import Data.Int (Int64)-import Data.Monoid ((<>))-import Data.Sequence (Seq, ViewL(..), (|>))-import Data.Text (Text)+import Data.String import Data.Typeable (Typeable)-import Data.Vector (Vector)-import Data.Word (Word32) import GHC.Generics (Generic)-import qualified Data.Sequence as Seq -import Control.Retry (RetryPolicy, limitRetries, exponentialBackoff)-import Data.Aeson ((.=), (.:))-import Data.Aeson.TH-import qualified Data.Aeson as A--import Database.InfluxDB.Types.Internal (stripPrefixOptions)+import Control.Lens+import Data.Text (Text)+import Data.Time.Clock+import Data.Time.Clock.POSIX+import Network.HTTP.Client (Manager, ManagerSettings, Request)+import qualified Data.ByteString.Lazy as BL+import qualified Data.Text as T -#if MIN_VERSION_aeson(0, 7, 0)-import Data.Scientific-#else-import Data.Attoparsec.Number-#endif+newtype Query = Query T.Text deriving IsString --------------------------------------------------------------- Compatibility for older GHC+instance Show Query where+  show (Query q) = show q -#if __GLASGOW_HASKELL__ < 706-import Control.Exception (evaluate)+data Server = Server+  { _host :: !Text+  , _port :: !Int+  , _ssl :: !Bool+  } deriving (Show, Generic, Eq) -atomicModifyIORef' :: IORef a -> (a -> (a, b)) -> IO b-atomicModifyIORef' ref f = do-    b <- atomicModifyIORef ref $ \x ->-      let (a, b) = f x-      in (a, a `seq` b)-    evaluate b-#endif------------------------------------------------------------+-- | Default server settings.+--+-- Default parameters:+--+--  * 'host': @"localhost"@+--  * 'port': @8086@+--  * 'ssl': 'False'+localServer :: Server+localServer = Server+  { _host = "localhost"+  , _port = 8086+  , _ssl = False+  } --- | A series consists of name, columns and points. The columns and points are--- expressed in a separate type 'SeriesData'.-data Series = Series-  { seriesName :: {-# UNPACK #-} !Text-  -- ^ Series name-  , seriesData :: {-# UNPACK #-} !SeriesData-  -- ^ Columns and data points in the series-  } deriving (Typeable, Generic)+makeLensesWith (lensRules & generateSignatures .~ False) ''Server --- | Convenient accessor for columns.-seriesColumns :: Series -> Vector Column-seriesColumns = seriesDataColumns . seriesData+-- | Host name of the server+host :: Lens' Server Text --- | Convenient accessor for points.-seriesPoints :: Series -> [Vector Value]-seriesPoints = seriesDataPoints . seriesData+-- | Port number of the server+port :: Lens' Server Int -instance A.ToJSON Series where-  toJSON Series {..} = A.object-    [ "name" .= seriesName-    , "columns" .= seriesDataColumns-    , "points" .= seriesDataPoints-    ]-    where-      SeriesData {..} = seriesData+-- | If SSL is enabled+ssl :: Lens' Server Bool -instance A.FromJSON Series where-  parseJSON (A.Object v) = do-    name <- v .: "name"-    columns <- v .: "columns"-    points <- v .: "points"-    return Series-      { seriesName = name-      , seriesData = SeriesData-          { seriesDataColumns = columns-          , seriesDataPoints = points-          }-      }-  parseJSON _ = empty+-- | User credentials+data Credentials = Credentials+  { _user :: !Text+  , _password :: !Text+  } --- | 'SeriesData' consists of columns and points.-data SeriesData = SeriesData-  { seriesDataColumns :: Vector Column-  , seriesDataPoints :: [Vector Value]-  } deriving (Eq, Show, Typeable, Generic)+makeLensesWith (lensRules & generateSignatures .~ False) ''Credentials -type Column = Text+-- | User name to access InfluxDB+user :: Lens' Credentials Text --- | An InfluxDB value represented as a Haskell value.-data Value-  = Int !Int64-  | Float !Double-  | String !Text-  | Bool !Bool-  | Null-  deriving (Eq, Show, Data, Typeable, Generic)+-- | Password to access InfluxDB+password :: Lens' Credentials Text -instance A.ToJSON Value where-  toJSON (Int n) = A.toJSON n-  toJSON (Float d) = A.toJSON d-  toJSON (String xs) = A.toJSON xs-  toJSON (Bool b) = A.toJSON b-  toJSON Null = A.Null+-- | Database name+newtype Database = Database { databaseName :: Text } deriving (Eq, Ord) -instance A.FromJSON Value where-  parseJSON (A.Object o) = fail $ "Unexpected object: " ++ show o-  parseJSON (A.Array a) = fail $ "Unexpected array: " ++ show a-  parseJSON (A.String xs) = return $ String xs-  parseJSON (A.Bool b) = return $ Bool b-  parseJSON A.Null = return Null-  parseJSON (A.Number n) = return $! numberToValue-    where-#if MIN_VERSION_aeson(0, 7, 0)-      numberToValue-        -- If the number is larger than Int64, it must be-        -- a float64 (Double in Haskell).-        | n > maxInt = Float $ toRealFloat n-        | e < 0 = Float $ realToFrac n-        | otherwise = Int $ fromIntegral $ coefficient n * 10 ^ e-        where-          e = base10Exponent n-#if !MIN_VERSION_scientific(0, 3, 0)-          toRealFloat = realToFrac--- scientific-#endif-#else-      numberToValue = case n of-        I i-          -- If the number is larger than Int64, it must be-          -- a float64 (Double in Haskell).-          | i > maxInt -> Float $ fromIntegral i-          | otherwise -> Int $ fromIntegral i-        D d -> Float d--- aeson-#endif-      maxInt = fromIntegral (maxBound :: Int64)+-- | String type that is used for measurements, tag keys and field keys.+newtype Key = Key Text deriving (Eq, Ord) ------------------------------------------------------------+instance IsString Database where+  fromString xs = Database $ fromNonEmptyString "Database" xs --- | User credentials.-data Credentials = Credentials-  { credsUser :: !Text-  , credsPassword :: !Text-  } deriving (Show, Typeable, Generic)+instance IsString Key where+  fromString xs = Key $ fromNonEmptyString "Key" xs --- | Server location.-data Server = Server-  { serverHost :: !Text-  -- ^ Hostname or IP address-  , serverPort :: !Int-  , serverSsl :: !Bool-  -- ^ SSL is enabled or not in the server side-  } deriving (Show, Typeable, Generic)+fromNonEmptyString :: String -> String -> Text+fromNonEmptyString ty xs+  | null xs = error $ ty ++ " should never be empty"+  | otherwise = fromString xs --- | Non-empty set of server locations. The active server will always be used--- until any HTTP communications fail.-data ServerPool = ServerPool-  { serverActive :: !Server-  -- ^ Current active server-  , serverBackup :: !(Seq Server)-  -- ^ The rest of the servers in the pool.-  , serverRetryPolicy :: !RetryPolicy-  }+instance Show Database where+  show (Database name) = show name -newtype Database = Database-  { databaseName :: Text-  } deriving (Show, Typeable, Generic)+instance Show Key where+  show (Key name) = show name --- | User-data User = User-  { userName :: Text-  , userIsAdmin :: Bool-  } deriving (Show, Typeable, Generic)+data FieldValue+  = FieldInt !Int64+  | FieldFloat !Double+  | FieldString !Text+  | FieldBool !Bool+  | FieldNull+  deriving (Eq, Show, Data, Typeable, Generic) --- | Administrator-newtype Admin = Admin-  { adminName :: Text-  } deriving (Show, Typeable, Generic)+instance IsString FieldValue where+  fromString = FieldString . T.pack -newtype Ping = Ping-  { pingStatus :: Text-  } deriving (Show, Typeable, Generic)+-- | Type of a request+data RequestType+  = QueryRequest+  -- ^ Request for @/query@+  | WriteRequest+  -- ^ Request for @/write@+  deriving Show -type Interface = Text+-- | Predefined set of time precision.+--+-- 'RFC3339' is only available for 'QueryRequest's.+data Precision (ty :: RequestType) where+  -- | POSIX time in ns+  Nanosecond :: Precision ty+  -- | POSIX time in μs+  Microsecond :: Precision ty+  -- | POSIX time in ms+  Millisecond :: Precision ty+  -- | POSIX time in s+  Second :: Precision ty+  -- | POSIX time in minutes+  Minute :: Precision ty+  -- | POSIX time in hours+  Hour :: Precision ty+  -- | Nanosecond precision time in a human readable format, like+  -- @2016-01-04T00:00:23.135623Z@. This is the default format for @/query@.+  RFC3339 :: Precision 'QueryRequest -data ShardSpace = ShardSpace-  { shardSpaceDatabase :: Maybe Text-  , shardSpaceName :: Text-  , shardSpaceRegex :: Text-  , shardSpaceRetentionPolicy :: Text-  , shardSpaceShardDuration :: Text-  , shardSpaceReplicationFactor :: Word32-  , shardSpaceSplit :: Word32-  } deriving (Show, Typeable, Generic)+deriving instance Show (Precision a) --------------------------------------------------------------- Server pool manipulation+precisionName :: Precision ty -> Text+precisionName = \case+  Nanosecond -> "n"+  Microsecond -> "u"+  Millisecond -> "ms"+  Second -> "s"+  Minute -> "m"+  Hour -> "h"+  RFC3339 -> "rfc3339" --- | Create a non-empty server pool. You must specify at least one server--- location to create a pool.-newServerPool :: Server -> [Server] -> IO (IORef ServerPool)-newServerPool = newServerPoolWithRetryPolicy defaultRetryPolicy-  where-    defaultRetryPolicy :: RetryPolicy-    defaultRetryPolicy = limitRetries 5 <> exponentialBackoff 50+-- | A 'Timestamp' is something that can be converted to a valid+-- InfluxDB timestamp, which is represented as a 64-bit integer.+class Timestamp time where+  -- | Round a time to the given precision and scale it to nanoseconds+  roundTo :: Precision 'WriteRequest -> time -> Int64+  -- | Scale a time to the given precision+  scaleTo :: Precision 'WriteRequest -> time -> Int64 -newServerPoolWithRetryPolicy-  :: RetryPolicy -> Server -> [Server] -> IO (IORef ServerPool)-newServerPoolWithRetryPolicy retryPolicy active backups =-  newIORef ServerPool-    { serverActive = active-    , serverBackup = Seq.fromList backups-    , serverRetryPolicy = retryPolicy-    }+roundAt :: RealFrac a => a -> a -> a+roundAt scale x = fromIntegral (round (x / scale) :: Int) * scale --- | Get a server from the pool.-activeServer :: IORef ServerPool -> IO Server-activeServer ref = do-  ServerPool { serverActive } <- readIORef ref-  return serverActive+precisionScale :: Fractional a => Precision ty -> a+precisionScale = \case+  RFC3339 ->     10^^(-9 :: Int)+  Nanosecond ->  10^^(-9 :: Int)+  Microsecond -> 10^^(-6 :: Int)+  Millisecond -> 10^^(-3 :: Int)+  Second -> 1+  Minute -> 60+  Hour ->   60 * 60 --- | Move the current server to the backup pool and pick one of the backup--- server as the new active server. Currently the scheduler works in--- round-robin fashion.-failover :: IORef ServerPool -> IO ()-failover ref = atomicModifyIORef' ref $ \pool@ServerPool {..} ->-  case Seq.viewl serverBackup of-    EmptyL -> (pool, ())-    active :< rest -> (newPool, ())-      where-        newPool = pool-          { serverActive = active-          , serverBackup = rest |> serverActive-          }+instance Timestamp UTCTime where+  roundTo prec = roundTo prec . utcTimeToPOSIXSeconds+  scaleTo prec = scaleTo prec . utcTimeToPOSIXSeconds --------------------------------------------------------------- Exceptions+instance Timestamp NominalDiffTime where+  roundTo prec time =+    round $ 10^(9 :: Int) * roundAt (precisionScale prec) time+  scaleTo prec time = round $ time / precisionScale prec +-- | Exceptions used in this library.+--+-- In general, the library tries to convert exceptions from the dependent+-- libraries to the following types of errors. data InfluxException-  = JsonDecodeError String-  | SeriesDecodeError String+  = ServerError String+  -- ^ Server side error.+  --+  -- You can expect to get a successful response once the issue is resolved on+  -- the server side.+  | BadRequest String Request+  -- ^ Client side error.+  --+  -- You need to fix your query to get a successful response.+  | IllformedJSON String BL.ByteString+  -- ^ Unexpected JSON response.+  --+  -- This can happen e.g. when the response from InfluxDB is incompatible with+  -- what this library expects due to an upstream format change etc.   deriving (Show, Typeable)  instance Exception InfluxException -jsonDecodeError :: String -> IO a-jsonDecodeError = throwIO . JsonDecodeError+class HasServer a where+  server :: Lens' a Server -seriesDecodeError :: String -> IO a-seriesDecodeError = throwIO . SeriesDecodeError+class HasDatabase a where+  database :: Lens' a Database --------------------------------------------------------------- Aeson instances+class HasPrecision (ty :: RequestType) a | a -> ty where+  -- Time precision parameter+  precision :: Lens' a (Precision ty) -deriveFromJSON (stripPrefixOptions "database") ''Database-deriveFromJSON (stripPrefixOptions "admin") ''Admin-deriveFromJSON (stripPrefixOptions "user") ''User-deriveFromJSON (stripPrefixOptions "ping") ''Ping-deriveFromJSON (stripPrefixOptions "shardSpace") ''ShardSpace+class HasManager a where+  -- | HTTP manager settings or a manager itself.+  --+  -- If it's set to 'ManagerSettings', the library will create a 'Manager' from+  -- the settings for you.+  manager :: Lens' a (Either ManagerSettings Manager)++class HasCredentials a where+  authentication :: Lens' a (Maybe Credentials)
− src/Database/InfluxDB/Types/Internal.hs
@@ -1,49 +0,0 @@-{-# LANGUAGE CPP #-}-module Database.InfluxDB.Types.Internal-  ( stripPrefixOptions-  , stripPrefixLower-  , stripPrefixSnake-  ) where-import Data.Char (isUpper, toLower)------------------------------------------------------ Conditional imports--#if MIN_VERSION_aeson(0, 6, 2)-import Data.Aeson.TH (Options(..), defaultOptions)-#endif-----------------------------------------------------#if MIN_VERSION_aeson(0, 6, 2)-stripPrefixOptions :: String -> Options-stripPrefixOptions name = defaultOptions-  { fieldLabelModifier = stripPrefixLower name-  }-#else-stripPrefixOptions :: String -> String -> String-stripPrefixOptions = stripPrefixLower-#endif---- | Strip the prefix then convert to 'lowerCamelCase'.-stripPrefixLower-  :: String -- ^ Prefix to be stripped-  -> String -- ^ Input string-  -> String-stripPrefixLower prefix xs = case drop (length prefix) xs of-  [] -> error "Insufficient length of field name"-  c:cs -> toLower c : cs---- | Strip the prefix then convert to 'snake_case'.-stripPrefixSnake-  :: String -- ^ Prefix to be stripped-  -> String -- ^ Input string-  -> String-stripPrefixSnake prefix xs = case drop (length prefix) xs of-  [] -> error "Insufficient length of field name"-  cs -> toSnake cs-  where-    toSnake = dropWhile (== '_') . foldr f []-    f c cs-      | isUpper c = '_':toLower c:cs-      | otherwise = c:cs
+ src/Database/InfluxDB/Write.hs view
@@ -0,0 +1,209 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ViewPatterns #-}+module Database.InfluxDB.Write+  ( -- * Writers+    write+  , writeBatch+  , writeByteString++  -- * Writer parameters+  , WriteParams+  , writeParams+  , Types.server+  , Types.database+  , retentionPolicy+  , Types.precision+  , Types.manager+) where+import Control.Exception+import Control.Monad+import Data.Maybe++import Control.Lens+import qualified Data.Aeson as A+import qualified Data.Aeson.Types as A+import qualified Data.ByteString.Char8 as B8+import qualified Data.ByteString.Lazy as BL+import qualified Data.Text.Encoding as TE+import qualified Network.HTTP.Types as HT++import Database.InfluxDB.Line+import Database.InfluxDB.Types as Types+import Database.InfluxDB.JSON+import qualified Network.HTTP.Client.Compat as HC++-- | The full set of parameters for the HTTP writer.+data WriteParams = WriteParams+  { _server :: !Server+  , _database :: !Database+  -- ^ Database to be written+  , _retentionPolicy :: !(Maybe Key)+  -- ^ 'Nothing' means the default retention policy for the database.+  , _precision :: !(Precision 'WriteRequest)+  -- ^ Timestamp precision+  --+  -- In the HTTP API, timestamps are scaled by the given precision.+  , _authentication  :: !(Maybe Credentials)+  -- ^ No authentication by default+  , _manager :: !(Either HC.ManagerSettings HC.Manager)+  -- ^ HTTP connection manager+  }++-- | Smart constructor for 'WriteParams'+--+-- Default parameters:+--+--   ['L.server'] 'localServer'+--   ['L.precision'] 'Nanosecond'+--   ['retentionPolicy'] 'Nothing'+--   ['L.manager'] @'Left' 'HC.defaultManagerSettings'@+writeParams :: Database -> WriteParams+writeParams _database = WriteParams+  { _server = localServer+  , _precision = Nanosecond+  , _retentionPolicy = Nothing+  , _authentication = Nothing+  , _manager = Left HC.defaultManagerSettings+  , ..+  }++-- | Write a 'Line'+write+  :: Timestamp time+  => WriteParams+  -> Line time+  -> IO ()+write p@WriteParams {_precision} =+  writeByteString p . encodeLine (scaleTo _precision)++-- | Write 'Line's in a batch+--+-- This is more efficient than 'write'.+writeBatch+  :: (Timestamp time, Traversable f)+  => WriteParams+  -> f (Line time)+  -> IO ()+writeBatch p@WriteParams {_precision} =+  writeByteString p . encodeLines (scaleTo _precision)++-- | Write a raw 'BL.ByteString'+writeByteString :: WriteParams -> BL.ByteString -> IO ()+writeByteString params payload = do+  manager' <- either HC.newManager return $ _manager params+  response <- HC.httpLbs request manager'+  let body = HC.responseBody response+      status = HC.responseStatus response+  if BL.null body+    then do+      let message = B8.unpack $ HT.statusMessage status+      when (HT.statusIsServerError status) $+        throwIO $ ServerError message+      when (HT.statusIsClientError status) $+        throwIO $ BadRequest message request+    else case A.eitherDecode' body of+      Left message ->+        throwIO $ IllformedJSON message body+      Right val -> case A.parse parseErrorObject val of+        A.Success _ ->+          fail $ "BUG: impossible code path in Database.InfluxDB.Write.writeByteString"+        A.Error message -> do+          when (HT.statusIsServerError status) $+            throwIO $ ServerError message+          when (HT.statusIsClientError status) $+            throwIO $ BadRequest message request+          fail $ "BUG: " ++ message+            ++ " in Database.InfluxDB.Write.writeByteString"++  where+    request = (writeRequest params)+      { HC.requestBody = HC.RequestBodyLBS payload+      }++writeRequest :: WriteParams -> HC.Request+writeRequest WriteParams {..} =+  HC.setQueryString qs HC.defaultRequest+    { HC.host = TE.encodeUtf8 _host+    , HC.port = fromIntegral _port+    , HC.secure = _ssl+    , HC.method = "POST"+    , HC.path = "/write"+    }+  where+    Server {..} = _server+    qs = concat+      [ [("db", Just $ TE.encodeUtf8 $ databaseName _database)]+      , fromMaybe [] $ do+        Key name <- _retentionPolicy+        return [("rp", Just (TE.encodeUtf8 name))]+      , fromMaybe [] $ do+        Credentials { _user = u, _password = p } <- _authentication+        return+          [ ("u", Just (TE.encodeUtf8 u))+          , ("p", Just (TE.encodeUtf8 p))+          ]+      ]++makeLensesWith (lensRules & generateSignatures .~ False) ''WriteParams++server :: Lens' WriteParams Server++-- |+-- >>> let p = writeParams "foo"+-- >>> p ^. server.host+-- "localhost"+instance HasServer WriteParams where+  server = Database.InfluxDB.Write.server++database :: Lens' WriteParams Database++-- |+-- >>> let p = writeParams "foo"+-- >>> p ^. database+-- "foo"+instance HasDatabase WriteParams where+  database = Database.InfluxDB.Write.database++-- | Target retention policy for the write.+--+-- InfluxDB writes to the @default@ retention policy if this parameter is set+-- to 'Nothing'.+--+-- >>> let p = writeParams "foo"+-- >>> let p' = p & retentionPolicy .~ Just "two_hours"+-- >>> p' ^. retentionPolicy+-- Just "two_hours"+retentionPolicy :: Lens' WriteParams (Maybe Key)++precision :: Lens' WriteParams (Precision 'WriteRequest)++-- |+-- >>> let p = writeParams "foo"+-- >>> p ^. precision+-- Nanosecond+instance HasPrecision 'WriteRequest WriteParams where+  precision = Database.InfluxDB.Write.precision++manager :: Lens' WriteParams (Either HC.ManagerSettings HC.Manager)++-- |+-- >>> let p = writeParams "foo"+-- >>> p & manager .~ Left HC.defaultManagerSettings+instance HasManager WriteParams where+  manager = Database.InfluxDB.Write.manager++-- | Authentication info for the write+--+-- >>> let p = writeParams "foo"+-- >>> p ^. authentication+-- Nothing+authentication :: Lens' WriteParams (Maybe Credentials)++instance HasCredentials WriteParams where+  authentication = Database.InfluxDB.Write.authentication
+ src/Database/InfluxDB/Write/UDP.hs view
@@ -0,0 +1,87 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TemplateHaskell #-}+module Database.InfluxDB.Write.UDP+  ( -- * Writers+    write+  , writeBatch+  , writeByteString++  -- * Writer parameters+  , WriteParams+  , writeParams+  , socket+  , sockAddr+  , Types.precision+  ) where++import Control.Lens+import Network.Socket (SockAddr, Socket)+import Network.Socket.ByteString (sendManyTo)+import qualified Data.ByteString.Lazy as BL++import Database.InfluxDB.Line+import Database.InfluxDB.Types as Types++-- | The full set of parameters for the UDP writer.+data WriteParams = WriteParams+  { _socket :: !Socket+  , _sockAddr :: !SockAddr+  , _precision :: !(Precision 'WriteRequest)+  }++-- | Smart constructor for 'WriteParams'+--+-- Default parameters:+--+--   ['L.precision'] 'Nanosecond'+writeParams :: Socket -> SockAddr -> WriteParams+writeParams _socket _sockAddr = WriteParams+  { _precision = Nanosecond+  , ..+  }++-- | Write a 'Line'+write+  :: Timestamp time+  => WriteParams+  -> Line time+  -> IO ()+write p@WriteParams {_precision} =+  writeByteString p . encodeLine (roundTo _precision)++-- | Write 'Line's in a batch+--+-- This is more efficient than 'write'.+writeBatch+  :: (Timestamp time, Traversable f)+  => WriteParams+  -> f (Line time)+  -> IO ()+writeBatch p@WriteParams {_precision} =+  writeByteString p . encodeLines (roundTo _precision)++-- | Write a raw 'L.ByteString'+writeByteString :: WriteParams -> BL.ByteString -> IO ()+writeByteString WriteParams {..} payload =+  sendManyTo _socket (BL.toChunks payload) _sockAddr++makeLensesWith (lensRules & generateSignatures .~ False) ''WriteParams++-- | Open UDP socket+socket :: Lens' WriteParams Socket++-- | UDP endopoint of the database+sockAddr :: Lens' WriteParams SockAddr++precision :: Lens' WriteParams (Precision 'WriteRequest)++-- | Timestamp precision.+--+-- In the UDP API, all timestamps are sent in nanosecond but you can specify+-- lower precision. The writer just rounds timestamps to the specified+-- precision.+instance HasPrecision 'WriteRequest WriteParams where+  precision = Database.InfluxDB.Write.UDP.precision
+ src/Network/HTTP/Client/Compat.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE CPP #-}+module Network.HTTP.Client.Compat+  ( defaultRequest+  , module X+  ) where++#if MIN_VERSION_http_client(0, 5, 0)+import Network.HTTP.Client (defaultRequest)+import Network.HTTP.Client as X hiding (defaultRequest)+#else+import Data.Default.Class (def)+import Network.HTTP.Client as X++defaultRequest :: Request+defaultRequest = def+#endif
tests/test-suite.hs view
@@ -16,469 +16,8 @@  import Test.Tasty.HUnit import Test.Tasty.TH-import Test.Tasty.QuickCheck hiding (reason)-import qualified Network.HTTP.Client as HC  import Database.InfluxDB-import Database.InfluxDB.TH-import qualified Database.InfluxDB.Stream as S -prop_fromValue_toValue_identity_Value :: Value -> Bool-prop_fromValue_toValue_identity_Value = fromValueToValueIdentity--prop_fromValue_toValue_identity_Bool :: Bool -> Bool-prop_fromValue_toValue_identity_Bool = fromValueToValueIdentity--prop_fromValue_toValue_identity_Int :: Int -> Bool-prop_fromValue_toValue_identity_Int = fromValueToValueIdentity--prop_fromValue_toValue_identity_Int8 :: Int8 -> Bool-prop_fromValue_toValue_identity_Int8 = fromValueToValueIdentity--prop_fromValue_toValue_identity_Int16 :: Int16 -> Bool-prop_fromValue_toValue_identity_Int16 = fromValueToValueIdentity--prop_fromValue_toValue_identity_Int32 :: Int32 -> Bool-prop_fromValue_toValue_identity_Int32 = fromValueToValueIdentity--prop_fromValue_toValue_identity_Int64 :: Int64 -> Bool-prop_fromValue_toValue_identity_Int64 = fromValueToValueIdentity--prop_fromValue_toValue_identity_Word8 :: Word8 -> Bool-prop_fromValue_toValue_identity_Word8 = fromValueToValueIdentity--prop_fromValue_toValue_identity_Word16 :: Word16 -> Bool-prop_fromValue_toValue_identity_Word16 = fromValueToValueIdentity--prop_fromValue_toValue_identity_Word32 :: Word32 -> Bool-prop_fromValue_toValue_identity_Word32 = fromValueToValueIdentity--prop_fromValue_toValue_identity_Double :: Double -> Bool-prop_fromValue_toValue_identity_Double = fromValueToValueIdentity--prop_fromValue_toValue_identity_Text :: T.Text -> Bool-prop_fromValue_toValue_identity_Text = fromValueToValueIdentity--prop_fromValue_toValue_identity_LazyText :: TL.Text -> Bool-prop_fromValue_toValue_identity_LazyText = fromValueToValueIdentity--prop_fromValue_toValue_identity_String :: String -> Bool-prop_fromValue_toValue_identity_String = fromValueToValueIdentity--prop_fromValue_toValue_identity_Maybe_Int :: Maybe Int -> Bool-prop_fromValue_toValue_identity_Maybe_Int = fromValueToValueIdentity-----------------------------------------------------instance Arbitrary Value where-  arbitrary = oneof-    [ Int <$> arbitrary-    , Float <$> arbitrary-    , String <$> arbitrary-    , Bool <$> arbitrary-    , pure Null-    ]--instance Arbitrary T.Text where-  arbitrary = T.pack <$> arbitrary--instance Arbitrary TL.Text where-  arbitrary = TL.pack <$> arbitrary--fromValueToValueIdentity :: (Eq a, FromValue a, ToValue a) => a -> Bool-fromValueToValueIdentity a = fromValue (toValue a) == Right a-----------------------------------------------------case_ping :: Assertion-case_ping = runTest $ \config -> do-  Ping status <- ping config-  status @?= "ok"--case_isInSync :: Assertion-case_isInSync = runTest $ \config -> do-  inSync <- isInSync config-  assertBool "The database is not in sync." inSync--case_post :: Assertion-case_post = runTest $ \config ->-  withTestDatabase config $ \database -> do-    name <- liftIO newName-    post config database $-      writeSeries name $ Val 42-    ss <- query config database $ "select value from " <> name-    case ss of-      [series] -> fromSeriesData series @?= Right [Val 42]-      _ -> assertFailure $ "Expect one series, but got: " ++ show ss--case_post_multi_series :: Assertion-case_post_multi_series = runTest $ \config ->-  withTestDatabase config $ \database -> do-    name <- liftIO newName-    post config database $ do-      writeSeries name $ Val 42-      writeSeries name $ Val 42-      writeSeries name $ Val 42-    ss <- query config database $ "select value from " <> name-    case ss of-      [series] -> fromSeriesData series @?= Right [Val 42, Val 42, Val 42]-      _ -> assertFailure $ "Expect one series, but got: " ++ show ss--case_post_multi_points :: Assertion-case_post_multi_points = runTest $ \config ->-  withTestDatabase config $ \database -> do-    name <- liftIO newName-    post config database $ withSeries name $ do-      writePoints $ Val 42-      writePoints $ Val 42-      writePoints $ Val 42-    ss <- query config database $ "select value from " <> name-    case ss of-      [series] -> fromSeriesData series @?= Right [Val 42, Val 42, Val 42]-      _ -> assertFailure $ "Expect one series, but got: " ++ show ss--case_query_nonexistent_series :: Assertion-case_query_nonexistent_series = runTest $ \config ->-  withTestDatabase config $ \database -> do-    name <- liftIO newName-    assertStatusCodeException-      (query config database $ "select * from " <> name :: IO [SeriesData])--case_query_empty_series :: Assertion-case_query_empty_series = runTest $ \config ->-  withTestDatabase config $ \database -> do-    name <- liftIO newName-    post config database $-      writeSeries name $ Val 42-    ss1 <- query config database $ "delete from " <> name-    ss1 @?= ([] :: [SeriesData])-    ss2 <- query config database $ "select * from " <> name-    ss2 @?= ([] :: [SeriesData])--case_queryChunked :: Assertion-case_queryChunked = runTest $ \config ->-  withTestDatabase config $ \database -> do-    name <- liftIO newName-    post config database $ withSeries name $ do-      writePoints $ Val 42-      writePoints $ Val 42-      writePoints $ Val 42-    ss <- queryChunked config database ("select value from " <> name) $-      S.fold step []-    mapM fromSeriesData ss @?= Right [[Val 42], [Val 42], [Val 42]]-  where-    step xs series = case fromSeriesData series of-      Left reason -> throwIO $ HUnitFailure reason-      Right values -> return $ xs ++ values--case_post_with_precision :: Assertion-case_post_with_precision = runTest $ \config ->-  withTestDatabase config $ \database -> do-    name <- liftIO newName-    postWithPrecision config database SecondsPrecision $-      writeSeries name $ Val 42-    ss <- query config database $ "select value from " <> name-    case ss of-      [series] -> fromSeriesData series @?= Right [Val 42]-      _ -> assertFailure $ "Expect one series, but got: " ++ show ss--case_delete_series :: Assertion-case_delete_series = runTest $ \config ->-  withTestDatabase config $ \database -> do-    name <- liftIO newName-    post config database $-      writeSeries name $ Val 42-    ss <- query config database $ "select value from " <> name-    case ss of-      [series] -> fromSeriesData series @?= Right [Val 42]-      _ -> assertFailure $ "Expect one series, but got: " ++ show ss-    deleteSeries config database name-    assertStatusCodeException-      (query config database $ "select value from " <> name :: IO [SeriesData])--case_listDatabases :: Assertion-case_listDatabases = runTest $ \config ->-  withTestDatabase config $ \name -> do-    databases <- listDatabases config-    assertBool ("No such database: " ++ T.unpack name) $-      any ((name ==) . databaseName) databases--case_configureDatabase :: Assertion-case_configureDatabase = runTest $ \config -> do-  dbName <- newName-  do-    configureDatabase config dbName $ DatabaseRequest shardSpaces contQueries-    listDatabases config >>= \databases ->-      assertBool ("No such database: " ++ T.unpack dbName) $-        any ((dbName ==) . databaseName) databases-    listShardSpaces config >>= \spaces ->-        assertBool "Missing shard space(s)" $-          any ((`elem` spaceNames) . shardSpaceName) spaces-    `finally`-      dropDatabase config dbName-  where-    spaceNames = map shardSpaceRequestName shardSpaces-    shardSpaces =-      [ ShardSpaceRequest-          { shardSpaceRequestName = "everything_30d"-          , shardSpaceRequestRetentionPolicy = "30d"-          , shardSpaceRequestShardDuration = "7d"-          , shardSpaceRequestRegex = "/.*/"-          , shardSpaceRequestReplicationFactor = 1-          , shardSpaceRequestSplit = 1-          }-      , ShardSpaceRequest-          { shardSpaceRequestName = "forever"-          , shardSpaceRequestRetentionPolicy = "inf"-          , shardSpaceRequestShardDuration = "7d"-          , shardSpaceRequestRegex = "/^_.*/"-          , shardSpaceRequestReplicationFactor = 1-          , shardSpaceRequestSplit = 1-          }-      , ShardSpaceRequest-          { shardSpaceRequestName = "rollups"-          , shardSpaceRequestRetentionPolicy = "365d"-          , shardSpaceRequestShardDuration = "30d"-          , shardSpaceRequestRegex = "/^\\d+.*/"-          , shardSpaceRequestReplicationFactor = 1-          , shardSpaceRequestSplit = 1-          }-      ]-    contQueries =-      [ "select * from events into events.[id]"-      , "select count(value) from events group by time(5m) into 5m.count.events"-      ]--case_shardSpaces :: Assertion-case_shardSpaces = runTest $ \config ->-  withTestDatabase config $ \name -> do-    spaceName <- newName-    createShardSpace config name $ ShardSpaceRequest-      { shardSpaceRequestName = spaceName-      , shardSpaceRequestRegex = "^[a-z].*"-      , shardSpaceRequestRetentionPolicy = "7d"-      , shardSpaceRequestShardDuration = "1d"-      , shardSpaceRequestReplicationFactor = 1-      , shardSpaceRequestSplit = 1-      }-    listShardSpaces config >>= \spaces ->-      assertBool ("No such shard space: " ++ T.unpack spaceName) $-        any ((spaceName ==) . shardSpaceName) spaces-    dropShardSpace config name spaceName-    listShardSpaces config >>= \spaces ->-      assertBool ("Found a dropped shard space: " ++ T.unpack spaceName) $-        all ((spaceName /=) . shardSpaceName) spaces--case_create_then_drop_database :: Assertion-case_create_then_drop_database = runTest $ \config -> do-  name <- newName-  dropDatabaseIfExists config name-  createDatabase config name-  listDatabases config >>= \databases ->-    assertBool ("No such database: " ++ T.unpack name) $-      any ((name ==) . databaseName) databases-  dropDatabase config name-  listDatabases config >>= \databases ->-    assertBool ("Found a dropped database: " ++ T.unpack name) $-      all ((name /=) . databaseName) databases--case_list_cluster_admins :: Assertion-case_list_cluster_admins = runTest $ \config -> do-  admins <- listClusterAdmins config-  assertBool "No root admin" $-    any (("root" ==) . adminName) admins--case_authenticate_cluster_admin :: Assertion-case_authenticate_cluster_admin = runTest authenticateClusterAdmin--case_add_then_delete_cluster_admin :: Assertion-case_add_then_delete_cluster_admin = runTest $ \config -> do-  name <- newName-  admin <- addClusterAdmin config name "somePassword"-  listClusterAdmins config >>= \admins ->-    assertBool ("No such admin: " ++ T.unpack name) $-      any ((name ==) . adminName) admins-  deleteClusterAdmin config admin-  listClusterAdmins config >>= \admins ->-    assertBool ("Found a deleted admin: " ++ T.unpack name) $-      all ((name /=) . adminName) admins--case_update_cluster_admin_password :: Assertion-case_update_cluster_admin_password = runTest $ \config -> do-  let curPassword = "somePassword"-      newPassword = "otherPassword"-  name <- newName-  deleteClusterAdminIfExists config name-  admin <- addClusterAdmin config name curPassword-  updateClusterAdminPassword config admin newPassword-  let newCreds = Credentials name newPassword-      newConfig = config { configCreds = newCreds }-  name' <- newName-  dropDatabaseIfExists config name'-  createDatabase newConfig name'-  listDatabases newConfig >>= \databases ->-    assertBool ("No such database: " ++ T.unpack name') $-      any ((name' ==) . databaseName) databases-  dropDatabase newConfig name'-  listDatabases newConfig >>= \databases ->-    assertBool ("Found a dropped database: " ++ T.unpack name') $-      all ((name' /=) . databaseName) databases-  deleteClusterAdmin config admin--case_add_then_delete_database_users :: Assertion-case_add_then_delete_database_users = runTest $ \config ->-  withTestDatabase config $ \name -> do-    listDatabaseUsers config name >>= \users ->-      assertBool "There shouldn't be any users" $ null users-    newUserName <- newName-    addDatabaseUser config name newUserName "somePassword"-    let newCreds = rootCreds-          { credsUser = newUserName-          , credsPassword = "somePassword" }-        newConfig = config { configCreds = newCreds }-    authenticateDatabaseUser newConfig name-    listDatabaseUsers config name >>= \users ->-      assertBool ("No such user: " <> T.unpack newUserName) $-        any ((newUserName ==) . userName) users-    deleteDatabaseUser config name newUserName-    listDatabaseUsers config name >>= \users ->-      assertBool ("Found a deleted user: " <> T.unpack newUserName) $-        all ((newUserName /=) . userName) users--case_update_database_user_password :: Assertion-case_update_database_user_password = runTest $ \config ->-  withTestDatabase config $ \name -> do-    newUserName <- newName-    addDatabaseUser config name newUserName "somePassword"-    listDatabaseUsers config name >>= \users ->-      assertBool ("No such user: " <> T.unpack newUserName) $-        any ((newUserName ==) . userName) users-    updateDatabaseUserPassword config name newUserName "otherPassword"-    deleteDatabaseUser config name newUserName--case_grant_revoke_database_user :: Assertion-case_grant_revoke_database_user = runTest $ \config ->-  withTestDatabase config $ \name -> do-    newUserName <- newName-    addDatabaseUser config name newUserName "somePassword"-    listDatabaseUsers config name >>= \users ->-      assertBool ("No such user: " <> T.unpack newUserName) $-        any ((newUserName ==) . userName) users-    grantAdminPrivilegeTo config name newUserName-    listDatabaseUsers config name >>= \users ->-      case find ((newUserName ==) . userName) users of-        Nothing -> assertFailure $ "No such user: " <> T.unpack newUserName-        Just user -> assertBool-          ("User is not privileged: " <> T.unpack newUserName)-          (userIsAdmin user)-    revokeAdminPrivilegeFrom config name newUserName-    listDatabaseUsers config name >>= \users ->-      case find ((newUserName ==) . userName) users of-        Nothing -> assertFailure $ "No such user: " <> T.unpack newUserName-        Just user -> assertBool-          ("User is still privileged: " <> T.unpack newUserName)-          (not $ userIsAdmin user)-    deleteDatabaseUser config name newUserName------------------------------------------------------ Regressions--newtype WholeFloat = WholeFloat-  { wholeFloatValue :: Double-  } deriving (Eq, Show)---- #14: InfluxDB may return Int instead of Float when--- the WholeFloat value happens to be a whole number.-case_regression_whole_Float_number :: Assertion-case_regression_whole_Float_number = runTest $ \config ->-  withTestDatabase config $ \database -> do-    series <- newName-    post config database $-      writeSeries series $ WholeFloat 42.0-    ss <- query config database $ "select value from " <> series-    case ss of-      [sd] -> fromSeriesData sd @?= Right [WholeFloat 42]-      _ -> assertFailure $ "Expect one series, but got: " ++ show ss--case_regression_really_big_Float_number :: Assertion-case_regression_really_big_Float_number = runTest $ \config ->-  withTestDatabase config $ \database -> do-    series <- newName-    post config database $-      writeSeries series $ WholeFloat 42e100-    ss <- query config database $ "select value from " <> series-    case ss of-      [sd] -> fromSeriesData sd @?= Right [WholeFloat 42e100]-      _ -> assertFailure $ "Expect one series, but got: " ++ show ss-----------------------------------------------------data Val = Val Int deriving (Eq, Show)--instance ToSeriesData Val where-  toSeriesColumns _ = V.fromList ["value"]-  toSeriesPoints (Val n) = V.fromList [toValue n]--instance FromSeriesData Val where-  parseSeriesData = withValues $ \values -> Val <$> values .: "value"-----------------------------------------------------dropDatabaseIfExists :: Config -> Text -> IO ()-dropDatabaseIfExists config name =-  dropDatabase config name-    `catchAll` \_ -> return ()--deleteClusterAdminIfExists :: Config -> Text -> IO ()-deleteClusterAdminIfExists config name =-  deleteClusterAdmin config (Admin name)-    `catchAll` \_ -> return ()-----------------------------------------------------runTest :: (Config -> IO a) -> IO a-runTest f = do-  pool <- newServerPool localServer []-  HC.withManager settings (f . Config rootCreds pool)-  where-    settings = HC.defaultManagerSettings--newName :: IO Text-newName = do-  uniq <- newUnique-  return $ T.pack $ "test_" ++ show (hashUnique uniq)--withTestDatabase :: Config -> (Text -> IO a) -> IO a-withTestDatabase config = bracket acquire release-  where-    acquire = do-      name <- newName-      dropDatabaseIfExists config name-      createDatabase config name-      return name-    release = dropDatabase config--catchAll :: IO a -> (SomeException -> IO a) -> IO a-catchAll = E.catch--assertStatusCodeException :: Show a => IO a -> IO ()-assertStatusCodeException io = do-  r <- try io-  case r of-    Left e -> case fromException e of-      Just HC.StatusCodeException {} -> return ()-      _ ->-        assertFailure $ "Expect a StatusCodeException, but got " ++ show e-    Right ss -> assertFailure $ "Expect an exception, but got " ++ show ss---------------------------------------------------- main :: IO () main = $defaultMainGenerator------------------------------------------------------ Instance deriving--deriveSeriesData defaultOptions-  { fieldLabelModifier = stripPrefixLower "wholeFloat" }-  ''WholeFloat