classy-influxdb-simple (empty) → 0.1.0.1
raw patch · 6 files changed
+278/−0 lines, 6 filesdep +aesondep +async-io-eitherdep +basesetup-changed
Dependencies added: aeson, async-io-either, base, bytestring, lens, mtl, text, wreq
Files
- LICENSE +30/−0
- README.md +8/−0
- Setup.hs +2/−0
- classy-influxdb-simple.cabal +32/−0
- src/Database/InfluxDB/Simple/Classy.hs +112/−0
- src/Database/InfluxDB/Simple/Classy/Types.hs +94/−0
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Sean Chalmers (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Sean Chalmers nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,8 @@+Classy InfluxDB Simple+======================++A simple, stupid, classy-mtl style, wrapper for interacting with [InfluxDB](https://www.influxdata.com/)+via the HTTP API.++It doesn't do a lot to protect you from yourself, however it does wire in nicely with an application+structured using the Classy Lenses/MTL style. Can/Has/As et al.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ classy-influxdb-simple.cabal view
@@ -0,0 +1,32 @@+name: classy-influxdb-simple+version: 0.1.0.1+synopsis: Super simple InfluxDB package in Classy-MTL style+-- description:+homepage: https://github.com/mankyKitty/classy-influxdb-simple#readme+license: BSD3+license-file: LICENSE+author: Sean Chalmers+maintainer: sclhiannan@gmail.com.au+copyright: 2017 Sean Chalmers+category: Database+build-type: Simple+extra-source-files: README.md+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: Database.InfluxDB.Simple.Classy+ , Database.InfluxDB.Simple.Classy.Types+ build-depends: base >= 4.7 && < 5+ , wreq+ , lens+ , aeson+ , mtl+ , bytestring+ , text+ , async-io-either+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/mankyKitty/classy-influxdb-simple
+ src/Database/InfluxDB/Simple/Classy.hs view
@@ -0,0 +1,112 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+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,+ 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++writeData+ :: ( CanInflux m e+ , IsDb db+ , ToLine l+ )+ => InfluxDBConfig+ -> db+ -> l+ -> m ()+writeData c db l =+ runInflux (W.postWith o (writeUrl c) (toLine l)) Write win+ where+ win = pure . const ()+ o = basicInfluxOpts W.defaults c db++queryDataToCSV+ :: ( CanInflux m e+ , IsDb db+ )+ => InfluxDBConfig+ -> db+ -> InfluxQuery+ -> m ByteString+queryDataToCSV c db (InfluxQuery q) =+ runInflux (W.getWith o (queryUrl c)) QueryCSV (pure . view W.responseBody)+ where+ o = basicInfluxOpts W.defaults c db+ & W.param "q" .~ [Text.decodeUtf8 q]+ & W.header "Accept" .~ ["application/csv"]++queryData+ :: ( CanInflux m e+ , IsDb db+ )+ => InfluxDBConfig+ -> db+ -> InfluxQuery+ -> m Value+queryData c db (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+ & W.param "q" .~ [Text.decodeUtf8 q]
+ src/Database/InfluxDB/Simple/Classy/Types.hs view
@@ -0,0 +1,94 @@+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE CPP #-}+module Database.InfluxDB.Simple.Classy.Types where++import Control.Exception (SomeException)+import Control.Lens (makeClassy, makeClassyPrisms, (.~),+ (?~), (^.))+import Control.Monad.Except (MonadError)+import Control.Monad.IO.Class (MonadIO)++import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as BS8++import Data.Function ((&))++import Data.Text (Text)++#if VERSION_vector+import Data.Vector (Vector)+import qualified Data.Vector as V+#endif++import GHC.Word (Word32)++import Network.Wreq (Options)+import qualified Network.Wreq as W++data InfluxRqType+ = Write+ | Query+ | QueryCSV+ deriving Show++rqWinCode :: InfluxRqType -> Int+rqWinCode Write = 204+rqWinCode Query = 200+rqWinCode QueryCSV = 200++class ToLine a where+ toLine :: a -> ByteString++instance ToLine a => ToLine [a] where+ toLine = BS8.unlines . fmap toLine++#if VERSION_vector+instance ToLine a => ToLine (Vector a) where+ toLine = BS8.unlines . V.toList . fmap toLine+#endif++class IsDb a where+ toDbName :: a -> Text++newtype Count = Count Word32+ deriving (Eq, Show, Ord, Num)++newtype InfluxQuery = InfluxQuery ByteString+ deriving (Eq, Show)++data InfluxDBConfig = InfluxDBConfig+ { _idbUser :: ByteString+ , _idbPass :: ByteString+ , _idbHost :: ByteString+ , _idbPort :: Int+ }+makeClassy ''InfluxDBConfig++data InfluxDbError+ = CommsOrInfluxError ByteString+ | ParseError ByteString+ | UnknownError InfluxRqType SomeException+ deriving Show+makeClassyPrisms ''InfluxDbError++type CanInflux m e =+ ( AsInfluxDbError e+ , MonadError e m+ , MonadIO m+ )++basicInfluxOpts+ :: IsDb a+ => Options+ -> InfluxDBConfig+ -> a+ -> Options+basicInfluxOpts o env db = o+ & W.param "db" .~ [toDbName db]+ & W.auth ?~ W.basicAuth user' pass'+ where+ user' = env ^. idbUser+ pass' = env ^. idbPass