packages feed

bugsnag-yesod-1.0.1.3: src/Network/Bugsnag/Yesod.hs

{-# LANGUAGE AllowAmbiguousTypes #-}

-- | A 'yesodMiddleware' that notifies Bugsnag of exceptions
--
-- 'yesodMiddleware' is the only way to handle things as actual exceptions. The
-- alternative, using 'errorHandler', means you would only ever see  an
-- "InternalError Text" value.
--
-- The main downside to this middleware is that short-circuit responses also
-- come through the middleware as exceptions, and must be filtered. Unless of
-- course you want to notify Bugsnag of 404s and such.
module Network.Bugsnag.Yesod
  ( bugsnagYesodMiddleware
  , bugsnagYesodMiddlewareWith
  ) where

import Prelude

import Control.Exception.Annotated (AnnotatedException)
import Control.Monad (unless)
import Control.Monad.IO.Class (liftIO)
import Data.Bugsnag.Settings
import Data.Maybe (isJust)
import Network.Bugsnag
import Network.Bugsnag.Wai
import qualified Network.Wai as Wai
import UnliftIO.Exception
  ( Exception
  , SomeException
  , fromException
  , withException
  )
import Yesod.Core (forkHandler, getsYesod, waiRequest)
import Yesod.Core.Types (HandlerContents, HandlerFor)

bugsnagYesodMiddleware
  :: (app -> Settings) -> HandlerFor app a -> HandlerFor app a
bugsnagYesodMiddleware = bugsnagYesodMiddlewareWith updateEventFromWaiRequest

bugsnagYesodMiddlewareWith
  :: (Wai.Request -> BeforeNotify)
  -> (app -> Settings)
  -> HandlerFor app a
  -> HandlerFor app a
bugsnagYesodMiddlewareWith mkBeforeNotify getSettings handler = do
  settings <- getsYesod getSettings
  request <- waiRequest

  handler `withException` \ex ->
    unless (isHandlerContents ex) $
      forkHandler (const $ pure ()) $
        liftIO $
          notifyBugsnagWith (mkBeforeNotify request) settings ex

isHandlerContents :: SomeException -> Bool
isHandlerContents ex =
  is @HandlerContents ex || is @(AnnotatedException HandlerContents) ex

is :: forall e. Exception e => SomeException -> Bool
is = isJust . fromException @e