packages feed

vigilance-0.1.0.0: src/Utils/Vigilance/Config.hs

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
module Utils.Vigilance.Config ( configNotifiers
                              , convertConfig
                              , reloadConfig
                              , loadRawConfig
                              , loadConfig) where

import ClassyPrelude hiding (FilePath)
import Control.Monad ((<=<))
import Control.Lens
import qualified Data.Configurator as C
import qualified Data.Configurator.Types as CT
import qualified Data.HashMap.Strict as HM
import qualified Data.Text as T
import Data.Time.Clock.POSIX ( POSIXTime
                             , getPOSIXTime )
import GHC.IO (FilePath)
import qualified Utils.Vigilance.Notifiers.HTTP as H
import qualified Utils.Vigilance.Notifiers.Email as E
import qualified Utils.Vigilance.Notifiers.Log   as L
import Utils.Vigilance.Types

configNotifiers :: Config -> NotifierGroup
configNotifiers cfg = NotifierGroup en H.notify L.notify
  where en = E.notify . E.EmailContext <$> cfg ^. configFromEmail

loadRawConfig :: FilePath -> IO CT.Config
loadRawConfig = C.load . pure . CT.Required

loadConfig :: FilePath -> IO Config
loadConfig = convertConfig <=< loadRawConfig

-- basically no point to this mappend at present
convertConfig :: CT.Config -> IO Config
convertConfig cfg = mempty <> Config <$> lud defaultAcidPath "vigilance.acid_path"
                                     <*> (toEmailAddress <$> lu "vigilance.from_email")
                                     <*> lud defaultPort "vigilance.port"
                                     <*> parseLogCfg
                                     <*> (parseWatches <$> getPOSIXTime <*> C.getMap cfg)
                                     <*> lud defaultMaxRetries "vigilance.max_retries"
  where lu             = C.lookup cfg
        lud d          = C.lookupDefault d cfg
        toEmailAddress = fmap (EmailAddress . pack)
        parseLogCfg = LogCfg <$> lud defaultLogPath "vigilance.log.path"
                             <*> lud False          "vigilance.log.verbose"

reloadConfig :: CT.Config -> IO ()
reloadConfig = C.reload

-- probably want to make this an either to fail on parse failures
parseWatches :: POSIXTime -> HashMap CT.Name CT.Value -> [NewWatch]
parseWatches time globalCfg = HM.foldrWithKey (addWatch time) [] rawWatches -- probably use a traverse
  where rawWatches :: HashMap Text WatchAttrs
        rawWatches = HM.foldrWithKey appendGroup mempty globalCfg

type WatchAttrs = HashMap CT.Name CT.Value

appendGroup :: CT.Name -> CT.Value -> HashMap Text WatchAttrs -> HashMap Text WatchAttrs
appendGroup fullKey val acc
  | nnull wName && nnull wAttr = HM.insertWith mappend wName (HM.singleton wAttr val) acc
  | otherwise                  = acc
  where (_, localKey)                = T.breakOnEnd "vigilance.watches." fullKey
        (wName, wAttrWithLeadingDot) = T.breakOn "." localKey
        wAttr                        = T.drop 1 wAttrWithLeadingDot
        nnull = not . null

addWatch :: POSIXTime -> Text -> WatchAttrs -> [NewWatch] -> [NewWatch]
addWatch time wName attrs = mappend watches
  where watches = maybeToList $ buildWatch time (WatchName wName) attrs

buildWatch :: POSIXTime -> WatchName -> WatchAttrs -> Maybe NewWatch
buildWatch time wName attrs = Watch <$> pure ()
                                    <*> pure wName
                                    <*> (parseInterval =<< lu "interval")
                                    <*> pure (Active time)
                                    <*> (pure . parseNotifications $ lud noNotifications "notifications") -- ehh, list of lists not that great, but making arbitrary names for notifications is dumb too
  where lu k            = HM.lookup k attrs
        lud d k         = HM.lookupDefault d k attrs
        noNotifications = CT.List []

parseNotifications :: CT.Value -> [NotificationPreference]
parseNotifications (CT.List vs) = mapMaybe parseNotification vs
parseNotifications _            = []

parseNotification :: CT.Value -> Maybe NotificationPreference
parseNotification (CT.List [CT.String "email", CT.String a]) = Just . EmailNotification . EmailAddress $ a
parseNotification (CT.List [CT.String "http",  CT.String u]) = Just . HTTPNotification . encodeUtf8 $ u
parseNotification _                                          = Nothing

parseInterval :: CT.Value -> Maybe WatchInterval
parseInterval (CT.List [CT.Number n, CT.String unit]) = Every <$> (pure . truncate $ n) 
                                                              <*> txtToTimeUnit unit
parseInterval _                                       = Nothing