packages feed

rollbar-yesod-0.1.0: src/Rollbar/Yesod.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

-- |
-- Module: Rollbar.Yesod
-- Copyright: (c) 2020 Stack Builders Inc.
-- License: MIT
-- Maintainer: Sebastián Estrella <sestrella@stackbuilders.com>
--
-- For a fully working example check the following link:
--
-- <https://github.com/stackbuilders/rollbar-haskell/blob/master/rollbar-yesod/example/Main.hs>
module Rollbar.Yesod
  ( rollbarYesodMiddleware
  , rollbarYesodMiddlewareWith
  ) where

import qualified Network.Wai as W

import Control.Exception (Exception(..), SomeException)
import Control.Monad (unless, void)
import Rollbar.Client
import Rollbar.Wai (rollbarOnExceptionWith)
import UnliftIO.Exception (catch, throwIO)
import Yesod.Core
import Yesod.Core.Types (HandlerContents)

-- | Captures non 'HandlerContents' exceptions and send them to Rollbar via the
-- API. Under the hood, this function uses 'createItem' function from
-- rollbar-client.
--
-- __Example__
--
-- > instance Yesod App where
-- >   yesodMiddleware = rollbarYesodMiddleware . defaultYesodMiddleware
--
-- @since 0.1.0
rollbarYesodMiddleware
  :: (HasSettings m, MonadHandler m, MonadUnliftIO m)
  => m a
  -> m a
rollbarYesodMiddleware = rollbarYesodMiddlewareWith $ \settings request ex ->
  rollbarOnExceptionWith handler settings (Just request) ex
  where
    handler item = void $ createItem item { itemFramework = Just "yesod" }

-- | Similar to 'rollbarYesodMiddleware', but it allows customize the function
-- used to send the 'SomeException' captured from a handler to Rollbar.
--
-- @since 0.1.0
rollbarYesodMiddlewareWith
  :: (HasSettings m, MonadHandler m, MonadUnliftIO m)
  => (Settings -> W.Request -> SomeException -> m ())
  -> m a
  -> m a
rollbarYesodMiddlewareWith f handler = handler `catch` \ex -> do
  unless (isHandlerContents ex) $ do
    settings <- getSettings
    wrequest <- waiRequest
    f settings wrequest ex

  throwIO ex

isHandlerContents :: SomeException -> Bool
isHandlerContents = maybe False handlerContents . fromException
  where
    handlerContents :: HandlerContents -> Bool
    handlerContents = const True