git.iko.soy/typeable/compaREST
on master help sign in
Fix some TODOs in schema and expand some comments (#72)
6e454bcmniip
openapi-diff.cabal+1
@@ -53,6 +53,7 @@ common common-options
53 , mtl
53 , mtl
54 , openapi3
54 , openapi3
55 , optparse-applicative
55 , optparse-applicative
56+ , pcre2
56 , scientific
57 , scientific
57 , tagged
58 , tagged
58 , text
59 , text
src/OpenAPI/Checker/Validate/Schema.hs+51−32
@@ -40,6 +40,7 @@ import qualified Data.Set as S
40import Data.Text (Text)
40import Data.Text (Text)
41import qualified Data.Text as T hiding (singleton)
41import qualified Data.Text as T hiding (singleton)
42import Data.Typeable
42import Data.Typeable
43+import Text.Regex.Pcre2
43import OpenAPI.Checker.Behavior
44import OpenAPI.Checker.Behavior
44import OpenAPI.Checker.Orphans ()
45import OpenAPI.Checker.Orphans ()
45import OpenAPI.Checker.Paths
46import OpenAPI.Checker.Paths
@@ -131,16 +132,16 @@ satisfiesTyped (TNumber n) (Maximum (Exclusive m)) = n < m
131satisfiesTyped (TNumber n) (Maximum (Inclusive m)) = n <= m
132satisfiesTyped (TNumber n) (Maximum (Inclusive m)) = n <= m
132satisfiesTyped (TNumber n) (Minimum (Down (Exclusive (Down m)))) = n > m
133satisfiesTyped (TNumber n) (Minimum (Down (Exclusive (Down m)))) = n > m
133satisfiesTyped (TNumber n) (Minimum (Down (Inclusive (Down m)))) = n >= m
134satisfiesTyped (TNumber n) (Minimum (Down (Inclusive (Down m)))) = n >= m
134−satisfiesTyped (TNumber n) (MultipleOf m) = denominator (toRational n / toRational m) == 1 -- TODO: could be better
135+satisfiesTyped (TNumber n) (MultipleOf m) = denominator (toRational n / toRational m) == 1 -- TODO: could be better #36
135satisfiesTyped (TNumber n) (NumberFormat f) = checkNumberFormat f n
136satisfiesTyped (TNumber n) (NumberFormat f) = checkNumberFormat f n
136satisfiesTyped (TString s) (MaxLength m) = fromIntegral (T.length s) <= m
137satisfiesTyped (TString s) (MaxLength m) = fromIntegral (T.length s) <= m
137satisfiesTyped (TString s) (MinLength m) = fromIntegral (T.length s) >= m
138satisfiesTyped (TString s) (MinLength m) = fromIntegral (T.length s) >= m
138−satisfiesTyped (TString s) (Pattern p) = undefined s p -- TODO: regex stuff
139+satisfiesTyped (TString s) (Pattern p) = isJust $ match p s -- TODO: regex stuff #32
139−satisfiesTyped (TString s) (StringFormat f) = undefined s f -- TODO: string format
140+satisfiesTyped (TString s) (StringFormat f) = checkStringFormat f s
140satisfiesTyped (TArray a) (Items f _) = all (`satisfies` f) a
141satisfiesTyped (TArray a) (Items f _) = all (`satisfies` f) a
141satisfiesTyped (TArray a) (MaxItems m) = fromIntegral (F.length a) <= m
142satisfiesTyped (TArray a) (MaxItems m) = fromIntegral (F.length a) <= m
142satisfiesTyped (TArray a) (MinItems m) = fromIntegral (F.length a) >= m
143satisfiesTyped (TArray a) (MinItems m) = fromIntegral (F.length a) >= m
143−satisfiesTyped (TArray a) UniqueItems = S.size (S.fromList $ F.toList a) == F.length a -- TODO: could be better
144+satisfiesTyped (TArray a) UniqueItems = S.size (S.fromList $ F.toList a) == F.length a -- TODO: could be better #36
144satisfiesTyped (TObject o) (Properties props additional _) =
145satisfiesTyped (TObject o) (Properties props additional _) =
145 all (`HM.member` o) (M.keys (M.filter propRequired props))
146 all (`HM.member` o) (M.keys (M.filter propRequired props))
146 && all (\(k, v) -> satisfies v $ maybe additional propFormula $ M.lookup k props) (HM.toList o)
147 && all (\(k, v) -> satisfies v $ maybe additional propFormula $ M.lookup k props) (HM.toList o)
@@ -160,6 +161,15 @@ checkNumberFormat "float" _n = True
160checkNumberFormat "double" _n = True
161checkNumberFormat "double" _n = True
161checkNumberFormat f _n = error $ "Invalid number format: " <> T.unpack f
162checkNumberFormat f _n = error $ "Invalid number format: " <> T.unpack f
162
163
164+checkStringFormat :: Format -> Text -> Bool
165+checkStringFormat "byte" _s = True -- TODO: regex stuff #32
166+checkStringFormat "binary" _s = True
167+checkStringFormat "date" _s = True
168+checkStringFormat "date-time" _s = True
169+checkStringFormat "password" _s = True
170+checkStringFormat "uuid" _s = True
171+checkStringFormat f _s = error $ "Invalid string format: " <> T.unpack f
172+
163deriving stock instance Eq (Condition t)
173deriving stock instance Eq (Condition t)
164
174
165deriving stock instance Ord (Condition t)
175deriving stock instance Ord (Condition t)
@@ -351,29 +361,6 @@ instance
351 , forObject = top
361 , forObject = top
352 }
362 }
353
363
354−{- TODO: remove
355−instance Typeable t => Steppable Schema (Condition t) where
356− data Step Schema (Condition t)
357− = EnumField
358− | MaximumFields -- maximum & exclusiveMaximum
359− | MinimumFields -- minimum & exclusiveMinimum
360− | MultipleOfField
361− | FormatField
362− | MaxLengthField
363− | MinLengthField
364− | PatternField
365− | ItemsField
366− | MaxItemsField
367− | MinItemsField
368− | UniqueItemsField
369− | PropertiesFields -- properties, additionalProperties & required
370− | MaxPropertiesField
371− | MinPropertiesField
372− | NullableField
373− | IntegerType -- type=integer
374− deriving (Eq, Ord, Show)
375−-}
376−
377instance Behavable 'SchemaLevel 'SchemaLevel where
364instance Behavable 'SchemaLevel 'SchemaLevel where
378 data Behave 'SchemaLevel 'SchemaLevel
365 data Behave 'SchemaLevel 'SchemaLevel
379 deriving (Eq, Ord, Show)
366 deriving (Eq, Ord, Show)
@@ -724,10 +711,10 @@ processSchema sch@(extract -> Schema {..}) = do
724 , minPropertiesClause
711 , minPropertiesClause
725 ])
712 ])
726
713
727−{- TODO: ReadOnly/WriteOnly -}
714+{- TODO: ReadOnly/WriteOnly #68 -}
728
715
729checkOneOfDisjoint :: [Traced (Referenced Schema)] -> ProcessM Bool
716checkOneOfDisjoint :: [Traced (Referenced Schema)] -> ProcessM Bool
730−checkOneOfDisjoint = const $ pure True -- TODO
717+checkOneOfDisjoint = const $ pure True -- TODO #69
731
718
732schemaToFormula
719schemaToFormula
733 :: Traced (Definitions Schema)
720 :: Traced (Definitions Schema)
@@ -788,7 +775,7 @@ checkContradiction
788 :: Behavior 'TypedSchemaLevel
775 :: Behavior 'TypedSchemaLevel
789 -> [Condition t]
776 -> [Condition t]
790 -> SemanticCompatFormula ()
777 -> SemanticCompatFormula ()
791−checkContradiction beh _ = issueAt beh NoContradiction -- TODO
778+checkContradiction beh _ = issueAt beh NoContradiction -- TODO #70
792
779
793checkImplication
780checkImplication
794 :: (HasAll (CheckEnv Schema) xs)
781 :: (HasAll (CheckEnv Schema) xs)
@@ -844,7 +831,7 @@ checkImplication env beh prods cons = case findExactly prods of
844 Pattern p ->
831 Pattern p ->
845 if any (\case Pattern p' -> p == p'; _ -> False) prods
832 if any (\case Pattern p' -> p == p'; _ -> False) prods
846 then pure ()
833 then pure ()
847− else issueAt beh (NoMatchingPattern p) -- TODO: regex comparison
834+ else issueAt beh (NoMatchingPattern p) -- TODO: regex comparison #32
848 StringFormat f ->
835 StringFormat f ->
849 if any (\case StringFormat f' -> f == f'; _ -> False) prods
836 if any (\case StringFormat f' -> f == f'; _ -> False) prods
850 then pure ()
837 then pure ()
@@ -872,7 +859,7 @@ checkImplication env beh prods cons = case findExactly prods of
872 then pure ()
859 then pure ()
873 else issueAt beh NoMatchingUniqueItems
860 else issueAt beh NoMatchingUniqueItems
874 Properties props _ madd -> case findRelevant (<>) (\case Properties props' _ madd' -> Just $ (props', madd') NE.:| []; _ -> Nothing) prods of
861 Properties props _ madd -> case findRelevant (<>) (\case Properties props' _ madd' -> Just $ (props', madd') NE.:| []; _ -> Nothing) prods of
875− Just ((props', madd') NE.:| []) -> do
862+ Just pm -> anyOfAt beh NoMatchingProperties $ NE.toList pm <&> \(props', madd') -> do
876 F.for_ (S.fromList $ M.keys props <> M.keys props') $ \k -> do
863 F.for_ (S.fromList $ M.keys props <> M.keys props') $ \k -> do
877 let go sch sch' = checkCompatibility env (beh >>> step (InProperty k)) (ProdCons sch sch')
864 let go sch sch' = checkCompatibility env (beh >>> step (InProperty k)) (ProdCons sch sch')
878 case (M.lookup k props', madd', M.lookup k props, madd) of
865 case (M.lookup k props', madd', M.lookup k props, madd) of
@@ -916,42 +903,74 @@ checkImplication env beh prods cons = case findExactly prods of
916instance Issuable 'TypedSchemaLevel where
903instance Issuable 'TypedSchemaLevel where
917 data Issue 'TypedSchemaLevel
904 data Issue 'TypedSchemaLevel
918 = EnumDoesntSatisfy A.Value
905 = EnumDoesntSatisfy A.Value
906+ -- ^ producer produces a specific value ($1), consumer has a condition that is not satisfied by said value
919 | NoMatchingEnum A.Value
907 | NoMatchingEnum A.Value
908+ -- ^ producer produces a specific value ($1) which is not listed in the consumer's list of specific values
920 | NoMatchingMaximum (Bound Scientific)
909 | NoMatchingMaximum (Bound Scientific)
910+ -- ^ producer declares a maximum numeric value ($1), consumer doesnt
921 | MatchingMaximumWeak (Bound Scientific) (Bound Scientific)
911 | MatchingMaximumWeak (Bound Scientific) (Bound Scientific)
912+ -- ^ producer declares a maximum numeric value ($1), consumer declares a weaker (higher) limit ($2)
922 | NoMatchingMinimum (Bound Scientific)
913 | NoMatchingMinimum (Bound Scientific)
914+ -- ^ producer declares a minimum numeric value, consumer doesnt
923 | MatchingMinimumWeak (Bound Scientific) (Bound Scientific)
915 | MatchingMinimumWeak (Bound Scientific) (Bound Scientific)
916+ -- ^ producer declares a minimum numeric value ($1), consumer declares a weaker (lower) limit ($2)
924 | NoMatchingMultipleOf Scientific
917 | NoMatchingMultipleOf Scientific
918+ -- ^ producer declares that the numeric value must be a multiple of $1, consumer doesn't
925 | MatchingMultipleOfWeak Scientific Scientific
919 | MatchingMultipleOfWeak Scientific Scientific
920+ -- ^ producer declares that the numeric value must be a multiple of $1, consumer declares a weaker condition (multiple of $2)
926 | NoMatchingFormat Format
921 | NoMatchingFormat Format
922+ -- ^ producer declares a string/number format, consumer declares none or a different format (TODO: improve via regex #32)
927 | NoMatchingMaxLength Integer
923 | NoMatchingMaxLength Integer
924+ -- ^ producer declares a maximum length of the string ($1), consumer doesn't.
928 | MatchingMaxLengthWeak Integer Integer
925 | MatchingMaxLengthWeak Integer Integer
926+ -- ^ producer declares a maximum length of the string ($1), consumer declares a weaker (higher) limit ($2)
929 | NoMatchingMinLength Integer
927 | NoMatchingMinLength Integer
928+ -- ^ producer declares a minimum length of the string ($1), consumer doesn't.
930 | MatchingMinLengthWeak Integer Integer
929 | MatchingMinLengthWeak Integer Integer
930+ -- ^ producer declares a minimum length of the string ($1), consumer declares a weaker (lower) limit ($2)
931 | NoMatchingPattern Pattern
931 | NoMatchingPattern Pattern
932+ -- ^ producer declares the string value must matrix a regex ($1), consumer doesn't declare or declares different regex (TODO: #32)
932 | NoMatchingItems
933 | NoMatchingItems
934+ -- ^ producer declares the items of an array must satisfy some condition, consumer doesn't
933 | NoMatchingMaxItems Integer
935 | NoMatchingMaxItems Integer
936+ -- ^ producer declares a maximum length of the array ($1), consumer doesn't.
934 | MatchingMaxItemsWeak Integer Integer
937 | MatchingMaxItemsWeak Integer Integer
938+ -- ^ producer declares a maximum length of the array ($1), consumer declares a weaker (higher) limit ($2)
935 | NoMatchingMinItems Integer
939 | NoMatchingMinItems Integer
940+ -- ^ producer declares a minimum length of the array ($1), consumer doesn't.
936 | MatchingMinItemsWeak Integer Integer
941 | MatchingMinItemsWeak Integer Integer
942+ -- ^ producer declares a minimum length of the array ($1), consumer declares a weaker (lower) limit ($2)
937 | NoMatchingUniqueItems
943 | NoMatchingUniqueItems
944+ -- ^ producer declares that items must be unique, consumer doesn't
938 | NoMatchingProperties
945 | NoMatchingProperties
946+ -- ^ producer declares the properties of an object must satisfy some condition, consumer doesn't
939 | UnexpectedProperty Text
947 | UnexpectedProperty Text
948+ -- ^ producer allows a property that is not allowed in the consumer
940 | PropertyNowRequired Text
949 | PropertyNowRequired Text
950+ -- ^ consumer requires a property that is not required/allowed in the consumer
941 | NoAdditionalProperties
951 | NoAdditionalProperties
952+ -- ^ producer allows additional properties, consumer doesn't
942 | NoMatchingMaxProperties Integer
953 | NoMatchingMaxProperties Integer
954+ -- ^ producer declares a maximum number of properties in the object ($1), consumer doesn't.
943 | MatchingMaxPropertiesWeak Integer Integer
955 | MatchingMaxPropertiesWeak Integer Integer
956+ -- ^ producer declares a maximum number of properties in the object ($1), consumer declares a weaker (higher) limit ($2)
944 | NoMatchingMinProperties Integer
957 | NoMatchingMinProperties Integer
958+ -- ^ producer declares a minimum number of properties in the object ($1), consumer doesn't.
945 | MatchingMinPropertiesWeak Integer Integer
959 | MatchingMinPropertiesWeak Integer Integer
960+ -- ^ producer declares a minimum number of properties in the object ($1), consumer declares a weaker (lower) limit ($2)
946 | NoMatchingCondition [SomeCondition]
961 | NoMatchingCondition [SomeCondition]
962+ -- ^ consumer declares that the value must satisfy a disjunction of some conditions, but producer's requirements couldn't be matched against any single one of them (TODO: split heuristic #71)
947 | NoContradiction
963 | NoContradiction
964+ -- ^ consumer indicates that values of this type are now allowed, but the producer does not do so (currently we only check immediate contradictions, c.f. #70)
948 deriving stock (Eq, Ord, Show)
965 deriving stock (Eq, Ord, Show)
949 issueIsUnsupported _ = False
966 issueIsUnsupported _ = False
950
967
951instance Issuable 'SchemaLevel where
968instance Issuable 'SchemaLevel where
952 data Issue 'SchemaLevel
969 data Issue 'SchemaLevel
953 = NotSupported Text
970 = NotSupported Text
971+ -- ^ Some (openapi-supported) feature that we do not support was encountered in the schema
954 | InvalidSchema Text
972 | InvalidSchema Text
973+ -- ^ The schema is actually invalid
955 deriving stock (Eq, Ord, Show)
974 deriving stock (Eq, Ord, Show)
956 issueIsUnsupported _ = True
975 issueIsUnsupported _ = True
957
976