{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-|
Module : Rollbar.Item.MissingHeaders
Description : Headers with some missing from the JSON instance.
Copyright : (c) Hardy Jones, 2017
License : BSD3
Maintainer : jones3.hardy@gmail.com
Stability : experimental
-}
module Rollbar.Item.MissingHeaders
( MissingHeaders(..)
, RemoveHeaders
) where
import Data.Aeson
(FromJSON, KeyValue, ToJSON, object, parseJSON, toJSON, (.=))
import Data.Bifunctor (bimap)
import Data.CaseInsensitive (mk, original)
import Data.Maybe (catMaybes)
import Data.Proxy (Proxy(Proxy))
import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
import Network.HTTP.Types (Header, RequestHeaders)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Char8 as BSC8
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
-- | The request headers with some missing
--
-- This is useful for removing sensitive information
-- like the `Authorization` header.
newtype MissingHeaders (headers :: [Symbol])
= MissingHeaders RequestHeaders
deriving (Eq, Show)
-- | Remove the headers given from the underlying request headers.
class RemoveHeaders (headers :: [Symbol]) where
removeHeaders :: MissingHeaders headers -> RequestHeaders
instance RemoveHeaders '[] where
removeHeaders (MissingHeaders rhs) = rhs
instance (KnownSymbol header, RemoveHeaders headers)
=> RemoveHeaders (header ': headers) where
removeHeaders (MissingHeaders rhs) =
removeHeaders (MissingHeaders $ filter go rhs :: MissingHeaders headers)
where
go (rh, _) =
rh /= (mk . BSC8.pack $ symbolVal (Proxy :: Proxy header))
instance FromJSON (MissingHeaders headers) where
parseJSON v = MissingHeaders . fmap (bimap (mk . BS.pack) BS.pack) <$> parseJSON v
instance RemoveHeaders headers => ToJSON (MissingHeaders headers) where
toJSON = object . catMaybes . requestHeadersKVs . removeHeaders
requestHeadersKVs :: forall kv. KeyValue kv => RequestHeaders -> [Maybe kv]
requestHeadersKVs = fmap go
where
go :: Header -> Maybe kv
go (key', val') = do
key <- myDecodeUtf8 $ original key'
val <- myDecodeUtf8 val'
pure (key .= val)
myDecodeUtf8 :: BS.ByteString -> Maybe T.Text
myDecodeUtf8 = either (const Nothing) Just . TE.decodeUtf8'