@@ -1,5 +1,4 @@
11{-# OPTIONS_GHC -Wno-orphans #-}
2−
32module Data.OpenApi.Compare.Validate.Schema
43 (
54 )
@@ -11,9 +10,9 @@ import Data.Coerce
1110import Data.Foldable (for_, toList)
1211import Data.Functor
1312import Data.HList
14−import Data.List (genericIndex, genericLength, group)
1513import qualified Data.List.NonEmpty as NE
1614import qualified Data.Map as M
15+import Data.List (group, genericLength, genericIndex)
1716import Data.Maybe
1817import Data.OpenApi
1918import Data.OpenApi.Compare.Behavior
@@ -23,24 +22,24 @@ import Data.OpenApi.Compare.Subtree
2322import Data.OpenApi.Compare.Validate.Schema.DNF
2423import Data.OpenApi.Compare.Validate.Schema.Issues
2524import Data.OpenApi.Compare.Validate.Schema.JsonFormula
26−import Data.OpenApi.Compare.Validate.Schema.Partition
27−import Data.OpenApi.Compare.Validate.Schema.Process
2825import Data.OpenApi.Compare.Validate.Schema.Traced
2926import Data.OpenApi.Compare.Validate.Schema.TypedJson
27+import Data.OpenApi.Compare.Validate.Schema.Partition
28+import Data.OpenApi.Compare.Validate.Schema.Process
3029import Data.Ord
3130import Data.Ratio
3231import Data.Semigroup
3332import qualified Data.Set as S
3433import Data.Text (Text)
3534
36−checkFormulas ::
37− (ReassembleHList xs (CheckEnv (Referenced Schema))) =>
38− HList xs ->
39− Behavior 'SchemaLevel ->
40− ProdCons (Trace Schema) ->
41− ProdCons (Traced (Definitions Schema)) ->
42− ProdCons (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel) ->
43− SemanticCompatFormula ()
35+checkFormulas
36+ :: (ReassembleHList xs (CheckEnv (Referenced Schema)))
37+ => HList xs
38+ -> Behavior 'SchemaLevel
39+ -> ProdCons (Trace Schema)
40+ -> ProdCons (Traced (Definitions Schema))
41+ -> ProdCons (ForeachType JsonFormula, P.PathsPrefixTree Behave AnIssue 'SchemaLevel)
42+ -> SemanticCompatFormula ()
4443checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
4544 case P.toList ep ++ P.toList ec of
4645 issues@(_ : _) -> for_ issues $ embedFormula beh . anItem
@@ -145,21 +144,21 @@ checkFormulas env beh trs defs (ProdCons (fp, ep) (fc, ec)) =
145144 EnumDoesntSatisfy $ untypeValue e -- what does this look like when partitioned?
146145 issueFromDisjunct mPart ps = NoMatchingCondition mPart $ SomeCondition <$> S.toList ps
147146
148−checkContradiction ::
149− Behavior 'TypedSchemaLevel ->
150− Maybe Partition ->
151− S.Set (Condition t) ->
152− SemanticCompatFormula ()
147+checkContradiction
148+ :: Behavior 'TypedSchemaLevel
149+ -> Maybe Partition
150+ -> S.Set (Condition t)
151+ -> SemanticCompatFormula ()
153152checkContradiction beh mPart _ = issueAt beh $ maybe TypeBecomesEmpty PartitionBecomesEmpty mPart -- TODO #70
154153
155−checkImplication ::
156− (ReassembleHList xs (CheckEnv (Referenced Schema))) =>
157− HList xs ->
158− Behavior 'TypedSchemaLevel ->
159− ProdCons (Trace Schema) -> -- the traces of the root schemas used in this comparison
160− S.Set (Condition t) ->
161− Condition t ->
162− SemanticCompatFormula ()
154+checkImplication
155+ :: (ReassembleHList xs (CheckEnv (Referenced Schema)))
156+ => HList xs
157+ -> Behavior 'TypedSchemaLevel
158+ -> ProdCons (Trace Schema) -- the traces of the root schemas used in this comparison
159+ -> S.Set (Condition t)
160+ -> Condition t
161+ -> SemanticCompatFormula ()
163162checkImplication env beh trs prods cons = case findExactly prods of
164163 Just e
165164 | all (satisfiesTyped e) prods ->
@@ -167,104 +166,124 @@ checkImplication env beh trs prods cons = case findExactly prods of
167166 then pure ()
168167 else issueAt beh (EnumDoesntSatisfy $ untypeValue e)
169168 | otherwise -> pure () -- vacuously true
169+
170170 Nothing -> case cons of
171171 -- the above code didn't catch it, so there's no Exactly condition on the lhs
172172 Exactly e -> issueAt beh (NoMatchingEnum $ untypeValue e)
173+
173174 Maximum m -> foldCheck min m NoMatchingMaximum MatchingMaximumWeak $ \case
174175 Maximum m' -> Just m'
175176 _ -> Nothing
177+
176178 Minimum m -> foldCheck max m (NoMatchingMinimum . coerce) (MatchingMinimumWeak . coerce) $ \case
177179 Minimum m' -> Just m'
178180 _ -> Nothing
181+
179182 MultipleOf m -> foldCheck lcmScientific m NoMatchingMultipleOf MatchingMultipleOfWeak $ \case
180183 MultipleOf m' -> Just m'
181184 _ -> Nothing
185+
182186 NumberFormat f -> case flip any prods $ \case
183− NumberFormat f' -> f == f'
184− _ -> False of
187+ NumberFormat f' -> f == f'
188+ _ -> False
189+ of
185190 True -> pure ()
186191 False -> issueAt beh (NoMatchingFormat f)
192+
187193 MaxLength m -> foldCheck min m NoMatchingMaxLength MatchingMaxLengthWeak $ \case
188194 MaxLength m' -> Just m'
189195 _ -> Nothing
196+
190197 MinLength m -> foldCheck max m NoMatchingMinLength MatchingMinLengthWeak $ \case
191198 MinLength m' -> Just m'
192199 _ -> Nothing
200+
193201 Pattern p -> case flip any prods $ \case
194− Pattern p' -> p == p'
195− _ -> False of
202+ Pattern p' -> p == p'
203+ _ -> False
204+ of
196205 True -> pure ()
197206 False -> issueAt beh (NoMatchingPattern p) -- TODO: regex comparison #32
207+
198208 StringFormat f -> case flip any prods $ \case
199− StringFormat f' -> f == f'
200− _ -> False of
209+ StringFormat f' -> f == f'
210+ _ -> False
211+ of
201212 True -> pure ()
202213 False -> issueAt beh (NoMatchingFormat f)
214+
203215 Items _ cons' -> case foldSome (<>) prods $ \case
204− Items _ rs -> Just (Just (rs NE.:| []), mempty)
205− TupleItems (map snd -> fs) -> Just (mempty, Just (fs NE.:| []))
206− _ -> Nothing of
216+ Items _ rs -> Just (Just (rs NE.:| []), mempty)
217+ TupleItems (map snd -> fs) -> Just (mempty, Just (fs NE.:| []))
218+ _ -> Nothing
219+ of
207220 Just (mItems, Just pfs)
208221 | not $ allSame (length <$> pfs) -> pure () -- vacuously
209− | let plen = genericLength (NE.head pfs) ->
210− clarifyIssue (AnItem beh (anIssue TupleToArray)) $
211− for_ [0 .. plen - 1] $ \i -> do
212− let prod' = tracedConjunct $ case mItems of
213− Just prods' -> ((`genericIndex` i) <$> pfs) <> prods'
214− Nothing -> (`genericIndex` i) <$> pfs
215− checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prod' cons'
222+ | let plen = genericLength (NE.head pfs)
223+ -> clarifyIssue (AnItem beh (anIssue TupleToArray)) $ for_ [0 .. plen - 1] $ \i -> do
224+ let prod' = tracedConjunct $ case mItems of
225+ Just prods' -> ((`genericIndex` i) <$> pfs) <> prods'
226+ Nothing -> (`genericIndex` i) <$> pfs
227+ checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prod' cons'
216228 Just (Just prods', Nothing) -> do
217229 let prod' = tracedConjunct prods'
218230 checkCompatibility (beh >>> step InItems) env $ ProdCons prod' cons'
219231 _ -> clarifyIssue (AnItem beh (anIssue NoMatchingItems)) $ do
220232 checkCompatibility (beh >>> step InItems) env $ ProdCons prodTopSchema cons'
233+
221234 TupleItems (map snd -> fs) -> case foldSome (<>) prods $ \case
222− TupleItems (map snd -> fs') -> Just (Just $ fs' NE.:| [], Just . Max $ genericLength fs', Just . Min $ genericLength fs', mempty)
223− MinItems m' -> Just (mempty, Just . Max $ m', mempty, mempty)
224− MaxItems m' -> Just (mempty, mempty, Just . Min $ m', mempty)
225− Items _ rs -> Just (mempty, mempty, mempty, Just (rs NE.:| []))
226− _ -> Nothing of
227− -- if the length constraints in the producer are contradictory:
228− Just (_, Just (Max lowest), Just (Min highest), _) | lowest > highest -> pure ()
229− -- We have an explicit tuple items clause...
230− Just (Just pfs, Just (Max plen), _, _)
231− | plen /= genericLength fs -> -- ...of wrong length
232− issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
233− | otherwise ->
234− for_ [0 .. plen - 1] $ \i -> do
235+ TupleItems (map snd -> fs') -> Just (Just $ fs' NE.:| [], Just . Max $ genericLength fs', Just . Min $ genericLength fs', mempty)
236+ MinItems m' -> Just (mempty, Just . Max $ m', mempty, mempty)
237+ MaxItems m' -> Just (mempty, mempty, Just . Min $ m', mempty)
238+ Items _ rs -> Just (mempty, mempty, mempty, Just (rs NE.:| []))
239+ _ -> Nothing
240+ of
241+ -- if the length constraints in the producer are contradictory:
242+ Just (_, Just (Max lowest), Just (Min highest), _) | lowest > highest -> pure ()
243+ -- We have an explicit tuple items clause...
244+ Just (Just pfs, Just (Max plen), _, _)
245+ | plen /= genericLength fs -- ...of wrong length
246+ -> issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
247+ | otherwise
248+ -> for_ [0 .. plen - 1] $ \i -> do
235249 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct $ (`genericIndex` i) <$> pfs) (fs `genericIndex` i)
236− -- We have a fixed length array in the producer...
237− Just (Nothing, Just (Max plen), Just (Min plen'), mProd)
238− | plen == plen' ->
239− clarifyIssue (AnItem beh (anIssue ArrayToTuple)) $ case mProd of
240− _
241− | plen /= genericLength fs -> -- ...of wrong length
242− issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
250+ -- We have a fixed length array in the producer...
251+ Just (Nothing, Just (Max plen), Just (Min plen'), mProd)
252+ | plen == plen'
253+ -> clarifyIssue (AnItem beh (anIssue ArrayToTuple)) $ case mProd of
254+ _ | plen /= genericLength fs -> -- ...of wrong length
255+ issueAt beh (TupleItemsLengthChanged ProdCons {producer = plen, consumer = genericLength fs})
243256 Just rs -> for_ [0 .. plen - 1] $ \i -> do
244257 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons (tracedConjunct rs) (fs `genericIndex` i)
245258 -- ...and no "items" schema
246259 Nothing -> clarifyIssue (AnItem beh (anIssue NoMatchingTupleItems)) $ do
247260 for_ [0 .. plen - 1] $ \i -> do
248261 checkCompatibility (beh >>> step (InItem i)) env $ ProdCons prodTopSchema (fs `genericIndex` i)
249− _ -> issueAt beh NoMatchingTupleItems
262+ _ -> issueAt beh NoMatchingTupleItems
263+
250264 MaxItems m -> foldCheck min m NoMatchingMaxItems MatchingMaxItemsWeak $ \case
251265 MaxItems m' -> Just m'
252266 TupleItems fs -> Just $ toInteger $ length fs
253267 _ -> Nothing
268+
254269 MinItems m -> foldCheck max m NoMatchingMinItems MatchingMinItemsWeak $ \case
255270 MinItems m' -> Just m'
256271 TupleItems fs -> Just $ toInteger $ length fs
257272 _ -> Nothing
273+
258274 UniqueItems -> case flip any prods $ \case
259− UniqueItems -> True
260− MaxItems 1 -> True
261− TupleItems fs | length fs == 1 -> True
262− _ -> False of
275+ UniqueItems -> True
276+ MaxItems 1 -> True
277+ TupleItems fs | length fs == 1 -> True
278+ _ -> False
279+ of
263280 True -> pure ()
264281 False -> issueAt beh NoMatchingUniqueItems
282+
265283 Properties props _ madd -> case foldSome (<>) prods $ \case
266− Properties props' _ madd' -> Just $ (props', madd') NE.:| []
267− _ -> Nothing of
284+ Properties props' _ madd' -> Just $ (props', madd') NE.:| []
285+ _ -> Nothing
286+ of
268287 Just pm ->
269288 anyOfAt beh NoMatchingProperties $ -- TODO: could first "concat" the lists
270289 NE.toList pm <&> \(props', madd') -> do
@@ -295,9 +314,11 @@ checkImplication env beh trs prods cons = case findExactly prods of
295314 (Just add', Just add) -> checkCompatibility (beh >>> step InAdditionalProperty) env (ProdCons add' add)
296315 pure ()
297316 Nothing -> issueAt beh NoMatchingProperties
317+
298318 MaxProperties m -> foldCheck min m NoMatchingMaxProperties MatchingMaxPropertiesWeak $ \case
299319 MaxProperties m' -> Just m'
300320 _ -> Nothing
321+
301322 MinProperties m -> foldCheck max m NoMatchingMinProperties MatchingMinPropertiesWeak $ \case
302323 MinProperties m' -> Just m'
303324 _ -> Nothing
@@ -305,14 +326,14 @@ checkImplication env beh trs prods cons = case findExactly prods of
305326 lcmScientific (toRational -> a) (toRational -> b) =
306327 fromRational $ lcm (numerator a) (numerator b) % gcd (denominator a) (denominator b)
307328
308− foldCheck ::
309− Eq a =>
310− (a -> a -> a) ->
311− a ->
312− (a -> Issue 'TypedSchemaLevel) ->
313− (ProdCons a -> Issue 'TypedSchemaLevel) ->
314− (forall t. Condition t -> Maybe a) ->
315− SemanticCompatFormula ()
329+ foldCheck
330+ :: Eq a
331+ => (a -> a -> a)
332+ -> a
333+ -> (a -> Issue 'TypedSchemaLevel)
334+ -> (ProdCons a -> Issue 'TypedSchemaLevel)
335+ -> (forall t. Condition t -> Maybe a)
336+ -> SemanticCompatFormula ()
316337 foldCheck f m missing weak extr = case foldSome f prods extr of
317338 Just m'
318339 | f m' m == m' -> pure ()