hjsonschema 0.4.0.0 → 0.5.0.0
raw patch · 6 files changed
+291/−268 lines, 6 filesdep ~lensPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: lens
API changes (from Hackage documentation)
- Data.JsonSchema.Utils: allUnique :: Eq a => Vector a -> Bool
- Data.JsonSchema.Utils: count :: Eq a => a -> Vector a -> Int
- Data.JsonSchema.Utils: fromJSONInt :: Value -> Maybe Int
- Data.JsonSchema.Utils: greaterThanZero :: (Num a, Ord a) => a -> Maybe ()
- Data.JsonSchema.Utils: isJsonType :: Value -> Vector Text -> Vector ValErr
- Data.JsonSchema.Utils: runMaybeVal :: Maybe Validator -> Value -> Vector ValErr
- Data.JsonSchema.Utils: runMaybeVal' :: Maybe (Value -> (Vector ValErr, Value)) -> Value -> (Vector ValErr, Value)
- Data.JsonSchema.Utils: toObj :: Value -> Maybe (HashMap Text Value)
- Data.JsonSchema.Utils: toTxt :: Value -> Maybe Text
- Data.JsonSchema.Utils: tshow :: Show a => a -> Text
- Data.JsonSchema.Validators: additionalItems' :: ValidatorGen
- Data.JsonSchema.Validators: additionalProperties' :: ValidatorGen
- Data.JsonSchema.Validators: arrayEmbed :: Text -> Value -> Vector RawSchema
- Data.JsonSchema.Validators: noEm :: Text -> Value -> Vector RawSchema
- Data.JsonSchema.Validators: objEmbed :: Text -> Value -> Vector RawSchema
- Data.JsonSchema.Validators: objMembersEmbed :: Text -> Value -> Vector RawSchema
- Data.JsonSchema.Validators: objOrArrayEmbed :: Text -> Value -> Vector RawSchema
- Data.JsonSchema.Validators: patternProperties'' :: Spec -> Graph -> RawSchema -> Value -> Maybe (Value -> (Vector ValErr, Value))
- Data.JsonSchema.Validators: properties'' :: Spec -> Graph -> RawSchema -> Value -> Maybe (Value -> (Vector ValErr, Value))
+ Data.JsonSchema.Helpers: allUnique :: Eq a => Vector a -> Bool
+ Data.JsonSchema.Helpers: arrayEmbed :: Text -> Value -> Vector RawSchema
+ Data.JsonSchema.Helpers: count :: Eq a => a -> Vector a -> Int
+ Data.JsonSchema.Helpers: fromJSONInt :: Value -> Maybe Int
+ Data.JsonSchema.Helpers: greaterThanZero :: (Num a, Ord a) => a -> Maybe ()
+ Data.JsonSchema.Helpers: hmToVector :: HashMap k a -> Vector (k, a)
+ Data.JsonSchema.Helpers: isJsonType :: Value -> Vector Text -> Vector ValErr
+ Data.JsonSchema.Helpers: noEm :: Text -> Value -> Vector RawSchema
+ Data.JsonSchema.Helpers: objEmbed :: Text -> Value -> Vector RawSchema
+ Data.JsonSchema.Helpers: objMembersEmbed :: Text -> Value -> Vector RawSchema
+ Data.JsonSchema.Helpers: objOrArrayEmbed :: Text -> Value -> Vector RawSchema
+ Data.JsonSchema.Helpers: patternPropertiesMatches :: Spec -> Graph -> RawSchema -> Value -> Maybe (Value -> (Vector ValErr, Value))
+ Data.JsonSchema.Helpers: propertiesMatches :: Spec -> Graph -> RawSchema -> Value -> Maybe (Value -> (Vector ValErr, Value))
+ Data.JsonSchema.Helpers: runMaybeVal :: Maybe Validator -> Value -> Vector ValErr
+ Data.JsonSchema.Helpers: runMaybeVal' :: Maybe (Value -> (Vector ValErr, Value)) -> Value -> (Vector ValErr, Value)
+ Data.JsonSchema.Helpers: toObj :: Value -> Maybe (HashMap Text Value)
+ Data.JsonSchema.Helpers: toTxt :: Value -> Maybe Text
+ Data.JsonSchema.Helpers: tshow :: Show a => a -> Text
+ Data.JsonSchema.Helpers: vectorOfElems :: HashMap k a -> Vector a
+ Data.JsonSchema.Helpers: vectorToHm :: (Eq k, Hashable k) => Vector (k, a) -> HashMap k a
+ Data.JsonSchema.Validators: additionalItems :: ValidatorGen
+ Data.JsonSchema.Validators: runAdditionalProperties :: ValidatorGen
Files
- changelog.txt +6/−0
- hjsonschema.cabal +6/−5
- src/Data/JsonSchema.hs +1/−1
- src/Data/JsonSchema/Helpers.hs +178/−0
- src/Data/JsonSchema/Utils.hs +0/−71
- src/Data/JsonSchema/Validators.hs +100/−191
+ changelog.txt view
@@ -0,0 +1,6 @@+# 0.5+++ Start changelog.++ Rename Utils.hs to Helpers.hs.++ Move all non-ValidatorGen functions in Validators.hs to Helpers.hs.++ Various touchups.
hjsonschema.cabal view
@@ -1,6 +1,6 @@ name: hjsonschema-version: 0.4.0.0-synopsis: Haskell implementation of JSON Schema Draft 4.+version: 0.5.0.0+synopsis: JSON Schema Draft 4 library homepage: https://github.com/seagreen/hjsonschema license: MIT license-file: MIT-LICENSE.txt@@ -9,7 +9,8 @@ category: Data build-type: Simple cabal-version: >=1.10-extra-source-files: draft4.json+extra-source-files: changelog.txt+ draft4.json JSON-Schema-Test-Suite/tests/draft4/*.json README.md tests/Lib.hs@@ -17,8 +18,8 @@ library hs-source-dirs: src exposed-modules: Data.JsonSchema+ , Data.JsonSchema.Helpers , Data.JsonSchema.Reference- , Data.JsonSchema.Utils , Data.JsonSchema.Validators other-modules: Data.JsonSchema.Core default-language: Haskell2010@@ -32,7 +33,7 @@ , hashable >= 1.2 && < 1.3 , hjsonpointer >= 0.1 && < 0.2 , http-types >= 0.8 && < 0.9- , lens >= 4.7 && < 4.8+ , lens >= 4.7 && < 4.9 , regexpr >= 0.5 && < 0.6 , scientific >= 0.3 && < 0.4 , unordered-containers >= 0.2 && < 0.3
src/Data/JsonSchema.hs view
@@ -12,8 +12,8 @@ import Data.Foldable import qualified Data.HashMap.Strict as H import Data.JsonSchema.Core+import Data.JsonSchema.Helpers import Data.JsonSchema.Reference-import Data.JsonSchema.Utils import Data.JsonSchema.Validators import Data.Maybe import Data.Text (Text)
+ src/Data/JsonSchema/Helpers.hs view
@@ -0,0 +1,178 @@+-- | Embedded schema layouts and functions for use in Validators.hs.+--+-- 'Data.JsonSchema.fetchRefs' uses embedded schema layouts to+-- correctly find subschemas so it can check for "$ref" and "id"+-- keywords.++module Data.JsonSchema.Helpers where++import Control.Applicative+import Control.Monad+import Data.Aeson+import Data.Hashable+import Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as H+import Data.JsonSchema.Core+import Data.List+import Data.Monoid+import Data.Scientific+import Data.Text (Text)+import qualified Data.Text as T+import Data.Traversable+import Data.Vector (Vector)+import qualified Data.Vector as V+import Text.RegexPR++--------------------------------------------------+-- Embedded Schema Layouts+--------------------------------------------------++noEm :: Text -> Value -> Vector RawSchema+noEm _ _ = V.empty++objEmbed :: Text -> Value -> Vector RawSchema+objEmbed t (Object o) = V.singleton $ RawSchema t o+objEmbed _ _ = V.empty++-- TODO: optimize+arrayEmbed :: Text -> Value -> Vector RawSchema+arrayEmbed t (Array vs) = V.concat . V.toList $ objEmbed t <$> vs+arrayEmbed _ _ = V.empty++objOrArrayEmbed :: Text -> Value -> Vector RawSchema+objOrArrayEmbed t v@(Object _) = objEmbed t v+objOrArrayEmbed t v@(Array _) = arrayEmbed t v+objOrArrayEmbed _ _ = V.empty++objMembersEmbed :: Text -> Value -> Vector RawSchema+objMembersEmbed t (Object o) = V.concat $ objEmbed t <$> H.elems o+objMembersEmbed _ _ = V.empty++--------------------------------------------------+-- Validator Helpers+--------------------------------------------------++propertiesMatches+ :: Spec+ -> Graph+ -> RawSchema+ -> Value+ -> Maybe (Value -> (Vector ValErr, Value))+propertiesMatches spec g s (Object val) = do+ os <- traverse toObj val+ let oss = compile spec g . RawSchema (_rsURI s) <$> os+ Just (\x ->+ case x of+ Object y -> ( join . vectorOfElems $ H.intersectionWith validate oss y+ , Object $ H.difference y oss)+ z -> (mempty, z))+propertiesMatches _ _ _ _ = Nothing++patternPropertiesMatches+ :: Spec+ -> Graph+ -> RawSchema+ -> Value+ -> Maybe (Value -> (Vector ValErr, Value))+patternPropertiesMatches spec g s (Object val) = do+ os <- traverse toObj val+ let vs = compile spec g . RawSchema (_rsURI s) <$> os+ Just (\x ->+ case x of+ Object y -> let ms = matches (hmToVector vs) <$> hmToVector y+ in (ms >>= runVals, leftovers ms)+ _ -> (mempty, x))+ where+ matches+ :: Vector (Text, Schema)+ -> (Text, Value)+ -> (Text, Value, Vector Schema)+ matches ss (k, v) = (k, v, ss >>= match k)++ match :: Text -> (Text, Schema) -> Vector Schema+ match k (r, sc) =+ case matchRegexPR (T.unpack r) (T.unpack k) of+ Nothing -> mempty+ Just _ -> V.singleton sc++ runVals :: (Text, Value, Vector Schema) -> Vector ValErr+ runVals (_,v,ss) = join $ validate <$> ss <*> pure v++ leftovers :: Vector (Text, Value, Vector Schema) -> Value+ leftovers possiblyMatched =+ let unmatched = V.filter (\(_,_,ss) -> V.length ss == 0) possiblyMatched+ in Object . vectorToHm $ (\(v,k,_) -> (v,k)) <$> unmatched++patternPropertiesMatches _ _ _ _ = Nothing++isJsonType :: Value -> Vector Text -> Vector ValErr+isJsonType x xs =+ case x of+ (Null) -> f "null" xs ("null" :: Text)+ (Array y) -> f "array" xs y+ (Bool y) -> f "boolean" xs y+ (Object y) -> f "object" xs y+ (String y) -> f "string" xs y+ (Number y) ->+ case toBoundedInteger y :: Maybe Int of+ Nothing -> f "number" xs y+ Just _ -> if V.elem "number" xs || V.elem "integer" xs+ then mempty+ else mkErr y xs+ where+ f :: (Show a) => Text -> Vector Text -> a -> Vector ValErr+ f t ts d = if V.elem t ts then mempty else mkErr d ts++ mkErr :: (Show a) => a -> Vector Text -> Vector ValErr+ mkErr y ts = V.singleton $ tshow y <> " is not one of the types " <> tshow ts++--------------------------------------------------+-- Utils+--------------------------------------------------++vectorOfElems :: HashMap k a -> Vector a+vectorOfElems = V.fromList . H.elems++hmToVector :: HashMap k a -> Vector (k, a)+hmToVector = V.fromList . H.toList++vectorToHm :: (Eq k, Hashable k) => Vector (k, a) -> HashMap k a+vectorToHm = H.fromList . V.toList++runMaybeVal :: Maybe Validator -> Value -> Vector ValErr+runMaybeVal Nothing _ = mempty+runMaybeVal (Just val) d = val d++runMaybeVal'+ :: Maybe (Value -> (Vector ValErr, Value))+ -> Value+ -> (Vector ValErr, Value)+runMaybeVal' Nothing d = (mempty, d)+runMaybeVal' (Just val) d = val d++-- TODO: optimize+-- see here: http://comments.gmane.org/gmane.comp.lang.haskell.cafe/106242+allUnique :: (Eq a) => Vector a -> Bool+allUnique bs = length (nub (V.toList bs)) == V.length bs++-- TODO: optimize+count :: (Eq a) => a -> Vector a -> Int+count b bs = V.length $ V.filter (== b) bs++toObj :: Value -> Maybe (HashMap Text Value)+toObj (Object a) = Just a+toObj _ = Nothing++fromJSONInt :: Value -> Maybe Int+fromJSONInt (Number n) = toBoundedInteger n+fromJSONInt _ = Nothing++toTxt :: Value -> Maybe Text+toTxt (String t) = Just t+toTxt _ = Nothing++greaterThanZero :: (Num a, Ord a) => a -> Maybe ()+greaterThanZero n = if n <= 0 then Nothing else Just ()++tshow :: Show a => a -> Text+tshow = T.pack . show
− src/Data/JsonSchema/Utils.hs
@@ -1,71 +0,0 @@-module Data.JsonSchema.Utils where--import Data.Aeson-import Data.HashMap.Strict (HashMap)-import Data.JsonSchema.Core-import Data.List-import Data.Monoid-import Data.Scientific-import Data.Text (Text)-import qualified Data.Text as T-import Data.Vector (Vector)-import qualified Data.Vector as V--isJsonType :: Value -> Vector Text -> Vector ValErr-isJsonType x xs =- case x of- (Null) -> f "null" xs ("null" :: Text)- (Array y) -> f "array" xs y- (Bool y) -> f "boolean" xs y- (Object y) -> f "object" xs y- (String y) -> f "string" xs y- (Number y) ->- case toBoundedInteger y :: Maybe Int of- Nothing -> f "number" xs y- Just _ -> if V.elem "number" xs || V.elem "integer" xs- then mempty- else mkErr y xs- where- f :: (Show a) => Text -> Vector Text -> a -> Vector ValErr- f t ts d = if V.elem t ts then mempty else mkErr d ts-- mkErr :: (Show a) => a -> Vector Text -> Vector ValErr- mkErr y ts = V.singleton $ tshow y <> " is not one of the types " <> tshow ts--runMaybeVal :: Maybe Validator -> Value -> Vector ValErr-runMaybeVal Nothing _ = mempty-runMaybeVal (Just val) d = val d--runMaybeVal'- :: Maybe (Value -> (Vector ValErr, Value))- -> Value- -> (Vector ValErr, Value)-runMaybeVal' Nothing d = (mempty, d)-runMaybeVal' (Just val) d = val d---- TODO: optimize--- see here: http://comments.gmane.org/gmane.comp.lang.haskell.cafe/106242-allUnique :: (Eq a) => Vector a -> Bool-allUnique bs = length (nub (V.toList bs)) == V.length bs---- TODO: optimize-count :: (Eq a) => a -> Vector a -> Int-count b bs = V.length $ V.filter (== b) bs--toObj :: Value -> Maybe (HashMap Text Value)-toObj (Object a) = Just a-toObj _ = Nothing--fromJSONInt :: Value -> Maybe Int-fromJSONInt (Number n) = toBoundedInteger n-fromJSONInt _ = Nothing--toTxt :: Value -> Maybe Text-toTxt (String t) = Just t-toTxt _ = Nothing--greaterThanZero :: (Num a, Ord a) => a -> Maybe ()-greaterThanZero n = if n <= 0 then Nothing else Just ()--tshow :: Show a => a -> Text-tshow = T.pack . show
src/Data/JsonSchema/Validators.hs view
@@ -1,85 +1,34 @@ -- | This is generally meant to be an internal module. -- It's only exposed in case you want to make your own--- 'Spec'. If you just want to use JSON Schema Draft 4--- use the preassembled 'Data.JsonSchema.draft4' instead.------ This module exposes three types of functions. To describe--- them we'll use the properties validators as an example.------ 1. Functions which describe how subschemas (if any)--- are embedded in this validator. This is how 'fetchRefs'--- is able to find "$refs" and "ids".------ 2. Functions that aren't followed by apostrophes have the--- type 'ValidatorGen' and are meant to be used in a 'Spec'.--- Examples are 'properties' and 'additionalProperties'.------ 3. Functions that are followed by a single apostrophe, such--- as 'additionalProperties'', aren't meant to be used standalone--- in a 'Spec'. Instead they're used by other validators. For--- instance, 'additionalProperties' disables itself if the key--- "properties" is present to allow 'additionalProperties' to--- handle its validation. Placing the actual meat of--- 'additionalProperties' in 'additionalProperties'' allows for--- code reuse between 'properties' and 'additionalProperties'.------ 4. Functions that are followed by a double apostrophe, such--- as 'patternProperties''', return an enhanced validator. It--- not only reports errors, but also the parts of the target--- data that it matches. These functions also aren't used in--- a 'Spec' instance, but are necessary helpers for actual--- validators in certain situations.+-- 'Data.JsonSchema.Core.Spec'. If you just want to use+-- JSON Schema Draft 4 use the preassembled+-- 'Data.JsonSchema.draft4' instead. module Data.JsonSchema.Validators where import Control.Applicative import Control.Monad import Data.Aeson-import Data.Fixed (mod')+import Data.Fixed (mod') import Data.Hashable-import Data.HashMap.Strict (HashMap)-import qualified Data.HashMap.Strict as H+import Data.HashMap.Strict (HashMap)+import qualified Data.HashMap.Strict as H import Data.JsonPointer import Data.JsonSchema.Core+import Data.JsonSchema.Helpers import Data.JsonSchema.Reference-import Data.JsonSchema.Utils import Data.Maybe import Data.Monoid-import Data.Text (Text)-import qualified Data.Text as T+import Data.Text (Text)+import qualified Data.Text as T import Data.Text.Encoding import Data.Traversable-import Data.Vector (Vector)-import qualified Data.Vector as V+import Data.Vector (Vector)+import qualified Data.Vector as V import Network.HTTP.Types.URI import Text.RegexPR ----------------------------------------------------- Embedded Schema Layouts-----------------------------------------------------noEm :: Text -> Value -> Vector RawSchema-noEm _ _ = V.empty--objEmbed :: Text -> Value -> Vector RawSchema-objEmbed t (Object o) = V.singleton $ RawSchema t o-objEmbed _ _ = V.empty---- TODO: optimize-arrayEmbed :: Text -> Value -> Vector RawSchema-arrayEmbed t (Array vs) = V.concat . V.toList $ objEmbed t <$> vs-arrayEmbed _ _ = V.empty--objOrArrayEmbed :: Text -> Value -> Vector RawSchema-objOrArrayEmbed t v@(Object _) = objEmbed t v-objOrArrayEmbed t v@(Array _) = arrayEmbed t v-objOrArrayEmbed _ _ = V.empty--objMembersEmbed :: Text -> Value -> Vector RawSchema-objMembersEmbed t (Object o) = V.concat $ objEmbed t <$> H.elems o-objMembersEmbed _ _ = V.empty---------------------------------------------------- -- Number Validators -------------------------------------------------- @@ -88,9 +37,10 @@ greaterThanZero val Just (\x -> case x of- (Number y) -> if y `mod'` val /= 0- then V.singleton $ tshow y <> " isn't a multiple of " <> tshow val- else mempty+ Number y ->+ if y `mod'` val /= 0+ then V.singleton $ tshow y <> " isn't a multiple of " <> tshow val+ else mempty _ -> mempty) multipleOf _ _ _ _ = Nothing @@ -101,9 +51,10 @@ _ -> (>) in Just (\x -> case x of- (Number y) -> if y `f` val- then V.singleton $ tshow y <> " fails to validate against maximum " <> tshow val- else mempty+ Number y ->+ if y `f` val+ then V.singleton $ tshow y <> " fails to validate against maximum " <> tshow val+ else mempty _ -> mempty) maximumVal _ _ _ _ = Nothing @@ -114,9 +65,10 @@ _ -> (<) in Just (\x -> case x of- (Number y) -> if y `f` val- then V.singleton $ tshow y <> " fails to validate against minimum " <> tshow val- else mempty+ Number y ->+ if y `f` val+ then V.singleton $ tshow y <> " fails to validate against minimum " <> tshow val+ else mempty _ -> mempty) minimumVal _ _ _ _ = Nothing @@ -130,9 +82,10 @@ greaterThanZero val Just (\x -> case x of- (String y) -> if T.length y > val- then V.singleton $ y <> " is greater than maxLength " <> tshow val- else mempty+ String y ->+ if T.length y > val+ then V.singleton $ y <> " is greater than maxLength " <> tshow val+ else mempty _ -> mempty) minLength :: ValidatorGen@@ -141,16 +94,17 @@ greaterThanZero val Just (\x -> case x of- (String y) -> if T.length y < val- then V.singleton $ y <> " is less than minLength " <> tshow val- else mempty+ String y ->+ if T.length y < val+ then V.singleton $ y <> " is less than minLength " <> tshow val+ else mempty _ -> mempty) pattern :: ValidatorGen pattern _ _ _ (String val) = Just (\x -> case x of- (String t) ->+ String t -> case matchRegexPR (T.unpack val) (T.unpack t) of Nothing -> V.singleton $ t <> " fails to validate against pattern " <> val Just _ -> mempty@@ -162,22 +116,19 @@ -------------------------------------------------- -- | Also covers additionalItems.------ We can leave additionalItems out of the spec HashMap, since items defaults to {},--- and if it isn't present additionalItems will always validate successfully. items :: ValidatorGen items spec g s (Object val) = let sub = compile spec g (RawSchema (_rsURI s) val) in Just (\x -> case x of- (Array ys) -> ys >>= validate sub- _ -> mempty)+ Array ys -> ys >>= validate sub+ _ -> mempty) items spec g s (Array vs) = do os <- traverse toObj vs let ss = compile spec g . RawSchema (_rsURI s) <$> os let addItems = do a <- H.lookup "additionalItems" (_rsObject s)- additionalItems' spec g s a+ additionalItems spec g s a Just (\x -> case x of (Array ys) ->@@ -186,21 +137,25 @@ _ -> mempty) items _ _ _ _ = Nothing -additionalItems' :: ValidatorGen-additionalItems' _ _ _ (Bool val) =+-- | Not included directly in the 'draft4' spec hashmap because it always+-- validates data unless 'items' is also present. This is because 'items'+-- defaults to {}.+additionalItems :: ValidatorGen+additionalItems _ _ _ (Bool val) = Just (\x -> case x of- (Array ys) -> if not val && V.length ys > 0- then V.singleton ("Val error against additionalItems false for: " <> tshow x)- else mempty+ Array ys ->+ if not val && V.length ys > 0+ then V.singleton ("Val error against additionalItems false for: " <> tshow x)+ else mempty _ -> mempty)-additionalItems' spec g s (Object val) =+additionalItems spec g s (Object val) = let sub = compile spec g (RawSchema (_rsURI s) val) in Just (\x -> case x of- (Array ys) -> ys >>= validate sub- _ -> mempty)-additionalItems' _ _ _ _ = Nothing+ Array ys -> ys >>= validate sub+ _ -> mempty)+additionalItems _ _ _ _ = Nothing maxItems :: ValidatorGen maxItems _ _ _ v = do@@ -208,9 +163,10 @@ greaterThanZero val Just (\x -> case x of- (Array ys) -> if V.length ys > val- then V.singleton $ tshow ys <> " has more items than maxItems " <> tshow val- else mempty+ Array ys ->+ if V.length ys > val+ then V.singleton $ tshow ys <> " has more items than maxItems " <> tshow val+ else mempty _ -> mempty) minItems :: ValidatorGen@@ -219,9 +175,10 @@ greaterThanZero val Just (\x -> case x of- (Array ys) -> if V.length ys < val- then V.singleton $ tshow ys <> " has fewer items than minItems " <> tshow val- else mempty+ Array ys ->+ if V.length ys < val+ then V.singleton $ tshow ys <> " has fewer items than minItems " <> tshow val+ else mempty _ -> mempty) uniqueItems :: ValidatorGen@@ -231,8 +188,7 @@ case x of (Array ys) -> if allUnique ys then mempty- else V.singleton- ("Val error against uniqueItems " <> tshow val <> " for: " <> tshow x)+ else V.singleton ("Val error against uniqueItems " <> tshow val <> " for: " <> tshow x) _ -> mempty) uniqueItems _ _ _ _ = Nothing @@ -246,9 +202,10 @@ greaterThanZero val Just (\x -> case x of- (Object o) -> if H.size o > val- then V.singleton $ tshow o <> " has more members than maxProperties " <> tshow val- else mempty+ Object o ->+ if H.size o > val+ then V.singleton $ tshow o <> " has more members than maxProperties " <> tshow val+ else mempty _ -> mempty) minProperties :: ValidatorGen@@ -257,145 +214,95 @@ greaterThanZero val Just (\x -> case x of- (Object o) -> if H.size o < val- then V.singleton $ tshow o <> " has fewer members than minProperties " <> tshow val- else mempty+ Object o ->+ if H.size o < val+ then V.singleton $ tshow o <> " has fewer members than minProperties " <> tshow val+ else mempty _ -> mempty) required :: ValidatorGen required _ _ _ (Array vs) = do when (V.length vs == 0) Nothing ts <- traverse toTxt vs- let a = vectorToMap ts+ let a = vectorToMapSet ts when (H.size a /= V.length ts) Nothing Just (\x -> case x of- (Object o) -> if H.size (H.difference a o) > 0- then V.singleton $ "the keys of " <> tshow o- <> " don't contain all the required elements " <> tshow vs- else mempty+ Object o ->+ if H.size (H.difference a o) > 0+ then V.singleton $ "the keys of " <> tshow o <>+ " don't contain all the required elements " <> tshow vs+ else mempty _ -> mempty) where- -- TODO: optimize- vectorToMap :: (Eq a, Hashable a) => Vector a -> HashMap a Bool- vectorToMap vec = H.fromList $ zip (V.toList vec) (repeat True)+ vectorToMapSet :: (Eq a, Hashable a) => Vector a -> HashMap a Bool+ vectorToMapSet vec = vectorToHm $ (\x -> (x, True)) <$> vec required _ _ _ _ = Nothing --- TODO: Fix up the properties* validators. They're all a huge mess, but at least+-- TODO: Fix up the properties validators. They're all a huge mess, but at least -- they work. -- -- In order of what's tried: properties, patternProperties, additionalProperties properties :: ValidatorGen properties spec g s v = do- let mProps = properties'' spec g s v+ let mProps = propertiesMatches spec g s v let mPatProp = do aV <- H.lookup "patternProperties" (_rsObject s)- patternProperties'' spec g s aV+ patternPropertiesMatches spec g s aV let mAdd = do aVal <- H.lookup "additionalProperties" (_rsObject s)- additionalProperties' spec g s aVal+ runAdditionalProperties spec g s aVal when (isNothing mProps && isNothing mPatProp && isNothing mAdd) Nothing Just (\x -> case x of- (Object y) -> -- Got myself into a mess here.+ Object y -> -- Got myself into a mess here. let (e1s, remaining) = runMaybeVal' mProps (Object y) (_, remaining') = runMaybeVal' mPatProp remaining (e2s, _) = runMaybeVal' mPatProp (Object y) in e1s <> e2s <> runMaybeVal mAdd remaining' _ -> mempty) --- TODO: optimize-properties''- :: Spec- -> Graph- -> RawSchema- -> Value- -> Maybe (Value -> (Vector ValErr, Value))-properties'' spec g s (Object val) = do- os <- traverse toObj val- let oss = compile spec g . RawSchema (_rsURI s) <$> os- Just (\x ->- case x of- (Object y) -> ( join . V.fromList . H.elems $ H.intersectionWith validate oss y- , Object (H.difference y oss))- z -> (mempty, z))-properties'' _ _ _ _ = Nothing---- TODO: optimize-patternProperties''- :: Spec- -> Graph- -> RawSchema- -> Value- -> Maybe (Value -> (Vector ValErr, Value))-patternProperties'' spec g s (Object val) = do- os <- traverse toObj val- let vs = compile spec g . RawSchema (_rsURI s) <$> os- Just (\x ->- case x of- (Object y) -> let ms = matches (V.fromList . H.toList $ vs) <$> (V.fromList . H.toList $ y)- in (ms >>= runVals, leftovers ms)- _ -> (mempty, x))- where- matches :: Vector (Text, Schema) -> (Text, Value) -> (Text, Value, Vector Schema)- matches ss (k, v) = (k, v, ss >>= match k)-- match :: Text -> (Text, Schema) -> Vector Schema- match k (r, sc) =- case matchRegexPR (T.unpack r) (T.unpack k) of- Nothing -> mempty- Just _ -> V.singleton sc-- runVals :: (Text, Value, Vector Schema) -> Vector ValErr- runVals (_,v,ss) = join $ validate <$> ss <*> pure v-- leftovers :: Vector (Text, Value, Vector Schema) -> Value- leftovers possiblyMatched =- let unmatched = V.filter (\(_,_,ss) -> V.length ss == 0) possiblyMatched- in Object . H.fromList . V.toList $ (\(v,k,_) -> (v,k)) <$> unmatched--patternProperties'' _ _ _ _ = Nothing- patternProperties :: ValidatorGen patternProperties spec g s v = do when (H.member "properties" (_rsObject s)) Nothing- let mPatProp = patternProperties'' spec g s v+ let mPatProp = patternPropertiesMatches spec g s v -- TODO: checking additionalProperties as well doesn't help with tests let mAdd = do aVal <- H.lookup "additionalProperties" (_rsObject s)- additionalProperties' spec g s aVal+ runAdditionalProperties spec g s aVal when (isNothing mPatProp && isNothing mAdd) Nothing Just (\x -> case x of- (Object y) ->+ Object y -> let (e2s, remaining') = runMaybeVal' mPatProp (Object y) in e2s <> runMaybeVal mAdd remaining' _ -> mempty) -additionalProperties' :: ValidatorGen-additionalProperties' _ _ _ (Bool val) =+-- | An implementation of the "additionalProperties" keyword that never+-- disables itself. Not included directly in the 'draft4' spec hashmap.+runAdditionalProperties :: ValidatorGen+runAdditionalProperties _ _ _ (Bool val) = Just (\x -> case x of- (Object y) -> if not val && H.size y > 0- then V.singleton ("Val error against additionalProperties false for: " <> tshow x)- else mempty+ Object y ->+ if not val && H.size y > 0+ then V.singleton ("Val error against additionalProperties false for: " <> tshow x)+ else mempty _ -> mempty)-additionalProperties' spec g s (Object val) =+runAdditionalProperties spec g s (Object val) = let sub = compile spec g (RawSchema (_rsURI s) val) in Just (\x -> case x of- (Object y) -> (V.fromList . H.elems $ y) >>= validate sub -- TODO: optimize- _ -> mempty)-additionalProperties' _ _ _ _ = Nothing+ Object y -> vectorOfElems y >>= validate sub+ _ -> mempty)+runAdditionalProperties _ _ _ _ = Nothing additionalProperties :: ValidatorGen additionalProperties spec g s v = do when (H.member "properties" (_rsObject s)) Nothing when (H.member "patternProperties" (_rsObject s)) Nothing- additionalProperties' spec g s v+ runAdditionalProperties spec g s v --- TODO: optimize--- -- http://json-schema.org/latest/json-schema-validation.html#anchor70 -- -- This keyword's value MUST be an object.@@ -409,17 +316,19 @@ -- This is called a property dependency. dependencies :: ValidatorGen dependencies spec g s (Object val) = do- let vs = V.fromList $ H.toList val+ let vs = hmToVector val let schemaDeps = vs >>= toSchemaDep spec g let propDeps = vs >>= toPropDep when (V.length schemaDeps + V.length propDeps /= V.length vs) Nothing Just (\x -> case x of- (Object y) -> join $ (valSD <$> schemaDeps <*> pure y) <> (valPD <$> propDeps <*> pure y)- _ -> mempty)+ Object y -> join $ (valSD <$> schemaDeps <*> pure y)+ <> (valPD <$> propDeps <*> pure y)+ _ -> mempty) where toSchemaDep :: Spec -> Graph -> (Text, Value) -> Vector (Text, Schema)- toSchemaDep spc gr (t, Object o) = V.singleton (t, compile spc gr $ RawSchema (_rsURI s) o)+ toSchemaDep spc gr (t, Object o) =+ V.singleton (t, compile spc gr $ RawSchema (_rsURI s) o) toSchemaDep _ _ _ = mempty toPropDep :: (Text, Value) -> Vector (Text, Vector Text)