{-# LANGUAGE OverloadedStrings #-} module Database.InfluxDB.Simple.Classy ( writeData , queryData , queryDataToCSV ) where import Control.Lens (to, view, (.~), (^.)) import Control.Monad.Error.Lens (throwing) import qualified Data.ByteString.Char8 as BS8 import Data.ByteString.Lazy (ByteString) import Data.ByteString.Lens (unpackedChars) import Data.Function ((&)) import Data.Monoid ((<>)) import qualified Data.Text.Encoding as Text import qualified Network.Wreq as W import Database.InfluxDB.Simple.Classy.Types (AsInfluxDbError (..), CanInflux, Precision, HasInfluxDBConfig (..), InfluxDBConfig, InfluxDbError (..), InfluxQuery (..), InfluxRqType (..), IsDb (..), ToLine (..), basicInfluxOpts, rqWinCode) import Data.Aeson (Value) import qualified Data.Aeson as A import Control.Concurrent.Async.Either (runAsyncToE) writeUrl,queryUrl,baseUrl :: HasInfluxDBConfig c => c -> String baseUrl c = c ^. idbHost . unpackedChars <> ":" <> c ^. idbPort . to show writeUrl c = baseUrl c <> "/" <> "write" queryUrl c = baseUrl c <> "/" <> "query" handleSuccess :: CanInflux m e => InfluxRqType -> (W.Response ByteString -> m a) -> W.Response ByteString -> m a handleSuccess rqT f r | r ^. W.responseStatus . W.statusCode . to (== rqWinCode rqT) = f r | otherwise = throwing _CommsOrInfluxError $ r ^. W.responseStatus . W.statusMessage runInflux :: CanInflux m e => IO (W.Response ByteString) -> InfluxRqType -> (W.Response ByteString -> m a) -> m a runInflux act rq resFn = do r <- runAsyncToE act (UnknownError rq) either (throwing _InfluxDbError) (handleSuccess rq resFn) r -- | -- Push some new data to Influx. -- The @ToLine@ typeclass isn't safe at the moment, so you're on your own -- when it comes to meeting the Line Protocol requirements for InfluxDB writeData :: ( CanInflux m e , IsDb db , ToLine l ) => InfluxDBConfig -> db -> Precision -> l -> m () writeData c db prec l = runInflux (W.postWith o (writeUrl c) (toLine l)) Write win where win = pure . const () o = basicInfluxOpts W.defaults c db prec -- | -- Ask Influx for some data and return any results in CSV format: -- Query: -- -- @ -- curl -H "Accept: application/csv" -G 'http://localhost:8086/query?db=mydb' --data-urlencode 'q=SELECT * FROM "mymeas" LIMIT 3' -- @ -- -- Result: -- -- @ -- name,tags,time,tag1,tag2,value -- mymeas,,1478030187213306198,blue,tag2,23 -- mymeas,,1478030189872408710,blue,tag2,44 -- mymeas,,1478030203683809554,blue,yellow,101 -- @ -- queryDataToCSV :: ( CanInflux m e , IsDb db ) => InfluxDBConfig -> db -> Precision -> InfluxQuery -> m ByteString queryDataToCSV c db prec (InfluxQuery q) = runInflux (W.getWith o (queryUrl c)) QueryCSV (pure . view W.responseBody) where o = basicInfluxOpts W.defaults c db prec & W.param "q" .~ [Text.decodeUtf8 q] & W.header "Accept" .~ ["application/csv"] -- | -- Ask Influx for some data or run a Downsample query. The queries are not -- type safe or verified for correctness, you're in the wild west here. queryData :: ( CanInflux m e , IsDb db ) => InfluxDBConfig -> db -> Precision -> InfluxQuery -> m Value queryData c db prec (InfluxQuery q) = runInflux (W.getWith o (queryUrl c)) Query success where success r = either (throwing _ParseError . BS8.pack) pure $ r ^. W.responseBody . to A.eitherDecode o = basicInfluxOpts W.defaults c db prec & W.param "q" .~ [Text.decodeUtf8 q]