servant-subscriber-0.2.0.0: src/Servant/Subscriber/Response.hs
{-# LANGUAGE DeriveGeneric #-}
module Servant.Subscriber.Response where
import qualified Blaze.ByteString.Builder as B
import qualified Blaze.ByteString.Builder.Char8 as B
import Data.Aeson
import Data.Aeson.Parser (value)
import Data.Aeson.Types (unsafeToEncoding)
import Data.Attoparsec.ByteString (parseOnly)
import Data.Bifunctor
import qualified Data.CaseInsensitive as Case
import Data.Monoid ((<>))
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import GHC.Generics
import qualified Network.HTTP.Types as H
import Servant.Server
import qualified Servant.Subscriber.Request as R
import Servant.Subscriber.Types
type ResponseHeader = R.RequestHeader
type ResponseHeaders = R.RequestHeaders
-- | Any message from the server is a Response.
data Response =
Subscribed !Path -- |< Resource was successfully subscribed
| Modified !Path !HttpResponse -- |< Can also be a non 2xx code, ServerError only triggers on the very first response, resulting in a failed subscription.
| Deleted !Path
| Unsubscribed !Path
| RequestError !RequestError
deriving Generic
instance ToJSON Response
data HttpResponse = HttpResponse {
httpStatus :: !Status
, httpHeaders :: !ResponseHeaders
, httpBody :: ResponseBody
} deriving (Generic)
instance ToJSON HttpResponse
data Status = Status {
statusCode :: !Int
, statusMessage :: !Text
} deriving Generic
instance ToJSON Status
-- | Your subscription did not work out because:
data RequestError =
ParseError
| HttpRequestFailed !R.HttpRequest !HttpResponse -- |< The server replied with some none 2xx status code. Thus your subscription failed.
| NoSuchSubscription !Path
| AlreadySubscribed !Path deriving Generic
instance ToJSON RequestError
data ResponseBody = ResponseBody B.Builder deriving Generic
instance ToJSON ResponseBody where
toJSON (ResponseBody b) = getValue $ parseOnly value (B.toByteString (wrapInString b))
where
getValue r = case r of
Left e -> error e
Right r -> r
toEncoding (ResponseBody b) = unsafeToEncoding . wrapInString $ b
fromHTTPHeader :: H.Header -> ResponseHeader
fromHTTPHeader = bimap (T.decodeUtf8 . Case.original) T.decodeUtf8
fromHTTPHeaders :: H.ResponseHeaders -> ResponseHeaders
fromHTTPHeaders = map fromHTTPHeader
fromHTTPStatus :: H.Status -> Status
fromHTTPStatus s = Status {
statusCode = H.statusCode s
, statusMessage = T.decodeUtf8 . H.statusMessage $ s
}
fromServantError :: ServantErr -> HttpResponse
fromServantError err = HttpResponse {
httpStatus = Status (errHTTPCode err) (T.pack $ errReasonPhrase err)
, httpHeaders = fromHTTPHeaders . errHeaders $ err
, httpBody = ResponseBody . B.fromLazyByteString . errBody $ err
}
wrapInString :: B.Builder -> B.Builder
wrapInString x = B.fromChar '"' <> x <> B.fromChar '"'