packages feed

vigilance-0.1.0.0: src/Utils/Vigilance/Workers/NotificationRetryWorker.hs

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
module Utils.Vigilance.Workers.NotificationRetryWorker ( runWorker
                                                       , failuresToRetry
                                                       , notifyOrBump
                                                       , renderFail ) where

import ClassyPrelude
import Control.Lens
import Data.Acid (AcidState)
import Text.InterpolatedString.Perl6 (qc)
import Utils.Vigilance.Logger ( pushLog
                              , renameLogCtx
                              , vLog )
import Utils.Vigilance.TableOps
import Utils.Vigilance.Types

-- | Intended to be exclusive
runWorker :: AcidState AppState -> Int -> NotifierGroup -> LogCtxT IO ()
runWorker acid maxRetries notifiers = renameLogCtx "Notification Retry Worker" $ do
  fails  <- lift $ allFailedNotificationsS acid
  unless (null fails) $ vLog [qc|Retrying failed notifications for {startLog fails}|]
  fails' <- catMaybes <$> mapM (notify notifiers) fails
  mapM_ logFail fails'
  let toRetry = failuresToRetry maxRetries fails'
  lift $ setFailedNotificationsS acid toRetry
  where startLog fails = intercalate ", " $ map (\w -> w ^. failedWatch . watchName . unWatchName) fails

notifyOrBump :: Notifier -> FailedNotification -> LogCtxT IO (Maybe FailedNotification)
notifyOrBump n fn = do
  vLog logMsg
  fn' <- listToMaybe <$> n [watch]
  maybe retrySuccessful retryFailed fn'
  where retrySuccessful = return Nothing
        retryFailed fn' = return . Just $ fn' & retries .~ (fn ^. retries + 1)
        watch           = fn ^. failedWatch
        wn              = watch ^. watchName . unWatchName
        logMsg = [qc|Retrying notification {fn ^. failedPref} for {wn} after {fn ^. retries} retries|]

notify :: NotifierGroup -> FailedNotification -> LogCtxT IO (Maybe FailedNotification)
notify NotifierGroup { _ngEmail = Just n}
       fn@FailedNotification { _failedPref = (EmailNotification _)} = notifyOrBump (n ^. notifier) fn
notify NotifierGroup {_ngHTTP}
       fn@FailedNotification { _failedPref = (HTTPNotification _)} = notifyOrBump (_ngHTTP ^. notifier) fn
notify _ fn = pushLog [qc|No notifier configured for {fn}|] >> return Nothing

logFail :: FailedNotification -> LogCtxT IO ()
logFail = pushLog . renderFail

renderFail :: FailedNotification -> Text
renderFail FailedNotification {..} = [qc|Watch {wn} failed to notify after {_retries} retries on {pref}: {_failedLastError}|]
  where wn = _failedWatch ^. watchName . unWatchName
        pref = renderPref _failedPref

renderPref :: NotificationPreference -> Text
renderPref (EmailNotification (EmailAddress a)) = [qc|EmailNotification {a}|]
renderPref (HTTPNotification u)                 = [qc|HTTPNotification {u}|]

failuresToRetry :: Int -> [FailedNotification] -> [FailedNotification]
failuresToRetry maxRetries = filter underLimit
  where underLimit fn = fn ^. retries < maxRetries