{-# 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'