{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE OverloadedStrings #-} module Pact ( Provider (..) , Consumer (..) , Request (..) , Response (..) , Interaction (..) , Pact (..) , serialise ) where import qualified Data.Aeson as Aeson import Data.Aeson ((.=)) import qualified Data.ByteString.Lazy as LazyByteString import qualified Data.CaseInsensitive as CaseInsensitive import Data.Monoid ((<>)) import Data.String (IsString (..)) import Data.Text (Text) import qualified Data.Text.Encoding as Text import qualified Network.HTTP.Types as HTTP newtype Provider = Provider { providerName :: Text } deriving (Eq, Ord, Show, IsString) newtype Consumer = Consumer { consumerName :: Text } deriving (Eq, Ord, Show, IsString) data Request = Request { requestMethod :: HTTP.StdMethod , requestPath :: Text , requestBody :: Maybe Aeson.Value } deriving (Eq, Show) data Response = Response { responseStatus :: HTTP.Status , responseHeaders :: [HTTP.Header] , responseBody :: Maybe Aeson.Value } deriving (Eq, Show) data Interaction = Interaction { interactionDescription :: Text , interactionRequest :: Request , interactionRespone :: Response } deriving (Eq, Show) data Pact = Pact { pactProvider :: Provider , pactConsumer :: Consumer , pactInteractions :: [Interaction] } deriving (Eq, Show) instance Aeson.ToJSON Interaction where toJSON (Interaction description request response) = Aeson.object [ "description" .= description , "request" .= Aeson.object ([ "method" .= (Text.decodeUtf8 . HTTP.renderStdMethod . requestMethod) request , "path" .= requestPath request ] <> maybe [] (\v -> ["body" .= v]) (requestBody request)) , "response" .= Aeson.object ([ "status" .= HTTP.statusCode (responseStatus response) , "headers" .= Aeson.object (flip fmap (responseHeaders response) $ \(k, v) -> (Text.decodeUtf8 . CaseInsensitive.original) k .= Text.decodeUtf8 v) ] <> maybe [] (\v -> ["body" .= v]) (responseBody response)) ] instance Aeson.ToJSON Pact where toJSON (Pact provider consumer interactions) = Aeson.object [ "provider" .= Aeson.object [ "name" .= providerName provider ] , "consumer" .= Aeson.object [ "name" .= consumerName consumer ] , "interactions" .= interactions , "metadata" .= Aeson.object [ "pactSpecification" .= Aeson.object [ "version" .= ("3.0.0" :: Text) ] ] ] serialise :: Pact -> LazyByteString.ByteString serialise pact = Aeson.encode pact