{-# 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