@@ -14,7 +14,11 @@ module OpenAPI.Checker.Validate.Schema
15import Algebra.Lattice
15import Algebra.Lattice
16import Control.Applicative
16import Control.Applicative
17−import Control.Monad.Reader
17+import Control.Arrow
18+import Control.Comonad.Env hiding (env)
19+import Control.Lens hiding (cons)
20+import Control.Monad.Reader hiding (ask)
21+import qualified Control.Monad.Reader as R
18import Control.Monad.Writer
22import Control.Monad.Writer
19import qualified Data.Aeson as A
23import qualified Data.Aeson as A
20import Data.Coerce
24import Data.Coerce
@@ -154,7 +158,7 @@ deriving stock instance Ord (Condition t)
154deriving stock instance Show (Condition t)
158deriving stock instance Show (Condition t)
156data SomeCondition where
160data SomeCondition where
157− SomeCondition :: Typeable t => Condition t -> SomeCondition
161+ SomeCondition :: Typeable t => Traced OpenApi (Condition t) -> SomeCondition
159instance Eq SomeCondition where
163instance Eq SomeCondition where
160 SomeCondition x == SomeCondition y = case cast x of
164 SomeCondition x == SomeCondition y = case cast x of
@@ -200,8 +204,8 @@ isSingleton s
200 | otherwise = Nothing
204 | otherwise = Nothing
202pattern Conjunct :: [Traced r (Condition t)] -> M.Map (Condition t) (Trace r (Condition t))
206pattern Conjunct :: [Traced r (Condition t)] -> M.Map (Condition t) (Trace r (Condition t))
203−pattern Conjunct xs <- (map (uncurry $ flip Traced) . M.toList -> xs)
207+pattern Conjunct xs <- (map (uncurry $ flip traced) . M.toList -> xs)
204− where Conjunct xs = M.fromList [(x, t) | Traced t x <- xs]
208+ where Conjunct xs = M.fromList $ (extract &&& ask) <$> xs
205{-# COMPLETE Conjunct #-}
209{-# COMPLETE Conjunct #-}
207pattern SingleConjunct :: [Traced r (Condition t)] -> JsonFormula r t
211pattern SingleConjunct :: [Traced r (Condition t)] -> JsonFormula r t
@@ -218,19 +222,16 @@ instance BoundedJoinSemiLattice (JsonFormula r t) where
218instance BoundedMeetSemiLattice (JsonFormula r t) where
222instance BoundedMeetSemiLattice (JsonFormula r t) where
219 top = TopFormula
223 top = TopFormula
221−singletonFormula :: Trace r (Condition t) -> Condition t -> JsonFormula r t
222−singletonFormula t x = SingleConjunct [Traced t x]
224foldLattice
225foldLattice
225 :: BoundedLattice l
226 :: BoundedLattice l
226 => (Traced r (Condition t) -> l)
227 => (Traced r (Condition t) -> l)
227 -> JsonFormula r t
228 -> JsonFormula r t
229foldLattice f (DNF xss) = S.foldl' (\z w ->
230foldLattice f (DNF xss) = S.foldl' (\z w ->
230− z \/ M.foldlWithKey' (\x y t -> x /\ f (Traced t y)) top w) bottom xss
231+ z \/ M.foldlWithKey' (\x y t -> x /\ f (traced t y)) top w) bottom xss
232satisfiesFormula :: TypedValue t -> JsonFormula r t -> Bool
233satisfiesFormula :: TypedValue t -> JsonFormula r t -> Bool
233−satisfiesFormula val = foldLattice (satisfiesTyped val . getTraced)
234+satisfiesFormula val = foldLattice (satisfiesTyped val . extract)
235data ForeachType (f :: JsonType -> Type) = ForeachType
236data ForeachType (f :: JsonType -> Type) = ForeachType
236 { forNull :: f 'Null
237 { forNull :: f 'Null
@@ -345,22 +346,52 @@ instance Steppable Schema (Referenced Schema) where
345 = AllOfStep Int
346 = AllOfStep Int
346 | OneOfStep Int
347 | OneOfStep Int
347 | AnyOfStep Int
348 | AnyOfStep Int
348− | ItemsStep
349+ | ItemsObjectStep
350+ | ItemsArrayStep Int
349 | AdditionalPropertiesStep
351 | AdditionalPropertiesStep
350 | PropertiesStep Text
352 | PropertiesStep Text
351 deriving (Eq, Ord, Show)
353 deriving (Eq, Ord, Show)
353type ProcessM = ReaderT (Definitions Schema) (Writer (T.TracePrefixTree SubtreeCheckIssue OpenApi))
355type ProcessM = ReaderT (Definitions Schema) (Writer (T.TracePrefixTree SubtreeCheckIssue OpenApi))
358+ :: (Subtree t, ComonadEnv (Trace OpenApi t) w)
355−warn :: Subtree t => Trace OpenApi t -> CheckIssue t -> ProcessM ()
359+ => w x -> CheckIssue t -> ProcessM ()
356−warn t x = tell $ T.singleton $ AnItem t $ SubtreeCheckIssue x
360+warn t issue = tell $ T.singleton $ AnItem (ask t) $ SubtreeCheckIssue issue
358processRefSchema
362processRefSchema
359 :: Traced OpenApi (Referenced Schema)
363 :: Traced OpenApi (Referenced Schema)
360 -> ProcessM (ForeachType (JsonFormula OpenApi))
364 -> ProcessM (ForeachType (JsonFormula OpenApi))
361processRefSchema x = do
365processRefSchema x = do
362− defs <- ask
366+ defs <- R.ask
363− processSchema $ dereferenceTraced defs x
367+ processSchema $ dereference defs x
369+tracedAllOf :: Traced r Schema -> Maybe [Traced r (Referenced Schema)]
370+tracedAllOf sch = _schemaAllOf (extract sch) <&> \xs ->
371+ [ traced (ask sch >>> step (AllOfStep i)) x | (i, x) <- zip [0..] xs ]
373+tracedAnyOf :: Traced r Schema -> Maybe [Traced r (Referenced Schema)]
374+tracedAnyOf sch = _schemaAnyOf (extract sch) <&> \xs ->
375+ [ traced (ask sch >>> step (AnyOfStep i)) x | (i, x) <- zip [0..] xs ]
377+tracedOneOf :: Traced r Schema -> Maybe [Traced r (Referenced Schema)]
378+tracedOneOf sch = _schemaOneOf (extract sch) <&> \xs ->
379+ [ traced (ask sch >>> step (OneOfStep i)) x | (i, x) <- zip [0..] xs ]
381+tracedItems :: Traced r Schema -> Maybe (Either (Traced r (Referenced Schema)) [Traced r (Referenced Schema)])
382+tracedItems sch = _schemaItems (extract sch) <&> \case
383+ OpenApiItemsObject x -> Left $ traced (ask sch >>> step ItemsObjectStep) x
384+ OpenApiItemsArray xs -> Right
385+ [ traced (ask sch >>> step (ItemsArrayStep i)) x | (i, x) <- zip [0..] xs ]
387+tracedAdditionalProperties :: Traced r Schema -> Maybe (Either Bool (Traced r (Referenced Schema)))
388+tracedAdditionalProperties sch = _schemaAdditionalProperties (extract sch) <&> \case
389+ AdditionalPropertiesAllowed b -> Left b
390+ AdditionalPropertiesSchema x -> Right $ traced (ask sch >>> step AdditionalPropertiesStep) x
392+tracedProperties :: Traced r Schema -> IOHM.InsOrdHashMap Text (Traced r (Referenced Schema))
393+tracedProperties sch = IOHM.mapWithKey (\k -> traced (ask sch >>> step (PropertiesStep k)))
394+ $ _schemaProperties $ extract sch
365-- | Turn a schema into a tuple of 'JsonFormula's that describes the condition
396-- | Turn a schema into a tuple of 'JsonFormula's that describes the condition
366-- for every possible type of a JSON value. The conditions are independent, and
397-- for every possible type of a JSON value. The conditions are independent, and
@@ -368,36 +399,33 @@ processRefSchema x = do
368processSchema
399processSchema
369 :: Traced OpenApi Schema
400 :: Traced OpenApi Schema
370 -> ProcessM (ForeachType (JsonFormula OpenApi))
401 -> ProcessM (ForeachType (JsonFormula OpenApi))
371−processSchema (Traced t Schema{..}) = do
402+processSchema sch@(extract -> Schema{..}) = do
404+ singletonFormula :: Typeable t => Step Schema (Condition t) -> Condition t -> JsonFormula OpenApi t
405+ singletonFormula t f = SingleConjunct [traced (ask sch >>> step t) f]
373− allClauses <- case _schemaAllOf of
407+ allClauses <- case tracedAllOf sch of
374 Nothing -> pure []
408 Nothing -> pure []
375− Just [] -> [] <$ warn t (InvalidSchema "no items in allOf")
409+ Just [] -> [] <$ warn sch (InvalidSchema "no items in allOf")
377− [ processRefSchema (Traced (t `Snoc` AllOfStep i) rs)
410+ Just xs -> mapM processRefSchema xs
378− | (i, rs) <- zip [0..] xs ]
380− anyClause <- case _schemaAnyOf of
412+ anyClause <- case tracedAnyOf sch of
381 Nothing -> pure top
413 Nothing -> pure top
382− Just [] -> bottom <$ warn t (InvalidSchema "no items in anyOf")
414+ Just [] -> bottom <$ warn sch (InvalidSchema "no items in anyOf")
383− Just xs -> joins <$> sequence
384− [ processRefSchema (Traced (t `Snoc` AnyOfStep i) rs)
415+ Just xs -> joins <$> mapM processRefSchema xs
385− | (i, rs) <- zip [0..] xs ]
387− oneClause <- case _schemaOneOf of
417+ oneClause <- case tracedOneOf sch of
388 Nothing -> pure top
418 Nothing -> pure top
389− Just [] -> bottom <$ warn t (InvalidSchema "no items in oneOf")
419+ Just [] -> bottom <$ warn sch (InvalidSchema "no items in oneOf")
390 Just xs -> do
420 Just xs -> do
391 checkOneOfDisjoint xs >>= \case
421 checkOneOfDisjoint xs >>= \case
392 True -> pure ()
422 True -> pure ()
393− False -> warn t (NotSupported "Could not determine that oneOf branches are disjoint")
423+ False -> warn sch (NotSupported "Could not determine that oneOf branches are disjoint")
395− [ processRefSchema (Traced (t `Snoc` OneOfStep i) rs)
424+ joins <$> mapM processRefSchema xs
396− | (i, rs) <- zip [0..] xs ]
398 case _schemaNot of
426 case _schemaNot of
399 Nothing -> pure ()
427 Nothing -> pure ()
400− Just _ -> warn t (NotSupported "not clause is unsupported")
428+ Just _ -> warn sch (NotSupported "not clause is unsupported")
403 typeClause = case _schemaType of
431 typeClause = case _schemaType of
@@ -409,7 +437,7 @@ processSchema (Traced t Schema{..}) = do
409 Just OpenApiNumber -> bottom
437 Just OpenApiNumber -> bottom
410 { forBoolean = top }
438 { forBoolean = top }
411 Just OpenApiInteger -> bottom
439 Just OpenApiInteger -> bottom
412− { forNumber = singletonFormula (t `Snoc` IntegerType) $ MultipleOf 1 }
440+ { forNumber = singletonFormula IntegerType $ MultipleOf 1 }
413 Just OpenApiString -> bottom
441 Just OpenApiString -> bottom
414 { forString = top }
442 { forString = top }
415 Just OpenApiArray -> bottom
443 Just OpenApiArray -> bottom
@@ -419,27 +447,27 @@ processSchema (Traced t Schema{..}) = do
421 valueEnum A.Null = bottom
449 valueEnum A.Null = bottom
422− { forNull = singletonFormula (t `Snoc` EnumField) $ Exactly TNull }
450+ { forNull = singletonFormula EnumField $ Exactly TNull }
423 valueEnum (A.Bool b) = bottom
451 valueEnum (A.Bool b) = bottom
424− { forBoolean = singletonFormula (t `Snoc` EnumField) $ Exactly $ TBool b }
452+ { forBoolean = singletonFormula EnumField $ Exactly $ TBool b }
425 valueEnum (A.Number n) = bottom
453 valueEnum (A.Number n) = bottom
426− { forNumber = singletonFormula (t `Snoc` EnumField) $ Exactly $ TNumber n }
454+ { forNumber = singletonFormula EnumField $ Exactly $ TNumber n }
427 valueEnum (A.String s) = bottom
455 valueEnum (A.String s) = bottom
428− { forString = singletonFormula (t `Snoc` EnumField) $ Exactly $ TString s }
456+ { forString = singletonFormula EnumField $ Exactly $ TString s }
429 valueEnum (A.Array a) = bottom
457 valueEnum (A.Array a) = bottom
430− { forArray = singletonFormula (t `Snoc` EnumField) $ Exactly $ TArray a }
458+ { forArray = singletonFormula EnumField $ Exactly $ TArray a }
431 valueEnum (A.Object o) = bottom
459 valueEnum (A.Object o) = bottom
432− { forObject = singletonFormula (t `Snoc` EnumField) $ Exactly $ TObject o }
460+ { forObject = singletonFormula EnumField $ Exactly $ TObject o }
433 enumClause <- case _schemaEnum of
461 enumClause <- case _schemaEnum of
434 Nothing -> pure top
462 Nothing -> pure top
435− Just [] -> bottom <$ warn t (InvalidSchema "no items in enum")
463+ Just [] -> bottom <$ warn sch (InvalidSchema "no items in enum")
436 Just xs -> pure $ joins (valueEnum <$> xs)
464 Just xs -> pure $ joins (valueEnum <$> xs)
439 maximumClause = case _schemaMaximum of
467 maximumClause = case _schemaMaximum of
440 Nothing -> top
468 Nothing -> top
441 Just n -> top
469 Just n -> top
442− { forNumber = singletonFormula (t `Snoc` MaximumFields) $ Maximum $
470+ { forNumber = singletonFormula MaximumFields $ Maximum $
443 case _schemaExclusiveMaximum of
471 case _schemaExclusiveMaximum of
444 Just True -> Exclusive n
472 Just True -> Exclusive n
445 _ -> Inclusive n }
473 _ -> Inclusive n }
@@ -447,7 +475,7 @@ processSchema (Traced t Schema{..}) = do
447 minimumClause = case _schemaMinimum of
475 minimumClause = case _schemaMinimum of
448 Nothing -> top
476 Nothing -> top
449 Just n -> top
477 Just n -> top
450− { forNumber = singletonFormula (t `Snoc` MinimumFields) $ Minimum $ Down $
478+ { forNumber = singletonFormula MinimumFields $ Minimum $ Down $
451 case _schemaExclusiveMinimum of
479 case _schemaExclusiveMinimum of
452 Just True -> Exclusive $ Down n
480 Just True -> Exclusive $ Down n
453 _ -> Inclusive $ Down n }
481 _ -> Inclusive $ Down n }
@@ -455,72 +483,68 @@ processSchema (Traced t Schema{..}) = do
455 multipleOfClause = case _schemaMultipleOf of
483 multipleOfClause = case _schemaMultipleOf of
456 Nothing -> top
484 Nothing -> top
457 Just n -> top
485 Just n -> top
458− { forNumber = singletonFormula (t `Snoc` MultipleOfField) $ MultipleOf n }
486+ { forNumber = singletonFormula MultipleOfField $ MultipleOf n }
460 formatClause <- case _schemaFormat of
488 formatClause <- case _schemaFormat of
461 Nothing -> pure top
489 Nothing -> pure top
462 Just f | f `elem` ["int32", "int64", "float", "double"] -> pure top
490 Just f | f `elem` ["int32", "int64", "float", "double"] -> pure top
463− { forNumber = singletonFormula (t `Snoc` FormatField) $ NumberFormat f }
491+ { forNumber = singletonFormula FormatField $ NumberFormat f }
464 Just f | f `elem` ["byte", "binary", "date", "date-time", "password"] -> pure top
492 Just f | f `elem` ["byte", "binary", "date", "date-time", "password"] -> pure top
465− { forString = singletonFormula (t `Snoc` FormatField) $ StringFormat f }
493+ { forString = singletonFormula FormatField $ StringFormat f }
466− Just f -> top <$ warn t (NotSupported $ "Unknown format: " <> f)
494+ Just f -> top <$ warn sch (NotSupported $ "Unknown format: " <> f)
469 maxLengthClause = case _schemaMaxLength of
497 maxLengthClause = case _schemaMaxLength of
470 Nothing -> top
498 Nothing -> top
471 Just n -> top
499 Just n -> top
472− { forString = singletonFormula (t `Snoc` MaxLengthField) $ MaxLength n }
500+ { forString = singletonFormula MaxLengthField $ MaxLength n }
474 minLengthClause = case _schemaMinLength of
502 minLengthClause = case _schemaMinLength of
475 Nothing -> top
503 Nothing -> top
476 Just n -> top
504 Just n -> top
477− { forString = singletonFormula (t `Snoc` MinLengthField) $ MinLength n }
505+ { forString = singletonFormula MinLengthField $ MinLength n }
479 patternClause = case _schemaPattern of
507 patternClause = case _schemaPattern of
480 Nothing -> top
508 Nothing -> top
481 Just p -> top
509 Just p -> top
482− { forString = singletonFormula (t `Snoc` PatternField) $ Pattern p }
510+ { forString = singletonFormula PatternField $ Pattern p }
484− itemsClause <- case _schemaItems of
512+ itemsClause <- case tracedItems sch of
485 Nothing -> pure top
513 Nothing -> pure top
486− Just (OpenApiItemsObject rs) -> do
514+ Just (Left rs) -> do
487− let trs = Traced (t `Snoc` ItemsStep) rs
488− f <- processRefSchema trs
515+ f <- processRefSchema rs
489− pure top { forArray = singletonFormula (t `Snoc` ItemsField) $ Items f trs }
516+ pure top { forArray = singletonFormula ItemsField $ Items f rs }
490− Just (OpenApiItemsArray _) -> top <$ warn t (NotSupported "array in items is not supported")
517+ Just (Right _) -> top <$ warn sch (NotSupported "array in items is not supported")
493 maxItemsClause = case _schemaMaxItems of
520 maxItemsClause = case _schemaMaxItems of
494 Nothing -> top
521 Nothing -> top
495 Just n -> top
522 Just n -> top
496− { forArray = singletonFormula (t `Snoc` MaxItemsField) $ MaxItems n }
523+ { forArray = singletonFormula MaxItemsField $ MaxItems n }
498 minItemsClause = case _schemaMinItems of
525 minItemsClause = case _schemaMinItems of
499 Nothing -> top
526 Nothing -> top
500 Just n -> top
527 Just n -> top
501− { forArray = singletonFormula (t `Snoc` MinItemsField) $ MinItems n }
528+ { forArray = singletonFormula MinItemsField $ MinItems n }
503 uniqueItemsClause = case _schemaUniqueItems of
530 uniqueItemsClause = case _schemaUniqueItems of
504 Just True -> top
531 Just True -> top
505− { forArray = singletonFormula (t `Snoc` UniqueItemsField) UniqueItems }
532+ { forArray = singletonFormula UniqueItemsField UniqueItems }
508− (addProps, addPropSchema) <- case _schemaAdditionalProperties of
535+ (addProps, addPropSchema) <- case tracedAdditionalProperties sch of
509− Just (AdditionalPropertiesSchema rs) -> do
510− let trs = Traced (t `Snoc` AdditionalPropertiesStep) rs
511− (,Just trs) <$> processRefSchema trs
536+ Just (Right rs) -> (,Just rs) <$> processRefSchema rs
512− Just (AdditionalPropertiesAllowed False) -> pure (bottom, Nothing)
537+ Just (Left False) -> pure (bottom, Nothing)
513− _ -> pure (top, Just $ Traced (t `Snoc` AdditionalPropertiesStep) $ Inline mempty)
538+ _ -> pure (top, Just $ traced (ask sch `Snoc` AdditionalPropertiesStep) $ Inline mempty)
514 propList <- forM (S.toList . S.fromList $ IOHM.keys _schemaProperties <> _schemaRequired) $ \k -> do
539 propList <- forM (S.toList . S.fromList $ IOHM.keys _schemaProperties <> _schemaRequired) $ \k -> do
515− (f, sch) <- case IOHM.lookup k _schemaProperties of
540+ (f, psch) <- case IOHM.lookup k $ tracedProperties sch of
517− let trs = Traced (t `Snoc` PropertiesStep k) rs
518− (,trs) <$> processRefSchema trs
541+ Just rs -> (,rs) <$> processRefSchema rs
519− Nothing -> pure (addProps, fromMaybe (Traced (t `Snoc` AdditionalPropertiesStep) $ Inline mempty) addPropSchema)
542+ Nothing -> let fakeSchema = traced (ask sch `Snoc` AdditionalPropertiesStep) $ Inline mempty
520 -- The mempty here is incorrect, but if addPropSchema was Nothing, then
543 -- The mempty here is incorrect, but if addPropSchema was Nothing, then
521 -- addProps is bottom, and k is in _schemaRequired. We handle this situation
544 -- addProps is bottom, and k is in _schemaRequired. We handle this situation
522 -- below and short-circuit the entire Properties condition to bottom
545 -- below and short-circuit the entire Properties condition to bottom
546+ in pure (addProps, fromMaybe fakeSchema addPropSchema)
523− pure (k, Property (k `elem` _schemaRequired) f sch)
547+ pure (k, Property (k `elem` _schemaRequired) f psch)
525 allBottom f = getAll $ foldType $ \ty -> case ty f of
549 allBottom f = getAll $ foldType $ \ty -> case ty f of
526 BottomFormula -> All True
550 BottomFormula -> All True
@@ -537,21 +561,21 @@ processSchema (Traced t Schema{..}) = do
537 = top -- if all fields are optional and have trivial schemata
561 = top -- if all fields are optional and have trivial schemata
538 | otherwise
562 | otherwise
540− { forObject = singletonFormula (t `Snoc` PropertiesFields) $ Properties propMap addProps addPropSchema }
564+ { forObject = singletonFormula PropertiesFields $ Properties propMap addProps addPropSchema }
542 maxPropertiesClause = case _schemaMaxProperties of
566 maxPropertiesClause = case _schemaMaxProperties of
543 Nothing -> top
567 Nothing -> top
544 Just n -> top
568 Just n -> top
545− { forObject = singletonFormula (t `Snoc` MaxPropertiesField) $ MaxProperties n }
569+ { forObject = singletonFormula MaxPropertiesField $ MaxProperties n }
547 minPropertiesClause = case _schemaMinProperties of
571 minPropertiesClause = case _schemaMinProperties of
548 Nothing -> top
572 Nothing -> top
549 Just n -> top
573 Just n -> top
550− { forObject = singletonFormula (t `Snoc` MinPropertiesField) $ MinProperties n }
574+ { forObject = singletonFormula MinPropertiesField $ MinProperties n }
552 nullableClause
576 nullableClause
553 | Just True <- _schemaNullable = bottom
577 | Just True <- _schemaNullable = bottom
554− { forNull = singletonFormula (t `Snoc` NullableField) $ Exactly TNull }
578+ { forNull = singletonFormula NullableField $ Exactly TNull }
555 | otherwise = bottom
579 | otherwise = bottom
557 pure $ nullableClause \/ meets (allClauses <>
581 pure $ nullableClause \/ meets (allClauses <>
@@ -561,7 +585,7 @@ processSchema (Traced t Schema{..}) = do
561 , uniqueItemsClause, propertiesClause, maxPropertiesClause, minPropertiesClause])
585 , uniqueItemsClause, propertiesClause, maxPropertiesClause, minPropertiesClause])
562{- TODO: ReadOnly/WriteOnly -}
586{- TODO: ReadOnly/WriteOnly -}
564−checkOneOfDisjoint :: [Referenced Schema] -> ProcessM Bool
588+checkOneOfDisjoint :: [Traced OpenApi (Referenced Schema)] -> ProcessM Bool
565checkOneOfDisjoint = const $ pure True -- TODO
589checkOneOfDisjoint = const $ pure True -- TODO
567schemaToFormula
591schemaToFormula
@@ -575,7 +599,7 @@ checkFormulas
575 => HList xs
599 => HList xs
576 -> Trace OpenApi Schema
600 -> Trace OpenApi Schema
577 -> ProdCons (ForeachType (JsonFormula OpenApi), T.TracePrefixTree SubtreeCheckIssue OpenApi)
601 -> ProdCons (ForeachType (JsonFormula OpenApi), T.TracePrefixTree SubtreeCheckIssue OpenApi)
578− -> CompatFormula Schema ()
602+ -> CompatFormula ()
579checkFormulas env tr (ProdCons (fp, ep) (fc, ec)) =
603checkFormulas env tr (ProdCons (fp, ep) (fc, ec)) =
580 case T.toList ep ++ T.toList ec of
604 case T.toList ep ++ T.toList ec of
581 issues@(_:_) -> F.for_ issues $ \(AnItem t (SubtreeCheckIssue e)) -> issueAtTrace t e
605 issues@(_:_) -> F.for_ issues $ \(AnItem t (SubtreeCheckIssue e)) -> issueAtTrace t e
@@ -613,13 +637,13 @@ checkFormulas env tr (ProdCons (fp, ep) (fc, ec)) =
613 (DNF pss, SingleConjunct cs) -> F.for_ pss $ \(Conjunct ps) -> do
637 (DNF pss, SingleConjunct cs) -> F.for_ pss $ \(Conjunct ps) -> do
614 F.for_ cs $ checkImplication env ps -- avoid disjuntion if there's only one conjunct
638 F.for_ cs $ checkImplication env ps -- avoid disjuntion if there's only one conjunct
615 (DNF pss, DNF css) -> F.for_ pss $ \(Conjunct ps) -> do
639 (DNF pss, DNF css) -> F.for_ pss $ \(Conjunct ps) -> do
616− anyOfM tr (SubtreeCheckIssue $ NoMatchingCondition $ SomeCondition . getTraced <$> ps)
640+ anyOfM tr (NoMatchingCondition $ SomeCondition <$> ps)
617 [F.for_ cs $ checkImplication env ps | Conjunct cs <- S.toList css]
641 [F.for_ cs $ checkImplication env ps | Conjunct cs <- S.toList css]
619checkContradiction
643checkContradiction
620 :: Trace OpenApi Schema
644 :: Trace OpenApi Schema
621 -> [Traced OpenApi (Condition t)]
645 -> [Traced OpenApi (Condition t)]
622− -> CompatFormula s ()
646+ -> CompatFormula ()
623checkContradiction tr _ = issueAtTrace tr NoContradiction -- TODO
647checkContradiction tr _ = issueAtTrace tr NoContradiction -- TODO
625checkImplication
649checkImplication
@@ -627,96 +651,93 @@ checkImplication
627 => HList xs
651 => HList xs
628 -> [Traced OpenApi (Condition t)]
652 -> [Traced OpenApi (Condition t)]
629 -> Traced OpenApi (Condition t)
653 -> Traced OpenApi (Condition t)
630− -> CompatFormula s ()
654+ -> CompatFormula ()
631−checkImplication env prods (Traced t cons) = case findExactly prods of
655+checkImplication env prods cons = case findExactly prods of
633− | all (satisfiesTyped e) (getTraced <$> prods) ->
657+ | all (satisfiesTyped e) (extract <$> prods) ->
634− if satisfiesTyped e cons then pure ()
658+ if satisfiesTyped e $ extract cons then pure ()
635− else issueAtTrace t (EnumDoesntSatisfy e)
659+ else issueAt cons (EnumDoesntSatisfy e)
636 | otherwise -> pure () -- vacuously true
660 | otherwise -> pure () -- vacuously true
637− Nothing -> case cons of
661+ Nothing -> case extract cons of
638 -- the above code didn't catch it, so there's no Exactly condition on the lhs
662 -- the above code didn't catch it, so there's no Exactly condition on the lhs
639− Exactly e -> issueAtTrace t (NoMatchingEnum e)
663+ Exactly e -> issueAt cons (NoMatchingEnum e)
640 Maximum m -> case findRelevant min (\case Maximum m' -> Just m'; _ -> Nothing) prods of
664 Maximum m -> case findRelevant min (\case Maximum m' -> Just m'; _ -> Nothing) prods of
641 Just m' -> if m' <= m then pure ()
665 Just m' -> if m' <= m then pure ()
642− else issueAtTrace t (MatchingMaximumWeak m m')
666+ else issueAt cons (MatchingMaximumWeak m m')
643− Nothing -> issueAtTrace t (NoMatchingMaximum m)
667+ Nothing -> issueAt cons (NoMatchingMaximum m)
644 Minimum m -> case findRelevant max (\case Minimum m' -> Just m'; _ -> Nothing) prods of
668 Minimum m -> case findRelevant max (\case Minimum m' -> Just m'; _ -> Nothing) prods of
645 Just m' -> if m' >= m then pure ()
669 Just m' -> if m' >= m then pure ()
646− else issueAtTrace t (MatchingMinimumWeak (coerce m) (coerce m'))
670+ else issueAt cons (MatchingMinimumWeak (coerce m) (coerce m'))
647− Nothing -> issueAtTrace t (NoMatchingMinimum (coerce m))
671+ Nothing -> issueAt cons (NoMatchingMinimum (coerce m))
648 MultipleOf m -> case findRelevant lcmScientific (\case MultipleOf m' -> Just m'; _ -> Nothing) prods of
672 MultipleOf m -> case findRelevant lcmScientific (\case MultipleOf m' -> Just m'; _ -> Nothing) prods of
649 Just m' -> if lcmScientific m m' == m' then pure ()
673 Just m' -> if lcmScientific m m' == m' then pure ()
650− else issueAtTrace t (MatchingMultipleOfWeak m m')
674+ else issueAt cons (MatchingMultipleOfWeak m m')
651− Nothing -> issueAtTrace t (NoMatchingMultipleOf m)
675+ Nothing -> issueAt cons (NoMatchingMultipleOf m)
652− NumberFormat f -> if any (\case NumberFormat f' -> f == f'; _ -> False) $ getTraced <$> prods
676+ NumberFormat f -> if any (\case NumberFormat f' -> f == f'; _ -> False) $ extract <$> prods
653− then pure () else issueAtTrace t (NoMatchingFormat f)
677+ then pure () else issueAt cons (NoMatchingFormat f)
654 MaxLength m -> case findRelevant min (\case MaxLength m' -> Just m'; _ -> Nothing) prods of
678 MaxLength m -> case findRelevant min (\case MaxLength m' -> Just m'; _ -> Nothing) prods of
655 Just m' -> if m' <= m then pure ()
679 Just m' -> if m' <= m then pure ()
656− else issueAtTrace t (MatchingMaxLengthWeak m m')
680+ else issueAt cons (MatchingMaxLengthWeak m m')
657− Nothing -> issueAtTrace t (NoMatchingMaxLength m)
681+ Nothing -> issueAt cons (NoMatchingMaxLength m)
658 MinLength m -> case findRelevant max (\case MinLength m' -> Just m'; _ -> Nothing) prods of
682 MinLength m -> case findRelevant max (\case MinLength m' -> Just m'; _ -> Nothing) prods of
659 Just m' -> if m' >= m then pure ()
683 Just m' -> if m' >= m then pure ()
660− else issueAtTrace t (MatchingMinLengthWeak m m')
684+ else issueAt cons (MatchingMinLengthWeak m m')
661− Nothing -> issueAtTrace t (NoMatchingMinLength m)
685+ Nothing -> issueAt cons (NoMatchingMinLength m)
662− Pattern p -> if any (\case Pattern p' -> p == p'; _ -> False) $ getTraced <$> prods
686+ Pattern p -> if any (\case Pattern p' -> p == p'; _ -> False) $ extract <$> prods
663− then pure () else issueAtTrace t (NoMatchingPattern p)
687+ then pure () else issueAt cons (NoMatchingPattern p)
664− StringFormat f -> if any (\case StringFormat f' -> f == f'; _ -> False) $ getTraced <$> prods
688+ StringFormat f -> if any (\case StringFormat f' -> f == f'; _ -> False) $ extract <$> prods
665− then pure () else issueAtTrace t (NoMatchingFormat f)
689+ then pure () else issueAt cons (NoMatchingFormat f)
666− Items _ (Traced t' cons') -> case findRelevant (<>) (\case Items _ rs -> Just (rs NE.:| []); _ -> Nothing) prods of
690+ Items _ cons' -> case findRelevant (<>) (\case Items _ rs -> Just (rs NE.:| []); _ -> Nothing) prods of
667− Just (rs NE.:| []) -> localTrace' (ProdCons (getTrace rs) t') $ checkCompatibility env $ ProdCons (getTraced rs) cons'
691+ Just (rs NE.:| []) -> checkCompatibility env $ ProdCons rs cons'
668 Just rs -> do
692 Just rs -> do
669− let sch = Inline mempty { _schemaAllOf = Just . NE.toList $ getTraced <$> rs }
693+ let sch = Inline mempty { _schemaAllOf = Just . NE.toList $ extract <$> rs }
670− localTrace' (pure t' {- TODO: what? -}) $ checkCompatibility env $ ProdCons sch cons'
694+ checkCompatibility env $ ProdCons (traced (ask $ NE.head rs) sch) cons' -- TODO: bad trace
671− Nothing -> issueAtTrace t NoMatchingItems
695+ Nothing -> issueAt cons NoMatchingItems
672 MaxItems m -> case findRelevant min (\case MaxItems m' -> Just m'; _ -> Nothing) prods of
696 MaxItems m -> case findRelevant min (\case MaxItems m' -> Just m'; _ -> Nothing) prods of
673 Just m' -> if m' <= m then pure ()
697 Just m' -> if m' <= m then pure ()
674− else issueAtTrace t (MatchingMaxItemsWeak m m')
698+ else issueAt cons (MatchingMaxItemsWeak m m')
675− Nothing -> issueAtTrace t (NoMatchingMaxItems m)
699+ Nothing -> issueAt cons (NoMatchingMaxItems m)
676 MinItems m -> case findRelevant max (\case MinItems m' -> Just m'; _ -> Nothing) prods of
700 MinItems m -> case findRelevant max (\case MinItems m' -> Just m'; _ -> Nothing) prods of
677 Just m' -> if m' >= m then pure ()
701 Just m' -> if m' >= m then pure ()
678− else issueAtTrace t (MatchingMinItemsWeak m m')
702+ else issueAt cons (MatchingMinItemsWeak m m')
679− Nothing -> issueAtTrace t (NoMatchingMinItems m)
703+ Nothing -> issueAt cons (NoMatchingMinItems m)
680− UniqueItems -> if any ((== UniqueItems) . getTraced) prods then pure ()
704+ UniqueItems -> if any (== UniqueItems) $ extract <$> prods then pure ()
681− else issueAtTrace t NoMatchingUniqueItems
705+ else issueAt cons NoMatchingUniqueItems
682 Properties props _ madd -> case findRelevant (<>) (\case Properties props' _ madd' -> Just $ (props', madd') NE.:| []; _ -> Nothing) prods of
706 Properties props _ madd -> case findRelevant (<>) (\case Properties props' _ madd' -> Just $ (props', madd') NE.:| []; _ -> Nothing) prods of
683 Just ((props', madd') NE.:| []) -> do
707 Just ((props', madd') NE.:| []) -> do
684 F.for_ (S.fromList $ M.keys props <> M.keys props') $ \k -> do
708 F.for_ (S.fromList $ M.keys props <> M.keys props') $ \k -> do
686− go sch' sch = let schs = ProdCons sch' sch
709+ let go sch sch' = checkCompatibility env (ProdCons sch sch')
687− in localTrace' (getTrace <$> schs) $ checkCompatibility env $ getTraced <$> schs
688 case (M.lookup k props', madd', M.lookup k props, madd) of
710 case (M.lookup k props', madd', M.lookup k props, madd) of
689 (Nothing, Nothing, _, _) -> pure () -- vacuously
711 (Nothing, Nothing, _, _) -> pure () -- vacuously
690− (_, _, Nothing, Nothing) -> issueAtTrace t (UnexpectedProperty k)
712+ (_, _, Nothing, Nothing) -> issueAt cons (UnexpectedProperty k)
691 (Just p', _, Just p, _) -> go (propRefSchema p') (propRefSchema p)
713 (Just p', _, Just p, _) -> go (propRefSchema p') (propRefSchema p)
692 (Nothing, Just add', Just p, _) -> go add' (propRefSchema p)
714 (Nothing, Just add', Just p, _) -> go add' (propRefSchema p)
693 (Just p', _, Nothing, Just add) -> go (propRefSchema p') add
715 (Just p', _, Nothing, Just add) -> go (propRefSchema p') add
694 (Nothing, Just _, Nothing, Just _) -> pure ()
716 (Nothing, Just _, Nothing, Just _) -> pure ()
695 case (maybe False propRequired $ M.lookup k props', maybe False propRequired $ M.lookup k props) of
717 case (maybe False propRequired $ M.lookup k props', maybe False propRequired $ M.lookup k props) of
696− (False, True) -> issueAtTrace t (PropertyNowRequired k)
718+ (False, True) -> issueAt cons (PropertyNowRequired k)
697 _ -> pure ()
719 _ -> pure ()
699 case (madd', madd) of
721 case (madd', madd) of
700 (Nothing, _) -> pure () -- vacuously
722 (Nothing, _) -> pure () -- vacuously
701− (_, Nothing) -> issueAtTrace t NoAdditionalProperties
723+ (_, Nothing) -> issueAt cons NoAdditionalProperties
702− (Just add', Just add) -> let schs = ProdCons add' add
724+ (Just add', Just add) -> checkCompatibility env (ProdCons add' add)
703− in localTrace' (getTrace <$> schs) $ checkCompatibility env $ getTraced <$> schs
705− Nothing -> issueAtTrace t NoMatchingProperties
726+ Nothing -> issueAt cons NoMatchingProperties
706 MaxProperties m -> case findRelevant min (\case MaxProperties m' -> Just m'; _ -> Nothing) prods of
727 MaxProperties m -> case findRelevant min (\case MaxProperties m' -> Just m'; _ -> Nothing) prods of
707 Just m' -> if m' <= m then pure ()
728 Just m' -> if m' <= m then pure ()
708− else issueAtTrace t (MatchingMaxPropertiesWeak m m')
729+ else issueAt cons (MatchingMaxPropertiesWeak m m')
709− Nothing -> issueAtTrace t (NoMatchingMaxProperties m)
730+ Nothing -> issueAt cons (NoMatchingMaxProperties m)
710 MinProperties m -> case findRelevant max (\case MinProperties m' -> Just m'; _ -> Nothing) prods of
731 MinProperties m -> case findRelevant max (\case MinProperties m' -> Just m'; _ -> Nothing) prods of
711 Just m' -> if m' >= m then pure ()
732 Just m' -> if m' >= m then pure ()
712− else issueAtTrace t (MatchingMinPropertiesWeak m m')
733+ else issueAt cons (MatchingMinPropertiesWeak m m')
713− Nothing -> issueAtTrace t (NoMatchingMinProperties m)
734+ Nothing -> issueAt cons (NoMatchingMinProperties m)
715− findExactly (Traced _ (Exactly x):_) = Just x
736+ findExactly ((extract -> Exactly x):_) = Just x
716 findExactly (_:xs) = findExactly xs
737 findExactly (_:xs) = findExactly xs
717 findExactly [] = Nothing
738 findExactly [] = Nothing
718− findRelevant combine extract
739+ findRelevant combine extr
719− = fmap (foldr1 combine) . NE.nonEmpty . mapMaybe (extract . getTraced)
740+ = fmap (foldr1 combine) . NE.nonEmpty . mapMaybe (extr . extract)
720 lcmScientific (toRational -> a) (toRational -> b)
741 lcmScientific (toRational -> a) (toRational -> b)
721 = fromRational $ lcm (numerator a) (numerator b) % gcd (denominator a) (denominator b)
742 = fromRational $ lcm (numerator a) (numerator b) % gcd (denominator a) (denominator b)
@@ -753,9 +774,7 @@ instance Typeable t => Subtree (Condition t) where
753 deriving stock (Eq, Ord, Show)
774 deriving stock (Eq, Ord, Show)
754 type CheckEnv (Condition t) = CheckEnv Schema
775 type CheckEnv (Condition t) = CheckEnv Schema
755 normalizeTrace = undefined
776 normalizeTrace = undefined
756− checkCompatibility env conds = withTrace $ \traces -> do
777+ checkCompatibility env pc = checkImplication env [producer pc] (consumer pc)
757− case Traced <$> traces <*> conds of
758− ProdCons prod cons -> checkImplication env [prod] cons
760instance Subtree Schema where
779instance Subtree Schema where
761 data CheckIssue Schema
780 data CheckIssue Schema
@@ -765,18 +784,16 @@ instance Subtree Schema where
765 | NoContradiction
784 | NoContradiction
766 deriving stock (Eq, Ord, Show)
785 deriving stock (Eq, Ord, Show)
767 type CheckEnv Schema = '[ProdCons (Definitions Schema)]
786 type CheckEnv Schema = '[ProdCons (Definitions Schema)]
768− checkCompatibility env schs = withTrace $ \traces -> do
787+ checkCompatibility env schs = do
769 let defs = getH env
788 let defs = getH env
770− checkFormulas env (producer traces) $ schemaToFormula <$> defs <*> (Traced <$> traces <*> schs)
789+ checkFormulas env (ask $ producer schs) $ schemaToFormula <$> defs <*> schs
772instance Subtree (Referenced Schema) where
791instance Subtree (Referenced Schema) where
773 data CheckIssue (Referenced Schema)
792 data CheckIssue (Referenced Schema)
774 deriving stock (Eq, Ord, Show)
793 deriving stock (Eq, Ord, Show)
775 type CheckEnv (Referenced Schema) = CheckEnv Schema
794 type CheckEnv (Referenced Schema) = CheckEnv Schema
776− checkCompatibility env refs = withTrace $ \traces -> do
795+ checkCompatibility env refs = do
778 defs = getH env
797 defs = getH env
779 schs = dereference <$> defs <*> refs
798 schs = dereference <$> defs <*> refs
780− schs' = retrace <$> traces <*> schs
781− localTrace (getTrace <$> schs) $ do
782− checkFormulas env (producer $ getTrace <$> schs') $ schemaToFormula <$> defs <*> schs'
799+ checkFormulas env (ask $ producer schs) $ schemaToFormula <$> defs <*> schs