on master help sign in
Non-schema golden tests (#40)
1d27fd1Alexey Uimanov
openapi-diff.cabal+2
@@ -112,11 +112,13 @@ library
112 , OpenAPI.Checker.Validate.Param
112 , OpenAPI.Checker.Validate.Param
113 , OpenAPI.Checker.Validate.PathFragment
113 , OpenAPI.Checker.Validate.PathFragment
114 , OpenAPI.Checker.Validate.ProcessedPathItem
114 , OpenAPI.Checker.Validate.ProcessedPathItem
115+ , OpenAPI.Checker.Validate.Products
115 , OpenAPI.Checker.Validate.RequestBody
116 , OpenAPI.Checker.Validate.RequestBody
116 , OpenAPI.Checker.Validate.Responses
117 , OpenAPI.Checker.Validate.Responses
117 , OpenAPI.Checker.Validate.Schema
118 , OpenAPI.Checker.Validate.Schema
118 , OpenAPI.Checker.Validate.SecurityRequirement
119 , OpenAPI.Checker.Validate.SecurityRequirement
119 , OpenAPI.Checker.Validate.Server
120 , OpenAPI.Checker.Validate.Server
121+ , OpenAPI.Checker.Validate.Sums
120
122
121executable openapi-diff
123executable openapi-diff
122 import: common-options
124 import: common-options
src/OpenAPI/Checker/Orphans.hs+3
@@ -21,7 +21,10 @@ deriving stock instance Ord AdditionalProperties
21deriving stock instance Ord Discriminator
21deriving stock instance Ord Discriminator
22deriving stock instance Ord Xml
22deriving stock instance Ord Xml
23deriving stock instance Ord OpenApiType
23deriving stock instance Ord OpenApiType
24+deriving stock instance Ord Style
24deriving stock instance Ord OpenApiItems
25deriving stock instance Ord OpenApiItems
26+deriving stock instance Ord ParamLocation
27+
25
28
26instance (Ord k, Ord v) => Ord (IOHM.InsOrdHashMap k v) where
29instance (Ord k, Ord v) => Ord (IOHM.InsOrdHashMap k v) where
27 compare xs ys = compare (IOHM.toList xs) (IOHM.toList ys)
30 compare xs ys = compare (IOHM.toList xs) (IOHM.toList ys)
src/OpenAPI/Checker/Validate/MediaTypeObject.hs+27−24
@@ -3,13 +3,16 @@
3module OpenAPI.Checker.Validate.MediaTypeObject () where
3module OpenAPI.Checker.Validate.MediaTypeObject () where
4
4
5import Data.Foldable as F
5import Data.Foldable as F
6+import Data.Functor
6import Data.HList
7import Data.HList
7import Data.HashMap.Strict.InsOrd as IOHM
8import Data.HashMap.Strict.InsOrd as IOHM
9+import Data.Map.Strict as M
8import Data.OpenApi
10import Data.OpenApi
9import Data.Text (Text)
11import Data.Text (Text)
10import Network.HTTP.Media (MediaType, mainType, subType)
12import Network.HTTP.Media (MediaType, mainType, subType)
11import OpenAPI.Checker.Subtree
13import OpenAPI.Checker.Subtree
12import OpenAPI.Checker.Trace
14import OpenAPI.Checker.Trace
15+import OpenAPI.Checker.Validate.Products
13import OpenAPI.Checker.Validate.Schema ()
16import OpenAPI.Checker.Validate.Schema ()
14
17
15instance Subtree MediaTypeObject where
18instance Subtree MediaTypeObject where
@@ -18,37 +21,37 @@ instance Subtree MediaTypeObject where
18 , ProdCons (Definitions Schema)
21 , ProdCons (Definitions Schema)
19 ]
22 ]
20 data CheckIssue MediaTypeObject
23 data CheckIssue MediaTypeObject
21− = MediaEncodingMissing
24+ = MediaEncodingMissing Text
22 | MediaEncodingIncompat
25 | MediaEncodingIncompat
23 | MediaTypeSchemaRequired
26 | MediaTypeSchemaRequired
24 deriving (Eq, Ord, Show)
27 deriving (Eq, Ord, Show)
25− checkCompatibility env (ProdCons p c) = do
28+ checkCompatibility env prodCons@(ProdCons p c) = do
26− tryCheckEncoding
27− checkSchema
28− pure ()
29− where
30− mediaType = getH @MediaType env
31− tryCheckEncoding =
32− if | "multipart" == mainType mediaType -> checkEncoding
29+ if | "multipart" == mainType mediaType -> checkEncoding
33− | "application" == mainType mediaType &&
30+ | "application" == mainType mediaType &&
34− "x-www-form-urlencoded" == subType mediaType -> checkEncoding
31+ "x-www-form-urlencoded" == subType mediaType -> checkEncoding
35− | otherwise -> pure ()
32+ | otherwise -> pure ()
36− where
37− -- Each parameter encoded by the producer must be parsed by the
38− -- consumer
39− checkEncoding = for_ (IOHM.toList $ _mediaTypeObjectEncoding p) $ \(paramName, prodEncoding) ->
40− case IOHM.lookup paramName $ _mediaTypeObjectEncoding c of
41− Nothing -> issueAt consumer MediaEncodingMissing
42− Just consEncoding -> localStep (MediaTypeParamEncoding paramName)
43− $ checkCompatibility HNil
44− $ ProdCons prodEncoding consEncoding
45− -- If consumer requires schema then producer must produce compatible
33+ -- If consumer requires schema then producer must produce compatible
46− -- request
34+ -- request
47− checkSchema = for_ (_mediaTypeObjectSchema c) $ \consRef ->
35+ for_ (_mediaTypeObjectSchema c) $ \consRef ->
48 case _mediaTypeObjectSchema p of
36 case _mediaTypeObjectSchema p of
49 Nothing -> issueAt producer MediaTypeSchemaRequired
37 Nothing -> issueAt producer MediaTypeSchemaRequired
50 Just prodRef -> localStep MediaTypeSchema
38 Just prodRef -> localStep MediaTypeSchema
51 $ checkCompatibility env $ ProdCons prodRef consRef
39 $ checkCompatibility env $ ProdCons prodRef consRef
40+ pure ()
41+ where
42+ mediaType = getH @MediaType env
43+ checkEncoding =
44+ let
45+ -- Parameters of the media type are product-like entities
46+ getEncoding mt = M.fromList
47+ $ (IOHM.toList $ _mediaTypeObjectEncoding mt) <&> \(k, enc) ->
48+ ( k
49+ , ProductLike
50+ { traced = Traced (step $ MediaTypeParamEncoding k) enc
51+ , required = True } )
52+ encProdCons = getEncoding <$> prodCons
53+ in checkProducts MediaEncodingMissing
54+ (const $ checkCompatibility HNil) encProdCons
52
55
53instance Subtree Encoding where
56instance Subtree Encoding where
54 type CheckEnv Encoding = '[]
57 type CheckEnv Encoding = '[]
src/OpenAPI/Checker/Validate/Operation.hs+150−92
@@ -1,123 +1,181 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2+{-# OPTIONS_GHC -Wno-name-shadowing #-}
2
3
3−module OpenAPI.Checker.Validate.Operation (Step (..)) where
4+module OpenAPI.Checker.Validate.Operation
5+ ( MatchedOperation(..)
6+ ) where
4
7
8+
5−import Data.Foldable
9+import Data.Foldable as F
10+import Data.Functor
6import Data.HList
11import Data.HList
7−import qualified Data.HashMap.Strict.InsOrd as IOHM
12+import qualified Data.List as L
13+import Data.Map.Strict as M
14+import Data.Maybe
8import Data.OpenApi
15import Data.OpenApi
9import Data.Text (Text)
16import Data.Text (Text)
10import OpenAPI.Checker.References
17import OpenAPI.Checker.References
11import OpenAPI.Checker.Subtree
18import OpenAPI.Checker.Subtree
12import OpenAPI.Checker.Trace
19import OpenAPI.Checker.Trace
13import OpenAPI.Checker.Validate.Param ()
20import OpenAPI.Checker.Validate.Param ()
21+import OpenAPI.Checker.Validate.PathFragment
22+import OpenAPI.Checker.Validate.Products
14import OpenAPI.Checker.Validate.RequestBody ()
23import OpenAPI.Checker.Validate.RequestBody ()
15import OpenAPI.Checker.Validate.Responses ()
24import OpenAPI.Checker.Validate.Responses ()
16import OpenAPI.Checker.Validate.SecurityRequirement ()
25import OpenAPI.Checker.Validate.SecurityRequirement ()
17import OpenAPI.Checker.Validate.Server ()
26import OpenAPI.Checker.Validate.Server ()
18
27
28+-- data ParamKey
29+
30+data MatchedOperation = MatchedOperation
31+ { operation :: !Operation
32+ , pathParams :: ![Traced OpenApi Param]
33+ -- ^ Params from the PathItem
34+ , getPathFragments :: !([Traced OpenApi Param] -> [Traced OpenApi PathFragmentParam])
35+ -- ^ Path fragments traced from PathItem. Takes full list of
36+ -- operation-specific parameters
37+ }
38+
39+type ParamKey = (ParamLocation, Text)
40+
41+paramKey :: Param -> ParamKey
42+paramKey param = (_paramIn param, _paramName param)
43+
19−instance Subtree Operation where
44+instance Subtree MatchedOperation where
20− type
45+ type CheckEnv MatchedOperation =
21− CheckEnv Operation =
22− '[ ProdCons (Definitions Param)
46+ '[ ProdCons (Definitions Param)
23− , ProdCons (Definitions RequestBody)
47+ , ProdCons (Definitions RequestBody)
24− , ProdCons (Definitions SecurityScheme)
48+ , ProdCons (Definitions SecurityScheme)
25− , ProdCons (Definitions Response)
49+ , ProdCons (Definitions Response)
26− , ProdCons (Definitions Header)
50+ , ProdCons (Definitions Header)
27− , ProdCons (Definitions Schema)
51+ , ProdCons (Definitions Schema)
28− ]
52+ ]
29− data CheckIssue Operation
53+ data CheckIssue MatchedOperation
30− = ParamNotMatched Text -- Param name
54+ = ParamNotMatched ParamLocation Text
55+ -- ^ Non-path param has no pair
56+ | PathFragmentNotMatched Int
57+ -- ^ Path fragment with given position has no match
31 | NoRequestBody
58 | NoRequestBody
32 | CallbacksNotSupported
59 | CallbacksNotSupported
33 | SecurityRequirementNotMet Int -- security indexs
60 | SecurityRequirementNotMet Int -- security indexs
34 | ServerNotConsumed Int -- server index
61 | ServerNotConsumed Int -- server index
35 deriving (Eq, Ord, Show)
62 deriving (Eq, Ord, Show)
36− checkCompatibility env prodCons = do
63+ checkCompatibility env prodCons = withTrace $ \myTrace -> do
37− let ProdCons {producer = pNonPathParams, consumer = cNonPathParams} = do
64+ checkParameters myTrace
38− op <- _operationParameters <$> prodCons
65+ checkRequestBodies
39− defParams <- getH @(ProdCons (Definitions Param)) env
66+ checkResponses
40− pure $
67+ checkCallbacks
41− filter ((/= ParamPath) . _paramIn . getTraced)
68+ checkOperationSecurity
42− . fmap (dereference defParams)
69+ checkServers
43− $ op
44− reqBody = do
45− op <- _operationRequestBody <$> prodCons
46− reqDefs <- getH @(ProdCons (Definitions RequestBody)) env
47− pure $ fmap (dereference reqDefs) op
48− for_ pNonPathParams $ \p@(Traced _ param) ->
49− anyOfAt
50− producer
51− (ParamNotMatched $ _paramName param)
52− [ checkProdCons HNil . fmap (retrace (step OperationParamsStep)) $ ProdCons p c
53− | c <- cNonPathParams
54− ]
55− case reqBody of
56− ProdCons Nothing Nothing -> pure ()
57− ProdCons (Just pBody) (Just cBody) ->
58− localStep OperationRequestBodyStep $
59− checkProdCons env (ProdCons pBody cBody)
60− ProdCons Nothing (Just _) -> issueAt producer NoRequestBody
61− ProdCons (Just _) Nothing -> issueAt consumer NoRequestBody
62− localStep OperationResponsesStep $
63− checkCompatibility env $ _operationResponses <$> prodCons
64− -- FIXME: https://github.com/typeable/openapi-diff/issues/27
65− case IOHM.null . _operationCallbacks <$> prodCons of
66− (ProdCons True True) -> pure ()
67− (ProdCons False _) -> issueAt producer CallbacksNotSupported
68− (ProdCons _ False) -> issueAt consumer CallbacksNotSupported
69− -- FIXME: https://github.com/typeable/openapi-diff/issues/28
70− sequenceA_
71− [ anyOfAt
72− producer
73− (SecurityRequirementNotMet i)
74− [ localStep OperationSecurityRequirementStep $
75− checkCompatibility env $ ProdCons prodSecurity consSecurity
76− | consSecurity <- _operationSecurity . consumer $ prodCons
77− ]
78− | (i, prodSecurity) <- zip [0 ..] . _operationSecurity . producer $ prodCons
79− ]
80− -- FIXME: https://github.com/typeable/openapi-diff/issues/29s
81− sequenceA_
82− [ anyOfAt
83− producer
84− (ServerNotConsumed i)
85− [ localStep OperationServerStep $
86− checkCompatibility env $ ProdCons pServer cServer
87− | cServer <- _operationServers . consumer $ prodCons
88− ]
89− | (i, pServer) <- zip [0 ..] . _operationServers . producer $ prodCons
90− ]
91 pure ()
70 pure ()
71+ where
72+ checkParameters myTrace = do
73+ let
74+ -- Merged parameters got from Operation and PathItem in one
75+ -- place. First element is path params, second is non-path params
76+ tracedParams :: ProdCons ([Traced OpenApi Param], [Traced OpenApi Param])
77+ tracedParams = getParams <$> myTrace <*> paramDefs <*> prodCons
78+ getParams root defs mp =
79+ let
80+ operationParamsMap :: Map ParamKey (Traced OpenApi Param)
81+ operationParamsMap = M.fromList $ do
82+ paramRef <- _operationParameters $ operation mp
83+ let
84+ tracedParam = retrace root
85+ $ dereferenceTraced defs
86+ $ Traced (step $ OperationParamsStep) paramRef
87+ key = paramKey $ getTraced tracedParam
88+ pure (key, tracedParam)
89+ pathParamsMap :: Map ParamKey (Traced OpenApi Param)
90+ pathParamsMap = M.fromList $ do
91+ param <- pathParams mp
92+ pure (paramKey $ getTraced param, param)
93+ params = M.elems $ M.union operationParamsMap pathParamsMap
94+ -- We prefer params from Operation
95+ splitted = L.partition
96+ (\p -> (_paramIn $ getTraced p) == ParamPath) params
97+ in splitted
98+ checkNonPathParams $ snd <$> tracedParams
99+ checkPathParams $ fst <$> tracedParams
100+ pure ()
101+ checkNonPathParams :: ProdCons [Traced OpenApi Param] -> CompatFormula MatchedOperation ()
102+ checkNonPathParams params = do
103+ let
104+ elements = getEls <$> params
105+ getEls traced = M.fromList $ do
106+ p <- traced
107+ let
108+ param = getTraced p
109+ k = (_paramIn param, _paramName param)
110+ v = ProductLike
111+ { traced = p
112+ , required = fromMaybe False $ _paramRequired param
113+ }
114+ pure (k, v)
115+ check _ param = do
116+ checkCompatibility @Param (singletonH schemaDefs) param
117+ checkProducts' (uncurry ParamNotMatched) check elements
118+ checkPathParams pathParams = do
119+ let
120+ fragments :: ProdCons [Traced OpenApi PathFragmentParam]
121+ fragments = getFragments <$> pathParams <*> prodCons
122+ getFragments params mop = (getPathFragments mop) params
123+ -- Feed path parameters to the fragments getter
124+ check _ frags = checkCompatibility @PathFragmentParam env frags
125+ elements = fragments <&> \frags -> M.fromList $ zip [0..] $ do
126+ frag <- frags
127+ pure $ ProductLike
128+ { traced = frag
129+ , required = True }
130+ checkProducts' PathFragmentNotMatched check elements
131+ checkRequestBodies = do
132+ let
133+ check _ reqBody = checkCompatibility @RequestBody env reqBody
134+ elements = getReqBody <$> bodyDefs <*> prodCons
135+ getReqBody bodyDef mop = M.fromList $ do
136+ bodyRef <- F.toList $ _operationRequestBody $ operation mop
137+ let
138+ traced = dereferenceTraced bodyDef
139+ $ Traced (step $ OperationRequestBodyStep) bodyRef
140+ required = fromMaybe False
141+ $ _requestBodyRequired $ getTraced traced
142+ elt = ProductLike { traced, required }
143+ -- Single element map
144+ pure ((), elt)
145+ checkProducts (const NoRequestBody) check elements
146+ checkResponses = do
147+ let
148+ resps = (_operationResponses . operation) <$> prodCons
149+ respEnv = HCons (swapProdCons respDefs)
150+ $ HCons (swapProdCons headerDefs)
151+ $ HCons (swapProdCons schemaDefs) HNil
152+ localStep OperationResponsesStep
153+ $ swapRoles $ checkCompatibility respEnv $ swapProdCons resps
154+ checkCallbacks = pure () -- (error "FIXME: not implemented")
155+ checkOperationSecurity = pure () -- (error "FIXME: not implemented")
156+ checkServers = pure () -- (error "FIXME: not implemented")
157+ bodyDefs = getH @(ProdCons (Definitions RequestBody)) env
158+ respDefs = getH @(ProdCons (Definitions Response)) env
159+ headerDefs = getH @(ProdCons (Definitions Header)) env
160+ schemaDefs = getH @(ProdCons (Definitions Schema)) env
161+ paramDefs = getH @(ProdCons (Definitions Param)) env
92
162
93−instance Steppable PathItem Operation where
94− data Step PathItem Operation
95− = GetStep
96− | PutStep
97− | PostStep
98− | DeleteStep
99− | OptionsStep
100− | HeadStep
101− | PatchStep
102− | TraceStep
103− deriving (Eq, Ord, Show)
104−
105−instance Steppable Operation (Referenced Param) where
163+instance Steppable MatchedOperation (Referenced Param) where
106− data Step Operation (Referenced Param) = OperationParamsStep
164+ data Step MatchedOperation (Referenced Param) = OperationParamsStep
107 deriving (Eq, Ord, Show)
165 deriving (Eq, Ord, Show)
108
166
109−instance Steppable Operation (Referenced RequestBody) where
167+instance Steppable MatchedOperation (Referenced RequestBody) where
110− data Step Operation (Referenced RequestBody) = OperationRequestBodyStep
168+ data Step MatchedOperation (Referenced RequestBody) = OperationRequestBodyStep
111 deriving (Eq, Ord, Show)
169 deriving (Eq, Ord, Show)
112
170
113−instance Steppable Operation Responses where
171+instance Steppable MatchedOperation Responses where
114− data Step Operation Responses = OperationResponsesStep
172+ data Step MatchedOperation Responses = OperationResponsesStep
115 deriving (Eq, Ord, Show)
173 deriving (Eq, Ord, Show)
116
174
117−instance Steppable Operation SecurityRequirement where
175+instance Steppable MatchedOperation SecurityRequirement where
118− data Step Operation SecurityRequirement = OperationSecurityRequirementStep
176+ data Step MatchedOperation SecurityRequirement = OperationSecurityRequirementStep
119 deriving (Eq, Ord, Show)
177 deriving (Eq, Ord, Show)
120
178
121−instance Steppable Operation Server where
179+instance Steppable MatchedOperation Server where
122− data Step Operation Server = OperationServerStep
180+ data Step MatchedOperation Server = OperationServerStep
123 deriving (Eq, Ord, Show)
181 deriving (Eq, Ord, Show)
src/OpenAPI/Checker/Validate/Param.hs+70−18
@@ -1,31 +1,83 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2
3
3module OpenAPI.Checker.Validate.Param () where
4module OpenAPI.Checker.Validate.Param () where
4
5
5−import Data.OpenApi
6+import Control.Monad
6import Data.Maybe
7import Data.Maybe
8+import Data.OpenApi
9+import OpenAPI.Checker.Orphans
7import OpenAPI.Checker.Subtree
10import OpenAPI.Checker.Subtree
11+import OpenAPI.Checker.Trace
12+import OpenAPI.Checker.Validate.Schema ()
13+
14+-- | The type is normalized encoding style of the parameter. If two encoding
15+-- styles are equal then parameters are compatible with their encoding style
16+data EncodingStyle = EncodingStyle
17+ { style :: Style
18+ , explode :: Bool
19+ , allowReserved :: Maybe Bool
20+ -- ^ Nothing when @in@ parameter is not @query@
21+ } deriving (Eq, Ord, Show)
22+
23+paramEncoding :: Param -> EncodingStyle
24+paramEncoding p = EncodingStyle
25+ { style, explode, allowReserved }
26+ where
27+ style = fromMaybe defaultStyle $ _paramStyle p
28+ defaultStyle = case _paramIn p of
29+ ParamQuery -> StyleForm
30+ ParamPath -> StyleSimple
31+ ParamHeader -> StyleSimple
32+ ParamCookie -> StyleForm
33+ explode = fromMaybe defaultExplode $ _paramExplode p
34+ defaultExplode = case style of
35+ StyleForm -> True
36+ _ -> False
37+ allowReserved = case _paramIn p of
38+ ParamQuery -> Just $ fromMaybe False $ _paramAllowReserved p
39+ _ -> Nothing
8
40
9instance Subtree Param where
41instance Subtree Param where
10− type CheckEnv Param = '[]
42+ type CheckEnv Param = '[ProdCons (Definitions Schema)]
11 data CheckIssue Param
43 data CheckIssue Param
12 = ParamNameMismatch
44 = ParamNameMismatch
13 -- ^ Params have different names
45 -- ^ Params have different names
14− | ParamOptionalityIncompatible
46+ | ParamEmptinessIncompatible
15− -- ^ Consumer requires non-empty param, but producer gives optional
47+ -- ^ Consumer requires non-empty param, but producer gives emptyable
48+ | ParamRequired
49+ -- ^ Consumer requires mandatory parm, but producer optional
16 | ParamPlaceIncompatible
50 | ParamPlaceIncompatible
51+ | ParamStyleMismatch
52+ -- ^ Params encoded in different styles
53+ | ParamSchemaMismatch
54+ -- ^ One of schemas not presented
55+ deriving (Eq, Ord, Show)
56+ checkCompatibility env (ProdCons p c) = do
57+ when (_paramName p /= _paramName c)
58+ $ issueAt producer ParamNameMismatch
59+ when ((fromMaybe False $ _paramRequired c) &&
60+ not (fromMaybe False $ _paramRequired p))
61+ $ issueAt producer ParamRequired
62+ case (_paramIn p, _paramIn c) of
63+ (ParamQuery, ParamQuery) -> do
64+ -- Emptiness is only for query params
65+ when ((fromMaybe False $ _paramAllowEmptyValue p)
66+ && not (fromMaybe False $ _paramAllowEmptyValue c))
67+ $ issueAt producer ParamEmptinessIncompatible
68+ (a, b) | a == b -> pure ()
69+ _ -> issueAt producer ParamPlaceIncompatible
70+ unless (paramEncoding p == paramEncoding c)
71+ $ issueAt producer ParamStyleMismatch
72+ case (_paramSchema p, _paramSchema c) of
73+ (Just prodSchema, Just consSchema) -> localStep ParamSchema
74+ $ checkCompatibility env (ProdCons prodSchema consSchema)
75+ (Nothing, Nothing) -> pure ()
76+ (Nothing, Just _consSchema) -> issueAt producer ParamSchemaMismatch
77+ (Just _prodSchema, Nothing) -> pure ()
78+ -- If consumer doesn't care then why we should?
79+ pure ()
80+
81+instance Steppable Param (Referenced Schema) where
82+ data Step Param (Referenced Schema) = ParamSchema
17 deriving (Eq, Ord, Show)
83 deriving (Eq, Ord, Show)
18− checkCompatibility _ (ProdCons p c) = case ( _paramIn p, _paramIn c) of
19− (ParamQuery, ParamQuery) ->
20− namesCompatible $
21− if (fromMaybe False $ _paramAllowEmptyValue p)
22− && not (fromMaybe False $ _paramAllowEmptyValue c)
23− then issueAt producer ParamOptionalityIncompatible
24− else pure ()
25− (a, b) | a == b -> namesCompatible $ pure ()
26− _ -> issueAt producer ParamPlaceIncompatible
27− where
28− namesCompatible next =
29− if _paramName p /= _paramName c
30− then issueAt producer ParamNameMismatch
31− else next
src/OpenAPI/Checker/Validate/PathFragment.hs+35−60
@@ -1,96 +1,71 @@
1module OpenAPI.Checker.Validate.PathFragment
1module OpenAPI.Checker.Validate.PathFragment
2− ( PathParamRefs
3− , TracedReferences
4− , getPathParamRefs
5− , parsePath
2+ ( parsePath
6 , PathFragment (..)
3 , PathFragment (..)
4+ , PathFragmentParam
7 )
5 )
8where
6where
9
7
10−import Control.Lens
11−import Control.Monad.Reader
12import qualified Data.Aeson as A
8import qualified Data.Aeson as A
13−import Data.HList
14−import qualified Data.List.NonEmpty as NE
15−import Data.Map.Strict (Map)
16−import qualified Data.Map.Strict as M
17−import Data.Maybe
18import Data.OpenApi
9import Data.OpenApi
19import Data.Text (Text)
10import Data.Text (Text)
20import qualified Data.Text as T
11import qualified Data.Text as T
21−import OpenAPI.Checker.References
12+import Data.Typeable
22import OpenAPI.Checker.Subtree
13import OpenAPI.Checker.Subtree
23import OpenAPI.Checker.Trace
14import OpenAPI.Checker.Trace
24import OpenAPI.Checker.Validate.Param ()
15import OpenAPI.Checker.Validate.Param ()
25
16
26−getPathParamRefs
27− :: Definitions Param
28− -> [Referenced Param]
29− -> Map Reference (Traced (Referenced Param) Param)
30−getPathParamRefs defs xs =
31− M.fromList $ do
32− x <- xs
33− let (Traced t param) = dereference defs x
34− guard (_paramIn param == ParamPath)
35− return (Reference $ _paramName param, Traced t param)
36−
37-- TODO: templates can be only part of the PathFragment. Currently only supports templates as full PathFragment.
17-- TODO: templates can be only part of the PathFragment. Currently only supports templates as full PathFragment.
38-- https://github.com/typeable/openapi-diff/issues/23
18-- https://github.com/typeable/openapi-diff/issues/23
39−parsePath :: FilePath -> [PathFragment]
19+parsePath :: FilePath -> [PathFragment Text]
40parsePath = fmap partition . T.splitOn "/" . T.pack
20parsePath = fmap partition . T.splitOn "/" . T.pack
41 where
21 where
42− partition :: Text -> PathFragment
22+ partition :: Text -> PathFragment Text
43 partition t
23 partition t
44 | Just ('{', rest) <- T.uncons t
24 | Just ('{', rest) <- T.uncons t
45 , Just (ref, '}') <- T.unsnoc rest =
25 , Just (ref, '}') <- T.unsnoc rest =
46− DynamicPath $ Reference ref
26+ DynamicPath ref
47 partition t = StaticPath t
27 partition t = StaticPath t
48
28
29+-- | Fragment parameterized by parameter. The dynamic part may be either
30+-- reference to some parameter (in context of operation) or dereferenced
31+-- parameter itself.
49−data PathFragment
32+data PathFragment param
50 = StaticPath Text
33 = StaticPath Text
51− | DynamicPath Reference
34+ | DynamicPath param
52 deriving stock (Eq, Ord)
35 deriving stock (Eq, Ord)
53
36
54−instance Steppable PathFragment Param where
37+type PathFragmentParam = PathFragment (Traced OpenApi Param)
55− data Step PathFragment Param = StaticPathParam
56− deriving (Eq, Ord, Show)
57−
58−type PathParamRefs = TracedReferences PathFragment Param
59
38
60−instance Subtree PathFragment where
39+instance (Typeable param) => Steppable (PathFragment param) Param where
61− type CheckEnv PathFragment = '[ProdCons PathParamRefs]
40+ data Step (PathFragment param) Param = StaticPathParam Text
62− data CheckIssue PathFragment = PathFragmentsDontMatch Text Text
63 deriving (Eq, Ord, Show)
41 deriving (Eq, Ord, Show)
64
42
43+instance Subtree PathFragmentParam where
44+ type CheckEnv PathFragmentParam =
45+ '[ ProdCons (Definitions Schema) ]
46+ data CheckIssue PathFragmentParam =
47+ PathFragmentsDontMatch Text Text
48+ deriving (Eq, Ord, Show)
65 -- This case isn't strictly needed. It is here for optimization.
49 -- This case isn't strictly needed. It is here for optimization.
66 checkCompatibility _ ProdCons {producer = (StaticPath x), consumer = (StaticPath y)} =
50 checkCompatibility _ ProdCons {producer = (StaticPath x), consumer = (StaticPath y)} =
67 if x == y
51 if x == y
68 then pure ()
52 then pure ()
69 else issueAt consumer (PathFragmentsDontMatch x y)
53 else issueAt consumer (PathFragmentsDontMatch x y)
70− checkCompatibility env prodCons = do
54+ checkCompatibility env prodCons = withTrace $ \myTrace -> do
71− let (t, param) =
55+ let
72− fsplit . fmap deTraced $
73− dePathFragment
56+ tracedParams = dePathFragment <$> myTrace <*> prodCons
74− <$> (singletonH <$> getH @(ProdCons PathParamRefs) env)
57+ dePathFragment root = \case
75− <*> prodCons
76− localTrace t $ checkCompatibility env param
77−
78−-- | A clearer name for 'NE.unzip' that can be used without qualifying it.
79−fsplit :: Functor f => f (a, b) -> (f a, f b)
80−fsplit = NE.unzip
81−
82−dePathFragment :: Has PathParamRefs xs => HList xs -> PathFragment -> Traced PathFragment Param
83−dePathFragment (getH @PathParamRefs -> params) = \case
84− (StaticPath s) ->
85− Traced (step StaticPathParam) $
58+ StaticPath s -> retrace root $ Traced (step $ StaticPathParam s)
86− mempty
59+ $ mempty
87− { _paramRequired = Just True
60+ { _paramRequired = Just True
88− , _paramIn = ParamPath
61+ , _paramIn = ParamPath
89− , _paramAllowEmptyValue = Just False
62+ , _paramAllowEmptyValue = Just False
90− , _paramAllowReserved = Just False
63+ , _paramAllowReserved = Just False
91− , _paramSchema = Just $ Inline $ staticStringSchema s
64+ , _paramSchema = Just $ Inline $ staticStringSchema s }
92− }
65+ DynamicPath p -> p
93− (DynamicPath ref) -> M.lookup ref params & fromMaybe (error $ show ref <> " not found.")
66+ params = getTraced <$> tracedParams
67+ paramTrace = getTrace <$> tracedParams
68+ localTrace' paramTrace $ checkCompatibility env params
94
69
95staticStringSchema :: Text -> Schema
70staticStringSchema :: Text -> Schema
96staticStringSchema t =
71staticStringSchema t =
src/OpenAPI/Checker/Validate/ProcessedPathItem.hs+148−129
@@ -1,4 +1,5 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2+{-# OPTIONS_GHC -Wno-name-shadowing #-}
2
3
3module OpenAPI.Checker.Validate.ProcessedPathItem
4module OpenAPI.Checker.Validate.ProcessedPathItem
4 ( ProcessedPathItem (..)
5 ( ProcessedPathItem (..)
@@ -8,16 +9,21 @@ module OpenAPI.Checker.Validate.ProcessedPathItem
8 )
9 )
9where
10where
10
11
12+import Control.Monad
11−import Data.Foldable
13+import Data.Foldable as F
14+import Data.Functor
12import Data.HList
15import Data.HList
16+import qualified Data.List as L
17+import Data.Map.Strict as M
13import Data.Maybe
18import Data.Maybe
14import Data.OpenApi
19import Data.OpenApi
15−import Generic.Data
20+import Data.Text as T
16import OpenAPI.Checker.References
21import OpenAPI.Checker.References
17import OpenAPI.Checker.Subtree
22import OpenAPI.Checker.Subtree
18import OpenAPI.Checker.Trace
23import OpenAPI.Checker.Trace
19import OpenAPI.Checker.Validate.Operation
24import OpenAPI.Checker.Validate.Operation
20import OpenAPI.Checker.Validate.PathFragment
25import OpenAPI.Checker.Validate.PathFragment
26+import OpenAPI.Checker.Validate.Sums
21
27
22-- FIXME: There's probably a better name for this, but `PathItem` is already taken ;(
28-- FIXME: There's probably a better name for this, but `PathItem` is already taken ;(
23data ProcessedPathItem = ProcessedPathItem
29data ProcessedPathItem = ProcessedPathItem
@@ -28,7 +34,8 @@ data ProcessedPathItem = ProcessedPathItem
28processPathItems :: [(FilePath, PathItem)] -> ProcessedPathItems
34processPathItems :: [(FilePath, PathItem)] -> ProcessedPathItems
29processPathItems = ProcessedPathItems . fmap (uncurry ProcessedPathItem)
35processPathItems = ProcessedPathItems . fmap (uncurry ProcessedPathItem)
30
36
37+newtype ProcessedPathItems =
31−newtype ProcessedPathItems = ProcessedPathItems {unProcessedPathItems :: [ProcessedPathItem]}
38+ ProcessedPathItems {unProcessedPathItems :: [ProcessedPathItem]}
32
39
33instance Subtree ProcessedPathItems where
40instance Subtree ProcessedPathItems where
34 type
41 type
@@ -40,48 +47,54 @@ instance Subtree ProcessedPathItems where
40 , ProdCons (Definitions Header)
47 , ProdCons (Definitions Header)
41 , ProdCons (Definitions Schema)
48 , ProdCons (Definitions Schema)
42 ]
49 ]
43− data CheckIssue ProcessedPathItems = NoPathsMatched | WrongNumberOfFragments
50+ data CheckIssue ProcessedPathItems
51+ = NoPathsMatched FilePath
52+ | AllPathsFailed FilePath
53+ -- When several paths match given but all checks failed
44 deriving (Eq, Ord, Show)
54 deriving (Eq, Ord, Show)
45− checkCompatibility env prodCons = do
55+ checkCompatibility env (ProdCons p c) = do
46− let ProdCons {producer = p, consumer = c} =
56+ -- Each path generated by producer must be handled by consumer with exactly
47− (\paramDefs -> fmap (processPathItem paramDefs) . unProcessedPathItems)
57+ -- one way
48− <$> getH @(ProdCons (Definitions Param)) env
58+ for_ (unProcessedPathItems p) $ \ prodItem -> do
49− <*> prodCons
59+ let
50− sequenceA_
60+ prodPath = path prodItem
51− [ anyOf'
61+ matchedItems = do
52− [ localTrace (step <$> ProdCons pSPath cSPath) $ do
62+ consItem <- unProcessedPathItems c
53− -- make sure every path fragment is compatible
63+ matched <- F.toList $ matchingPathItems $ ProdCons prodItem consItem
54− sequenceA_
64+ return matched
55− [ localTrace (pure . step $ PathFragmentStep i) $
65+ case matchedItems of
56− checkCompatibility (singletonH $ ProdCons pPathFragmentParams cPathFragmentParams) pair
66+ [] -> issueAt producer $ NoPathsMatched prodPath
57− | (i, pair) <- zip [0 ..] pathFragments
67+ [matched] -> do
58− ]
68+ -- Checking exact match with no wrapper
59− -- make sure the operation is compatible.
69+ let trace = matchedTrace <$> matched
60− localTrace (pure . step $ getter stepProcessedPathItem) $
70+ localTrace trace $ checkCompatibility env matched
61− checkCompatibility env $ ProdCons pOperation cOperation
71+ matches -> anyOfAt consumer (AllPathsFailed prodPath) $ do
62− pure ()
72+ match <- matches
63− | (cSPath, cPath, cPathItem) <- c
73+ let trace = matchedTrace <$> match
64− , -- ... and try to match it with every endpoint in the consumer.
74+ pure $ localTrace trace $ checkCompatibility env match
65− --
66− -- This is required because the meaning of path fragments can change on
67− -- a per-method basis even within the same 'PathItem'
68− --
69− -- Here we only need to look for the method that the current producer
70− -- endpoint is using.
71− (cParams, cOperation) <- maybeToList $ getter cPathItem
72− , let cPathFragmentParams = retrace (step PathFragmentParentStep) <$> cParams
73− , -- make sure the paths are the same length
74− pathFragments <- maybeToList $ zipAllWith ProdCons pPath cPath
75− ]
76− | (pSPath, pPath, pPathItem) <- p
77− , -- look at every endpoint in the producer ...
78− (ProcessedPathItemGetter getter, (pParams, pOperation)) <-
79− toList (fmap . (,) <$> processedPathItemGetters <*> pPathItem) >>= maybeToList
80− , let pPathFragmentParams = retrace (step PathFragmentParentStep) <$> pParams
81− ]
82 where
75 where
83− anyOf' [x] = x -- preserve errors if there's only one choice
76+ matchedTrace :: MatchedPathItem -> Trace ProcessedPathItems MatchedPathItem
84− anyOf' xs = anyOfAt producer NoPathsMatched xs
77+ matchedTrace mpi = step $ MatchedPathStep $ matchedPath mpi
78+
79+-- | Preliminary checks two paths for compatibility. Returns Nothing if two
80+-- paths obviously do not match: static parts differ or count of path elements
81+-- is not equal
82+matchingPathItems :: ProdCons ProcessedPathItem -> Maybe (ProdCons MatchedPathItem)
83+matchingPathItems prodCons = do
84+ let frags = parsePath . path <$> prodCons
85+ guard $ fragsMatch frags
86+ let
87+ mkMatchedItems frag ppi = MatchedPathItem
88+ { pathItem = item ppi
89+ , matchedPath = path ppi
90+ , pathFragments = frag }
91+ return $ mkMatchedItems <$> frags <*> prodCons
92+
93+fragsMatch :: ProdCons [PathFragment Text] -> Bool
94+fragsMatch (ProdCons p c) = maybe False and $ zipAllWith check p c
95+ where
96+ check (StaticPath s1) (StaticPath s2) = s1 == s2
97+ check _ _ = True
85
98
86zipAllWith :: (a -> b -> c) -> [a] -> [b] -> Maybe [c]
99zipAllWith :: (a -> b -> c) -> [a] -> [b] -> Maybe [c]
87zipAllWith _ [] [] = Just []
100zipAllWith _ [] [] = Just []
@@ -89,96 +102,102 @@ zipAllWith f (x : xs) (y : ys) = (f x y :) <$> zipAllWith f xs ys
89zipAllWith _ (_ : _) [] = Nothing
102zipAllWith _ (_ : _) [] = Nothing
90zipAllWith _ [] (_ : _) = Nothing
103zipAllWith _ [] (_ : _) = Nothing
91
104
92−processPathItem
105+data MatchedPathItem = MatchedPathItem
93− :: Definitions Param -- ^ from components
106+ { pathItem :: !PathItem
94− -> ProcessedPathItem
107+ , matchedPath :: !FilePath
95− -> ( Step ProcessedPathItems PathItem
96− , [PathFragment]
108+ , pathFragments :: ![PathFragment Text]
97− , ForeachOperation (Maybe (TracedReferences PathItem Param, Operation))
109+ -- ^ Pre-parsed path from PathItem
98− )
110+ }
99−processPathItem componentParams ProcessedPathItem {path = pathS, item = pathItem} =
100− let path = parsePath pathS
101− commonPathParams =
102− retrace (step PathItemParametersStep)
103− <$> getPathParamRefs componentParams (_pathItemParameters pathItem)
104− processOperation (s :: Step PathItem Operation) op =
105− let operationParams =
106− retrace (Root `Snoc` s `Snoc` OperationParamsStep)
107− <$> getPathParamRefs componentParams (_operationParameters op)
108− pathParams =
109− operationParams <> commonPathParams
110− in (pathParams, op)
111− in ( PathStep pathS
112− , path
113− , fmap . processOperation
114− <$> stepProcessedPathItem
115− <*> ForeachOperation
116− { processedPathItemGet = _pathItemGet pathItem
117− , processedPathItemPut = _pathItemPut pathItem
118− , processedPathItemPost = _pathItemPost pathItem
119− , processedPathItemDelete = _pathItemDelete pathItem
120− , processedPathItemOptions = _pathItemOptions pathItem
121− , processedPathItemHead = _pathItemHead pathItem
122− , processedPathItemPatch = _pathItemPatch pathItem
123− , processedPathItemTrace = _pathItemTrace pathItem
124− }
125− )
126−
127−instance Steppable ProcessedPathItems PathItem where
128− data Step ProcessedPathItems PathItem = PathStep FilePath
129− deriving (Eq, Ord, Show)
130
111
131−instance Steppable PathItem PathFragment where
112+instance Subtree MatchedPathItem where
132− data Step PathItem PathFragment
113+ type CheckEnv MatchedPathItem =
133− = -- | The index of the path item
114+ '[ ProdCons (Definitions Param)
134− PathFragmentStep Int
115+ , ProdCons (Definitions RequestBody)
116+ , ProdCons (Definitions SecurityScheme)
117+ , ProdCons (Definitions Response)
118+ , ProdCons (Definitions Header)
119+ , ProdCons (Definitions Schema)
120+ ]
121+ data CheckIssue MatchedPathItem
122+ = OperationMissing (Step MatchedPathItem MatchedOperation)
123+ deriving (Eq, Ord, Show)
124+ checkCompatibility env prodCons = withTrace $ \rootTrace -> do
125+ let
126+ paramDefs = getH @(ProdCons (Definitions Param)) env
127+ pathTracedParams = getPathParams <$> rootTrace <*> paramDefs <*> prodCons
128+ getPathParams
129+ :: Trace OpenApi MatchedPathItem
130+ -> Definitions Param
131+ -> MatchedPathItem
132+ -> [Traced OpenApi Param]
133+ getPathParams root defs mpi = do
134+ paramRef <- _pathItemParameters $ pathItem mpi
135+ let
136+ traced = dereferenceTraced defs
137+ $ Traced (step PathItemParam) paramRef
138+ res = retrace root traced
139+ pure res
140+ pathTracedFragments = mkPathFragments <$> rootTrace <*> prodCons
141+ mkPathFragments myRoot mpi operationParams =
142+ -- operationParams will be known on Operation check stage, so we give a
143+ -- function, returning fragments
144+ let
145+ paramsMap :: Map Text (Traced OpenApi Param)
146+ paramsMap = M.fromList $ do
147+ tracedParam <- operationParams
148+ let pname = _paramName $ getTraced tracedParam
149+ pure (pname, tracedParam)
150+ fragments :: [PathFragmentParam]
151+ fragments = (pathFragments mpi) <&> \case
152+ StaticPath t -> StaticPath t
153+ DynamicPath pname -> DynamicPath
154+ $ fromMaybe (error $ "Param not found " <> T.unpack pname)
155+ $ M.lookup pname paramsMap
156+ tracedFragments :: [Traced OpenApi PathFragmentParam]
157+ tracedFragments = L.zip [0..] fragments <&> \(pos, frag) ->
158+ retrace myRoot $ Traced (step $ PathFragmentStep pos) frag
159+ in tracedFragments
160+ operations = getOperations <$> pathTracedParams <*> pathTracedFragments <*> prodCons
161+ getOperations pathParams getPathFragments mpi = M.fromList $ do
162+ (getOp, s) <-
163+ [ (_pathItemGet, GetStep)
164+ , (_pathItemPut, PutStep)
165+ , (_pathItemPost, PostStep)
166+ , (_pathItemDelete, DeleteStep)
167+ , (_pathItemOptions, OptionsStep)
168+ , (_pathItemHead, HeadStep)
169+ , (_pathItemPatch, PatchStep)
170+ , (_pathItemTrace, TraceStep) ]
171+ operation <- F.toList $ getOp $ pathItem mpi
172+ -- Got only Justs here
173+ let mop = MatchedOperation { operation , pathParams, getPathFragments }
174+ pure (s, Traced (step s) mop)
175+ check _ pc = checkCompatibility @MatchedOperation env pc
176+ -- Operations are sum-like entities. Use step to operation as key because
177+ -- why not
178+ checkSums OperationMissing check operations
179+
180+
181+instance Steppable ProcessedPathItems MatchedPathItem where
182+ data Step ProcessedPathItems MatchedPathItem = MatchedPathStep FilePath
135 deriving (Eq, Ord, Show)
183 deriving (Eq, Ord, Show)
136
184
137−instance Steppable PathFragment PathItem where
185+instance Steppable MatchedPathItem MatchedOperation where
138− data Step PathFragment PathItem = PathFragmentParentStep
186+ data Step MatchedPathItem MatchedOperation
187+ = GetStep
188+ | PutStep
189+ | PostStep
190+ | DeleteStep
191+ | OptionsStep
192+ | HeadStep
193+ | PatchStep
194+ | TraceStep
139 deriving (Eq, Ord, Show)
195 deriving (Eq, Ord, Show)
140
196
141−instance Steppable PathItem (Referenced Param) where
197+instance Steppable MatchedPathItem (Referenced Param) where
142− data Step PathItem (Referenced Param) = PathItemParametersStep
198+ data Step MatchedPathItem (Referenced Param) = PathItemParam
143 deriving (Eq, Ord, Show)
199 deriving (Eq, Ord, Show)
144
200
145−data ForeachOperation a = ForeachOperation
201+instance Steppable MatchedPathItem PathFragmentParam where
146− { processedPathItemGet :: a
202+ data Step MatchedPathItem PathFragmentParam = PathFragmentStep Int
147− , processedPathItemPut :: a
203+ deriving (Eq, Ord, Show)
148− , processedPathItemPost :: a
149− , processedPathItemDelete :: a
150− , processedPathItemOptions :: a
151− , processedPathItemHead :: a
152− , processedPathItemPatch :: a
153− , processedPathItemTrace :: a
154− }
155− deriving stock (Functor, Generic1)
156− deriving (Applicative, Foldable) via Generically1 ForeachOperation
157−
158−newtype ProcessedPathItemGetter = ProcessedPathItemGetter (forall a. ForeachOperation a -> a)
159−
160−processedPathItemGetters :: ForeachOperation ProcessedPathItemGetter
161−processedPathItemGetters =
162− ForeachOperation
163− { processedPathItemGet = ProcessedPathItemGetter processedPathItemGet
164− , processedPathItemPut = ProcessedPathItemGetter processedPathItemPut
165− , processedPathItemPost = ProcessedPathItemGetter processedPathItemPost
166− , processedPathItemDelete = ProcessedPathItemGetter processedPathItemDelete
167− , processedPathItemOptions = ProcessedPathItemGetter processedPathItemOptions
168− , processedPathItemHead = ProcessedPathItemGetter processedPathItemHead
169− , processedPathItemPatch = ProcessedPathItemGetter processedPathItemPatch
170− , processedPathItemTrace = ProcessedPathItemGetter processedPathItemTrace
171− }
172−
173−stepProcessedPathItem :: ForeachOperation (Step PathItem Operation)
174−stepProcessedPathItem =
175− ForeachOperation
176− { processedPathItemGet = GetStep
177− , processedPathItemPut = PutStep
178− , processedPathItemPost = PostStep
179− , processedPathItemDelete = DeleteStep
180− , processedPathItemOptions = OptionsStep
181− , processedPathItemHead = HeadStep
182− , processedPathItemPatch = PatchStep
183− , processedPathItemTrace = TraceStep
184− }
src/OpenAPI/Checker/Validate/Products.hsnew file+71
@@ -0,0 +1,71 @@
1+{- | Checks product-like entities. The key is some identificator for the product
2+element. Each element may be required or optional.
3+
4+One example of product is request parameters. There are optional and required
5+parameters. The client and server have possibly different set of
6+parameters. What we must check is if server requires some request parameter,
7+then this parameter must be presented by client and their schemas must match.
8+
9+So when we checking products we are checking from the server's (consumer)
10+perspective, ensuring that all parameters are provided by the client (producer)
11+and their schemas match.
12+
13+This module abstracts this logic for arbitrary elements -}
14+
15+module OpenAPI.Checker.Validate.Products
16+ ( checkProducts
17+ , checkProducts'
18+ , ProductLike(..)
19+ ) where
20+
21+import Data.Foldable
22+import Data.Functor
23+import Data.Map.Strict (Map)
24+import qualified Data.Map.Strict as M
25+import Data.OpenApi.Internal
26+import OpenAPI.Checker.Subtree
27+import OpenAPI.Checker.Trace
28+
29+-- | Some entity which is product-like
30+data ProductLike root a = ProductLike
31+ { traced :: Traced root a
32+ , required :: Bool
33+ }
34+
35+checkProducts'
36+ :: forall k root t
37+ . (Subtree root, Ord k)
38+ => (k -> CheckIssue root)
39+ -- ^ No required element found
40+ -> (k -> ProdCons t -> CompatFormula t ())
41+ -> ProdCons (Map k (ProductLike OpenApi t))
42+ -> CompatFormula root ()
43+checkProducts' noElt check (ProdCons p c) = for_ (M.toList c) $ \(key, consElt) ->
44+ case M.lookup key p of
45+ Nothing -> case required consElt of
46+ True -> issueAt producer $ noElt key
47+ False -> pure ()
48+ Just prodElt -> do
49+ let
50+ elts :: ProdCons (ProductLike OpenApi t)
51+ elts = ProdCons prodElt consElt
52+ trace = getTrace . traced <$> elts
53+ elements = getTraced . traced <$> elts
54+ localTrace' trace $ check key elements
55+
56+checkProducts
57+ :: forall k root t
58+ . (Subtree root, Ord k)
59+ => (k -> CheckIssue root)
60+ -- ^ No required element found
61+ -> (k -> ProdCons t -> CompatFormula t ())
62+ -> ProdCons (Map k (ProductLike root t))
63+ -> CompatFormula root ()
64+checkProducts noElt check prodCons = withTrace $ \myTrace ->
65+ let
66+ retracedPC = retracePC <$> myTrace <*> prodCons
67+ retracePC rootTrace els = els <&> \productLike ->
68+ ProductLike
69+ { traced = retrace rootTrace $ traced productLike
70+ , required = required productLike }
71+ in checkProducts' noElt check retracedPC
src/OpenAPI/Checker/Validate/RequestBody.hs+16−10
@@ -5,15 +5,17 @@ module OpenAPI.Checker.Validate.RequestBody
5 )
5 )
6where
6where
7
7
8−import Data.Foldable as F
8+import Data.Functor
9import Data.HList
9import Data.HList
10import Data.HashMap.Strict.InsOrd as IOHM
10import Data.HashMap.Strict.InsOrd as IOHM
11+import Data.Map.Strict as M
11import Data.Maybe
12import Data.Maybe
12import Data.OpenApi
13import Data.OpenApi
13import Network.HTTP.Media (MediaType)
14import Network.HTTP.Media (MediaType)
14import OpenAPI.Checker.Subtree
15import OpenAPI.Checker.Subtree
15import OpenAPI.Checker.Trace
16import OpenAPI.Checker.Trace
16import OpenAPI.Checker.Validate.MediaTypeObject ()
17import OpenAPI.Checker.Validate.MediaTypeObject ()
18+import OpenAPI.Checker.Validate.Sums
17
19
18instance Subtree RequestBody where
20instance Subtree RequestBody where
19 type CheckEnv RequestBody =
21 type CheckEnv RequestBody =
@@ -22,19 +24,23 @@ instance Subtree RequestBody where
22 = RequestBodyRequired
24 = RequestBodyRequired
23 | RequestMediaTypeNotFound MediaType
25 | RequestMediaTypeNotFound MediaType
24 deriving (Eq, Ord, Show)
26 deriving (Eq, Ord, Show)
25− checkCompatibility env (ProdCons p c) =
27+ checkCompatibility env prodCons@(ProdCons p c) =
26 if not (fromMaybe False $ _requestBodyRequired p)
28 if not (fromMaybe False $ _requestBodyRequired p)
27 && (fromMaybe False $ _requestBodyRequired c)
29 && (fromMaybe False $ _requestBodyRequired c)
28 then issueAt producer RequestBodyRequired
30 then issueAt producer RequestBodyRequired
29 else
31 else
30− -- Each media type generated by the producer must be parsed by the
32+ -- Media type object are sums-like entities.
31− -- consumer
33+ let
34+ check mediaType pc = checkCompatibility @MediaTypeObject (HCons mediaType env) pc
35+ sumElts = getSum <$> prodCons
36+ getSum rb = M.fromList
32− for_ (IOHM.toList $ _requestBodyContent p) $ \(mediaType, prodMedia) ->
37+ $ (IOHM.toList $ _requestBodyContent rb) <&> \(mt, mto) ->
33− case IOHM.lookup mediaType $ _requestBodyContent c of
38+ ( mt
39+ , Traced
40+ { getTraced = mto
41+ , getTrace = step $ RequestMediaTypeObject mt })
34− Nothing -> issueAt consumer (RequestMediaTypeNotFound mediaType)
42+ in checkSums RequestMediaTypeNotFound check sumElts
35− Just consMedia -> localStep (MediaTypeStep mediaType) $
36− checkCompatibility (HCons mediaType env) (ProdCons prodMedia consMedia)
37
43
38instance Steppable RequestBody MediaTypeObject where
44instance Steppable RequestBody MediaTypeObject where
39− data Step RequestBody MediaTypeObject = MediaTypeStep MediaType
45+ data Step RequestBody MediaTypeObject = RequestMediaTypeObject MediaType
40 deriving (Eq, Ord, Show)
46 deriving (Eq, Ord, Show)
src/OpenAPI/Checker/Validate/Responses.hs+50−41
@@ -1,4 +1,5 @@
1{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
2+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
2
3
3module OpenAPI.Checker.Validate.Responses
4module OpenAPI.Checker.Validate.Responses
4 (
5 (
@@ -8,6 +9,7 @@ where
8import Data.Foldable
9import Data.Foldable
9import Data.HList
10import Data.HList
10import Data.HashMap.Strict.InsOrd as IOHM
11import Data.HashMap.Strict.InsOrd as IOHM
12+import Data.Map.Strict as M
11import Data.Maybe
13import Data.Maybe
12import Data.OpenApi
14import Data.OpenApi
13import Network.HTTP.Media (MediaType)
15import Network.HTTP.Media (MediaType)
@@ -15,7 +17,9 @@ import OpenAPI.Checker.References
15import OpenAPI.Checker.Subtree
17import OpenAPI.Checker.Subtree
16import OpenAPI.Checker.Trace
18import OpenAPI.Checker.Trace
17import OpenAPI.Checker.Validate.MediaTypeObject ()
19import OpenAPI.Checker.Validate.MediaTypeObject ()
20+import OpenAPI.Checker.Validate.Products
18import OpenAPI.Checker.Validate.Schema ()
21import OpenAPI.Checker.Validate.Schema ()
22+import OpenAPI.Checker.Validate.Sums
19
23
20instance Subtree Responses where
24instance Subtree Responses where
21 type CheckEnv Responses =
25 type CheckEnv Responses =
@@ -25,19 +29,21 @@ instance Subtree Responses where
25 ]
29 ]
26 data CheckIssue Responses = ResponseCodeNotFound HttpStatusCode
30 data CheckIssue Responses = ResponseCodeNotFound HttpStatusCode
27 deriving (Eq, Ord, Show)
31 deriving (Eq, Ord, Show)
28− -- Here we are checking responses, so, the consumer and producer swap their
32+ -- Roles are already swapped. Producer is a server and consumer is a
29− -- roles. The consumer now generates the response and producer consumes
33+ -- client. Response codes are sum-like entity because we can answer with only
30− -- it. So, the logic is swapped.
34+ -- one element
31− checkCompatibility env (ProdCons p c) = do
35+ checkCompatibility env prodCons = do
36+ let
32− let defs = getH @(ProdCons (Definitions Response)) env
37+ defs = getH @(ProdCons (Definitions Response)) env
33− for_ (IOHM.toList $ _responsesResponses c) $ \(prodStatus, consRef) ->
38+ check _ resps = checkCompatibility @Response env resps
34− case IOHM.lookup prodStatus $ _responsesResponses p of
39+ elements = getEls <$> defs <*> prodCons
35− Nothing -> issueAt producer $ ResponseCodeNotFound prodStatus
40+ getEls respDef resps = M.fromList $ do
36− Just prodRef -> do
41+ (code, respRef) <- IOHM.toList $ _responsesResponses resps
37− let tracedRefs = dereference <$> defs <*> ProdCons prodRef consRef
42+ let
38− localStep (ResponseCodeStep prodStatus)
43+ traced = dereferenceTraced respDef
39− $ checkProdCons env tracedRefs
44+ $ Traced (step $ ResponseCodeStep code) respRef
40− -- FIXME: Do we need to check "default" fields somehow here?
45+ pure (code, traced)
46+ checkSums ResponseCodeNotFound check elements
41
47
42instance Subtree Response where
48instance Subtree Response where
43 type CheckEnv Response =
49 type CheckEnv Response =
@@ -48,37 +54,40 @@ instance Subtree Response where
48 = ResponseMediaTypeMissing MediaType
54 = ResponseMediaTypeMissing MediaType
49 | ResponseHeaderMissing HeaderName
55 | ResponseHeaderMissing HeaderName
50 deriving (Eq, Ord, Show)
56 deriving (Eq, Ord, Show)
51− -- Here we are checking responses, so, the consumer and producer swap their
52− -- roles. The consumer now generates the response and producer consumes
53− -- it. So, the logic is swapped.
54− checkCompatibility env (ProdCons p c) = do
57+ checkCompatibility env prodCons = do
58+ -- Roles are already swapped. Producer is a server and consumer is a client
55 checkMediaTypes
59 checkMediaTypes
56 checkHeaders
60 checkHeaders
57 pure ()
61 pure ()
58 where
62 where
59− -- Each response type, generated by the consumer must be parseable by the
60− -- producer
61 checkMediaTypes = do
63 checkMediaTypes = do
62− for_ (IOHM.toList $ _responseContent c) $ \ (mediaType, consMediaObject) ->
64+ -- Media types are sum-like entity
63− case IOHM.lookup mediaType $ _responseContent p of
65+ let
64− Nothing -> issueAt producer $ ResponseMediaTypeMissing mediaType
66+ check mediaType mtObj =
65− Just prodMediaObject -> localStep (ResponseMediaObject mediaType)
67+ let mtEnv = HCons mediaType $ env
66− $ swapRoles
67− $ checkCompatibility @MediaTypeObject (mediaType `HCons` swapProdCons schemaRefs `HCons` HNil)
68+ in checkCompatibility @MediaTypeObject mtEnv mtObj
68− $ ProdCons consMediaObject prodMediaObject
69+ elements = getEls <$> prodCons
69− -- Each header expected by the producer must be provided by the
70+ getEls resp = M.fromList $ do
70− -- consumer. Assume, that extra consumer headers are ignored.
71+ (mediaType, mtObj) <- IOHM.toList $ _responseContent resp
72+ let traced = Traced (step $ ResponseMediaObject mediaType) mtObj
73+ pure (mediaType, traced)
74+ checkSums ResponseMediaTypeMissing check elements
75+
71 checkHeaders = do
76 checkHeaders = do
72− for_ (IOHM.toList $ _responseHeaders p) $ \ (hname, prodRef) ->
77+ -- Headers are product-like entities
73− case IOHM.lookup hname $ _responseHeaders c of
78+ let
74− Nothing -> issueAt consumer $ ResponseHeaderMissing hname
79+ check _hname hdrs = checkCompatibility @Header env hdrs
75− Just consRef -> do
80+ elements = getEls <$> headerDefs <*> prodCons
76− let headerRefs = dereference <$> headerDefs <*> ProdCons prodRef consRef
81+ getEls headerDef resp = M.fromList $ do
82+ (hname, headerRef) <- IOHM.toList $ _responseHeaders resp
83+ let
84+ traced = dereferenceTraced headerDef
77− localStep (ResponseHeader hname)
85+ $ Traced (step $ ResponseHeader hname) headerRef
78− $ swapRoles
86+ required = fromMaybe False $ _headerRequired $ getTraced traced
79− $ checkProdCons (singletonH $ swapProdCons schemaRefs) $ swapProdCons headerRefs
87+ elt = ProductLike { traced, required }
88+ pure (hname, elt)
89+ checkProducts ResponseHeaderMissing check elements
80 headerDefs = getH @(ProdCons (Definitions Header)) env
90 headerDefs = getH @(ProdCons (Definitions Header)) env
81− schemaRefs = getH @(ProdCons (Definitions Schema)) env
82
91
83instance Subtree Header where
92instance Subtree Header where
84 type CheckEnv Header = '[ProdCons (Definitions Schema)]
93 type CheckEnv Header = '[ProdCons (Definitions Schema)]
@@ -100,6 +109,10 @@ instance Subtree Header where
100 $ checkCompatibility env $ ProdCons prodRef consRef
109 $ checkCompatibility env $ ProdCons prodRef consRef
101 pure ()
110 pure ()
102
111
112+instance Steppable Responses (Referenced Response) where
113+ data Step Responses (Referenced Response) = ResponseCodeStep HttpStatusCode
114+ deriving (Eq, Ord, Show)
115+
103instance Steppable Header (Referenced Schema) where
116instance Steppable Header (Referenced Schema) where
104 data Step Header (Referenced Schema) = HeaderSchema
117 data Step Header (Referenced Schema) = HeaderSchema
105 deriving (Eq, Ord, Show)
118 deriving (Eq, Ord, Show)
@@ -111,7 +124,3 @@ instance Steppable Response (Referenced Header) where
111instance Steppable Response MediaTypeObject where
124instance Steppable Response MediaTypeObject where
112 data Step Response MediaTypeObject = ResponseMediaObject MediaType
125 data Step Response MediaTypeObject = ResponseMediaObject MediaType
113 deriving (Eq, Ord, Show)
126 deriving (Eq, Ord, Show)
114−
115−instance Steppable Responses (Referenced Response) where
116− data Step Responses (Referenced Response) = ResponseCodeStep HttpStatusCode
117− deriving (Eq, Ord, Show)
src/OpenAPI/Checker/Validate/Sums.hsnew file+28
@@ -0,0 +1,28 @@
1+module OpenAPI.Checker.Validate.Sums
2+ ( checkSums
3+ ) where
4+
5+import Data.Foldable
6+import Data.Map.Strict (Map)
7+import qualified Data.Map.Strict as M
8+import OpenAPI.Checker.Subtree
9+import OpenAPI.Checker.Trace
10+
11+
12+checkSums
13+ :: forall k root t
14+ . (Ord k, Subtree root)
15+ => (k -> CheckIssue root)
16+ -> (k -> ProdCons t -> CompatFormula t ())
17+ -> ProdCons (Map k (Traced root t))
18+ -> CompatFormula root ()
19+checkSums noElt check (ProdCons p c) = for_ (M.toList p) $ \(key, prodElt) ->
20+ case M.lookup key c of
21+ Nothing -> issueAt consumer $ noElt key
22+ Just consElt ->
23+ let
24+ sumElts :: ProdCons (Traced root t)
25+ sumElts = ProdCons prodElt consElt
26+ trace = getTrace <$> sumElts
27+ elements = getTraced <$> sumElts
28+ in localTrace trace $ check key elements
test/golden/common/maximum-lowered/trace-tree.yaml+2−2
@@ -1,10 +1,10 @@
1Left:
1Left:
2 OpenApiPathsStep:
2 OpenApiPathsStep:
3− PathStep "/test":
3+ MatchedPathStep "/test":
4 PostStep:
4 PostStep:
5 OperationRequestBodyStep:
5 OperationRequestBodyStep:
6 InlineStep:
6 InlineStep:
7− MediaTypeStep application/json:
7+ RequestMediaTypeObject application/json:
8 MediaTypeSchema:
8 MediaTypeSchema:
9 ReferencedStep (Reference {getReference = "Test"}):
9 ReferencedStep (Reference {getReference = "Test"}):
10 MaximumFields: MatchingMaximumWeak (Inclusive 2.0) (Inclusive 3.0)
10 MaximumFields: MatchingMaximumWeak (Inclusive 2.0) (Inclusive 3.0)
test/golden/common/pathItem/operation/parameters/allowEmptyValue/reset/a.yamlnew file+19
@@ -0,0 +1,19 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ allowEmptyValue: true
16+ responses:
17+ "200":
18+ description: "Test"
19+components: {}
test/golden/common/pathItem/operation/parameters/allowEmptyValue/reset/b.yamlnew file+19
@@ -0,0 +1,19 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ allowEmptyValue: false
16+ responses:
17+ "200":
18+ description: "Test"
19+components: {}
test/golden/common/pathItem/operation/parameters/allowEmptyValue/reset/trace-tree.yamlnew file+6
@@ -0,0 +1,6 @@
1+Left:
2+ OpenApiPathsStep:
3+ MatchedPathStep "/test":
4+ PostStep:
5+ OperationParamsStep:
6+ InlineStep: ParamEmptinessIncompatible
test/golden/common/pathItem/operation/parameters/allowEmptyValue/set/a.yamlnew file+19
@@ -0,0 +1,19 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ allowEmptyValue: false
16+ responses:
17+ "200":
18+ description: "Test"
19+components: {}
test/golden/common/pathItem/operation/parameters/allowEmptyValue/set/b.yamlnew file+19
@@ -0,0 +1,19 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ allowEmptyValue: true
16+ responses:
17+ "200":
18+ description: "Test"
19+components: {}
test/golden/common/pathItem/operation/parameters/allowEmptyValue/set/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/pathItem/operation/parameters/change/a.yamlnew file+20
@@ -0,0 +1,20 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test
14+ in: query
15+ schema:
16+ type: "string"
17+ responses:
18+ "200":
19+ description: "Test"
20+components: {}
test/golden/common/pathItem/operation/parameters/change/b.yamlnew file+20
@@ -0,0 +1,20 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test
14+ in: query
15+ schema:
16+ type: "number"
17+ responses:
18+ "200":
19+ description: "Test"
20+components: {}
test/golden/common/pathItem/operation/parameters/change/trace-tree.yamlnew file+8
@@ -0,0 +1,8 @@
1+Left:
2+ OpenApiPathsStep:
3+ MatchedPathStep "/test":
4+ PostStep:
5+ OperationParamsStep:
6+ InlineStep:
7+ ParamSchema:
8+ InlineStep: NoContradiction
test/golden/common/pathItem/operation/parameters/required/false/add/a.yamlnew file+18
@@ -0,0 +1,18 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ responses:
16+ "200":
17+ description: "Test"
18+components: {}
test/golden/common/pathItem/operation/parameters/required/false/add/b.yamlnew file+20
@@ -0,0 +1,20 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ - name: test2
16+ in: query
17+ responses:
18+ "200":
19+ description: "Test"
20+components: {}
test/golden/common/pathItem/operation/parameters/required/false/add/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/pathItem/operation/parameters/required/false/del/a.yamlnew file+20
@@ -0,0 +1,20 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ - name: test2
16+ in: query
17+ responses:
18+ "200":
19+ description: "Test"
20+components: {}
test/golden/common/pathItem/operation/parameters/required/false/del/b.yamlnew file+18
@@ -0,0 +1,18 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ responses:
16+ "200":
17+ description: "Test"
18+components: {}
test/golden/common/pathItem/operation/parameters/required/false/del/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/pathItem/operation/parameters/required/reset/a.yamlnew file+19
@@ -0,0 +1,19 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ required: true
16+ responses:
17+ "200":
18+ description: "Test"
19+components: {}
test/golden/common/pathItem/operation/parameters/required/reset/b.yamlnew file+19
@@ -0,0 +1,19 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ required: false
16+ responses:
17+ "200":
18+ description: "Test"
19+components: {}
test/golden/common/pathItem/operation/parameters/required/reset/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/pathItem/operation/parameters/required/set/a.yamlnew file+19
@@ -0,0 +1,19 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ required: false
16+ responses:
17+ "200":
18+ description: "Test"
19+components: {}
test/golden/common/pathItem/operation/parameters/required/set/b.yamlnew file+19
@@ -0,0 +1,19 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ required: true
16+ responses:
17+ "200":
18+ description: "Test"
19+components: {}
test/golden/common/pathItem/operation/parameters/required/set/trace-tree.yamlnew file+6
@@ -0,0 +1,6 @@
1+Left:
2+ OpenApiPathsStep:
3+ MatchedPathStep "/test":
4+ PostStep:
5+ OperationParamsStep:
6+ InlineStep: ParamRequired
test/golden/common/pathItem/operation/parameters/required/true/add/a.yamlnew file+18
@@ -0,0 +1,18 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ responses:
16+ "200":
17+ description: "Test"
18+components: {}
test/golden/common/pathItem/operation/parameters/required/true/add/b.yamlnew file+21
@@ -0,0 +1,21 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ - name: test2
16+ in: query
17+ required: true
18+ responses:
19+ "200":
20+ description: "Test"
21+components: {}
test/golden/common/pathItem/operation/parameters/required/true/add/trace-tree.yamlnew file+4
@@ -0,0 +1,4 @@
1+Left:
2+ OpenApiPathsStep:
3+ MatchedPathStep "/test":
4+ PostStep: ParamNotMatched ParamQuery "test2"
test/golden/common/pathItem/operation/parameters/required/true/del/a.yamlnew file+21
@@ -0,0 +1,21 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ - name: test2
16+ in: query
17+ required: true
18+ responses:
19+ "200":
20+ description: "Test"
21+components: {}
test/golden/common/pathItem/operation/parameters/required/true/del/b.yamlnew file+18
@@ -0,0 +1,18 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ parameters:
13+ - name: test1
14+ in: query
15+ responses:
16+ "200":
17+ description: "Test"
18+components: {}
test/golden/common/pathItem/operation/parameters/required/true/del/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/add/a.yamlnew file+20
@@ -0,0 +1,20 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ responses:
18+ "200":
19+ description: "Test"
20+components: {}
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/add/b.yamlnew file+23
@@ -0,0 +1,23 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ "application/x-www-form-urlencoded":
18+ schema:
19+ type: "string"
20+ responses:
21+ "200":
22+ description: "Test"
23+components: {}
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/add/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/change/a.yamlnew file+20
@@ -0,0 +1,20 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ responses:
18+ "200":
19+ description: "Test"
20+components: {}
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/change/b.yamlnew file+20
@@ -0,0 +1,20 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "number"
17+ responses:
18+ "200":
19+ description: "Test"
20+components: {}
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/change/trace-tree.yamlnew file+9
@@ -0,0 +1,9 @@
1+Left:
2+ OpenApiPathsStep:
3+ MatchedPathStep "/test":
4+ PostStep:
5+ OperationRequestBodyStep:
6+ InlineStep:
7+ RequestMediaTypeObject application/json:
8+ MediaTypeSchema:
9+ InlineStep: NoContradiction
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/del/a.yamlnew file+23
@@ -0,0 +1,23 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ "application/x-www-form-urlencoded":
18+ schema:
19+ type: "string"
20+ responses:
21+ "200":
22+ description: "Test"
23+components: {}
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/del/b.yamlnew file+20
@@ -0,0 +1,20 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ responses:
18+ "200":
19+ description: "Test"
20+components: {}
test/golden/common/pathItem/operation/requestBody/mediaTypeObject/del/trace-tree.yamlnew file+6
@@ -0,0 +1,6 @@
1+Left:
2+ OpenApiPathsStep:
3+ MatchedPathStep "/test":
4+ PostStep:
5+ OperationRequestBodyStep:
6+ InlineStep: RequestMediaTypeNotFound application/x-www-form-urlencoded
test/golden/common/pathItem/operation/requestBody/required/reset/a.yamlnew file+21
@@ -0,0 +1,21 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: true
18+ responses:
19+ "200":
20+ description: "Test"
21+components: {}
test/golden/common/pathItem/operation/requestBody/required/reset/b.yamlnew file+21
@@ -0,0 +1,21 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+components: {}
test/golden/common/pathItem/operation/requestBody/required/reset/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/pathItem/operation/requestBody/required/set/a.yamlnew file+21
@@ -0,0 +1,21 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+components: {}
test/golden/common/pathItem/operation/requestBody/required/set/b.yamlnew file+21
@@ -0,0 +1,21 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: true
18+ responses:
19+ "200":
20+ description: "Test"
21+components: {}
test/golden/common/pathItem/operation/requestBody/required/set/trace-tree.yamlnew file+6
@@ -0,0 +1,6 @@
1+Left:
2+ OpenApiPathsStep:
3+ MatchedPathStep "/test":
4+ PostStep:
5+ OperationRequestBodyStep:
6+ InlineStep: RequestBodyRequired
test/golden/common/pathItem/operation/responses/add/a.yamlnew file+21
@@ -0,0 +1,21 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+components: {}
test/golden/common/pathItem/operation/responses/add/b.yamlnew file+23
@@ -0,0 +1,23 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ "500":
22+ description: "Added"
23+components: {}
test/golden/common/pathItem/operation/responses/add/trace-tree.yamlnew file+5
@@ -0,0 +1,5 @@
1+Left:
2+ OpenApiPathsStep:
3+ MatchedPathStep "/test":
4+ PostStep:
5+ OperationResponsesStep: ResponseCodeNotFound 500
test/golden/common/pathItem/operation/responses/change/headers/mandatory/add/a.yamlnew file+25
@@ -0,0 +1,25 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ headers:
22+ "Test":
23+ description: "Test header"
24+ required: true
25+components: {}
test/golden/common/pathItem/operation/responses/change/headers/mandatory/add/b.yamlnew file+28
@@ -0,0 +1,28 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ headers:
22+ "Test":
23+ description: "Test header"
24+ required: true
25+ "Test2":
26+ description: "Test2 header"
27+ required: true
28+components: {}
test/golden/common/pathItem/operation/responses/change/headers/mandatory/add/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/pathItem/operation/responses/change/headers/mandatory/del/a.yamlnew file+28
@@ -0,0 +1,28 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ headers:
22+ "Test":
23+ description: "Test header"
24+ required: true
25+ "Test2":
26+ description: "Test2 header"
27+ required: true
28+components: {}
test/golden/common/pathItem/operation/responses/change/headers/mandatory/del/b.yamlnew file+25
@@ -0,0 +1,25 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ headers:
22+ "Test":
23+ description: "Test header"
24+ required: true
25+components: {}
test/golden/common/pathItem/operation/responses/change/headers/mandatory/del/trace-tree.yamlnew file+7
@@ -0,0 +1,7 @@
1+Left:
2+ OpenApiPathsStep:
3+ MatchedPathStep "/test":
4+ PostStep:
5+ OperationResponsesStep:
6+ ResponseCodeStep 200:
7+ InlineStep: ResponseHeaderMissing "Test2"
test/golden/common/pathItem/operation/responses/change/headers/optional/add/a.yamlnew file+24
@@ -0,0 +1,24 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ headers:
22+ "Test":
23+ description: "Test header"
24+components: {}
test/golden/common/pathItem/operation/responses/change/headers/optional/add/b.yamlnew file+26
@@ -0,0 +1,26 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ headers:
22+ "Test":
23+ description: "Test header"
24+ "Test2":
25+ description: "Test2 header"
26+components: {}
test/golden/common/pathItem/operation/responses/change/headers/optional/add/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/pathItem/operation/responses/change/headers/optional/del/a.yamlnew file+26
@@ -0,0 +1,26 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ headers:
22+ "Test":
23+ description: "Test header"
24+ "Test2":
25+ description: "Test2 header"
26+components: {}
test/golden/common/pathItem/operation/responses/change/headers/optional/del/b.yamlnew file+24
@@ -0,0 +1,24 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ headers:
22+ "Test":
23+ description: "Test header"
24+components: {}
test/golden/common/pathItem/operation/responses/change/headers/optional/del/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/add/a.yamlnew file+25
@@ -0,0 +1,25 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ content:
22+ "application/json":
23+ schema:
24+ type: "string"
25+components: {}
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/add/b.yamlnew file+28
@@ -0,0 +1,28 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ content:
22+ "application/json":
23+ schema:
24+ type: "string"
25+ "application/x-www-form-urlencoded":
26+ schema:
27+ type: "string"
28+components: {}
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/add/trace-tree.yamlnew file+7
@@ -0,0 +1,7 @@
1+Left:
2+ OpenApiPathsStep:
3+ MatchedPathStep "/test":
4+ PostStep:
5+ OperationResponsesStep:
6+ ResponseCodeStep 200:
7+ InlineStep: ResponseMediaTypeMissing application/x-www-form-urlencoded
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/change/a.yamlnew file+25
@@ -0,0 +1,25 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ content:
22+ "application/json":
23+ schema:
24+ type: "string"
25+components: {}
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/change/b.yamlnew file+25
@@ -0,0 +1,25 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ content:
22+ "application/json":
23+ schema:
24+ type: "number"
25+components: {}
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/change/trace-tree.yamlnew file+10
@@ -0,0 +1,10 @@
1+Left:
2+ OpenApiPathsStep:
3+ MatchedPathStep "/test":
4+ PostStep:
5+ OperationResponsesStep:
6+ ResponseCodeStep 200:
7+ InlineStep:
8+ ResponseMediaObject application/json:
9+ MediaTypeSchema:
10+ InlineStep: NoContradiction
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/del/a.yamlnew file+28
@@ -0,0 +1,28 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ content:
22+ "application/json":
23+ schema:
24+ type: "string"
25+ "application/x-www-form-urlencoded":
26+ schema:
27+ type: "string"
28+components: {}
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/del/b.yamlnew file+25
@@ -0,0 +1,25 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ content:
22+ "application/json":
23+ schema:
24+ type: "string"
25+components: {}
test/golden/common/pathItem/operation/responses/change/mediaTypeObject/del/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/pathItem/operation/responses/del/a.yamlnew file+23
@@ -0,0 +1,23 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+ "500":
22+ description: "Added"
23+components: {}
test/golden/common/pathItem/operation/responses/del/b.yamlnew file+21
@@ -0,0 +1,21 @@
1+openapi: "3.0.0"
2+info:
3+ version: 1.0.0
4+ title: Swagger Petstore
5+ license:
6+ name: MIT
7+servers:
8+ - url: http://petstore.swagger.io/v1
9+paths:
10+ /test:
11+ post:
12+ requestBody:
13+ content:
14+ "application/json" :
15+ schema:
16+ type: "string"
17+ required: false
18+ responses:
19+ "200":
20+ description: "Test"
21+components: {}
test/golden/common/pathItem/operation/responses/del/trace-tree.yamlnew file+1
@@ -0,0 +1 @@
1+Right: []
test/golden/common/property-removed/trace-tree.yaml+2−2
@@ -1,6 +1,6 @@
1Left:
1Left:
2 OpenApiPathsStep:
2 OpenApiPathsStep:
3− PathStep "/test":
3+ MatchedPathStep "/test":
4 PostStep:
4 PostStep:
5 OperationResponsesStep:
5 OperationResponsesStep:
6 ResponseCodeStep 200:
6 ResponseCodeStep 200:
@@ -11,7 +11,7 @@ Left:
11 PropertiesFields: PropertyNowRequired "property2"
11 PropertiesFields: PropertyNowRequired "property2"
12 OperationRequestBodyStep:
12 OperationRequestBodyStep:
13 InlineStep:
13 InlineStep:
14− MediaTypeStep application/json:
14+ RequestMediaTypeObject application/json:
15 MediaTypeSchema:
15 MediaTypeSchema:
16 ReferencedStep (Reference {getReference = "Test"}):
16 ReferencedStep (Reference {getReference = "Test"}):
17 PropertiesFields: UnexpectedProperty "property2"
17 PropertiesFields: UnexpectedProperty "property2"
test/golden/common/property-required/trace-tree.yaml+2−2
@@ -1,10 +1,10 @@
1Left:
1Left:
2 OpenApiPathsStep:
2 OpenApiPathsStep:
3− PathStep "/test":
3+ MatchedPathStep "/test":
4 PostStep:
4 PostStep:
5 OperationRequestBodyStep:
5 OperationRequestBodyStep:
6 InlineStep:
6 InlineStep:
7− MediaTypeStep application/json:
7+ RequestMediaTypeObject application/json:
8 MediaTypeSchema:
8 MediaTypeSchema:
9 ReferencedStep (Reference {getReference = "Test"}):
9 ReferencedStep (Reference {getReference = "Test"}):
10 PropertiesFields: PropertyNowRequired "property2"
10 PropertiesFields: PropertyNowRequired "property2"