@@ -843,10 +843,18 @@ checkFormulas env beh (ProdCons (fp, ep) (fc, ec)) =
843 -- (⋃_i ⋂_j A[i,j]) ⊂ ∅ = /\_i (⋂_j A[i,j]) ⊂ ∅
843 -- (⋃_i ⋂_j A[i,j]) ⊂ ∅ = /\_i (⋂_j A[i,j]) ⊂ ∅
844 -- where we again delegate (⋂_j A[j]) ⊂ ∅ to a heuristic, though here the
844 -- where we again delegate (⋂_j A[j]) ⊂ ∅ to a heuristic, though here the
845 -- shortcut of \/_j A[j] ⊂ ∅ hardly helps.
845 -- shortcut of \/_j A[j] ⊂ ∅ hardly helps.
846+ let typesRestricted = not (anyBottomTypes fp) && anyBottomTypes fc
847+ -- Specifically handle the case when a schema's type has been
848+ -- restricted from "all" to specific types: if all types were allowed
849+ -- in the producer and not all types are allowed in the consumer, it's
850+ -- usually easier to say what's left than what's removed
851+ when typesRestricted $ issueAt beh $ TypesRestricted $ nonBottomTypes fc
846 forType_ $ \tyName ty -> do
852 forType_ $ \tyName ty -> do
847 let beh' = beh >>> step (OfType tyName)
853 let beh' = beh >>> step (OfType tyName)
848 case (ty fp, ty fc) of
854 case (ty fp, ty fc) of
855+ (DNF pss, BottomFormula) -> unless typesRestricted $ do
856+ -- don't repeat the TypesRestricted issue
849− (DNF pss, BottomFormula) -> F.for_ pss $ \(Conjunct ps) -> checkContradiction beh' ps
857+ F.for_ pss $ \(Conjunct ps) -> checkContradiction beh' ps
850 (DNF pss, SingleConjunct cs) -> F.for_ pss $ \(Conjunct ps) -> do
858 (DNF pss, SingleConjunct cs) -> F.for_ pss $ \(Conjunct ps) -> do
851 F.for_ cs $ checkImplication env beh' ps -- avoid disjunction if there's only one conjunct
859 F.for_ cs $ checkImplication env beh' ps -- avoid disjunction if there's only one conjunct
852 (TopFormula, DNF css) ->
860 (TopFormula, DNF css) ->
@@ -859,7 +867,15 @@ checkFormulas env beh (ProdCons (fp, ep) (fc, ec)) =
860 (issueFromConjunct ps)
868 (issueFromConjunct ps)
861 [F.for_ cs $ checkImplication env beh' ps | Conjunct cs <- S.toList css]
869 [F.for_ cs $ checkImplication env beh' ps | Conjunct cs <- S.toList css]
872+ anyBottomTypes f = getAny $
873+ foldType $ \_ ty -> case ty f of
874+ BottomFormula -> Any True
876+ nonBottomTypes f = foldType $ \tyName ty -> case ty f of
877+ BottomFormula -> mempty
863 issueFromConjunct :: Typeable t => [Condition t] -> Issue 'TypedSchemaLevel
879 issueFromConjunct :: Typeable t => [Condition t] -> Issue 'TypedSchemaLevel
864 issueFromConjunct ps
880 issueFromConjunct ps
865 | Just e <- findExactly ps
881 | Just e <- findExactly ps
@@ -959,20 +975,26 @@ checkImplication env beh prods cons = case findExactly prods of
959 anyOfAt beh NoMatchingProperties $
975 anyOfAt beh NoMatchingProperties $
960 NE.toList pm <&> \(props', madd') -> do
976 NE.toList pm <&> \(props', madd') -> do
961 F.for_ (S.fromList $ M.keys props <> M.keys props') $ \k -> do
977 F.for_ (S.fromList $ M.keys props <> M.keys props') $ \k -> do
978+ let beh' = beh >>> step (InProperty k)
979+ go sch sch' = checkCompatibility beh' env (ProdCons sch sch')
962 case (maybe False propRequired $ M.lookup k props', maybe False propRequired $ M.lookup k props) of
980 case (maybe False propRequired $ M.lookup k props', maybe False propRequired $ M.lookup k props) of
963 -- producer does not require field, but consumer does (can fail)
981 -- producer does not require field, but consumer does (can fail)
964− (False, True) -> issueAt beh (PropertyNowRequired k)
982+ (False, True) -> issueAt beh' PropertyNowRequired
965− _ -> do
983+ _ -> pure ()
966− let go sch sch' = checkCompatibility (beh >>> step (InProperty k)) env (ProdCons sch sch')
967− case (M.lookup k props', madd', M.lookup k props, madd) of
984+ case (M.lookup k props', madd', M.lookup k props, madd) of
968− -- (producer, additional producer, consumer, additional consumer)
985+ -- (producer, additional producer, consumer, additional consumer)
969− (Nothing, Nothing, _, _) -> pure () -- vacuously
986+ (Nothing, Nothing, _, _) -> pure () -- vacuously: the producer asserts that this field cannot exist,
970− -- this ^ seems fishy. Producer does not produce anything, but consumer might expect a field.
987+ -- and the consumer either doesn't require it, or it does and we've already raised an error about it.
971− (_, _, Nothing, Nothing) -> issueAt beh (UnexpectedProperty k)
988+ (_, _, Nothing, Nothing) -> issueAt beh' UnexpectedProperty
972− (Just p', _, Just p, _) -> go (propRefSchema p') (propRefSchema p)
989+ (Just p', _, Just p, _) -> go (propRefSchema p') (propRefSchema p)
973− (Nothing, Just add', Just p, _) -> go add' (propRefSchema p)
990+ (Nothing, Just add', Just p, _) ->
991+ clarifyIssue (AnItem beh' (anIssue AdditionalToProperty)) $
992+ go add' (propRefSchema p)
974− (Just p', _, Nothing, Just add) -> go (propRefSchema p') add
993+ (Just p', _, Nothing, Just add) ->
994+ clarifyIssue (AnItem beh' (anIssue PropertyToAdditional)) $
995+ go (propRefSchema p') add
975− (Nothing, Just _, Nothing, Just _) -> pure ()
996+ (Nothing, Just _, Nothing, Just _) -> pure ()
976 case (madd', madd) of
998 case (madd', madd) of
977 (Nothing, _) -> pure () -- vacuously
999 (Nothing, _) -> pure () -- vacuously
978 (_, Nothing) -> issueAt beh NoAdditionalProperties
1000 (_, Nothing) -> issueAt beh NoAdditionalProperties
@@ -1046,10 +1068,6 @@ instance Issuable 'TypedSchemaLevel where
1046 NoMatchingUniqueItems
1068 NoMatchingUniqueItems
1047 | -- | consumer declares the properties of an object must satisfy some condition, producer doesn't
1069 | -- | consumer declares the properties of an object must satisfy some condition, producer doesn't
1048 NoMatchingProperties
1070 NoMatchingProperties
1049− | -- | producer allows a property that is not allowed in the consumer
1050− UnexpectedProperty Text
1051− | -- | consumer requires a property that is not required/allowed in the producer
1052− PropertyNowRequired Text
1053 | -- | producer allows additional properties, consumer doesn't
1071 | -- | producer allows additional properties, consumer doesn't
1054 NoAdditionalProperties
1072 NoAdditionalProperties
1055 | -- | consumer declares a maximum number of properties in the object ($1), producer doesn't.
1073 | -- | consumer declares a maximum number of properties in the object ($1), producer doesn't.
@@ -1102,18 +1120,14 @@ instance Issuable 'TypedSchemaLevel where
1102 describeIssue Backward NoMatchingUniqueItems = para "Items are no longer required to be unique."
1120 describeIssue Backward NoMatchingUniqueItems = para "Items are no longer required to be unique."
1103 describeIssue Forward NoMatchingProperties = para "Property added."
1121 describeIssue Forward NoMatchingProperties = para "Property added."
1104 describeIssue Backward NoMatchingProperties = para "Property removed."
1122 describeIssue Backward NoMatchingProperties = para "Property removed."
1105− describeIssue Forward (UnexpectedProperty p) = para $ "Property " <> code p <> " has been removed."
1106− describeIssue Backward (UnexpectedProperty p) = para $ "Property " <> code p <> " has been added."
1107− describeIssue Forward (PropertyNowRequired p) = para $ "Property " <> code p <> " has become required."
1108− describeIssue Backward (PropertyNowRequired p) = para $ "Property " <> code p <> " may not be present."
1109 describeIssue Forward NoAdditionalProperties = para "Additional properties have been removed."
1123 describeIssue Forward NoAdditionalProperties = para "Additional properties have been removed."
1110 describeIssue Backward NoAdditionalProperties = para "Additional properties have been added."
1124 describeIssue Backward NoAdditionalProperties = para "Additional properties have been added."
1111 describeIssue Forward (NoMatchingMaxProperties n) = para $ "Maximum number of properties has been added: " <> show' n <> "."
1125 describeIssue Forward (NoMatchingMaxProperties n) = para $ "Maximum number of properties has been added: " <> show' n <> "."
1112 describeIssue Backward (NoMatchingMaxProperties n) = para $ "Maximum number of properties has been removed: " <> show' n <> "."
1126 describeIssue Backward (NoMatchingMaxProperties n) = para $ "Maximum number of properties has been removed: " <> show' n <> "."
1113− describeIssue _ (MatchingMaxPropertiesWeak (ProdCons p c)) = para $ "Maximum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
1127+ describeIssue _ (MatchingMaxPropertiesWeak (ProdCons p c)) = para $ "Maximum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
1114 describeIssue Forward (NoMatchingMinProperties n) = para $ "Minimum number of properties added: " <> show' n <> "."
1128 describeIssue Forward (NoMatchingMinProperties n) = para $ "Minimum number of properties added: " <> show' n <> "."
1115 describeIssue Backward (NoMatchingMinProperties n) = para $ "Minimum number of properties removed: " <> show' n <> "."
1129 describeIssue Backward (NoMatchingMinProperties n) = para $ "Minimum number of properties removed: " <> show' n <> "."
1116− describeIssue _ (MatchingMinPropertiesWeak (ProdCons p c)) = para $ "Minimum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
1130+ describeIssue _ (MatchingMinPropertiesWeak (ProdCons p c)) = para $ "Minimum number of properties has changed from " <> show' p <> " to " <> show' c <> "."
1117 describeIssue _ (NoMatchingCondition conds) =
1131 describeIssue _ (NoMatchingCondition conds) =
1118 para "Expected the following conditions to hold, but they didn't (please file a bug if you see this):"
1132 para "Expected the following conditions to hold, but they didn't (please file a bug if you see this):"
1119 <> bulletList ((\(SomeCondition c) -> showCondition c) <$> conds)
1133 <> bulletList ((\(SomeCondition c) -> showCondition c) <$> conds)
@@ -1138,8 +1152,22 @@ instance Issuable 'SchemaLevel where
1138 InvalidSchema Text
1152 InvalidSchema Text
1139 | -- | The schema contains a reference loop along "anyOf"/"allOf"/"oneOf".
1153 | -- | The schema contains a reference loop along "anyOf"/"allOf"/"oneOf".
1140 UnguardedRecursion
1154 UnguardedRecursion
1155+ | -- | Producer doesn't place any restrictions on the types, but the consumer does. List what types remain available in the consumer.
1156+ TypesRestricted [JsonType]
1157+ | -- | in the producer this field used to be handled as part of "additionalProperties", and the consumer this is a specific "properties" entry. Only thrown when this change actually causes other issues
1158+ AdditionalToProperty
1159+ | -- | in the consumer this field used to be handled as part of "additionalProperties", and the producer this is a specific "properties" entry. Only thrown when this change actually causes other issues
1160+ PropertyToAdditional
1161+ | -- | consumer requires a property that is not required/allowed in the producer
1162+ PropertyNowRequired
1163+ | -- | producer allows a property that is not allowed in the consumer
1141 deriving stock (Eq, Ord, Show)
1165 deriving stock (Eq, Ord, Show)
1142− issueIsUnsupported _ = True
1166+ issueIsUnsupported = \case
1167+ NotSupported _ -> True
1168+ InvalidSchema _ -> True
1169+ UnguardedRecursion -> True
1144 describeIssue _ (NotSupported i) =
1172 describeIssue _ (NotSupported i) =
1145 para (emph "Encountered a feature that OpenApi Diff does not support: " <> text i <> ".")
1173 para (emph "Encountered a feature that OpenApi Diff does not support: " <> text i <> ".")
@@ -1147,6 +1175,20 @@ instance Issuable 'SchemaLevel where
1147 para (emph "The schema is invalid: " <> text i <> ".")
1175 para (emph "The schema is invalid: " <> text i <> ".")
1148 describeIssue _ UnguardedRecursion =
1176 describeIssue _ UnguardedRecursion =
1149 para "Encountered recursion that is too complex for OpenApi Diff to untangle."
1177 para "Encountered recursion that is too complex for OpenApi Diff to untangle."
1178+ describeIssue Forward (TypesRestricted tys) = case tys of
1179+ [] -> para "No longer has any valid values." -- weird
1180+ _ -> para "Values are now limited to the following types: " <> bulletList (para . describeJSONType <$> tys)
1181+ describeIssue Backward (TypesRestricted tys) = case tys of
1182+ [] -> para "Any value of any type is now allowed." -- weird
1183+ _ -> para "Values are no longer limited to the following types: " <> bulletList (para . describeJSONType <$> tys)
1184+ describeIssue Forward AdditionalToProperty = para "The property was previously implicitly described by the catch-all \"additional properties\" case. It is now explicitly defined."
1185+ describeIssue Backward AdditionalToProperty = para "The property was previously explicitly defined. It is now implicitly described by the catch-all \"additional properties\" case."
1186+ describeIssue Forward PropertyToAdditional = para "The property was previously explicitly defined. It is now implicitly described by the catch-all \"additional properties\" case."
1187+ describeIssue Backward PropertyToAdditional = para "The property was previously implicitly described by the catch-all \"additional properties\" case. It is now explicitly defined."
1188+ describeIssue Forward PropertyNowRequired = para "The property has become required."
1189+ describeIssue Backward PropertyNowRequired = para "The property may not be present."
1190+ describeIssue Forward UnexpectedProperty = para "The property has been removed."
1191+ describeIssue Backward UnexpectedProperty = para "The property has been added."
1151instance Behavable 'SchemaLevel 'TypedSchemaLevel where
1193instance Behavable 'SchemaLevel 'TypedSchemaLevel where
1152 data Behave 'SchemaLevel 'TypedSchemaLevel
1194 data Behave 'SchemaLevel 'TypedSchemaLevel