compaREST-0.1.0.0: src/Data/OpenApi/Compare/Validate/Responses.hs
{-# OPTIONS_GHC -Wno-orphans #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
module Data.OpenApi.Compare.Validate.Responses
( Behave (..),
)
where
import Data.HList
import Data.HashMap.Strict.InsOrd as IOHM
import Data.Map.Strict as M
import Data.Maybe
import Data.OpenApi
import Data.OpenApi.Compare.Behavior
import Data.OpenApi.Compare.References
import Data.OpenApi.Compare.Subtree
import Data.OpenApi.Compare.Validate.Header ()
import Data.OpenApi.Compare.Validate.Link ()
import Data.OpenApi.Compare.Validate.MediaTypeObject
import Data.OpenApi.Compare.Validate.Products
import Data.OpenApi.Compare.Validate.Schema ()
import Data.OpenApi.Compare.Validate.Sums
import qualified Data.Text as T
import Network.HTTP.Media (MediaType)
import Text.Pandoc.Builder
tracedResponses :: Traced Responses -> IOHM.InsOrdHashMap HttpStatusCode (Traced (Referenced Response))
tracedResponses resp =
IOHM.mapWithKey (\k -> traced (ask resp >>> step (ResponseCodeStep k))) $
_responsesResponses $ extract resp
instance Subtree Responses where
type SubtreeLevel Responses = 'OperationLevel
type
CheckEnv Responses =
'[ ProdCons (Traced (Definitions Response))
, ProdCons (Traced (Definitions Header))
, ProdCons (Traced (Definitions Schema))
, ProdCons (Traced (Definitions Link))
]
checkStructuralCompatibility env pc = do
structuralMaybe env $ sequence . stepTraced ResponseDefaultStep . fmap _responsesDefault <$> pc
iohmStructural env $ stepTraced ResponsesStep . fmap _responsesResponses <$> pc
pure ()
-- Roles are already swapped. Producer is a server and consumer is a
-- client. Response codes are sum-like entity because we can answer with only
-- one element
checkSemanticCompatibility env beh prodCons = do
let defs = getH @(ProdCons (Traced (Definitions Response))) env
check code resps = checkCompatibility @Response (beh >>> step (WithStatusCode code)) env resps
elements = getEls <$> defs <*> prodCons
getEls respDef resps = M.fromList $ do
(code, respRef) <- IOHM.toList $ tracedResponses resps
pure (code, dereference respDef respRef)
checkSums beh ConsumerDoesntHaveResponseCode check elements
tracedContent :: Traced Response -> IOHM.InsOrdHashMap MediaType (Traced MediaTypeObject)
tracedContent resp =
IOHM.mapWithKey (\k -> traced (ask resp >>> step (ResponseMediaObject k))) $
_responseContent $ extract resp
tracedHeaders :: Traced Response -> IOHM.InsOrdHashMap HeaderName (Traced (Referenced Header))
tracedHeaders resp =
IOHM.mapWithKey (\k -> traced (ask resp >>> step (ResponseHeader k))) $
_responseHeaders $ extract resp
instance Issuable 'ResponseLevel where
data Issue 'ResponseLevel
= ConsumerDoesntHaveMediaType MediaType
| ProducerDoesntHaveResponseHeader HeaderName
deriving stock (Eq, Ord, Show)
issueKind = \case
_ -> CertainIssue
describeIssue Forward (ConsumerDoesntHaveMediaType t) =
para $ "Media type was removed: " <> (code . T.pack . show $ t) <> "."
describeIssue Backward (ConsumerDoesntHaveMediaType t) =
para $ "Media type was added: " <> (code . T.pack . show $ t) <> "."
describeIssue Forward (ProducerDoesntHaveResponseHeader h) =
para $ "New header was added " <> code h <> "."
describeIssue Backward (ProducerDoesntHaveResponseHeader h) =
para $ "Header was removed " <> code h <> "."
instance Behavable 'ResponseLevel 'PayloadLevel where
data Behave 'ResponseLevel 'PayloadLevel
= ResponsePayload
deriving stock (Eq, Ord, Show)
describeBehavior ResponsePayload = "Payload"
instance Behavable 'ResponseLevel 'HeaderLevel where
data Behave 'ResponseLevel 'HeaderLevel
= InHeader HeaderName
deriving stock (Eq, Ord, Show)
describeBehavior (InHeader name) = "Header " <> code name
instance Subtree Response where
type SubtreeLevel Response = 'ResponseLevel
type
CheckEnv Response =
'[ ProdCons (Traced (Definitions Header))
, ProdCons (Traced (Definitions Schema))
, ProdCons (Traced (Definitions Link))
]
checkStructuralCompatibility env pc = do
iohmStructural env $ stepTraced ResponseMediaObjects . fmap _responseContent <$> pc
iohmStructural env $ stepTraced ResponseHeaders . fmap _responseHeaders <$> pc
iohmStructural env $ stepTraced ResponseLinks . fmap _responseLinks <$> pc
pure ()
checkSemanticCompatibility env beh prodCons = do
-- Roles are already swapped. Producer is a server and consumer is a client
checkMediaTypes
checkHeaders
pure ()
where
checkMediaTypes = do
-- Media types are sum-like entity
let check mediaType mtObj =
let mtEnv = HCons mediaType env
in checkCompatibility @MediaTypeObject (beh >>> step ResponsePayload) mtEnv mtObj
elements = getEls <$> prodCons
getEls resp = M.fromList . IOHM.toList $ tracedContent resp
checkSums beh ConsumerDoesntHaveMediaType check elements
checkHeaders = do
-- Headers are product-like entities
let check name hdrs = checkCompatibility @Header (beh >>> step (InHeader name)) env hdrs
elements = getEls <$> headerDefs <*> prodCons
getEls headerDef resp = M.fromList $ do
(hname, headerRef) <- IOHM.toList $ tracedHeaders resp
let header = dereference headerDef headerRef
pure
( hname
, ProductLike
{ productValue = header
, required = fromMaybe False . _headerRequired . extract $ header
}
)
checkProducts beh ProducerDoesntHaveResponseHeader check elements
headerDefs = getH @(ProdCons (Traced (Definitions Header))) env
instance Steppable Responses (Referenced Response) where
data Step Responses (Referenced Response)
= ResponseCodeStep HttpStatusCode
| ResponseDefaultStep
deriving stock (Eq, Ord, Show)
instance Steppable Response MediaTypeObject where
data Step Response MediaTypeObject = ResponseMediaObject MediaType
deriving stock (Eq, Ord, Show)
instance Steppable Response (IOHM.InsOrdHashMap MediaType MediaTypeObject) where
data Step Response (IOHM.InsOrdHashMap MediaType MediaTypeObject) = ResponseMediaObjects
deriving stock (Eq, Ord, Show)
instance Steppable Response (Definitions (Referenced Header)) where
data Step Response (Definitions (Referenced Header)) = ResponseHeaders
deriving stock (Eq, Ord, Show)
instance Steppable Response (Definitions (Referenced Link)) where
data Step Response (Definitions (Referenced Link)) = ResponseLinks
deriving stock (Eq, Ord, Show)
instance Steppable Response (Referenced Header) where
data Step Response (Referenced Header) = ResponseHeader HeaderName
deriving stock (Eq, Ord, Show)
instance Steppable Responses (IOHM.InsOrdHashMap HttpStatusCode (Referenced Response)) where
data Step Responses (IOHM.InsOrdHashMap HttpStatusCode (Referenced Response)) = ResponsesStep
deriving stock (Eq, Ord, Show)