packages feed

hjsonschema 0.10.0.3 → 1.0.0.0

raw patch · 41 files changed

+3089/−1720 lines, 41 filesdep +profunctorsdep ~QuickCheckdep ~directorydep ~hjsonpointerPVP ok

version bump matches the API change (PVP)

Dependencies added: profunctors

Dependency ranges changed: QuickCheck, directory, hjsonpointer, http-client, semigroups, unordered-containers

API changes (from Hackage documentation)

- Data.JsonSchema.Draft4: Invalid :: !err -> !Value -> !Pointer -> Failure err
- Data.JsonSchema.Draft4: [_invalidFinalValidator] :: Failure err -> !Value
- Data.JsonSchema.Draft4: [_invalidOffendingData] :: Failure err -> !Pointer
- Data.JsonSchema.Draft4: [_invalidValidatorsCalled] :: Failure err -> !err
- Data.JsonSchema.Draft4: data Failure err
- Data.JsonSchema.Draft4: draft4Spec :: Spec Schema
- Data.JsonSchema.Draft4: runValidate :: ReferencedSchemas Schema -> SchemaWithURI Schema -> Value -> [Invalid]
- Data.JsonSchema.Draft4.Internal: descendNextLevel :: ReferencedSchemas Schema -> SchemaWithURI Schema -> Schema -> Value -> [Invalid]
- Data.JsonSchema.Draft4.Internal: embedded :: Schema -> [Schema]
- Data.JsonSchema.Draft4.Internal: runSingle :: Schema -> dta -> (Schema -> Maybe val) -> (err -> Invalid) -> (val -> dta -> [err]) -> [Invalid]
- Data.JsonSchema.Draft4.Internal: runValidate :: ReferencedSchemas Schema -> SchemaWithURI Schema -> Value -> [Invalid]
- Data.JsonSchema.Draft4.Internal: validateArray :: ReferencedSchemas Schema -> SchemaWithURI Schema -> Vector Value -> [Invalid]
- Data.JsonSchema.Draft4.Internal: validateNumber :: Schema -> Scientific -> [Invalid]
- Data.JsonSchema.Draft4.Internal: validateObject :: ReferencedSchemas Schema -> SchemaWithURI Schema -> HashMap Text Value -> [Invalid]
- Data.JsonSchema.Draft4.Internal: validateString :: Schema -> Text -> [Invalid]
- Data.JsonSchema.Fetch: Spec :: (schema -> [schema]) -> (schema -> Maybe Text) -> (schema -> Maybe Text) -> Spec schema
- Data.JsonSchema.Fetch: [_ssEmbedded] :: Spec schema -> schema -> [schema]
- Data.JsonSchema.Fetch: [_ssGetId] :: Spec schema -> schema -> Maybe Text
- Data.JsonSchema.Fetch: [_ssGetRef] :: Spec schema -> schema -> Maybe Text
- Data.JsonSchema.Fetch: data Spec schema
- Data.JsonSchema.Fetch: foldFunction :: forall schema. FromJSON schema => (Text -> IO ByteString) -> Spec schema -> URISchemaMap schema -> SchemaWithURI schema -> IO (Either Text (URISchemaMap schema))
- Data.Validator.Failure: Invalid :: !err -> !Value -> !Pointer -> Failure err
- Data.Validator.Failure: [_invalidFinalValidator] :: Failure err -> !Value
- Data.Validator.Failure: [_invalidOffendingData] :: Failure err -> !Pointer
- Data.Validator.Failure: [_invalidValidatorsCalled] :: Failure err -> !err
- Data.Validator.Failure: addToPath :: Token -> Failure a -> Failure a
- Data.Validator.Failure: data Failure err
- Data.Validator.Failure: instance GHC.Classes.Eq err => GHC.Classes.Eq (Data.Validator.Failure.Failure err)
- Data.Validator.Failure: instance GHC.Show.Show err => GHC.Show.Show (Data.Validator.Failure.Failure err)
- Data.Validator.Failure: modFailure :: (a -> b) -> Failure a -> Failure b
- Data.Validator.Failure: setFailure :: b -> Failure a -> Failure b
+ Data.JsonSchema.Draft4: Failure :: !err -> !Value -> !Pointer -> !Value -> Fail err
+ Data.JsonSchema.Draft4: RefLoop :: ValidatorChain
+ Data.JsonSchema.Draft4: [_failureFinalValidator] :: Fail err -> !Value
+ Data.JsonSchema.Draft4: [_failureOffendingData] :: Fail err -> !Value
+ Data.JsonSchema.Draft4: [_failureOffendingPointer] :: Fail err -> !Pointer
+ Data.JsonSchema.Draft4: [_failureValidatorsCalled] :: Fail err -> !err
+ Data.JsonSchema.Draft4: data Fail err
+ Data.JsonSchema.Draft4: draft4FetchInfo :: FetchInfo Schema
+ Data.JsonSchema.Draft4: instance GHC.Classes.Eq Data.JsonSchema.Draft4.FilesystemValidationFailure
+ Data.JsonSchema.Draft4: referencesValidity :: URISchemaMap Schema -> [(Text, Failure)]
+ Data.JsonSchema.Draft4: schemaForSchemas :: Schema
+ Data.JsonSchema.Draft4: schemaForSchemasBytes :: ByteString
+ Data.JsonSchema.Draft4: type Failure = Fail ValidatorChain
+ Data.JsonSchema.Draft4: type InvalidSchema = NonEmpty (Maybe Text, Failure)
+ Data.JsonSchema.Draft4.Failure: AdditionalItemsBool :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: AdditionalItemsObject :: ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: AdditionalPropertiesBool :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: AdditionalPropertiesObject :: ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: AllOf :: ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: AnyOf :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: Enum :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: ExclusiveMaximum :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: ExclusiveMinimum :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: Items :: ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: MaxItems :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: MaxLength :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: MaxProperties :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: Maximum :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: MinItems :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: MinLength :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: MinProperties :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: Minimum :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: MultipleOf :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: NotValidator :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: OneOf :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: PatternProperties :: ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: PatternValidator :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: Properties :: ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: PropertyDependency :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: Ref :: ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: RefLoop :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: RefResolution :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: Required :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: SchemaDependency :: ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: TypeValidator :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: UniqueItems :: ValidatorChain
+ Data.JsonSchema.Draft4.Failure: addPropE :: AdditionalPropertiesInvalid ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: data ValidatorChain
+ Data.JsonSchema.Draft4.Failure: depsE :: DependencyInvalid ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: instance GHC.Classes.Eq Data.JsonSchema.Draft4.Failure.ValidatorChain
+ Data.JsonSchema.Draft4.Failure: instance GHC.Show.Show Data.JsonSchema.Draft4.Failure.ValidatorChain
+ Data.JsonSchema.Draft4.Failure: itemsE :: ItemsInvalid ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: maxE :: MaximumInvalid -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: minE :: MinimumInvalid -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: patPropE :: PatternPropertiesInvalid ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: propE :: PropertiesInvalid ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: refE :: RefInvalid ValidatorChain -> ValidatorChain
+ Data.JsonSchema.Draft4.Failure: type Failure = Fail ValidatorChain
+ Data.JsonSchema.Draft4.Failure: type Invalid = NonEmpty Failure
+ Data.JsonSchema.Draft4.Failure: type InvalidSchema = NonEmpty (Maybe Text, Failure)
+ Data.JsonSchema.Draft4.Schema: Schema :: Maybe Text -> Maybe Text -> Maybe Text -> Maybe (HashMap Text Schema) -> HashMap Text Value -> Maybe Scientific -> Maybe Scientific -> Maybe Bool -> Maybe Scientific -> Maybe Bool -> Maybe Int -> Maybe Int -> Maybe Text -> Maybe Int -> Maybe Int -> Maybe Bool -> Maybe (Items Schema) -> Maybe (AdditionalItems Schema) -> Maybe Int -> Maybe Int -> Maybe Required -> Maybe (HashMap Text (Dependency Schema)) -> Maybe (HashMap Text Schema) -> Maybe (HashMap Text Schema) -> Maybe (AdditionalProperties Schema) -> Maybe EnumVal -> Maybe TypeVal -> Maybe (NonEmpty Schema) -> Maybe (NonEmpty Schema) -> Maybe (NonEmpty Schema) -> Maybe Schema -> Schema
+ Data.JsonSchema.Draft4.Schema: [_schemaAdditionalItems] :: Schema -> Maybe (AdditionalItems Schema)
+ Data.JsonSchema.Draft4.Schema: [_schemaAdditionalProperties] :: Schema -> Maybe (AdditionalProperties Schema)
+ Data.JsonSchema.Draft4.Schema: [_schemaAllOf] :: Schema -> Maybe (NonEmpty Schema)
+ Data.JsonSchema.Draft4.Schema: [_schemaAnyOf] :: Schema -> Maybe (NonEmpty Schema)
+ Data.JsonSchema.Draft4.Schema: [_schemaDefinitions] :: Schema -> Maybe (HashMap Text Schema)
+ Data.JsonSchema.Draft4.Schema: [_schemaDependencies] :: Schema -> Maybe (HashMap Text (Dependency Schema))
+ Data.JsonSchema.Draft4.Schema: [_schemaEnum] :: Schema -> Maybe EnumVal
+ Data.JsonSchema.Draft4.Schema: [_schemaExclusiveMaximum] :: Schema -> Maybe Bool
+ Data.JsonSchema.Draft4.Schema: [_schemaExclusiveMinimum] :: Schema -> Maybe Bool
+ Data.JsonSchema.Draft4.Schema: [_schemaId] :: Schema -> Maybe Text
+ Data.JsonSchema.Draft4.Schema: [_schemaItems] :: Schema -> Maybe (Items Schema)
+ Data.JsonSchema.Draft4.Schema: [_schemaMaxItems] :: Schema -> Maybe Int
+ Data.JsonSchema.Draft4.Schema: [_schemaMaxLength] :: Schema -> Maybe Int
+ Data.JsonSchema.Draft4.Schema: [_schemaMaxProperties] :: Schema -> Maybe Int
+ Data.JsonSchema.Draft4.Schema: [_schemaMaximum] :: Schema -> Maybe Scientific
+ Data.JsonSchema.Draft4.Schema: [_schemaMinItems] :: Schema -> Maybe Int
+ Data.JsonSchema.Draft4.Schema: [_schemaMinLength] :: Schema -> Maybe Int
+ Data.JsonSchema.Draft4.Schema: [_schemaMinProperties] :: Schema -> Maybe Int
+ Data.JsonSchema.Draft4.Schema: [_schemaMinimum] :: Schema -> Maybe Scientific
+ Data.JsonSchema.Draft4.Schema: [_schemaMultipleOf] :: Schema -> Maybe Scientific
+ Data.JsonSchema.Draft4.Schema: [_schemaNot] :: Schema -> Maybe Schema
+ Data.JsonSchema.Draft4.Schema: [_schemaOneOf] :: Schema -> Maybe (NonEmpty Schema)
+ Data.JsonSchema.Draft4.Schema: [_schemaOther] :: Schema -> HashMap Text Value
+ Data.JsonSchema.Draft4.Schema: [_schemaPatternProperties] :: Schema -> Maybe (HashMap Text Schema)
+ Data.JsonSchema.Draft4.Schema: [_schemaPattern] :: Schema -> Maybe Text
+ Data.JsonSchema.Draft4.Schema: [_schemaProperties] :: Schema -> Maybe (HashMap Text Schema)
+ Data.JsonSchema.Draft4.Schema: [_schemaRef] :: Schema -> Maybe Text
+ Data.JsonSchema.Draft4.Schema: [_schemaRequired] :: Schema -> Maybe Required
+ Data.JsonSchema.Draft4.Schema: [_schemaType] :: Schema -> Maybe TypeVal
+ Data.JsonSchema.Draft4.Schema: [_schemaUniqueItems] :: Schema -> Maybe Bool
+ Data.JsonSchema.Draft4.Schema: [_schemaVersion] :: Schema -> Maybe Text
+ Data.JsonSchema.Draft4.Schema: data Schema
+ Data.JsonSchema.Draft4.Schema: emptySchema :: Schema
+ Data.JsonSchema.Draft4.Schema: instance Data.Aeson.Types.Class.FromJSON Data.JsonSchema.Draft4.Schema.Schema
+ Data.JsonSchema.Draft4.Schema: instance Data.Aeson.Types.Class.ToJSON Data.JsonSchema.Draft4.Schema.Schema
+ Data.JsonSchema.Draft4.Schema: instance GHC.Classes.Eq Data.JsonSchema.Draft4.Schema.Schema
+ Data.JsonSchema.Draft4.Schema: instance GHC.Show.Show Data.JsonSchema.Draft4.Schema.Schema
+ Data.JsonSchema.Draft4.Schema: instance Test.QuickCheck.Arbitrary.Arbitrary Data.JsonSchema.Draft4.Schema.Schema
+ Data.JsonSchema.Draft4.Schema: internalSchemaHashMap :: HashMap Text (Schema -> Maybe Value)
+ Data.JsonSchema.Draft4.Spec: continueValidating :: ReferencedSchemas Schema -> VisitedSchemas -> SchemaWithURI Schema -> Value -> [Failure]
+ Data.JsonSchema.Draft4.Spec: d4Spec :: ReferencedSchemas Schema -> VisitedSchemas -> Maybe Text -> Spec Schema ValidatorChain
+ Data.JsonSchema.Draft4.Spec: embedded :: Schema -> ([Schema], [Schema])
+ Data.JsonSchema.Draft4.Spec: validate :: ReferencedSchemas Schema -> SchemaWithURI Schema -> Value -> [Failure]
+ Data.JsonSchema.Fetch: FetchInfo :: (schema -> ([schema], [schema])) -> (schema -> Maybe Text) -> (schema -> Maybe Text) -> FetchInfo schema
+ Data.JsonSchema.Fetch: [_fiEmbedded] :: FetchInfo schema -> schema -> ([schema], [schema])
+ Data.JsonSchema.Fetch: [_fiId] :: FetchInfo schema -> schema -> Maybe Text
+ Data.JsonSchema.Fetch: [_fiRef] :: FetchInfo schema -> schema -> Maybe Text
+ Data.JsonSchema.Fetch: data FetchInfo schema
+ Data.JsonSchema.Fetch: getRecursiveReferences :: forall schema. FromJSON schema => (Text -> IO ByteString) -> FetchInfo schema -> URISchemaMap schema -> SchemaWithURI schema -> IO (Either Text (URISchemaMap schema))
+ Data.JsonSchema.Fetch: getReference :: ReferencedSchemas schema -> Maybe Text -> Maybe schema
+ Data.JsonSchema.Fetch: instance GHC.Classes.Eq Data.JsonSchema.Fetch.FilesystemFailure
+ Data.JsonSchema.Types: Schema :: HashMap Text Value -> Schema
+ Data.JsonSchema.Types: Spec :: [Validator schema schema err] -> Spec schema err
+ Data.JsonSchema.Types: [_unSchema] :: Schema -> HashMap Text Value
+ Data.JsonSchema.Types: [_unSpec] :: Spec schema err -> [Validator schema schema err]
+ Data.JsonSchema.Types: embedded :: Spec schema a -> schema -> ([schema], [schema])
+ Data.JsonSchema.Types: instance Data.Aeson.Types.Class.FromJSON Data.JsonSchema.Types.Schema
+ Data.JsonSchema.Types: instance Data.Aeson.Types.Class.ToJSON Data.JsonSchema.Types.Schema
+ Data.JsonSchema.Types: instance GHC.Classes.Eq Data.JsonSchema.Types.Schema
+ Data.JsonSchema.Types: instance GHC.Show.Show Data.JsonSchema.Types.Schema
+ Data.JsonSchema.Types: newtype Schema
+ Data.JsonSchema.Types: newtype Spec schema err
+ Data.JsonSchema.Types: validate :: Spec schema err -> schema -> Value -> [Fail err]
+ Data.Validator.Draft4: AdditionalItemsContext :: AdditionalItems schema -> AdditionalItemsContext schema
+ Data.Validator.Draft4: AdditionalPropertiesContext :: Bool -> (AdditionalProperties schema) -> AdditionalPropertiesContext schema
+ Data.Validator.Draft4: AllOf :: NonEmpty schema -> AllOf schema
+ Data.Validator.Draft4: AnyOf :: NonEmpty schema -> AnyOf schema
+ Data.Validator.Draft4: Definitions :: HashMap Text schema -> Definitions schema
+ Data.Validator.Draft4: DependenciesContext :: HashMap Text (Dependency schema) -> DependenciesContext schema
+ Data.Validator.Draft4: EnumContext :: EnumVal -> EnumContext
+ Data.Validator.Draft4: ItemsContext :: (Maybe (AdditionalItems schema)) -> (Items schema) -> ItemsContext schema
+ Data.Validator.Draft4: MaxItems :: Int -> MaxItems
+ Data.Validator.Draft4: MaxLength :: Int -> MaxLength
+ Data.Validator.Draft4: MaxProperties :: Int -> MaxProperties
+ Data.Validator.Draft4: MaximumContext :: Bool -> Scientific -> MaximumContext
+ Data.Validator.Draft4: MinItems :: Int -> MinItems
+ Data.Validator.Draft4: MinLength :: Int -> MinLength
+ Data.Validator.Draft4: MinProperties :: Int -> MinProperties
+ Data.Validator.Draft4: MinimumContext :: Bool -> Scientific -> MinimumContext
+ Data.Validator.Draft4: MultipleOf :: Scientific -> MultipleOf
+ Data.Validator.Draft4: NotVal :: schema -> NotVal schema
+ Data.Validator.Draft4: OneOf :: NonEmpty schema -> OneOf schema
+ Data.Validator.Draft4: PatternPropertiesContext :: Bool -> (Maybe (AdditionalProperties schema)) -> (HashMap Text schema) -> PatternPropertiesContext schema
+ Data.Validator.Draft4: PatternVal :: Text -> PatternVal
+ Data.Validator.Draft4: PropertiesContext :: (Maybe (HashMap Text schema)) -> (Maybe (AdditionalProperties schema)) -> (HashMap Text schema) -> PropertiesContext schema
+ Data.Validator.Draft4: Ref :: Text -> Ref
+ Data.Validator.Draft4: RequiredContext :: Required -> RequiredContext
+ Data.Validator.Draft4: TypeContext :: TypeVal -> TypeContext
+ Data.Validator.Draft4: UniqueItems :: Bool -> UniqueItems
+ Data.Validator.Draft4: [_unAdditionalItemsContext] :: AdditionalItemsContext schema -> AdditionalItems schema
+ Data.Validator.Draft4: [_unAllOf] :: AllOf schema -> NonEmpty schema
+ Data.Validator.Draft4: [_unAnyOf] :: AnyOf schema -> NonEmpty schema
+ Data.Validator.Draft4: [_unDefinitions] :: Definitions schema -> HashMap Text schema
+ Data.Validator.Draft4: [_unDependenciesContext] :: DependenciesContext schema -> HashMap Text (Dependency schema)
+ Data.Validator.Draft4: [_unEnumContext] :: EnumContext -> EnumVal
+ Data.Validator.Draft4: [_unMaxItems] :: MaxItems -> Int
+ Data.Validator.Draft4: [_unMaxLength] :: MaxLength -> Int
+ Data.Validator.Draft4: [_unMaxProperties] :: MaxProperties -> Int
+ Data.Validator.Draft4: [_unMinItems] :: MinItems -> Int
+ Data.Validator.Draft4: [_unMinLength] :: MinLength -> Int
+ Data.Validator.Draft4: [_unMinProperties] :: MinProperties -> Int
+ Data.Validator.Draft4: [_unMultipleOf] :: MultipleOf -> Scientific
+ Data.Validator.Draft4: [_unNotVal] :: NotVal schema -> schema
+ Data.Validator.Draft4: [_unOneOf] :: OneOf schema -> NonEmpty schema
+ Data.Validator.Draft4: [_unPatternVal] :: PatternVal -> Text
+ Data.Validator.Draft4: [_unRef] :: Ref -> Text
+ Data.Validator.Draft4: [_unRequiredContext] :: RequiredContext -> Required
+ Data.Validator.Draft4: [_unTypeContext] :: TypeContext -> TypeVal
+ Data.Validator.Draft4: [_unUniqueItems] :: UniqueItems -> Bool
+ Data.Validator.Draft4: additionalItemsEmbedded :: Validator schema (Maybe (AdditionalItemsContext schema)) err
+ Data.Validator.Draft4: additionalProperties :: (schema -> Value -> [Fail err]) -> Validator schema (Maybe (AdditionalPropertiesContext schema)) (AdditionalPropertiesInvalid err)
+ Data.Validator.Draft4: allOf :: (schema -> Value -> [Fail err]) -> Validator schema (Maybe (AllOf schema)) err
+ Data.Validator.Draft4: anyOf :: ToJSON schema => (schema -> Value -> [Fail err]) -> Validator schema (Maybe (AnyOf schema)) ()
+ Data.Validator.Draft4: data AdditionalPropertiesContext schema
+ Data.Validator.Draft4: data ItemsContext schema
+ Data.Validator.Draft4: data MaximumContext
+ Data.Validator.Draft4: data MinimumContext
+ Data.Validator.Draft4: data PatternPropertiesContext schema
+ Data.Validator.Draft4: data PropertiesContext schema
+ Data.Validator.Draft4: definitionsEmbedded :: Validator schema (Maybe (Definitions schema)) err
+ Data.Validator.Draft4: dependencies :: (schema -> Value -> [Fail err]) -> Validator schema (Maybe (DependenciesContext schema)) (DependencyInvalid err)
+ Data.Validator.Draft4: enumVal :: Validator a (Maybe EnumContext) ()
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.EnumContext
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.MaxItems
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.MaxLength
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.MaxProperties
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.MaximumContext
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.MinItems
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.MinLength
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.MinProperties
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.MinimumContext
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.MultipleOf
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.PatternVal
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.Ref
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.RequiredContext
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.TypeContext
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.UniqueItems
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.AdditionalItemsContext schema)
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.AdditionalPropertiesContext schema)
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.AllOf schema)
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.AnyOf schema)
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.Definitions schema)
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.DependenciesContext schema)
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.ItemsContext schema)
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.NotVal schema)
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.OneOf schema)
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.PatternPropertiesContext schema)
+ Data.Validator.Draft4: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.PropertiesContext schema)
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.EnumContext
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.MaxItems
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.MaxLength
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.MaxProperties
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.MaximumContext
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.MinItems
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.MinLength
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.MinProperties
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.MinimumContext
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.MultipleOf
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.PatternVal
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.Ref
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.RequiredContext
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.TypeContext
+ Data.Validator.Draft4: instance GHC.Classes.Eq Data.Validator.Draft4.UniqueItems
+ Data.Validator.Draft4: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.AdditionalItemsContext schema)
+ Data.Validator.Draft4: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.AdditionalPropertiesContext schema)
+ Data.Validator.Draft4: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.AllOf schema)
+ Data.Validator.Draft4: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.AnyOf schema)
+ Data.Validator.Draft4: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.Definitions schema)
+ Data.Validator.Draft4: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.DependenciesContext schema)
+ Data.Validator.Draft4: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.ItemsContext schema)
+ Data.Validator.Draft4: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.NotVal schema)
+ Data.Validator.Draft4: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.OneOf schema)
+ Data.Validator.Draft4: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.PatternPropertiesContext schema)
+ Data.Validator.Draft4: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.PropertiesContext schema)
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.EnumContext
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.MaxItems
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.MaxLength
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.MaxProperties
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.MaximumContext
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.MinItems
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.MinLength
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.MinProperties
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.MinimumContext
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.MultipleOf
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.PatternVal
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.Ref
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.RequiredContext
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.TypeContext
+ Data.Validator.Draft4: instance GHC.Show.Show Data.Validator.Draft4.UniqueItems
+ Data.Validator.Draft4: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.AdditionalItemsContext schema)
+ Data.Validator.Draft4: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.AdditionalPropertiesContext schema)
+ Data.Validator.Draft4: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.AllOf schema)
+ Data.Validator.Draft4: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.AnyOf schema)
+ Data.Validator.Draft4: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.Definitions schema)
+ Data.Validator.Draft4: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.DependenciesContext schema)
+ Data.Validator.Draft4: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.ItemsContext schema)
+ Data.Validator.Draft4: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.NotVal schema)
+ Data.Validator.Draft4: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.OneOf schema)
+ Data.Validator.Draft4: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.PatternPropertiesContext schema)
+ Data.Validator.Draft4: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.PropertiesContext schema)
+ Data.Validator.Draft4: items :: (schema -> Value -> [Fail err]) -> Validator schema (Maybe (ItemsContext schema)) (ItemsInvalid err)
+ Data.Validator.Draft4: maxItems :: Validator a (Maybe MaxItems) ()
+ Data.Validator.Draft4: maxLength :: Validator a (Maybe MaxLength) ()
+ Data.Validator.Draft4: maxProperties :: Validator a (Maybe MaxProperties) ()
+ Data.Validator.Draft4: maximumVal :: Validator a (Maybe MaximumContext) MaximumInvalid
+ Data.Validator.Draft4: minItems :: Validator a (Maybe MinItems) ()
+ Data.Validator.Draft4: minLength :: Validator a (Maybe MinLength) ()
+ Data.Validator.Draft4: minProperties :: Validator a (Maybe MinProperties) ()
+ Data.Validator.Draft4: minimumVal :: Validator a (Maybe MinimumContext) MinimumInvalid
+ Data.Validator.Draft4: multipleOf :: Validator a (Maybe MultipleOf) ()
+ Data.Validator.Draft4: newtype AdditionalItemsContext schema
+ Data.Validator.Draft4: newtype AllOf schema
+ Data.Validator.Draft4: newtype AnyOf schema
+ Data.Validator.Draft4: newtype Definitions schema
+ Data.Validator.Draft4: newtype DependenciesContext schema
+ Data.Validator.Draft4: newtype EnumContext
+ Data.Validator.Draft4: newtype MaxItems
+ Data.Validator.Draft4: newtype MaxLength
+ Data.Validator.Draft4: newtype MaxProperties
+ Data.Validator.Draft4: newtype MinItems
+ Data.Validator.Draft4: newtype MinLength
+ Data.Validator.Draft4: newtype MinProperties
+ Data.Validator.Draft4: newtype MultipleOf
+ Data.Validator.Draft4: newtype NotVal schema
+ Data.Validator.Draft4: newtype OneOf schema
+ Data.Validator.Draft4: newtype PatternVal
+ Data.Validator.Draft4: newtype Ref
+ Data.Validator.Draft4: newtype RequiredContext
+ Data.Validator.Draft4: newtype TypeContext
+ Data.Validator.Draft4: newtype UniqueItems
+ Data.Validator.Draft4: noEmbedded :: a -> ([b], [b])
+ Data.Validator.Draft4: notVal :: ToJSON schema => (schema -> Value -> [Fail err]) -> Validator schema (Maybe (NotVal schema)) ()
+ Data.Validator.Draft4: oneOf :: ToJSON schema => (schema -> Value -> [Fail err]) -> Validator schema (Maybe (OneOf schema)) ()
+ Data.Validator.Draft4: patternProperties :: (schema -> Value -> [Fail err]) -> Validator schema (Maybe (PatternPropertiesContext schema)) (PatternPropertiesInvalid err)
+ Data.Validator.Draft4: patternVal :: Validator a (Maybe PatternVal) ()
+ Data.Validator.Draft4: properties :: (schema -> Value -> [Fail err]) -> Validator schema (Maybe (PropertiesContext schema)) (PropertiesInvalid err)
+ Data.Validator.Draft4: ref :: (FromJSON schema, ToJSON schema) => VisitedSchemas -> Maybe Text -> (Maybe Text -> Maybe schema) -> (VisitedSchemas -> Maybe Text -> schema -> Value -> [Fail err]) -> Validator a (Maybe Ref) (RefInvalid err)
+ Data.Validator.Draft4: required :: Validator a (Maybe RequiredContext) ()
+ Data.Validator.Draft4: run :: FromJSON b => (a -> b -> [c]) -> Maybe a -> Value -> [c]
+ Data.Validator.Draft4: typeVal :: Validator a (Maybe TypeContext) ()
+ Data.Validator.Draft4: uniqueItems :: Validator a (Maybe UniqueItems) ()
+ Data.Validator.Draft4.Any: EnumVal :: NonEmpty Value -> EnumVal
+ Data.Validator.Draft4.Any: RefInvalid :: err -> RefInvalid err
+ Data.Validator.Draft4.Any: RefLoop :: RefInvalid err
+ Data.Validator.Draft4.Any: RefResolution :: RefInvalid err
+ Data.Validator.Draft4.Any: TypeValArray :: (Set Text) -> TypeVal
+ Data.Validator.Draft4.Any: TypeValString :: Text -> TypeVal
+ Data.Validator.Draft4.Any: VisitedSchemas :: [URIBaseAndFragment] -> VisitedSchemas
+ Data.Validator.Draft4.Any: [_unEnumVal] :: EnumVal -> NonEmpty Value
+ Data.Validator.Draft4.Any: [_unVisited] :: VisitedSchemas -> [URIBaseAndFragment]
+ Data.Validator.Draft4.Any: allOf :: (schema -> Value -> [Fail err]) -> NonEmpty schema -> Value -> [Fail err]
+ Data.Validator.Draft4.Any: anyOf :: ToJSON schema => (schema -> Value -> [Fail err]) -> NonEmpty schema -> Value -> Maybe (Fail ())
+ Data.Validator.Draft4.Any: data RefInvalid err
+ Data.Validator.Draft4.Any: data TypeVal
+ Data.Validator.Draft4.Any: enumVal :: EnumVal -> Value -> Maybe (Fail ())
+ Data.Validator.Draft4.Any: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.Any.EnumVal
+ Data.Validator.Draft4.Any: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.Any.TypeVal
+ Data.Validator.Draft4.Any: instance Data.Aeson.Types.Class.ToJSON Data.Validator.Draft4.Any.EnumVal
+ Data.Validator.Draft4.Any: instance Data.Aeson.Types.Class.ToJSON Data.Validator.Draft4.Any.TypeVal
+ Data.Validator.Draft4.Any: instance GHC.Base.Monoid Data.Validator.Draft4.Any.VisitedSchemas
+ Data.Validator.Draft4.Any: instance GHC.Classes.Eq Data.Validator.Draft4.Any.EnumVal
+ Data.Validator.Draft4.Any: instance GHC.Classes.Eq Data.Validator.Draft4.Any.TypeVal
+ Data.Validator.Draft4.Any: instance GHC.Classes.Eq Data.Validator.Draft4.Any.VisitedSchemas
+ Data.Validator.Draft4.Any: instance GHC.Classes.Eq err => GHC.Classes.Eq (Data.Validator.Draft4.Any.RefInvalid err)
+ Data.Validator.Draft4.Any: instance GHC.Show.Show Data.Validator.Draft4.Any.EnumVal
+ Data.Validator.Draft4.Any: instance GHC.Show.Show Data.Validator.Draft4.Any.TypeVal
+ Data.Validator.Draft4.Any: instance GHC.Show.Show Data.Validator.Draft4.Any.VisitedSchemas
+ Data.Validator.Draft4.Any: instance GHC.Show.Show err => GHC.Show.Show (Data.Validator.Draft4.Any.RefInvalid err)
+ Data.Validator.Draft4.Any: instance Test.QuickCheck.Arbitrary.Arbitrary Data.Validator.Draft4.Any.EnumVal
+ Data.Validator.Draft4.Any: instance Test.QuickCheck.Arbitrary.Arbitrary Data.Validator.Draft4.Any.TypeVal
+ Data.Validator.Draft4.Any: newtype EnumVal
+ Data.Validator.Draft4.Any: newtype VisitedSchemas
+ Data.Validator.Draft4.Any: notVal :: ToJSON schema => (schema -> Value -> [Fail err]) -> schema -> Value -> Maybe (Fail ())
+ Data.Validator.Draft4.Any: oneOf :: forall err schema. ToJSON schema => (schema -> Value -> [Fail err]) -> NonEmpty schema -> Value -> Maybe (Fail ())
+ Data.Validator.Draft4.Any: ref :: forall err schema. (FromJSON schema, ToJSON schema) => VisitedSchemas -> Maybe Text -> (Maybe Text -> Maybe schema) -> (VisitedSchemas -> Maybe Text -> schema -> Value -> [Fail err]) -> Text -> Value -> [Fail (RefInvalid err)]
+ Data.Validator.Draft4.Any: setFromTypeVal :: TypeVal -> Set Text
+ Data.Validator.Draft4.Any: typeVal :: TypeVal -> Value -> Maybe (Fail ())
+ Data.Validator.Draft4.Array: AdditionalBool :: Bool -> AdditionalItems schema
+ Data.Validator.Draft4.Array: AdditionalBoolInvalid :: AdditionalItemsInvalid err
+ Data.Validator.Draft4.Array: AdditionalItemsBoolInvalid :: ItemsInvalid err
+ Data.Validator.Draft4.Array: AdditionalItemsObjectInvalid :: err -> ItemsInvalid err
+ Data.Validator.Draft4.Array: AdditionalObject :: schema -> AdditionalItems schema
+ Data.Validator.Draft4.Array: AdditionalObjectInvalid :: err -> AdditionalItemsInvalid err
+ Data.Validator.Draft4.Array: Items :: err -> ItemsInvalid err
+ Data.Validator.Draft4.Array: ItemsArray :: [schema] -> Items schema
+ Data.Validator.Draft4.Array: ItemsObject :: schema -> Items schema
+ Data.Validator.Draft4.Array: additionalItems :: forall err schema. (schema -> Value -> [Fail err]) -> AdditionalItems schema -> Vector Value -> [Fail (AdditionalItemsInvalid err)]
+ Data.Validator.Draft4.Array: data AdditionalItems schema
+ Data.Validator.Draft4.Array: data AdditionalItemsInvalid err
+ Data.Validator.Draft4.Array: data Items schema
+ Data.Validator.Draft4.Array: data ItemsInvalid err
+ Data.Validator.Draft4.Array: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.Array.AdditionalItems schema)
+ Data.Validator.Draft4.Array: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.Array.Items schema)
+ Data.Validator.Draft4.Array: instance Data.Aeson.Types.Class.ToJSON schema => Data.Aeson.Types.Class.ToJSON (Data.Validator.Draft4.Array.AdditionalItems schema)
+ Data.Validator.Draft4.Array: instance Data.Aeson.Types.Class.ToJSON schema => Data.Aeson.Types.Class.ToJSON (Data.Validator.Draft4.Array.Items schema)
+ Data.Validator.Draft4.Array: instance GHC.Classes.Eq err => GHC.Classes.Eq (Data.Validator.Draft4.Array.AdditionalItemsInvalid err)
+ Data.Validator.Draft4.Array: instance GHC.Classes.Eq err => GHC.Classes.Eq (Data.Validator.Draft4.Array.ItemsInvalid err)
+ Data.Validator.Draft4.Array: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.Array.AdditionalItems schema)
+ Data.Validator.Draft4.Array: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.Array.Items schema)
+ Data.Validator.Draft4.Array: instance GHC.Show.Show err => GHC.Show.Show (Data.Validator.Draft4.Array.AdditionalItemsInvalid err)
+ Data.Validator.Draft4.Array: instance GHC.Show.Show err => GHC.Show.Show (Data.Validator.Draft4.Array.ItemsInvalid err)
+ Data.Validator.Draft4.Array: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.Array.AdditionalItems schema)
+ Data.Validator.Draft4.Array: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.Array.Items schema)
+ Data.Validator.Draft4.Array: instance Test.QuickCheck.Arbitrary.Arbitrary schema => Test.QuickCheck.Arbitrary.Arbitrary (Data.Validator.Draft4.Array.AdditionalItems schema)
+ Data.Validator.Draft4.Array: instance Test.QuickCheck.Arbitrary.Arbitrary schema => Test.QuickCheck.Arbitrary.Arbitrary (Data.Validator.Draft4.Array.Items schema)
+ Data.Validator.Draft4.Array: items :: forall err schema. (schema -> Value -> [Fail err]) -> Maybe (AdditionalItems schema) -> Items schema -> Vector Value -> [Fail (ItemsInvalid err)]
+ Data.Validator.Draft4.Array: maxItems :: Int -> Vector Value -> Maybe (Fail ())
+ Data.Validator.Draft4.Array: minItems :: Int -> Vector Value -> Maybe (Fail ())
+ Data.Validator.Draft4.Array: uniqueItems :: Bool -> Vector Value -> Maybe (Fail ())
+ Data.Validator.Draft4.Number: ExclusiveMaximum :: MaximumInvalid
+ Data.Validator.Draft4.Number: ExclusiveMinimum :: MinimumInvalid
+ Data.Validator.Draft4.Number: Maximum :: MaximumInvalid
+ Data.Validator.Draft4.Number: Minimum :: MinimumInvalid
+ Data.Validator.Draft4.Number: data MaximumInvalid
+ Data.Validator.Draft4.Number: data MinimumInvalid
+ Data.Validator.Draft4.Number: instance GHC.Classes.Eq Data.Validator.Draft4.Number.MaximumInvalid
+ Data.Validator.Draft4.Number: instance GHC.Classes.Eq Data.Validator.Draft4.Number.MinimumInvalid
+ Data.Validator.Draft4.Number: instance GHC.Show.Show Data.Validator.Draft4.Number.MaximumInvalid
+ Data.Validator.Draft4.Number: instance GHC.Show.Show Data.Validator.Draft4.Number.MinimumInvalid
+ Data.Validator.Draft4.Number: maximumVal :: Bool -> Scientific -> Scientific -> Maybe (Fail MaximumInvalid)
+ Data.Validator.Draft4.Number: minimumVal :: Bool -> Scientific -> Scientific -> Maybe (Fail MinimumInvalid)
+ Data.Validator.Draft4.Number: multipleOf :: Scientific -> Scientific -> Maybe (Fail ())
+ Data.Validator.Draft4.Object: APBoolInvalid :: AdditionalPropertiesInvalid err
+ Data.Validator.Draft4.Object: APObjectInvalid :: err -> AdditionalPropertiesInvalid err
+ Data.Validator.Draft4.Object: AdditionalPropertiesBool :: Bool -> AdditionalProperties schema
+ Data.Validator.Draft4.Object: AdditionalPropertiesObject :: schema -> AdditionalProperties schema
+ Data.Validator.Draft4.Object: PPAdditionalPropertiesInvalid :: (AdditionalPropertiesInvalid err) -> PatternPropertiesInvalid err
+ Data.Validator.Draft4.Object: PPInvalid :: err -> PatternPropertiesInvalid err
+ Data.Validator.Draft4.Object: PropAdditionalInvalid :: (AdditionalPropertiesInvalid err) -> PropertiesInvalid err
+ Data.Validator.Draft4.Object: PropPatternInvalid :: err -> PropertiesInvalid err
+ Data.Validator.Draft4.Object: PropertiesInvalid :: err -> PropertiesInvalid err
+ Data.Validator.Draft4.Object: PropertyDependency :: (Set Text) -> Dependency schema
+ Data.Validator.Draft4.Object: PropertyDependencyInvalid :: DependencyInvalid err
+ Data.Validator.Draft4.Object: Remaining :: HashMap Text Value -> Remaining
+ Data.Validator.Draft4.Object: Required :: Set Text -> Required
+ Data.Validator.Draft4.Object: SchemaDependency :: schema -> Dependency schema
+ Data.Validator.Draft4.Object: SchemaDependencyInvalid :: err -> DependencyInvalid err
+ Data.Validator.Draft4.Object: [_unRemaining] :: Remaining -> HashMap Text Value
+ Data.Validator.Draft4.Object: [_unRequired] :: Required -> Set Text
+ Data.Validator.Draft4.Object: additionalProperties :: forall err schema. (schema -> Value -> [Fail err]) -> Bool -> AdditionalProperties schema -> HashMap Text Value -> [Fail (AdditionalPropertiesInvalid err)]
+ Data.Validator.Draft4.Object: additionalPropertiesBool :: Bool -> HashMap Text Value -> [Fail ()]
+ Data.Validator.Draft4.Object: additionalPropertiesObject :: forall err schema. (schema -> Value -> [Fail err]) -> schema -> HashMap Text Value -> [Fail err]
+ Data.Validator.Draft4.Object: data AdditionalProperties schema
+ Data.Validator.Draft4.Object: data AdditionalPropertiesInvalid err
+ Data.Validator.Draft4.Object: data Dependency schema
+ Data.Validator.Draft4.Object: data DependencyInvalid err
+ Data.Validator.Draft4.Object: data PatternPropertiesInvalid err
+ Data.Validator.Draft4.Object: data PropertiesInvalid err
+ Data.Validator.Draft4.Object: dependencies :: forall err schema. (schema -> Value -> [Fail err]) -> HashMap Text (Dependency schema) -> HashMap Text Value -> [Fail (DependencyInvalid err)]
+ Data.Validator.Draft4.Object: instance Data.Aeson.Types.Class.FromJSON Data.Validator.Draft4.Object.Required
+ Data.Validator.Draft4.Object: instance Data.Aeson.Types.Class.FromJSON schema => Data.Aeson.Types.Class.FromJSON (Data.Validator.Draft4.Object.Dependency schema)
+ Data.Validator.Draft4.Object: instance Data.Aeson.Types.Class.ToJSON Data.Validator.Draft4.Object.Required
+ Data.Validator.Draft4.Object: instance Data.Aeson.Types.Class.ToJSON schema => Data.Aeson.Types.Class.ToJSON (Data.Validator.Draft4.Object.Dependency schema)
+ Data.Validator.Draft4.Object: instance GHC.Classes.Eq Data.Validator.Draft4.Object.Required
+ Data.Validator.Draft4.Object: instance GHC.Classes.Eq err => GHC.Classes.Eq (Data.Validator.Draft4.Object.DependencyInvalid err)
+ Data.Validator.Draft4.Object: instance GHC.Classes.Eq schema => GHC.Classes.Eq (Data.Validator.Draft4.Object.Dependency schema)
+ Data.Validator.Draft4.Object: instance GHC.Show.Show Data.Validator.Draft4.Object.Required
+ Data.Validator.Draft4.Object: instance GHC.Show.Show err => GHC.Show.Show (Data.Validator.Draft4.Object.DependencyInvalid err)
+ Data.Validator.Draft4.Object: instance GHC.Show.Show schema => GHC.Show.Show (Data.Validator.Draft4.Object.Dependency schema)
+ Data.Validator.Draft4.Object: instance Test.QuickCheck.Arbitrary.Arbitrary Data.Validator.Draft4.Object.Required
+ Data.Validator.Draft4.Object: instance Test.QuickCheck.Arbitrary.Arbitrary schema => Test.QuickCheck.Arbitrary.Arbitrary (Data.Validator.Draft4.Object.Dependency schema)
+ Data.Validator.Draft4.Object: maxProperties :: Int -> HashMap Text Value -> Maybe (Fail ())
+ Data.Validator.Draft4.Object: minProperties :: Int -> HashMap Text Value -> Maybe (Fail ())
+ Data.Validator.Draft4.Object: newtype Remaining
+ Data.Validator.Draft4.Object: newtype Required
+ Data.Validator.Draft4.Object: patternAndUnmatched :: forall err schema. (schema -> Value -> [Fail err]) -> HashMap Text schema -> HashMap Text Value -> ([Fail err], Remaining)
+ Data.Validator.Draft4.Object: patternProperties :: forall err schema. (schema -> Value -> [Fail err]) -> Bool -> Maybe (AdditionalProperties schema) -> HashMap Text schema -> HashMap Text Value -> [Fail (PatternPropertiesInvalid err)]
+ Data.Validator.Draft4.Object: properties :: forall err schema. (schema -> Value -> [Fail err]) -> Maybe (HashMap Text schema) -> Maybe (AdditionalProperties schema) -> HashMap Text schema -> HashMap Text Value -> [Fail (PropertiesInvalid err)]
+ Data.Validator.Draft4.Object: required :: Required -> HashMap Text Value -> Maybe (Fail ())
+ Data.Validator.Draft4.String: maxLength :: Int -> Text -> Maybe (Fail ())
+ Data.Validator.Draft4.String: minLength :: Int -> Text -> Maybe (Fail ())
+ Data.Validator.Draft4.String: patternVal :: Text -> Text -> Maybe (Fail ())
+ Data.Validator.Failure: Failure :: !err -> !Value -> !Pointer -> !Value -> Fail err
+ Data.Validator.Failure: [_failureFinalValidator] :: Fail err -> !Value
+ Data.Validator.Failure: [_failureOffendingData] :: Fail err -> !Value
+ Data.Validator.Failure: [_failureOffendingPointer] :: Fail err -> !Pointer
+ Data.Validator.Failure: [_failureValidatorsCalled] :: Fail err -> !err
+ Data.Validator.Failure: data Fail err
+ Data.Validator.Failure: instance GHC.Base.Functor Data.Validator.Failure.Fail
+ Data.Validator.Failure: instance GHC.Classes.Eq err => GHC.Classes.Eq (Data.Validator.Failure.Fail err)
+ Data.Validator.Failure: instance GHC.Show.Show err => GHC.Show.Show (Data.Validator.Failure.Fail err)
+ Data.Validator.Failure: prependToPath :: Token -> Fail a -> Fail a
+ Data.Validator.Types: Validator :: (val -> ([schema], [schema])) -> (val -> Value -> [Fail err]) -> Validator schema val err
+ Data.Validator.Types: [_embedded] :: Validator schema val err -> val -> ([schema], [schema])
+ Data.Validator.Types: [_validate] :: Validator schema val err -> val -> Value -> [Fail err]
+ Data.Validator.Types: data Validator schema val err
+ Data.Validator.Types: instance Data.Profunctor.Unsafe.Profunctor (Data.Validator.Types.Validator schema)
+ Data.Validator.Types: instance GHC.Base.Functor (Data.Validator.Types.Validator schema val)
+ Data.Validator.Utils: fromJSONEither :: FromJSON a => Value -> Either Text a
- Data.JsonSchema.Draft4: FVData :: (NonEmpty Invalid) -> FilesystemValidationFailure
+ Data.JsonSchema.Draft4: FVData :: Invalid -> FilesystemValidationFailure
- Data.JsonSchema.Draft4: FVSchema :: (NonEmpty Invalid) -> FilesystemValidationFailure
+ Data.JsonSchema.Draft4: FVSchema :: InvalidSchema -> FilesystemValidationFailure
- Data.JsonSchema.Draft4: HVData :: (NonEmpty Invalid) -> HTTPValidationFailure
+ Data.JsonSchema.Draft4: HVData :: Invalid -> HTTPValidationFailure
- Data.JsonSchema.Draft4: HVSchema :: (NonEmpty Invalid) -> HTTPValidationFailure
+ Data.JsonSchema.Draft4: HVSchema :: InvalidSchema -> HTTPValidationFailure
- Data.JsonSchema.Draft4: checkSchema :: ReferencedSchemas Schema -> SchemaWithURI Schema -> Either (NonEmpty Invalid) (Value -> [Invalid])
+ Data.JsonSchema.Draft4: checkSchema :: URISchemaMap Schema -> SchemaWithURI Schema -> Either InvalidSchema (Value -> [Failure])
- Data.JsonSchema.Draft4: referencesViaFilesystem :: SchemaWithURI Schema -> IO (Either FilesystemFailure (ReferencedSchemas Schema))
+ Data.JsonSchema.Draft4: referencesViaFilesystem :: SchemaWithURI Schema -> IO (Either FilesystemFailure (URISchemaMap Schema))
- Data.JsonSchema.Draft4: referencesViaHTTP :: SchemaWithURI Schema -> IO (Either HTTPFailure (ReferencedSchemas Schema))
+ Data.JsonSchema.Draft4: referencesViaHTTP :: SchemaWithURI Schema -> IO (Either HTTPFailure (URISchemaMap Schema))
- Data.JsonSchema.Draft4: schemaValidity :: Schema -> [Invalid]
+ Data.JsonSchema.Draft4: schemaValidity :: Schema -> [Failure]
- Data.JsonSchema.Draft4: type Invalid = Failure ValidatorChain
+ Data.JsonSchema.Draft4: type Invalid = NonEmpty Failure
- Data.JsonSchema.Fetch: includeSubschemas :: forall schema. Spec schema -> SchemaWithURI schema -> [SchemaWithURI schema]
+ Data.JsonSchema.Fetch: includeSubschemas :: forall schema. FetchInfo schema -> SchemaWithURI schema -> [SchemaWithURI schema]
- Data.JsonSchema.Fetch: referencesMethodAgnostic :: forall schema. FromJSON schema => (Text -> IO ByteString) -> Spec schema -> SchemaWithURI schema -> IO (Either Text (ReferencedSchemas schema))
+ Data.JsonSchema.Fetch: referencesMethodAgnostic :: forall schema. FromJSON schema => (Text -> IO ByteString) -> FetchInfo schema -> SchemaWithURI schema -> IO (Either Text (URISchemaMap schema))
- Data.JsonSchema.Fetch: referencesViaFilesystem' :: forall schema. FromJSON schema => Spec schema -> SchemaWithURI schema -> IO (Either FilesystemFailure (ReferencedSchemas schema))
+ Data.JsonSchema.Fetch: referencesViaFilesystem' :: forall schema. FromJSON schema => FetchInfo schema -> SchemaWithURI schema -> IO (Either FilesystemFailure (URISchemaMap schema))
- Data.JsonSchema.Fetch: referencesViaHTTP' :: forall schema. FromJSON schema => Spec schema -> SchemaWithURI schema -> IO (Either HTTPFailure (ReferencedSchemas schema))
+ Data.JsonSchema.Fetch: referencesViaHTTP' :: forall schema. FromJSON schema => FetchInfo schema -> SchemaWithURI schema -> IO (Either HTTPFailure (URISchemaMap schema))
- Data.Validator.Reference: resolveFragment :: (FromJSON schema, ToJSON schema, Show schema) => Maybe Text -> schema -> Maybe schema
+ Data.Validator.Reference: resolveFragment :: (FromJSON schema, ToJSON schema) => Maybe Text -> schema -> Maybe schema

Files

+ JSON-Schema-Test-Suite/tests/draft4/optional/bignum.json view
@@ -0,0 +1,107 @@+[+    {+        "description": "integer",+        "schema": {"type": "integer"},+        "tests": [+            {+                "description": "a bignum is an integer",+                "data": 12345678910111213141516171819202122232425262728293031,+                "valid": true+            }+        ]+    },+    {+        "description": "number",+        "schema": {"type": "number"},+        "tests": [+            {+                "description": "a bignum is a number",+                "data": 98249283749234923498293171823948729348710298301928331,+                "valid": true+            }+        ]+    },+    {+        "description": "integer",+        "schema": {"type": "integer"},+        "tests": [+            {+                "description": "a negative bignum is an integer",+                "data": -12345678910111213141516171819202122232425262728293031,+                "valid": true+            }+        ]+    },+    {+        "description": "number",+        "schema": {"type": "number"},+        "tests": [+            {+                "description": "a negative bignum is a number",+                "data": -98249283749234923498293171823948729348710298301928331,+                "valid": true+            }+        ]+    },+    {+        "description": "string",+        "schema": {"type": "string"},+        "tests": [+            {+                "description": "a bignum is not a string",+                "data": 98249283749234923498293171823948729348710298301928331,+                "valid": false+            }+        ]+    },+    {+        "description": "integer comparison",+        "schema": {"maximum": 18446744073709551615},+        "tests": [+            {+                "description": "comparison works for high numbers",+                "data": 18446744073709551600,+                "valid": true+            }+        ]+    },+    {+        "description": "float comparison with high precision",+        "schema": {+            "maximum": 972783798187987123879878123.18878137,+            "exclusiveMaximum": true+        },+        "tests": [+            {+                "description": "comparison works for high numbers",+                "data": 972783798187987123879878123.188781371,+                "valid": false+            }+        ]+    },+    {+        "description": "integer comparison",+        "schema": {"minimum": -18446744073709551615},+        "tests": [+            {+                "description": "comparison works for very negative numbers",+                "data": -18446744073709551600,+                "valid": true+            }+        ]+    },+    {+        "description": "float comparison with high precision on negative numbers",+        "schema": {+            "minimum": -972783798187987123879878123.18878137,+            "exclusiveMinimum": true+        },+        "tests": [+            {+                "description": "comparison works for very negative numbers",+                "data": -972783798187987123879878123.188781371,+                "valid": false+            }+        ]+    }+]
+ JSON-Schema-Test-Suite/tests/draft4/optional/format.json view
@@ -0,0 +1,148 @@+[+    {+        "description": "validation of date-time strings",+        "schema": {"format": "date-time"},+        "tests": [+            {+                "description": "a valid date-time string",+                "data": "1963-06-19T08:30:06.283185Z",+                "valid": true+            },+            {+                "description": "an invalid date-time string",+                "data": "06/19/1963 08:30:06 PST",+                "valid": false+            },+            {+                "description": "only RFC3339 not all of ISO 8601 are valid",+                "data": "2013-350T01:01:01",+                "valid": false+            }+        ]+    },+    {+        "description": "validation of URIs",+        "schema": {"format": "uri"},+        "tests": [+            {+                "description": "a valid URI",+                "data": "http://foo.bar/?baz=qux#quux",+                "valid": true+            },+            {+                "description": "a valid protocol-relative URI",+                "data": "//foo.bar/?baz=qux#quux",+                "valid": true+            },+            {+                "description": "an invalid URI",+                "data": "\\\\WINDOWS\\fileshare",+                "valid": false+            },+            {+                "description": "an invalid URI though valid URI reference",+                "data": "abc",+                "valid": false+            }+        ]+    },+    {+        "description": "validation of e-mail addresses",+        "schema": {"format": "email"},+        "tests": [+            {+                "description": "a valid e-mail address",+                "data": "joe.bloggs@example.com",+                "valid": true+            },+            {+                "description": "an invalid e-mail address",+                "data": "2962",+                "valid": false+            }+        ]+    },+    {+        "description": "validation of IP addresses",+        "schema": {"format": "ipv4"},+        "tests": [+            {+                "description": "a valid IP address",+                "data": "192.168.0.1",+                "valid": true+            },+            {+                "description": "an IP address with too many components",+                "data": "127.0.0.0.1",+                "valid": false+            },+            {+                "description": "an IP address with out-of-range values",+                "data": "256.256.256.256",+                "valid": false+            },+            {+                "description": "an IP address without 4 components",+                "data": "127.0",+                "valid": false+            },+            {+                "description": "an IP address as an integer",+                "data": "0x7f000001",+                "valid": false+            }+        ]+    },+    {+        "description": "validation of IPv6 addresses",+        "schema": {"format": "ipv6"},+        "tests": [+            {+                "description": "a valid IPv6 address",+                "data": "::1",+                "valid": true+            },+            {+                "description": "an IPv6 address with out-of-range values",+                "data": "12345::",+                "valid": false+            },+            {+                "description": "an IPv6 address with too many components",+                "data": "1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1",+                "valid": false+            },+            {+                "description": "an IPv6 address containing illegal characters",+                "data": "::laptop",+                "valid": false+            }+        ]+    },+    {+        "description": "validation of host names",+        "schema": {"format": "hostname"},+        "tests": [+            {+                "description": "a valid host name",+                "data": "www.example.com",+                "valid": true+            },+            {+                "description": "a host name starting with an illegal character",+                "data": "-a-host-name-that-starts-with--",+                "valid": false+            },+            {+                "description": "a host name containing illegal characters",+                "data": "not_a_valid_host_name",+                "valid": false+            },+            {+                "description": "a host name with a component too long",+                "data": "a-vvvvvvvvvvvvvvvveeeeeeeeeeeeeeeerrrrrrrrrrrrrrrryyyyyyyyyyyyyyyy-long-host-name-component",+                "valid": false+            }+        ]+    }+]
+ JSON-Schema-Test-Suite/tests/draft4/optional/zeroTerminatedFloats.json view
@@ -0,0 +1,15 @@+[+    {+        "description": "some languages do not distinguish between different types of numeric value",+        "schema": {+            "type": "integer"+        },+        "tests": [+            {+                "description": "a float is not an integer even without fractional part",+                "data": 1.0,+                "valid": false+            }+        ]+    }+]
JSON-Schema-Test-Suite/tests/draft4/pattern.json view
@@ -19,5 +19,16 @@                 "valid": true             }         ]+    },+    {+        "description": "pattern is not anchored",+        "schema": {"pattern": "a+"},+        "tests": [+            {+                "description": "matches a substring",+                "data": "xxaayy",+                "valid": true+            }+        ]     } ]
JSON-Schema-Test-Suite/tests/draft4/ref.json view
@@ -86,19 +86,34 @@         },         "tests": [             {-                "description": "slash",+                "description": "slash invalid",                 "data": {"slash": "aoeu"},                 "valid": false             },             {-                "description": "tilda",+                "description": "tilda invalid",                 "data": {"tilda": "aoeu"},                 "valid": false             },             {-                "description": "percent",+                "description": "percent invalid",                 "data": {"percent": "aoeu"},                 "valid": false+            },+            {+                "description": "slash valid",+                "data": {"slash": 123},+                "valid": true+            },+            {+                "description": "tilda valid",+                "data": {"tilda": 123},+                "valid": true+            },+            {+                "description": "percent valid",+                "data": {"percent": 123},+                "valid": true             }         ]     },
JSON-Schema-Test-Suite/tests/draft4/type.json view
@@ -188,7 +188,7 @@                 "valid": false             },             {-                "description": "an array is not an array",+                "description": "an array is an array",                 "data": [],                 "valid": true             },@@ -234,7 +234,7 @@                 "valid": false             },             {-                "description": "a boolean is not a boolean",+                "description": "a boolean is a boolean",                 "data": true,                 "valid": true             },
README.md view
@@ -4,8 +4,10 @@  [Hackage](https://hackage.haskell.org/package/hjsonschema) / [GitHub](https://github.com/seagreen/hjsonschema) / [Travis CI](https://travis-ci.org/seagreen/hjsonschema) -NOTE: You CANNOT use untrusted JSON data to make schemas. Schemas with circular references can cause infinite loops. See the issue list for more info.+Requires [pcre](http://www.pcre.org/) (`pkgs.pcre` in Nixpkgs). +NOTE: Schemas with circular references can cause infinite loops. hjsonschema does loop detection but it may not be solid yet -- please open an issue if you find a situation where it fails.+ # Example  See [here](https://github.com/seagreen/hjsonschema/blob/master/examples/Simple.hs).@@ -34,7 +36,7 @@  ## Good Parts -+ Passes all the tests in the [language agnostic test suite](https://github.com/json-schema/JSON-Schema-Test-Suite).++ Passes all the required tests in the [language agnostic test suite](https://github.com/json-schema/JSON-Schema-Test-Suite).  + Very modular, which should make it easy to support future versions of the specification. @@ -42,11 +44,13 @@  + Uses the [pcre-heavy](https://hackage.haskell.org/package/pcre-heavy) regular expression library for the "pattern" validator. It should use a library based on the ECMA 262 regex dialect, which the [spec](http://json-schema.org/latest/json-schema-validation.html#anchor33) requires. ++ Currently doesn't support the optional `"format"` validators.+ ## Notes  + `JSON-Schema-Test-Suite` is vendored from commit # aabcb3427745ade7a0b4d49ff016ad7eda8b898b [here](https://github.com/json-schema-org/JSON-Schema-Test-Suite). -+ `src/draft4.json` is from commit # cc8ec81ce0abe2385ebd6c2a6f2d6deb646f874a [here](https://github.com/json-schema/json-schema).++ `src/draft4.json` is from commit # f3d5aeb5ffbe9d9a5a0ceb761dc47c7c4c2efa68 [here](https://github.com/json-schema/json-schema).  ## Credits 
changelog.txt view
@@ -1,3 +1,39 @@+# 1.0.0.0++## Bug fixes:+++ Fix JSON Pointer bug. Pointers were being built in reverse order and so were+totally invalid.++ Use `.:!` instead of `.:?` to parse the draft 4 schema. The only way to omit+optional fields in JSON Schema Draft 4 is to omit them entirely, `"null"`+can't be used for this.++## API Changes:+++ Add referenced schema loop detection.++ Add a new `referencesValidity` function.++ `checkSchema` now checks referenced schema's validity in addition to the+starting schema's validity. This change bubbles up to the one-step validation+functions as well.++ Switch most of the fetching code to use `URISchemaMap` instead of+`ReferencedSchemas`. It didn't need to know about the more complicated data+type.++ Rething failure related names. Change `Invalid` to `Failure`, add a new+`Invalid` type alias which is only used for final results.++ Failures now include the failing part of the data as well as a JSON Pointer to+it, so you don't have to worry about resolving the pointer.++## Fundamental Changes:+++ Make `Fail` (previously `Failure`) an instance of `Functor`.++ Add a `Validator` data type which is an instance of `Profunctor`.++ Add a `Spec` data type for collections of `Validators`.++## General:+++ Switch from 2 to 4 space indentation.++ Update the vendored JSON Schema Test Suite.+ # 0.10.0.3  + Bump http-client.
+ examples/AlternateSchema.hs view
@@ -0,0 +1,160 @@+-- | An implementation of JSON Schema Draft 4 based on 'HashMap Text Value'+-- instead of a custom record type like 'Data.JsonSchema.Draft4'.+--+-- If you're writing code for a new schema specification you probably want+-- to copy this module instead of the 'Data.JsonSchema.Draft4'. While it's+-- less convenient to write schemas in Haskell without a record type, you+-- can get the implementation finished with far fewer lines of code.+--+-- Note that this module imports the the failure sum type and failure related+-- helper functions from the library. If you're implementing a custom schema+-- with different error types from JSON Schema Draft 4 you'll have to make+-- your own.++module AlternateSchema where++import           Data.Aeson                     (FromJSON(..), Value(..),+                                                 decode)+import qualified Data.Aeson                     as AE+import qualified Data.ByteString.Lazy           as LBS+import qualified Data.HashMap.Strict            as HM+import           Data.Maybe                     (fromMaybe)+import           Data.Monoid+import           Data.Profunctor                (Profunctor (..))+import           Data.Text                      (Text)++import           Data.JsonSchema.Draft4         (schemaForSchemasBytes)+import           Data.JsonSchema.Draft4.Failure+import           Data.JsonSchema.Fetch          (ReferencedSchemas(..),+                                                 SchemaWithURI(..))+import qualified Data.JsonSchema.Fetch          as FE+import           Data.JsonSchema.Types          (Schema(..), Spec(..))+import qualified Data.JsonSchema.Types          as JT+import qualified Data.Validator.Draft4          as D4+import qualified Data.Validator.Draft4.Any      as AN+import           Data.Validator.Reference       (updateResolutionScope)++--------------------------------------------------+-- * Basic fetching tools+--------------------------------------------------++referencesViaHTTP+    :: SchemaWithURI Schema+    -> IO (Either FE.HTTPFailure (FE.URISchemaMap Schema))+referencesViaHTTP = FE.referencesViaHTTP' draft4FetchInfo++draft4FetchInfo :: FE.FetchInfo Schema+draft4FetchInfo = FE.FetchInfo embedded (get "id") (get "$ref")+  where+    get :: Text -> Schema -> Maybe Text+    get k (Schema s) = case HM.lookup k s of+                           Just (String t) -> Just t+                           _ -> Nothing++embedded :: Schema -> ([Schema], [Schema])+embedded s = JT.embedded (d4Spec (ReferencedSchemas s mempty) mempty Nothing) s++--------------------------------------------------+-- * Main API+--------------------------------------------------++validate+    :: ReferencedSchemas Schema+    -> Maybe Text+    -> Schema+    -> Value+    -> [Failure]+validate rs = continueValidating rs (AN.VisitedSchemas [(Nothing, Nothing)])++-- A schema for schemas themselves, using @src/draft4.json@ which is loaded+-- at compile time.+schemaForSchemas :: Schema+schemaForSchemas =+      fromMaybe (error "Schema decode failed (this should never happen)")+    . decode+    . LBS.fromStrict+    $ schemaForSchemasBytes++checkSchema :: Schema -> [Failure]+checkSchema = validate referenced Nothing schemaForSchemas . Object . _unSchema+  where+    referenced :: ReferencedSchemas Schema+    referenced = ReferencedSchemas+                     schemaForSchemas+                     (HM.singleton "http://json-schema.org/draft-04/schema"+                         schemaForSchemas)++--------------------------------------------------+-- * Spec+--------------------------------------------------++continueValidating+    :: ReferencedSchemas Schema+    -> AN.VisitedSchemas+    -> Maybe Text+    -> Schema+    -> Value+    -> [Failure]+continueValidating referenced visited mURI sc =+    JT.validate (d4Spec referenced visited newScope) sc+  where+    schemaId :: Maybe Text+    schemaId = case HM.lookup "id" (_unSchema sc) of+                   Just (String t) -> Just t+                   _               -> Nothing++    newScope :: Maybe Text+    newScope = updateResolutionScope mURI schemaId++d4Spec+    :: ReferencedSchemas Schema+    -> AN.VisitedSchemas+    -> Maybe Text+    -> Spec Schema ValidatorChain+d4Spec referenced visited scope =+    Spec+        [ dimap f (const MultipleOf) D4.multipleOf+        , dimap f maxE D4.maximumVal+        , dimap f minE D4.minimumVal++        , dimap f (const MaxLength) D4.maxLength+        , dimap f (const MinLength) D4.minLength+        , dimap f (const PatternValidator) D4.patternVal++        , dimap f (const MaxItems) D4.maxItems+        , dimap f (const MinItems) D4.minItems+        , dimap f (const UniqueItems) D4.uniqueItems+        , dimap f itemsE (D4.items descend)++        , dimap f (const MaxProperties) D4.maxProperties+        , dimap f (const MinProperties) D4.minProperties+        , dimap f (const Required) D4.required+        , dimap f depsE (D4.dependencies descend)+        , dimap f propE (D4.properties descend)+        , dimap f patPropE (D4.patternProperties descend)+        , dimap f addPropE (D4.additionalProperties descend)++        , dimap f refE (D4.ref visited scope (FE.getReference referenced) refVal)+        , dimap f (const Enum) D4.enumVal+        , dimap f (const TypeValidator) D4.typeVal+        , dimap f AllOf (D4.allOf lateral)+        , dimap f (const AnyOf) (D4.anyOf lateral)+        , dimap f (const OneOf) (D4.oneOf lateral)+        , dimap f (const NotValidator) (D4.notVal lateral)+        ]+  where+    f :: FromJSON a => Schema -> Maybe a+    f (Schema a) = case AE.fromJSON (Object a) of+                       AE.Error _   -> Nothing+                       AE.Success b -> Just b++    -- 'Maybe Text' is the URI the refernced schema is fetch from,+    -- this probably needs a 'newtype' wrapper.+    refVal :: AN.VisitedSchemas -> Maybe Text -> Schema -> Value -> [Failure]+    refVal = continueValidating referenced++    descend :: Schema -> Value -> [Failure]+    descend = continueValidating referenced mempty scope++    lateral :: Schema -> Value -> [Failure]+    lateral = continueValidating referenced visited scope
− examples/CustomSchema.hs
@@ -1,71 +0,0 @@--- | A custom schema specification using one validator from--- 'Data.Validator.Draft4' and one original validator.------ This is a simple example because it doesn't allow references (so it--- doesn't need to define an 'embed' function @Schema -> [Schema]@ for use--- with 'fetchReferencedSchemas'.------ For a full example see 'Data.JsonSchema.Draft4' and its submodules.--- Code use between schema specifications will likely be OK but not great.--- All the 'Data.Validator' code as well as the 'Data.JsonSchema.Fetch' code--- is reusable, but there's a lot of boilerplate to tie it together.--module CustomSchema where--import           Control.Applicative-import           Data.Aeson-import           Data.Maybe             (maybeToList)-import           Data.Monoid-import           Data.Text              (Text)-import qualified Data.Text              as T--import qualified Data.Validator.Draft4  as VA-import qualified Data.Validator.Failure as FR---- | Our custom validator.-oddLength :: Bool -> Text -> Maybe (FR.Failure () )-oddLength b t-  | b == odd (T.length t) = Nothing-  | otherwise             = Just (FR.Invalid () (Bool b) mempty)--data CustomError-  = MaxLength-  | OddLength---- If we were really using the schema we would also need ToJSON and FromJSON--- instances.-data Schema = Schema-  { _schemaMaxLength :: Maybe Int-  , _schemaOddLength :: Maybe Bool-  }---- | If our 'Schema's themselves could be invalid we might want to write--- something like 'Data.JsonSchema.Draft4.schemaValidityy' for them.-validate :: Schema -> Value -> [FR.Failure CustomError]-validate s (String x) = concat-  [ f _schemaMaxLength (FR.setFailure MaxLength) (fmap maybeToList . VA.maxLength)-  , f _schemaOddLength (FR.setFailure OddLength) (fmap maybeToList . oddLength)-  ]-  where-    -- This pattern is overkill here, but is helpful if you have lots of-    -- validators (e.g. the Draft 4 schema has 27).-    f :: (Schema -> Maybe val)-      -> (err -> FR.Failure CustomError)-      -> (val -> Text -> [err])-      -> [FR.Failure CustomError]-    f field modifyError runVal =-      maybe mempty (\val -> modifyError <$> runVal val x) (field s)-validate _ _ = mempty -- Our schema passes everything that isn't a string.--example :: IO ()-example =-  case validate schema badData of-    [] -> error "We validated bad data."-    [FR.Invalid OddLength _ _] -> return () -- Success.-    _ -> error "We got a different failure than expected."-  where-    schema :: Schema-    schema = Schema (Just 100) (Just True)--    badData :: Value-    badData = String "even"
examples/Full.hs view
@@ -1,9 +1,15 @@--- | Step by step validation using 'D4.referencesViaFilesystem' and--- 'D4.checkSchema'. This means the actual validation invovles no IO.+-- | Two differences from @examples/Simple.hs@:+--+-- * This shows how to write the starting schema in Haskell instead+-- of parsing it from JSON.+--+-- * Validation is done in two steps using 'D4.referencesViaFilesystem' and+-- 'D4.checkSchema' instead of 'D4.fetchFilesystemAndValidate'. This means+-- that the actual validation involves no IO.  module Full where -import           Data.Aeson+import           Data.Aeson             (Value (..), toJSON)  import qualified Data.JsonSchema.Draft4 as D4 @@ -12,23 +18,23 @@  schemaContext :: D4.SchemaWithURI D4.Schema schemaContext = D4.SchemaWithURI-  { D4._swSchema = schema-  , D4._swURI    = Just "./examples/json/imaginary.json"-  }+    { D4._swSchema = schema+    , D4._swURI    = Just "./examples/json/start.json"+    }  badData :: Value badData = toJSON (["foo", "foo"] :: [String])  example :: IO () example = do-  res <- D4.referencesViaFilesystem schemaContext-  case res of-    Left _      -> error "Couldn't fetch referenced schemas."-    Right references -> do-      let validate = case D4.checkSchema references schemaContext of+    res <- D4.referencesViaFilesystem schemaContext+    let references = case res of+                         Left _  -> error "Couldn't fetch referenced schemas."+                         Right a -> a+        validate = case D4.checkSchema references schemaContext of                        Left _  -> error "Not a valid schema."                        Right f -> f-      case validate badData of+    case validate badData of         [] -> error "We validated bad data."-        [D4.Invalid (D4.Ref D4.UniqueItems) _ _] -> return () -- Success.-        e -> error "We got a different failure than expected."+        [D4.Failure (D4.Ref D4.UniqueItems) _ _ _] -> return () -- Success+        _ -> error "We got a different failure than expected."
− examples/PrettyShowFailure.hs
@@ -1,53 +0,0 @@--- | 'D4.Invalid's contain a JSON Pointer to the subset of the data that--- caused validation to fail, but they don't contain that data itself.------ If you want to display the invalid subset of the data here's how you--- resolve the JSON Pointer (which has the field name--- 'D4._invalidOffendingData') against the original data.------ NOTE: You have to have hjsonpointer in your build-depends.--module PrettyShowFailure where--import           Data.Aeson-import qualified Data.Aeson.Pointer     as AP-import           Data.Monoid--import qualified Data.JsonSchema.Draft4 as D4--badData :: Value-badData = toJSON [1, 2 :: Int]--failure :: D4.Invalid-failure = D4.Invalid-  { D4._invalidValidatorsCalled = D4.Items D4.MultipleOf-  , D4._invalidFinalValidator   = Number 2-  , D4._invalidOffendingData    = AP.Pointer [AP.Token "0"]-  }--example :: IO ()-example =-  case AP.resolve (D4._invalidOffendingData failure) badData of-    Left _  -> error "Couldn't resolve pointer."-    Right _ -> return () -- Success. We could feed the 'Right' value into-                         -- the otherwise unused 'msg' if we wanted to-                         -- display it.--msg :: Value -> String-msg subsetOfData = unlines-  [ "Invalid data. Here's the sequence of validators that caught it:"-  , ""-  , "  " <> show (D4._invalidValidatorsCalled failure)-  , ""-  , "Here's the contents of the final validator in that sequence:"-  , ""-  , "  " <> show (D4._invalidFinalValidator failure)-  , ""-  , "Here's a JSON Pointer to the invalid part of the data:"-  , ""-  , "  " <> show (D4._invalidOffendingData failure)-  , ""-  , "Here's the invalid part of the data:"-  , ""-  , "  " <> show subsetOfData-  ]
examples/Simple.hs view
@@ -1,39 +1,37 @@--- | Fetch any referenced schemas, check that our original schema is itself--- valid, then validate our data.+-- | Demonstrate 'D4.fetchFilesystemAndValidate'. -- -- To fetch schemas using HTTP instead of from the filesystem use -- 'D4.fetchHTTPAndValidate'.  module Simple where -import           Data.Aeson-import qualified Data.List.NonEmpty as NE+import           Data.Aeson             (Value(..), decode, toJSON)+import qualified Data.ByteString.Lazy   as LBS+import qualified Data.List.NonEmpty     as NE+import           Data.Maybe             (fromMaybe)  import qualified Data.JsonSchema.Draft4 as D4 -schema :: D4.Schema-schema = D4.emptySchema { D4._schemaRef = Just "./unique.json" }--schemaContext :: D4.SchemaWithURI D4.Schema-schemaContext = D4.SchemaWithURI-  { D4._swSchema = schema-  , D4._swURI    = Just "./examples/json/imaginary.json"-  -- ^ For this example we're pretending we found 'schema' at this location.-  -- Its relative links will be resolved from here.-  }- badData :: Value badData = toJSON (["foo", "foo"] :: [String])  example :: IO () example = do-  res <- D4.fetchFilesystemAndValidate schemaContext badData-  case res of-    Right () -> error "We validated bad data."-    Left (D4.FVRead _) -> error ("Error fetching a referenced schema"-                                ++ " (either during IO or parsing).")-    Left (D4.FVSchema _) -> error "Our 'schema' itself was invalid."-    Left (D4.FVData failures) ->-      case NE.toList failures of-        [D4.Invalid (D4.Ref D4.UniqueItems) _ _] -> return () -- Success.-        _ -> error "Got more invalidation reasons than we expected."++    -- Get the starting schema.+    bts <- LBS.readFile "./examples/json/start.json"+    let schema = fromMaybe (error "Invalid schema JSON.") (decode bts)+        schemaWithURI = D4.SchemaWithURI schema (Just "./examples/json/start.json")++    -- Fetch any referenced schemas, check that our original schema itself+    -- is valid, and validate the data.+    res <- D4.fetchFilesystemAndValidate schemaWithURI badData+    case res of+        Right ()                  -> error "We validated bad data."+        Left (D4.FVRead _)        -> error ("Error fetching a referenced schema"+                                            ++ " (either during IO or parsing).")+        Left (D4.FVSchema _)      -> error "Our 'schema' itself was invalid."+        Left (D4.FVData failures) ->+            case NE.toList failures of+                [D4.Failure (D4.Ref D4.UniqueItems) _ _ _] -> return () -- Success.+                _ -> error "Got more invalidation reasons than we expected."
+ examples/json/start.json view
@@ -0,0 +1,3 @@+{+  "$ref": "./unique.json"+}
hjsonschema.cabal view
@@ -1,5 +1,5 @@ name:                   hjsonschema-version:                0.10.0.3+version:                1.0.0.0 synopsis:               JSON Schema library homepage:               https://github.com/seagreen/hjsonschema license:                MIT@@ -14,33 +14,43 @@                         JSON-Schema-Test-Suite/remotes/*.json                         JSON-Schema-Test-Suite/remotes/folder/*.json                         JSON-Schema-Test-Suite/tests/draft4/*.json+                        JSON-Schema-Test-Suite/tests/draft4/optional/*.json                         README.md                         src/draft4.json                         test/Local/*.json+                        test/supplement/*.json                         examples/json/*.json  library   hs-source-dirs:       src   exposed-modules:      Data.JsonSchema.Draft4-                      , Data.JsonSchema.Draft4.Internal+                      , Data.JsonSchema.Draft4.Failure+                      , Data.JsonSchema.Draft4.Schema+                      , Data.JsonSchema.Draft4.Spec                       , Data.JsonSchema.Fetch+                      , Data.JsonSchema.Types                       , Data.Validator.Draft4-                      , Data.Validator.Failure-                      , Data.Validator.Reference-                      , Data.Validator.Utils--  other-modules:        Data.JsonSchema.Draft4.Failure-                      , Data.JsonSchema.Draft4.Schema                       , Data.Validator.Draft4.Any                       , Data.Validator.Draft4.Array                       , Data.Validator.Draft4.Number                       , Data.Validator.Draft4.Object-                      , Data.Validator.Draft4.Object.Properties                       , Data.Validator.Draft4.String+                      , Data.Validator.Failure+                      , Data.Validator.Reference+                      , Data.Validator.Types+                      , Data.Validator.Utils++  other-modules:        Data.Validator.Draft4.Object.Properties                       , Import   default-language:     Haskell2010-  default-extensions:   ScopedTypeVariables+  default-extensions:   NoImplicitPrelude                         OverloadedStrings+                        ScopedTypeVariables+  if impl(ghc >= 8)+    default-extensions: StrictData+  other-extensions:     DeriveFunctor+                        GeneralizedNewtypeDeriving+                        TemplateHaskell   ghc-options:          -Wall   build-depends:        aeson                >= 0.7    && < 0.12                       , base                 >= 4.7    && < 4.10@@ -49,10 +59,12 @@                       , file-embed           >= 0.0.8  && < 0.1                       , filepath             >= 1.3    && < 1.5                       , hjsonpointer         >= 0.3    && < 0.4-                      , http-client          >= 0.4    && < 0.6+                      -- 0.4.30 is for parseUrlThrow:+                      , http-client          >= 0.4.30 && < 0.6                       , http-types           >= 0.8    && < 0.10                       , pcre-heavy           >= 1.0    && < 1.1-                      , QuickCheck           >= 2.8.1  && < 2.9+                      , profunctors          >= 5.0    && < 5.3+                      , QuickCheck           >= 2.8    && < 2.10                       , scientific           >= 0.3    && < 0.4                       , semigroups           >= 0.18   && < 0.19                       , unordered-containers >= 0.2    && < 0.3@@ -65,49 +77,61 @@                         examples   main-is:              Local.hs   other-modules:        Local.Failure-                      , Local.Filesystem                       , Local.Reference+                      , Local.Validation                       , Shared                       -- from ./examples:-                      , CustomSchema+                      , AlternateSchema                       , Full-                      , PrettyShowFailure                       , Simple   default-language:     Haskell2010-  ghc-options:          -Wall -fno-warn-orphans+  ghc-options:          -Wall+                        -fno-warn-orphans   default-extensions:   OverloadedStrings+                        ScopedTypeVariables   build-depends:        aeson                       , base                       , bytestring                       , filepath                       , hjsonpointer                       , hjsonschema+                      , profunctors                       , semigroups                       , text                       , QuickCheck                       , unordered-containers                       , vector-                      , directory            >= 1.2  && < 1.3-                      , HUnit                >= 1.2  && < 1.4-                      , tasty                >= 0.11 && < 0.12-                      , tasty-hunit          >= 0.9  && < 0.10-                      , tasty-quickcheck     >= 0.8  && < 0.9+                      -- directory-1.2.5 required for `listDirectory`:+                      , directory            >= 1.2.5 && < 1.3+                      , HUnit                >= 1.2   && < 1.4+                      , tasty                >= 0.11  && < 0.12+                      , tasty-hunit          >= 0.9   && < 0.10+                      , tasty-quickcheck     >= 0.8   && < 0.9  test-suite remote   type:                 exitcode-stdio-1.0   hs-source-dirs:       test+                        examples   main-is:              Remote.hs   other-modules:        Shared+                      -- from ./examples:+                      , AlternateSchema   default-language:     Haskell2010-  ghc-options:          -Wall -fno-warn-orphans+  ghc-options:          -Wall+                        -fno-warn-orphans   default-extensions:   OverloadedStrings+                        ScopedTypeVariables   build-depends:        aeson                       , async                       , base                       , bytestring                       , filepath+                      , hjsonpointer                       , hjsonschema+                      , profunctors+                      , semigroups                       , text+                      , unordered-containers                       , vector                       , directory                       , HUnit
src/Data/JsonSchema/Draft4.hs view
@@ -1,136 +1,185 @@-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TemplateHaskell     #-}+{-# LANGUAGE TemplateHaskell #-}  module Data.JsonSchema.Draft4-  ( Schema(..)-  , emptySchema+    ( -- * Draft 4 Schema+      SchemaWithURI(..)+    , Schema(..)+    , SC.emptySchema -    -- * One-step validation-  , HTTPValidationFailure(..)-  , fetchHTTPAndValidate-  , FilesystemValidationFailure(..)-  , fetchFilesystemAndValidate+      -- * One-step validation (getting references over HTTP)+    , fetchHTTPAndValidate+    , HTTPValidationFailure(..)+    , FE.HTTPFailure(..)+    , InvalidSchema -    -- * Fetching tools-  , SchemaWithURI(..)-  , ReferencedSchemas(..)-  , HTTPFailure(..)-  , referencesViaHTTP-  , FilesystemFailure(..)-  , referencesViaFilesystem+      -- * One-step validation (getting references from the filesystem)+    , fetchFilesystemAndValidate+    , FilesystemValidationFailure(..)+    , FE.FilesystemFailure(..) -    -- * Failure-  , Invalid-  , FR.Failure(..)-  , ValidatorChain(..)+      -- * Validation failure+    , Invalid+    , Failure+    , FR.Fail(..)+    , ValidatorChain(..) -    -- * Other Draft 4 things exported just in case-  , checkSchema-  , schemaValidity-  , IN.runValidate-  , draft4Spec-  ) where+      -- * Fetching tools+    , ReferencedSchemas(..)+    , referencesViaHTTP+    , referencesViaFilesystem -import           Control.Applicative-import           Control.Arrow                   (left)-import           Data.Aeson+      -- * Other Draft 4 things exported just in case+    , schemaForSchemas+    , schemaForSchemasBytes+    , schemaValidity+    , referencesValidity+    , checkSchema+    , draft4FetchInfo+    ) where++import           Import+import           Prelude++import           Control.Arrow                   (first, left)+import qualified Data.ByteString                 as BS import qualified Data.ByteString.Lazy            as LBS-import           Data.FileEmbed-import qualified Data.HashMap.Strict             as H-import           Data.List.NonEmpty              (NonEmpty)-import qualified Data.List.NonEmpty              as N+import           Data.FileEmbed                  (embedFile,+                                                  makeRelativeToProject)+import qualified Data.HashMap.Strict             as HM+import qualified Data.List.NonEmpty              as NE import           Data.Maybe                      (fromMaybe) -import           Data.JsonSchema.Draft4.Failure  (Invalid, ValidatorChain(..))-import qualified Data.JsonSchema.Draft4.Internal as IN-import           Data.JsonSchema.Draft4.Schema-import           Data.JsonSchema.Fetch           (FilesystemFailure(..),-                                                  HTTPFailure(..),-                                                  ReferencedSchemas(..),+import           Data.JsonSchema.Draft4.Failure  (Failure, Invalid,+                                                  InvalidSchema,+                                                  ValidatorChain(..))+import           Data.JsonSchema.Draft4.Schema   (Schema)+import qualified Data.JsonSchema.Draft4.Schema   as SC+import qualified Data.JsonSchema.Draft4.Spec     as Spec+import           Data.JsonSchema.Fetch           (ReferencedSchemas(..),                                                   SchemaWithURI(..)) import qualified Data.JsonSchema.Fetch           as FE import qualified Data.Validator.Failure          as FR  data HTTPValidationFailure-  = HVRequest HTTPFailure-  | HVSchema  (NonEmpty Invalid)-  | HVData    (NonEmpty Invalid)-  deriving Show+    = HVRequest FE.HTTPFailure+    | HVSchema  InvalidSchema+    | HVData    Invalid+    deriving Show +-- | Fetch recursively referenced schemas over HTTP, check that both the+-- original and referenced schemas are valid, and then validate data. fetchHTTPAndValidate-  :: SchemaWithURI Schema-  -> Value-  -> IO (Either HTTPValidationFailure ())+    :: SchemaWithURI Schema+    -> Value+    -> IO (Either HTTPValidationFailure ()) fetchHTTPAndValidate sw v = do-  res <- referencesViaHTTP sw-  pure (g =<< f =<< left HVRequest res)+    res <- referencesViaHTTP sw+    pure (g =<< f =<< left HVRequest res)   where-    f :: ReferencedSchemas Schema-      -> Either HTTPValidationFailure (Value -> [Invalid])+    f :: FE.URISchemaMap Schema+      -> Either HTTPValidationFailure (Value -> [Failure])     f references = left HVSchema (checkSchema references sw) -    g :: (Value -> [Invalid]) -> Either HTTPValidationFailure ()-    g validate = case N.nonEmpty (validate v) of-                   Nothing       -> Right ()-                   Just failures -> Left (HVData failures)+    g :: (Value -> [Failure]) -> Either HTTPValidationFailure ()+    g validate = case NE.nonEmpty (validate v) of+                     Nothing       -> Right ()+                     Just failures -> Left (HVData failures)  data FilesystemValidationFailure-  = FVRead   FilesystemFailure-  | FVSchema (NonEmpty Invalid)-  | FVData   (NonEmpty Invalid)-  deriving Show+    = FVRead   FE.FilesystemFailure+    | FVSchema InvalidSchema+    | FVData   Invalid+    deriving (Show, Eq) +-- | Fetch recursively referenced schemas from the filesystem, check that+-- both the original and referenced schemas are valid, and then+-- validate data. fetchFilesystemAndValidate-  :: SchemaWithURI Schema-  -> Value-  -> IO (Either FilesystemValidationFailure ())+    :: SchemaWithURI Schema+    -> Value+    -> IO (Either FilesystemValidationFailure ()) fetchFilesystemAndValidate sw v = do-  res <- referencesViaFilesystem sw-  pure (g =<< f =<< left FVRead res)+    res <- referencesViaFilesystem sw+    pure (g =<< f =<< left FVRead res)   where-    f :: ReferencedSchemas Schema-      -> Either FilesystemValidationFailure (Value -> [Invalid])+    f :: FE.URISchemaMap Schema+      -> Either FilesystemValidationFailure (Value -> [Failure])     f references = left FVSchema (checkSchema references sw) -    g :: (Value -> [Invalid]) -> Either FilesystemValidationFailure ()-    g validate = case N.nonEmpty (validate v) of-                   Nothing       -> Right ()-                   Just failures -> Left (FVData failures)---- | Check the that a schema itself is valid.------ Return a function to validate data.-checkSchema-  :: ReferencedSchemas Schema-  -> SchemaWithURI Schema-  -> Either (NonEmpty Invalid) (Value -> [Invalid])-checkSchema referenced schemaWithURI =-  case N.nonEmpty . schemaValidity . _swSchema $ schemaWithURI of-    Nothing       -> Right (IN.runValidate referenced schemaWithURI)-    Just failures -> Left failures+    g :: (Value -> [Failure]) -> Either FilesystemValidationFailure ()+    g validate = case NE.nonEmpty (validate v) of+                     Nothing      -> Right ()+                     Just invalid -> Left (FVData invalid) -draft4Spec :: FE.Spec Schema-draft4Spec = FE.Spec IN.embedded _schemaId _schemaRef+-- | An instance of 'Data.JsonSchema.Fetch.FetchInfo' specialized for+-- JSON Schema Draft 4.+draft4FetchInfo :: FE.FetchInfo Schema+draft4FetchInfo = FE.FetchInfo Spec.embedded SC._schemaId SC._schemaRef +-- | Fetch the schemas recursively referenced by a starting schema over HTTP. referencesViaHTTP-  :: SchemaWithURI Schema-  -> IO (Either HTTPFailure (ReferencedSchemas Schema))-referencesViaHTTP = FE.referencesViaHTTP' draft4Spec+    :: SchemaWithURI Schema+    -> IO (Either FE.HTTPFailure (FE.URISchemaMap Schema))+referencesViaHTTP = FE.referencesViaHTTP' draft4FetchInfo +-- | Fetch the schemas recursively referenced by a starting schema from+-- the filesystem. referencesViaFilesystem-  :: SchemaWithURI Schema-  -> IO (Either FilesystemFailure (ReferencedSchemas Schema))-referencesViaFilesystem = FE.referencesViaFilesystem' draft4Spec+    :: SchemaWithURI Schema+    -> IO (Either FE.FilesystemFailure (FE.URISchemaMap Schema))+referencesViaFilesystem = FE.referencesViaFilesystem' draft4FetchInfo --- | Check that a schema itself is valid.-schemaValidity :: Schema -> [Invalid]-schemaValidity = IN.runValidate referenced (SchemaWithURI d4 Nothing) . toJSON+-- | A helper function.+--+-- Checks if a schema and a set of referenced schemas are valid.+--+-- Return a function to validate data.+checkSchema+    :: FE.URISchemaMap Schema+    -> SchemaWithURI Schema+    -> Either InvalidSchema (Value -> [Failure])+checkSchema sm sw =+    case NE.nonEmpty failures of+        Nothing -> Right (Spec.validate (ReferencedSchemas (_swSchema sw) sm) sw)+        Just fs -> Left fs   where-    d4 :: Schema-    d4 = fromMaybe (error "Schema decode failed (this should never happen)")-       . decode . LBS.fromStrict $ $(embedFile "src/draft4.json")+    failures :: [(Maybe Text, Failure)]+    failures = ((\v -> (Nothing, v)) <$> schemaValidity (_swSchema sw))+            <> (first Just <$> referencesValidity sm) +schemaForSchemas :: Schema+schemaForSchemas =+      fromMaybe (error "Schema decode failed (this should never happen)")+    . decode+    . LBS.fromStrict+    $ schemaForSchemasBytes++schemaForSchemasBytes :: BS.ByteString+schemaForSchemasBytes =+    $(makeRelativeToProject "src/draft4.json" >>= embedFile)++-- | Check that a schema itself is valid+-- (if so the returned list will be empty).+schemaValidity :: Schema -> [Failure]+schemaValidity =+    Spec.validate referenced (SchemaWithURI schemaForSchemas Nothing) . toJSON+  where     referenced :: ReferencedSchemas Schema     referenced = ReferencedSchemas-                   d4-                   (H.singleton "http://json-schema.org/draft-04/schema" d4)+                     schemaForSchemas+                     (HM.singleton "http://json-schema.org/draft-04/schema"+                         schemaForSchemas)++-- | Check that a set of referenced schemas are valid+-- (if so the returned list will be empty).+referencesValidity+  :: FE.URISchemaMap Schema+  -> [(Text, Failure)]+  -- ^ The first value in the tuple is the URI of a referenced schema.+referencesValidity = HM.foldlWithKey' f mempty+  where+    f :: [(Text, Failure)]+      -> Text+      -> Schema+      -> [(Text, Failure)]+    f acc k v = ((\a -> (k,a)) <$> schemaValidity v) <> acc
src/Data/JsonSchema/Draft4/Failure.hs view
@@ -1,53 +1,118 @@  module Data.JsonSchema.Draft4.Failure where -import qualified Data.Validator.Failure as FR+import           Prelude -type Invalid = FR.Failure ValidatorChain+import           Data.List.NonEmpty            (NonEmpty)+import           Data.Text                     (Text) +import qualified Data.Validator.Draft4.Any    as AN+import qualified Data.Validator.Draft4.Array  as AR+import qualified Data.Validator.Draft4.Number as NU+import qualified Data.Validator.Draft4.Object as OB+import qualified Data.Validator.Failure       as FR++-- | A description of why a schema (or one of its reference) is itself+-- invalid.+--+-- 'Nothing' indicates the starting schema. 'Just' indicates a referenced+-- schema -- the contents of the 'Just' is the schema's URI.+--+-- NOTE: 'HashMap (Maybe Text) Invalid' would be a nicer way of defining+-- this, but then we lose the guarantee that there's at least one key.+type InvalidSchema = NonEmpty (Maybe Text, Failure)++type Invalid = NonEmpty Failure++type Failure = FR.Fail ValidatorChain++-- | Distinguish all the different possible causes of failure for+-- Draft 4 validation. data ValidatorChain-  = MultipleOf-  | Maximum-  | ExclusiveMaximum-  | Minimum-  | ExclusiveMinimum+    = MultipleOf+    | Maximum+    | ExclusiveMaximum+    | Minimum+    | ExclusiveMinimum -  | MaxLength-  | MinLength-  | PatternValidator+    | MaxLength+    | MinLength+    | PatternValidator -  | MaxItems-  | MinItems-  | UniqueItems-  | Items ValidatorChain-  | AdditionalItemsBool-  | AdditionalItemsObject ValidatorChain+    | MaxItems+    | MinItems+    | UniqueItems+    | Items ValidatorChain+    | AdditionalItemsBool+    | AdditionalItemsObject ValidatorChain -  | MaxProperties-  | MinProperties-  | Required-  | SchemaDependency ValidatorChain-  | PropertyDependency-  | Properties ValidatorChain-  | PatternProperties ValidatorChain-  | AdditionalPropertiesBool-  | AdditionalPropertiesObject ValidatorChain+    | MaxProperties+    | MinProperties+    | Required+    | SchemaDependency ValidatorChain+    | PropertyDependency+    | Properties ValidatorChain+    | PatternProperties ValidatorChain+    | AdditionalPropertiesBool+    | AdditionalPropertiesObject ValidatorChain -  | RefResolution-    -- ^ Indicates a reference that failed to resolve.-    ---    -- NOTE: The language agnostic test suite doesn't specify if this should-    -- cause a validation error or should allow data to pass. We choose to-    -- return a validation error.-    ---    -- Also note that ideally we would enforce in the type system that any-    -- failing references be dealt with before valididation. Then this could-    -- be removed entirely.-  | Ref ValidatorChain-  | Enum-  | TypeValidator-  | AllOf ValidatorChain-  | AnyOf-  | OneOf-  | NotValidator-  deriving (Eq, Show)+    | RefResolution+      -- ^ Indicates a reference that failed to resolve.+      --+      -- NOTE: The language agnostic test suite doesn't specify if this should+      -- cause a validation error or should allow data to pass. We choose to+      -- return a validation error.+      --+      -- Also note that ideally we would enforce in the type system that any+      -- failing references be dealt with before valididation. Then this could+      -- be removed entirely.+    | RefLoop+    | Ref ValidatorChain+    | Enum+    | TypeValidator+    | AllOf ValidatorChain+    | AnyOf+    | OneOf+    | NotValidator+    deriving (Eq, Show)++maxE :: NU.MaximumInvalid -> ValidatorChain+maxE NU.Maximum          = Maximum+maxE NU.ExclusiveMaximum = ExclusiveMaximum++minE :: NU.MinimumInvalid -> ValidatorChain+minE NU.Minimum          = Minimum+minE NU.ExclusiveMinimum = ExclusiveMinimum++itemsE :: AR.ItemsInvalid ValidatorChain -> ValidatorChain+itemsE (AR.Items err)                        = Items err+itemsE AR.AdditionalItemsBoolInvalid         = AdditionalItemsBool+itemsE (AR.AdditionalItemsObjectInvalid err) = AdditionalItemsObject err++depsE :: OB.DependencyInvalid ValidatorChain -> ValidatorChain+depsE (OB.SchemaDependencyInvalid err) = SchemaDependency err+depsE OB.PropertyDependencyInvalid     = PropertyDependency++propE :: OB.PropertiesInvalid ValidatorChain -> ValidatorChain+propE (OB.PropertiesInvalid err)   = Properties err+propE (OB.PropPatternInvalid err)  = PatternProperties err+propE (OB.PropAdditionalInvalid a) =+    case a of+        OB.APBoolInvalid       -> AdditionalPropertiesBool+        OB.APObjectInvalid err -> AdditionalPropertiesObject err++patPropE :: OB.PatternPropertiesInvalid ValidatorChain -> ValidatorChain+patPropE (OB.PPInvalid err) = PatternProperties err+patPropE (OB.PPAdditionalPropertiesInvalid a)   =+    case a of+        OB.APBoolInvalid       -> AdditionalPropertiesBool+        OB.APObjectInvalid err -> AdditionalPropertiesObject err++addPropE :: OB.AdditionalPropertiesInvalid ValidatorChain -> ValidatorChain+addPropE OB.APBoolInvalid         = AdditionalPropertiesBool+addPropE (OB.APObjectInvalid err) = AdditionalItemsObject err++refE :: AN.RefInvalid ValidatorChain -> ValidatorChain+refE AN.RefResolution    = RefResolution+refE AN.RefLoop          = RefLoop+refE (AN.RefInvalid err) = Ref err
− src/Data/JsonSchema/Draft4/Internal.hs
@@ -1,267 +0,0 @@--module Data.JsonSchema.Draft4.Internal where--import           Data.Aeson-import qualified Data.HashMap.Strict            as H-import qualified Data.List.NonEmpty             as N-import           Data.Maybe                     (catMaybes, fromMaybe, isJust,-                                                 maybeToList)-import           Data.Scientific--import           Data.JsonSchema.Draft4.Failure-import           Data.JsonSchema.Draft4.Schema-import           Data.JsonSchema.Fetch          (ReferencedSchemas(..),-                                                 SchemaWithURI(..))-import qualified Data.Validator.Draft4.Any      as AN-import qualified Data.Validator.Draft4.Array    as AR-import qualified Data.Validator.Draft4.Number   as NU-import qualified Data.Validator.Draft4.Object   as OB-import qualified Data.Validator.Draft4.String   as ST-import           Data.Validator.Failure         (modFailure, setFailure)-import qualified Data.Validator.Failure         as FR-import           Data.Validator.Reference       (updateResolutionScope)-import           Import---- For GHCs before 7.10:-import           Prelude                        hiding (concat)------------------------------------------------------- * Embedded Schemas------------------------------------------------------- | Return a schema's immediate subschemas.------ Pass this to 'fetchReferencedSchemas' so that function can find all the--- subschemas in a document. This allows 'fetchReferencedSchemas' to process--- only "$ref"s and "id"s that are actual schema keywords. For example,--- within a "properties" validator object an "id" key doesn't actually change--- any scope, but instead serves a validator-specific function.-embedded :: Schema -> [Schema]-embedded schema = concat-  [ f _schemaItems-      (\x -> case x of-               AR.ItemsObject s -> pure s-               AR.ItemsArray ss -> ss-      )-  , f _schemaAdditionalItems-      (\x -> case x of-               AR.AdditionalObject s -> pure s-               _                     -> mempty-      )-  , f _schemaDependencies (catMaybes . fmap checkDependency . H.elems)-  , f _schemaProperties H.elems-  , f _schemaPatternProperties H.elems-  , f _schemaAdditionalProperties-      (\x -> case x of-               OB.AdditionalPropertiesObject s -> pure s-               _                               -> mempty-      )-  , f _schemaAllOf N.toList-  , f _schemaAnyOf N.toList-  , f _schemaOneOf N.toList-  , f _schemaNot pure-  , f _schemaDefinitions H.elems-  ]-  where-    f :: (Schema -> Maybe a) -> (a -> [Schema]) -> [Schema]-    f field nextLevelEmbedded = maybe mempty nextLevelEmbedded (field schema)--    checkDependency :: OB.Dependency Schema -> Maybe Schema-    checkDependency (OB.PropertyDependency _) = Nothing-    checkDependency (OB.SchemaDependency s)   = Just s------------------------------------------------------- * Validation (Main internal functions)-----------------------------------------------------runValidate-  :: ReferencedSchemas Schema-  -> SchemaWithURI Schema-  -> Value-  -> [Invalid]-runValidate referenced sw x = concat-  [ f _schemaEnum  (setFailure Enum)          (fmap maybeToList . AN.enumVal)-  , f _schemaType  (setFailure TypeValidator) (fmap maybeToList . AN.typeVal)-  , f _schemaAllOf (modFailure AllOf)         (AN.allOf recurse)-  , f _schemaAnyOf (setFailure AnyOf)         (fmap maybeToList . AN.anyOf recurse)-  , f _schemaOneOf (setFailure OneOf)         (fmap maybeToList . AN.oneOf recurse)-  , f _schemaNot   (setFailure NotValidator)  (fmap maybeToList . AN.notVal recurse)-  , refFailures-  ] <> specificValidators-  where-    specificValidators :: [Invalid]-    specificValidators =-      case x of-        Number y -> validateNumber (_swSchema sw) y-        String y -> validateString (_swSchema sw) y-        Array y  -> validateArray referenced sw y-        Object y -> validateObject referenced sw y-        _        -> mempty--    f = runSingle (_swSchema sw) x--    recurse = descendNextLevel referenced sw--    -- Since the results of the 'AN.ref' validator are fairly complicated [1]-    -- it's simpler not to use our 'f' helper function for it.-    ---    -- [1] A list of errors wrapped in a 'Maybe' where 'Nothing' represents-    -- if resolving the reference itself failed.-    refFailures :: [Invalid]-    refFailures =-      case _schemaRef (_swSchema sw) of-        Nothing        -> mempty-        Just reference ->-          maybe [FR.Invalid RefResolution (toJSON reference) mempty]-                (fmap (modFailure Ref))-                $ AN.ref scope-                         getReference-                         (\a b -> runValidate referenced (SchemaWithURI b a))-                         reference-                         x-      where-        scope :: Maybe Text-        scope = updateResolutionScope (_swURI sw) (_schemaId (_swSchema sw))--        getReference :: Maybe Text -> Maybe Schema-        getReference Nothing  = Just (_rsStarting referenced)-        getReference (Just t) = H.lookup t (_rsSchemaMap referenced)--validateString-  :: Schema-  -> Text-  -> [Invalid]-validateString schema x = concat-  [ f _schemaMaxLength (setFailure MaxLength)        (fmap maybeToList . ST.maxLength)-  , f _schemaMinLength (setFailure MinLength)        (fmap maybeToList . ST.minLength)-  , f _schemaPattern   (setFailure PatternValidator) (fmap maybeToList . ST.patternVal)-  ]-  where-    f = runSingle schema x--validateNumber-  :: Schema-  -> Scientific-  -> [Invalid]-validateNumber schema x = concat-  [ f _schemaMultipleOf (setFailure MultipleOf) (fmap maybeToList . NU.multipleOf)-  , f _schemaMaximum-      (modFailure fMax)-      ( fmap maybeToList-      . NU.maximumVal (fromMaybe False (_schemaExclusiveMaximum schema))-      )-  , f _schemaMinimum-      (modFailure fMin)-      ( fmap maybeToList-      . NU.minimumVal (fromMaybe False (_schemaExclusiveMinimum schema))-      )-  ]-  where-    f = runSingle schema x--    fMax NU.Maximum          = Maximum-    fMax NU.ExclusiveMaximum = ExclusiveMaximum--    fMin NU.Minimum          = Minimum-    fMin NU.ExclusiveMinimum = ExclusiveMinimum--validateArray-  :: ReferencedSchemas Schema-  -> SchemaWithURI Schema-  -> Vector Value-  -> [Invalid]-validateArray referenced (SchemaWithURI schema mUri) x = concat-  [ f _schemaMaxItems    (setFailure MaxItems)    (fmap maybeToList . AR.maxItems)-  , f _schemaMinItems    (setFailure MinItems)    (fmap maybeToList . AR.minItems)-  , f _schemaUniqueItems (setFailure UniqueItems) (fmap maybeToList . AR.uniqueItems)-  , f _schemaItems-      (modFailure fItems)-      (AR.items recurse (_schemaAdditionalItems schema))-  ]-  where-    f = runSingle schema x--    recurse = descendNextLevel referenced (SchemaWithURI schema mUri)--    fItems (AR.Items err)                        = Items err-    fItems AR.AdditionalItemsBoolInvalid         = AdditionalItemsBool-    fItems (AR.AdditionalItemsObjectInvalid err) = AdditionalItemsObject err--validateObject-  :: ReferencedSchemas Schema-  -> SchemaWithURI Schema-  -> HashMap Text Value-  -> [Invalid]-validateObject referenced (SchemaWithURI schema mUri) x = concat-  [ f _schemaMaxProperties (setFailure MaxProperties) (fmap maybeToList . OB.maxProperties)-  , f _schemaMinProperties (setFailure MinProperties) (fmap maybeToList . OB.minProperties)-  , f _schemaRequired      (setFailure Required)      (fmap maybeToList . OB.required)-  , f _schemaDependencies  (modFailure fDeps)         (OB.dependencies recurse)--  , f _schemaProperties-      (modFailure fProp)-      (OB.properties recurse-                     (_schemaPatternProperties schema)-                     (_schemaAdditionalProperties schema))--  , f _schemaPatternProperties-      (modFailure fPatProp)-      (case _schemaProperties schema of-        Just _  -> const (const mempty)-        Nothing -> OB.patternProperties recurse (_schemaAdditionalProperties schema))--  , f _schemaAdditionalProperties-      (modFailure fAddProp)-      (if isJust (_schemaProperties schema) || isJust (_schemaPatternProperties schema)-        then const (const mempty)-        else OB.additionalProperties recurse)-  ]-  where-    f = runSingle schema x--    recurse = descendNextLevel referenced (SchemaWithURI schema mUri)--    fDeps (OB.SchemaDependencyInvalid err) = SchemaDependency err-    fDeps OB.PropertyDependencyInvalid     = PropertyDependency--    fProp (OB.PropertiesInvalid err)   = Properties err-    fProp (OB.PropPatternInvalid err)  = PatternProperties err-    fProp (OB.PropAdditionalInvalid a) =-      case a of-        OB.APBoolInvalid       -> AdditionalPropertiesBool-        OB.APObjectInvalid err -> AdditionalPropertiesObject err--    fPatProp (OB.PPInvalid err) = PatternProperties err-    fPatProp (OB.PPAdditionalPropertiesInvalid a)   =-      case a of-        OB.APBoolInvalid       -> AdditionalPropertiesBool-        OB.APObjectInvalid err -> AdditionalPropertiesObject err--    fAddProp OB.APBoolInvalid         = AdditionalPropertiesBool-    fAddProp (OB.APObjectInvalid err) = AdditionalItemsObject err------------------------------------------------------- * Validation (Internal utils)-----------------------------------------------------descendNextLevel-  :: ReferencedSchemas Schema-  -> SchemaWithURI Schema-  -> Schema-  -> Value-  -> [Invalid]-descendNextLevel referenced (SchemaWithURI schema mUri) =-  runValidate referenced . flip SchemaWithURI scope-  where-    scope :: Maybe Text-    scope = updateResolutionScope mUri (_schemaId schema)--runSingle-  :: Schema-  -> dta-  -> (Schema -> Maybe val)-  -> (err -> Invalid)-  -> (val -> dta -> [err])-  -> [Invalid]-runSingle schema dta field modifyError validate =-  maybe mempty (\val -> modifyError <$> validate val dta) (field schema)
src/Data/JsonSchema/Draft4/Schema.hs view
@@ -1,7 +1,10 @@  module Data.JsonSchema.Draft4.Schema where -import qualified Data.HashMap.Strict          as H+import           Import+import           Prelude++import qualified Data.HashMap.Strict          as HM import           Data.List.NonEmpty           (NonEmpty) import           Data.Maybe                   (fromJust, isJust) import           Data.Scientific@@ -10,325 +13,326 @@ import qualified Data.Validator.Draft4.Array  as AR import qualified Data.Validator.Draft4.Object as OB import           Data.Validator.Utils-import           Import  data Schema = Schema-  { _schemaVersion              :: Maybe Text-  , _schemaId                   :: Maybe Text-  , _schemaRef                  :: Maybe Text-  , _schemaDefinitions          :: Maybe (HashMap Text Schema)-  -- ^ A standardized location for embedding schemas-  -- to be referenced from elsewhere in the document.-  , _schemaOther                :: HashMap Text Value-  -- ^ Since the JSON document this Schema was built from could-  -- contain schemas anywhere (not just in "definitions" or any-  -- of the other official keys) we save any leftover key/value-  -- pairs not covered by them here.+    { _schemaVersion              :: Maybe Text+    , _schemaId                   :: Maybe Text+    , _schemaRef                  :: Maybe Text+    , _schemaDefinitions          :: Maybe (HashMap Text Schema)+    -- ^ A standardized location for embedding schemas+    -- to be referenced from elsewhere in the document.+    , _schemaOther                :: HashMap Text Value+    -- ^ Since the JSON document this schema was built from could+    -- contain schemas anywhere (not just in "definitions" or any+    -- of the other official keys) we save any leftover key/value+    -- pairs not covered by them here. -  , _schemaMultipleOf           :: Maybe Scientific-  , _schemaMaximum              :: Maybe Scientific-  , _schemaExclusiveMaximum     :: Maybe Bool-  , _schemaMinimum              :: Maybe Scientific-  , _schemaExclusiveMinimum     :: Maybe Bool+    , _schemaMultipleOf           :: Maybe Scientific+    , _schemaMaximum              :: Maybe Scientific+    , _schemaExclusiveMaximum     :: Maybe Bool+    , _schemaMinimum              :: Maybe Scientific+    , _schemaExclusiveMinimum     :: Maybe Bool -  , _schemaMaxLength            :: Maybe Int-  , _schemaMinLength            :: Maybe Int-  , _schemaPattern              :: Maybe Text+    , _schemaMaxLength            :: Maybe Int+    , _schemaMinLength            :: Maybe Int+    , _schemaPattern              :: Maybe Text -  , _schemaMaxItems             :: Maybe Int-  , _schemaMinItems             :: Maybe Int-  , _schemaUniqueItems          :: Maybe Bool-  , _schemaItems                :: Maybe (AR.Items Schema)-  -- Note that '_schemaAdditionalItems' is left out of 'runValidate'-  -- because its functionality is handled by '_schemaItems'. It always-  -- validates data unless 'Items' is present.-  , _schemaAdditionalItems      :: Maybe (AR.AdditionalItems Schema)+    , _schemaMaxItems             :: Maybe Int+    , _schemaMinItems             :: Maybe Int+    , _schemaUniqueItems          :: Maybe Bool+    , _schemaItems                :: Maybe (AR.Items Schema)+    -- Note that '_schemaAdditionalItems' is left out of 'runValidate'+    -- because its functionality is handled by '_schemaItems'. It always+    -- validates data unless 'Items' is present.+    , _schemaAdditionalItems      :: Maybe (AR.AdditionalItems Schema) -  , _schemaMaxProperties        :: Maybe Int-  , _schemaMinProperties        :: Maybe Int-  , _schemaRequired             :: Maybe OB.Required-  , _schemaDependencies         :: Maybe (HashMap Text (OB.Dependency Schema))-  , _schemaProperties           :: Maybe (HashMap Text Schema)-  , _schemaPatternProperties    :: Maybe (HashMap Text Schema)-  , _schemaAdditionalProperties :: Maybe (OB.AdditionalProperties Schema)+    , _schemaMaxProperties        :: Maybe Int+    , _schemaMinProperties        :: Maybe Int+    , _schemaRequired             :: Maybe OB.Required+    , _schemaDependencies         :: Maybe (HashMap Text (OB.Dependency Schema))+    , _schemaProperties           :: Maybe (HashMap Text Schema)+    , _schemaPatternProperties    :: Maybe (HashMap Text Schema)+    , _schemaAdditionalProperties :: Maybe (OB.AdditionalProperties Schema) -  , _schemaEnum                 :: Maybe AN.EnumVal-  , _schemaType                 :: Maybe AN.TypeVal-  , _schemaAllOf                :: Maybe (NonEmpty Schema)-  , _schemaAnyOf                :: Maybe (NonEmpty Schema)-  , _schemaOneOf                :: Maybe (NonEmpty Schema)-  , _schemaNot                  :: Maybe Schema-  } deriving (Eq, Show)+    , _schemaEnum                 :: Maybe AN.EnumVal+    , _schemaType                 :: Maybe AN.TypeVal+    , _schemaAllOf                :: Maybe (NonEmpty Schema)+    , _schemaAnyOf                :: Maybe (NonEmpty Schema)+    , _schemaOneOf                :: Maybe (NonEmpty Schema)+    , _schemaNot                  :: Maybe Schema+    } deriving (Eq, Show)  emptySchema :: Schema emptySchema = Schema-  { _schemaVersion              = Nothing-  , _schemaId                   = Nothing-  , _schemaRef                  = Nothing-  , _schemaDefinitions          = Nothing-  , _schemaOther                = mempty+    { _schemaVersion              = Nothing+    , _schemaId                   = Nothing+    , _schemaRef                  = Nothing+    , _schemaDefinitions          = Nothing+    , _schemaOther                = mempty -  , _schemaMultipleOf           = Nothing-  , _schemaMaximum              = Nothing-  , _schemaExclusiveMaximum     = Nothing-  , _schemaMinimum              = Nothing-  , _schemaExclusiveMinimum     = Nothing+    , _schemaMultipleOf           = Nothing+    , _schemaMaximum              = Nothing+    , _schemaExclusiveMaximum     = Nothing+    , _schemaMinimum              = Nothing+    , _schemaExclusiveMinimum     = Nothing -  , _schemaMaxLength            = Nothing-  , _schemaMinLength            = Nothing-  , _schemaPattern              = Nothing+    , _schemaMaxLength            = Nothing+    , _schemaMinLength            = Nothing+    , _schemaPattern              = Nothing -  , _schemaMaxItems             = Nothing-  , _schemaMinItems             = Nothing-  , _schemaUniqueItems          = Nothing-  , _schemaItems                = Nothing-  , _schemaAdditionalItems      = Nothing+    , _schemaMaxItems             = Nothing+    , _schemaMinItems             = Nothing+    , _schemaUniqueItems          = Nothing+    , _schemaItems                = Nothing+    , _schemaAdditionalItems      = Nothing -  , _schemaMaxProperties        = Nothing-  , _schemaMinProperties        = Nothing-  , _schemaRequired             = Nothing-  , _schemaDependencies         = Nothing-  , _schemaProperties           = Nothing-  , _schemaPatternProperties    = Nothing-  , _schemaAdditionalProperties = Nothing+    , _schemaMaxProperties        = Nothing+    , _schemaMinProperties        = Nothing+    , _schemaRequired             = Nothing+    , _schemaDependencies         = Nothing+    , _schemaProperties           = Nothing+    , _schemaPatternProperties    = Nothing+    , _schemaAdditionalProperties = Nothing -  , _schemaEnum                 = Nothing-  , _schemaType                 = Nothing-  , _schemaAllOf                = Nothing-  , _schemaAnyOf                = Nothing-  , _schemaOneOf                = Nothing-  , _schemaNot                  = Nothing-  }+    , _schemaEnum                 = Nothing+    , _schemaType                 = Nothing+    , _schemaAllOf                = Nothing+    , _schemaAnyOf                = Nothing+    , _schemaOneOf                = Nothing+    , _schemaNot                  = Nothing+    }  instance FromJSON Schema where-  parseJSON = withObject "Schema" $ \o -> do-    a  <- o .:? "$schema"-    b  <- o .:? "id"-    c  <- o .:? "$ref"-    d  <- o .:? "definitions"-    e  <- parseJSON (Object (H.difference o internalSchemaHashMap))+    parseJSON = withObject "Schema" $ \o -> do+        a  <- o .:! "$schema"+        b  <- o .:! "id"+        c  <- o .:! "$ref"+        d  <- o .:! "definitions"+        e  <- parseJSON (Object (HM.difference o internalSchemaHashMap)) -    f  <- o .:? "multipleOf"-    g  <- o .:? "maximum"-    h  <- o .:? "exclusiveMaximum"-    i  <- o .:? "minimum"-    j  <- o .:? "exclusiveMinimum"+        f  <- o .:! "multipleOf"+        g  <- o .:! "maximum"+        h  <- o .:! "exclusiveMaximum"+        i  <- o .:! "minimum"+        j  <- o .:! "exclusiveMinimum" -    k  <- o .:? "maxLength"-    l  <- o .:? "minLength"-    m  <- o .:? "pattern"+        k  <- o .:! "maxLength"+        l  <- o .:! "minLength"+        m  <- o .:! "pattern" -    n  <- o .:? "maxItems"-    o' <- o .:? "minItems"-    p  <- o .:? "uniqueItems"-    q  <- o .:? "items"-    r  <- o .:? "additionalItems"+        n  <- o .:! "maxItems"+        o' <- o .:! "minItems"+        p  <- o .:! "uniqueItems"+        q  <- o .:! "items"+        r  <- o .:! "additionalItems" -    s  <- o .:? "maxProperties"-    t  <- o .:? "minProperties"-    u  <- o .:? "required"-    v  <- o .:? "dependencies"-    w  <- o .:? "properties"-    x  <- o .:? "patternProperties"-    y  <- o .:? "additionalProperties"+        s  <- o .:! "maxProperties"+        t  <- o .:! "minProperties"+        u  <- o .:! "required"+        v  <- o .:! "dependencies"+        w  <- o .:! "properties"+        x  <- o .:! "patternProperties"+        y  <- o .:! "additionalProperties" -    z  <- o .:? "enum"-    a2 <- o .:? "type"-    b2 <- fmap _unNonEmpty' <$> o .:? "allOf"-    c2 <- fmap _unNonEmpty' <$> o .:? "anyOf"-    d2 <- fmap _unNonEmpty' <$> o .:? "oneOf"-    e2 <- o .:? "not"-    pure Schema-      { _schemaVersion              = a-      , _schemaId                   = b-      , _schemaRef                  = c-      , _schemaDefinitions          = d-      , _schemaOther                = e+        z  <- o .:! "enum"+        a2 <- o .:! "type"+        b2 <- fmap _unNonEmpty' <$> o .:! "allOf"+        c2 <- fmap _unNonEmpty' <$> o .:! "anyOf"+        d2 <- fmap _unNonEmpty' <$> o .:! "oneOf"+        e2 <- o .:! "not"+        pure Schema+            { _schemaVersion              = a+            , _schemaId                   = b+            , _schemaRef                  = c+            , _schemaDefinitions          = d+            , _schemaOther                = e -      , _schemaMultipleOf           = f-      , _schemaMaximum              = g-      , _schemaExclusiveMaximum     = h-      , _schemaMinimum              = i-      , _schemaExclusiveMinimum     = j+            , _schemaMultipleOf           = f+            , _schemaMaximum              = g+            , _schemaExclusiveMaximum     = h+            , _schemaMinimum              = i+            , _schemaExclusiveMinimum     = j -      , _schemaMaxLength            = k-      , _schemaMinLength            = l-      , _schemaPattern              = m+            , _schemaMaxLength            = k+            , _schemaMinLength            = l+            , _schemaPattern              = m -      , _schemaMaxItems             = n-      , _schemaMinItems             = o'-      , _schemaUniqueItems          = p-      , _schemaItems                = q-      , _schemaAdditionalItems      = r+            , _schemaMaxItems             = n+            , _schemaMinItems             = o'+            , _schemaUniqueItems          = p+            , _schemaItems                = q+            , _schemaAdditionalItems      = r -      , _schemaMaxProperties        = s-      , _schemaMinProperties        = t-      , _schemaRequired             = u-      , _schemaDependencies         = v-      , _schemaProperties           = w-      , _schemaPatternProperties    = x-      , _schemaAdditionalProperties = y+            , _schemaMaxProperties        = s+            , _schemaMinProperties        = t+            , _schemaRequired             = u+            , _schemaDependencies         = v+            , _schemaProperties           = w+            , _schemaPatternProperties    = x+            , _schemaAdditionalProperties = y -      , _schemaEnum                 = z-      , _schemaType                 = a2-      , _schemaAllOf                = b2-      , _schemaAnyOf                = c2-      , _schemaOneOf                = d2-      , _schemaNot                  = e2-      }+            , _schemaEnum                 = z+            , _schemaType                 = a2+            , _schemaAllOf                = b2+            , _schemaAnyOf                = c2+            , _schemaOneOf                = d2+            , _schemaNot                  = e2+            }  instance ToJSON Schema where-  -- | The way we resolve JSON Pointers to embedded schemas is by-  -- serializing the containing schema to a value and then resolving the-  -- pointer against it. This means that FromJSON and ToJSON must be-  -- isomorphic.-  ---  -- This influences the design choices in the library. E.g. right now-  -- there are two false values for "exclusiveMaximum" -- Nothing and-  -- Just False. We could have condensed them down by using () instead-  -- of Bool for "exclusiveMaximum". This would have made writing schemas-  -- in haskell easier, but we could no longer round trip through/from-  -- JSON without losing information.-  toJSON s = Object $ H.union (mapMaybe ($ s) internalSchemaHashMap) (toJSON <$> _schemaOther s)-    where-      -- 'mapMaybe' is provided by unordered-containers after unordered-container-2.6.0.0,-      -- but until that is a little older (and has time to get into Stackage etc.)-      -- we use our own implementation.-      mapMaybe :: (v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2-      mapMaybe f = fmap fromJust . H.filter isJust . fmap f+    -- | The way we resolve JSON Pointers to embedded schemas is by+    -- serializing the containing schema to a value and then resolving the+    -- pointer against it. This means that FromJSON and ToJSON must be+    -- isomorphic.+    --+    -- This influences the design choices in the library. E.g. right now+    -- there are two false values for "exclusiveMaximum" -- Nothing and+    -- Just False. We could have condensed them down by using () instead+    -- of Bool for "exclusiveMaximum". This would have made writing schemas+    -- in haskell easier, but we could no longer round trip through/from+    -- JSON without losing information.+    toJSON s = Object $ HM.union (mapMaybe ($ s) internalSchemaHashMap)+                                 (toJSON <$> _schemaOther s)+      where+        -- 'mapMaybe' is provided by unordered-containers after+        -- unordered-container-2.6.0.0, but until that is a little older+        -- (and has time to get into Stackage etc.) we use our own+        -- implementation.+        mapMaybe :: (v1 -> Maybe v2) -> HashMap k v1 -> HashMap k v2+        mapMaybe f = fmap fromJust . HM.filter isJust . fmap f  -- | Internal. Separate from ToJSON because it's also used -- by FromJSON to determine what keys aren't official schema -- keys and therefor should be included in _schemaOther. internalSchemaHashMap :: HashMap Text (Schema -> Maybe Value)-internalSchemaHashMap = H.fromList-  [ ("$schema"             , f _schemaVersion)-  , ("id"                  , f _schemaId)-  , ("$ref"                , f _schemaRef)-  , ("definitions"         , f _schemaDefinitions)+internalSchemaHashMap = HM.fromList+    [ ("$schema"             , f _schemaVersion)+    , ("id"                  , f _schemaId)+    , ("$ref"                , f _schemaRef)+    , ("definitions"         , f _schemaDefinitions) -  , ("multipleOf"          , f _schemaMultipleOf)-  , ("maximum"             , f _schemaMaximum)-  , ("exclusiveMaximum"    , f _schemaExclusiveMaximum)-  , ("minimum"             , f _schemaMinimum)-  , ("exclusiveMinimum"    , f _schemaExclusiveMinimum)+    , ("multipleOf"          , f _schemaMultipleOf)+    , ("maximum"             , f _schemaMaximum)+    , ("exclusiveMaximum"    , f _schemaExclusiveMaximum)+    , ("minimum"             , f _schemaMinimum)+    , ("exclusiveMinimum"    , f _schemaExclusiveMinimum) -  , ("maxLength"           , f _schemaMaxLength)-  , ("minLength"           , f _schemaMinLength)-  , ("pattern"             , f _schemaPattern)+    , ("maxLength"           , f _schemaMaxLength)+    , ("minLength"           , f _schemaMinLength)+    , ("pattern"             , f _schemaPattern) -  , ("maxItems"            , f _schemaMaxItems)-  , ("minItems"            , f _schemaMinItems)-  , ("uniqueItems"         , f _schemaUniqueItems)-  , ("items"               , f _schemaItems)-  , ("additionalItems"     , f _schemaAdditionalItems)+    , ("maxItems"            , f _schemaMaxItems)+    , ("minItems"            , f _schemaMinItems)+    , ("uniqueItems"         , f _schemaUniqueItems)+    , ("items"               , f _schemaItems)+    , ("additionalItems"     , f _schemaAdditionalItems) -  , ("maxProperties"       , f _schemaMaxProperties)-  , ("minProperties"       , f _schemaMinProperties)-  , ("required"            , f _schemaRequired)-  , ("dependencies"        , f _schemaDependencies)-  , ("properties"          , f _schemaProperties)-  , ("patternProperties"   , f _schemaPatternProperties)-  , ("additionalProperties", f _schemaAdditionalProperties)+    , ("maxProperties"       , f _schemaMaxProperties)+    , ("minProperties"       , f _schemaMinProperties)+    , ("required"            , f _schemaRequired)+    , ("dependencies"        , f _schemaDependencies)+    , ("properties"          , f _schemaProperties)+    , ("patternProperties"   , f _schemaPatternProperties)+    , ("additionalProperties", f _schemaAdditionalProperties) -  , ("enum"                , f _schemaEnum)-  , ("type"                , f _schemaType)-  , ("allOf"               , f (fmap NonEmpty' . _schemaAllOf))-  , ("anyOf"               , f (fmap NonEmpty' . _schemaAnyOf))-  , ("oneOf"               , f (fmap NonEmpty' . _schemaOneOf))-  , ("not"                 , f _schemaNot)-  ]+    , ("enum"                , f _schemaEnum)+    , ("type"                , f _schemaType)+    , ("allOf"               , f (fmap NonEmpty' . _schemaAllOf))+    , ("anyOf"               , f (fmap NonEmpty' . _schemaAnyOf))+    , ("oneOf"               , f (fmap NonEmpty' . _schemaOneOf))+    , ("not"                 , f _schemaNot)+    ]   where     f :: ToJSON a => (Schema -> Maybe a) -> Schema -> Maybe Value     f = (fmap.fmap) toJSON  instance Arbitrary Schema where-  arbitrary = sized f-    where-      maybeGen :: Gen a -> Gen (Maybe a)-      maybeGen a = oneof [pure Nothing, Just <$> a]+    arbitrary = sized f+      where+        maybeGen :: Gen a -> Gen (Maybe a)+        maybeGen a = oneof [pure Nothing, Just <$> a] -      maybeRecurse :: Int -> Gen a -> Gen (Maybe a)-      maybeRecurse n a-        | n < 1     = pure Nothing-        | otherwise = maybeGen $ resize (n `div` 10) a+        maybeRecurse :: Int -> Gen a -> Gen (Maybe a)+        maybeRecurse n a+            | n < 1     = pure Nothing+            | otherwise = maybeGen $ resize (n `div` 10) a -      f :: Int -> Gen Schema-      f n = do-        a  <- maybeGen arbitraryText-        b  <- maybeGen arbitraryText-        c  <- maybeGen arbitraryText-           -- NOTE: The next two fields are empty to generate cleaner schemas,-           -- but note that this means we don't test e.g. the invertability-           -- of these fields.-        d  <- pure Nothing -- _schemaDefinitions-        e  <- pure mempty -- _otherPairs+        f :: Int -> Gen Schema+        f n = do+            a  <- maybeGen arbitraryText+            b  <- maybeGen arbitraryText+            c  <- maybeGen arbitraryText+               -- NOTE: The next two fields are empty to generate cleaner schemas,+               -- but note that this means we don't test e.g. the invertability+               -- of these fields.+            d  <- pure Nothing -- _schemaDefinitions+            e  <- pure mempty -- _otherPairs -        f' <- maybeGen arbitraryPositiveScientific-        g  <- maybeGen arbitraryScientific-        h  <- arbitrary-        i  <- maybeGen arbitraryScientific-        j  <- arbitrary+            f' <- maybeGen arbitraryPositiveScientific+            g  <- maybeGen arbitraryScientific+            h  <- arbitrary+            i  <- maybeGen arbitraryScientific+            j  <- arbitrary -        k  <- maybeGen (getPositive <$> arbitrary)-        l  <- maybeGen (getPositive <$> arbitrary)-        m  <- maybeGen arbitraryText+            k  <- maybeGen (getPositive <$> arbitrary)+            l  <- maybeGen (getPositive <$> arbitrary)+            m  <- maybeGen arbitraryText -        n' <- maybeGen (getPositive <$> arbitrary)-        o  <- maybeGen (getPositive <$> arbitrary)-        p  <- arbitrary-        q  <- maybeRecurse n arbitrary-        r  <- maybeRecurse n arbitrary+            n' <- maybeGen (getPositive <$> arbitrary)+            o  <- maybeGen (getPositive <$> arbitrary)+            p  <- arbitrary+            q  <- maybeRecurse n arbitrary+            r  <- maybeRecurse n arbitrary -        s  <- maybeGen (getPositive <$> arbitrary)-        t  <- maybeGen (getPositive <$> arbitrary)-        u  <- arbitrary-        v  <- maybeRecurse n arbitraryHashMap-        w  <- maybeRecurse n arbitraryHashMap-        x  <- maybeRecurse n arbitraryHashMap-        y  <- maybeRecurse n arbitrary+            s  <- maybeGen (getPositive <$> arbitrary)+            t  <- maybeGen (getPositive <$> arbitrary)+            u  <- arbitrary+            v  <- maybeRecurse n arbitraryHashMap+            w  <- maybeRecurse n arbitraryHashMap+            x  <- maybeRecurse n arbitraryHashMap+            y  <- maybeRecurse n arbitrary -        z  <- arbitrary-        a2 <- arbitrary-        b2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)-        c2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)-        d2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)-        e2 <- maybeRecurse n arbitrary-        pure Schema-          { _schemaVersion              = a-          , _schemaId                   = b-          , _schemaRef                  = c-          , _schemaDefinitions          = d-          , _schemaOther                = e+            z  <- arbitrary+            a2 <- arbitrary+            b2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)+            c2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)+            d2 <- maybeRecurse n (_unNonEmpty' <$> arbitrary)+            e2 <- maybeRecurse n arbitrary+            pure Schema+                { _schemaVersion              = a+                , _schemaId                   = b+                , _schemaRef                  = c+                , _schemaDefinitions          = d+                , _schemaOther                = e -          , _schemaMultipleOf           = f'-          , _schemaMaximum              = g-          , _schemaExclusiveMaximum     = h-          , _schemaMinimum              = i-          , _schemaExclusiveMinimum     = j+                , _schemaMultipleOf           = f'+                , _schemaMaximum              = g+                , _schemaExclusiveMaximum     = h+                , _schemaMinimum              = i+                , _schemaExclusiveMinimum     = j -          , _schemaMaxLength            = k-          , _schemaMinLength            = l-          , _schemaPattern              = m+                , _schemaMaxLength            = k+                , _schemaMinLength            = l+                , _schemaPattern              = m -          , _schemaMaxItems             = n'-          , _schemaMinItems             = o-          , _schemaUniqueItems          = p-          , _schemaItems                = q-          , _schemaAdditionalItems      = r+                , _schemaMaxItems             = n'+                , _schemaMinItems             = o+                , _schemaUniqueItems          = p+                , _schemaItems                = q+                , _schemaAdditionalItems      = r -          , _schemaMaxProperties        = s-          , _schemaMinProperties        = t-          , _schemaRequired             = u-          , _schemaDependencies         = v-          , _schemaProperties           = w-          , _schemaPatternProperties    = x-          , _schemaAdditionalProperties = y+                , _schemaMaxProperties        = s+                , _schemaMinProperties        = t+                , _schemaRequired             = u+                , _schemaDependencies         = v+                , _schemaProperties           = w+                , _schemaPatternProperties    = x+                , _schemaAdditionalProperties = y -          , _schemaEnum                 = z-          , _schemaType                 = a2-          , _schemaAllOf                = b2-          , _schemaAnyOf                = c2-          , _schemaOneOf                = d2-          , _schemaNot                  = e2-          }+                , _schemaEnum                 = z+                , _schemaType                 = a2+                , _schemaAllOf                = b2+                , _schemaAnyOf                = c2+                , _schemaOneOf                = d2+                , _schemaNot                  = e2+                }
+ src/Data/JsonSchema/Draft4/Spec.hs view
@@ -0,0 +1,130 @@++module Data.JsonSchema.Draft4.Spec where++import           Import+-- Hiding is for GHCs before 7.10:+import           Prelude                        hiding (concat)++import           Data.Maybe                     (fromMaybe, isNothing)+import           Data.Profunctor                (Profunctor(..))++import           Data.JsonSchema.Draft4.Failure+import           Data.JsonSchema.Draft4.Schema  (Schema(..))+import           Data.JsonSchema.Fetch          (ReferencedSchemas(..),+                                                 SchemaWithURI(..))+import qualified Data.JsonSchema.Fetch          as FE+import           Data.JsonSchema.Types          (Spec(..))+import qualified Data.JsonSchema.Types          as JT+import qualified Data.Validator.Draft4          as D4+import qualified Data.Validator.Draft4.Any      as AN+import           Data.Validator.Reference       (updateResolutionScope)++embedded :: Schema -> ([Schema], [Schema])+embedded s = JT.embedded (d4Spec (ReferencedSchemas s mempty) mempty Nothing) s++validate+    :: ReferencedSchemas Schema+    -> SchemaWithURI Schema+    -> Value+    -> [Failure]+validate rs = continueValidating rs (AN.VisitedSchemas [(Nothing, Nothing)])++continueValidating+    :: ReferencedSchemas Schema+    -> AN.VisitedSchemas+    -> SchemaWithURI Schema+    -> Value+    -> [Failure]+continueValidating referenced visited sw =+    JT.validate (d4Spec referenced visited currentScope)+                (_swSchema sw)+  where+    currentScope :: Maybe Text+    currentScope = updateResolutionScope+                       (_swURI sw)+                       (_schemaId (_swSchema sw))++d4Spec+    :: ReferencedSchemas Schema+    -> AN.VisitedSchemas+    -> Maybe Text+    -> Spec Schema ValidatorChain+d4Spec referenced visited scope = Spec+    [ dimap+        (fmap D4.MultipleOf . _schemaMultipleOf)+        (const MultipleOf)+        D4.multipleOf+    , dimap+        (\s -> D4.MaximumContext (fromMaybe False (_schemaExclusiveMaximum s))+                 <$> _schemaMaximum s)+        maxE+        D4.maximumVal+    , dimap+        (\s -> D4.MinimumContext (fromMaybe False (_schemaExclusiveMinimum s))+                 <$> _schemaMinimum s)+        minE+        D4.minimumVal++    , dimap (fmap D4.MaxLength . _schemaMaxLength) (const MaxLength) D4.maxLength+    , dimap (fmap D4.MinLength . _schemaMinLength) (const MinLength) D4.minLength+    , dimap (fmap D4.PatternVal . _schemaPattern) (const PatternValidator) D4.patternVal++    , dimap (fmap D4.MaxItems . _schemaMaxItems) (const MaxItems) D4.maxItems+    , dimap (fmap D4.MinItems . _schemaMinItems) (const MinItems) D4.minItems+    , dimap (fmap D4.UniqueItems . _schemaUniqueItems) (const UniqueItems) D4.uniqueItems+    , dimap+        (\s -> D4.ItemsContext (_schemaAdditionalItems s) <$> _schemaItems s)+        itemsE+        (D4.items descend)+    , lmap (fmap D4.AdditionalItemsContext . _schemaAdditionalItems) D4.additionalItemsEmbedded+    , lmap (fmap D4.Definitions . _schemaDefinitions) D4.definitionsEmbedded++    , dimap (fmap D4.MaxProperties . _schemaMaxProperties) (const MaxProperties) D4.maxProperties+    , dimap (fmap D4.MinProperties . _schemaMinProperties) (const MinProperties) D4.minProperties+    , dimap (fmap D4.RequiredContext . _schemaRequired) (const Required) D4.required+    , dimap (fmap D4.DependenciesContext . _schemaDependencies) depsE (D4.dependencies descend)+    , dimap+        (\s -> D4.PropertiesContext+                 (_schemaPatternProperties s)+                 (_schemaAdditionalProperties s)+                 <$> _schemaProperties s)+        propE+        (D4.properties descend)+    , dimap+        (\s -> D4.PatternPropertiesContext+                 (isNothing (_schemaProperties s))+                 (_schemaAdditionalProperties s)+                 <$> _schemaPatternProperties s)+        patPropE+        (D4.patternProperties descend)+    , dimap+        (\s -> D4.AdditionalPropertiesContext+                 (isNothing (_schemaProperties s)+                    && isNothing (_schemaPatternProperties s))+                 <$> _schemaAdditionalProperties s)+        addPropE+        (D4.additionalProperties descend)++    , dimap+        (\s -> D4.Ref <$> _schemaRef s)+        refE+        (D4.ref visited scope (FE.getReference referenced) refVal)+    , dimap (fmap D4.EnumContext . _schemaEnum) (const Enum) D4.enumVal+    , dimap (fmap D4.TypeContext . _schemaType) (const TypeValidator) D4.typeVal+    , dimap (fmap D4.AllOf . _schemaAllOf) AllOf (D4.allOf lateral)+    , dimap (fmap D4.AnyOf . _schemaAnyOf) (const AnyOf) (D4.anyOf lateral)+    , dimap (fmap D4.OneOf . _schemaOneOf) (const OneOf) (D4.oneOf lateral)+    , dimap (fmap D4.NotVal . _schemaNot) (const NotValidator) (D4.notVal lateral)+    ]+  where+    refVal :: AN.VisitedSchemas -> Maybe Text -> Schema -> Value -> [Failure]+    refVal newVisited newScope schema =+        continueValidating referenced newVisited (SchemaWithURI schema newScope)++    descend :: Schema -> Value -> [Failure]+    descend schema =+        continueValidating referenced mempty (SchemaWithURI schema scope)++    lateral :: Schema -> Value -> [Failure]+    lateral schema =+        continueValidating referenced visited (SchemaWithURI schema scope)
src/Data/JsonSchema/Fetch.hs view
@@ -1,83 +1,104 @@-{-# LANGUAGE ScopedTypeVariables #-}  module Data.JsonSchema.Fetch where +import           Import+-- Hiding is for GHCs before 7.10:+import           Prelude                  hiding (concat, sequence)+ import           Control.Arrow            (left) import           Control.Exception        (catch)+import           Control.Monad            (foldM) import qualified Data.ByteString.Lazy     as LBS import qualified Data.ByteString          as BS import qualified Data.HashMap.Strict      as H import qualified Data.Text                as T-import           Network.HTTP.Client+import qualified Network.HTTP.Client      as NC  import           Data.Validator.Reference (resolveReference,                                            updateResolutionScope)-import           Import --- For GHCs before 7.10:-import           Prelude                  hiding (concat, sequence)- -------------------------------------------------- -- * Types -------------------------------------------------- -data Spec schema = Spec-  { _ssEmbedded :: schema -> [schema]-  , _ssGetId    :: schema -> Maybe Text-  , _ssGetRef   :: schema -> Maybe Text-  }--data SchemaWithURI schema = SchemaWithURI-  { _swSchema :: !schema-  , _swURI    :: !(Maybe Text)-  -- ^ Must not include a URI fragment, e.g. use-  -- "http://example.com/foo" not "http://example.com/foo#bar".-  ---  -- This is the URI identifying the document containing the schema.-  -- It's different than the schema's "id" field, which controls scope-  -- when resolving references contained in the schema.+-- | This is all the fetching functions need to know about a particular+-- JSON Schema draft, e.g. JSON Schema Draft 4.+data FetchInfo schema = FetchInfo+    { _fiEmbedded :: schema -> ([schema], [schema])+    , _fiId       :: schema -> Maybe Text+    , _fiRef      :: schema -> Maybe Text+    } -  -- TODO: Make the no URI fragment requirement unnecessary.-  } deriving (Eq, Show)+data ReferencedSchemas schema = ReferencedSchemas+    { _rsStarting  :: !schema+      -- ^ Used to resolve relative references when we don't know what the scope+      -- of the current schema is. This only happens with starting schemas+      -- because if we're using a remote schema we had to know its URI in order+      -- to fetch it.+      --+      -- Tracking the starting schema (instead of just resolving the reference to+      -- the current schema being used for validation) is necessary for cases+      -- where schemas are embedded inside one another. For instance in this+      -- case not distinguishing the starting and "foo" schemas sends the code+      -- into an infinite loop:+      --+      -- {+      --   "additionalProperties": false,+      --   "properties": {+      --     "foo": {+      --       "$ref": "#"+      --     }+      --   }+      -- }+    , _rsSchemaMap :: !(URISchemaMap schema)+    } deriving (Eq, Show)  -- | Keys are URIs (without URI fragments). type URISchemaMap schema = HashMap Text schema -data ReferencedSchemas schema = ReferencedSchemas-  { _rsStarting  :: !schema-  -- ^ Used to resolve relative references.-  , _rsSchemaMap :: !(URISchemaMap schema)-  } deriving (Eq, Show)+data SchemaWithURI schema = SchemaWithURI+    { _swSchema :: !schema+    , _swURI    :: !(Maybe Text)+      -- ^ This is the URI identifying the document containing the schema.+      -- It's different than the schema's "id" field, which controls scope+      -- when resolving references contained in the schema. +      -- TODO: Make the no URI fragment requirement unnecessary.+    } deriving (Eq, Show)++getReference :: ReferencedSchemas schema -> Maybe Text -> Maybe schema+getReference referenced Nothing  = Just (_rsStarting referenced)+getReference referenced (Just t) = H.lookup t (_rsSchemaMap referenced)+ -------------------------------------------------- -- * Fetch via HTTP --------------------------------------------------  data HTTPFailure-  = HTTPParseFailure   Text-  | HTTPRequestFailure HttpException-  deriving Show+    = HTTPParseFailure   Text+    | HTTPRequestFailure NC.HttpException+    deriving Show  -- | Take a schema. Retrieve every document either it or its subschemas -- include via the "$ref" keyword. referencesViaHTTP'-  :: forall schema. FromJSON schema-  => Spec schema-  -> SchemaWithURI schema-  -> IO (Either HTTPFailure (ReferencedSchemas schema))-referencesViaHTTP' spec sw = do-  manager <- newManager defaultManagerSettings-  let f = referencesMethodAgnostic (get manager) spec sw-  catch (left HTTPParseFailure <$> f) handler+    :: forall schema. FromJSON schema+    => FetchInfo schema+    -> SchemaWithURI schema+    -> IO (Either HTTPFailure (URISchemaMap schema))+referencesViaHTTP' info sw = do+    manager <- NC.newManager NC.defaultManagerSettings+    let f = referencesMethodAgnostic (get manager) info sw+    catch (left HTTPParseFailure <$> f) handler   where-    get :: Manager -> Text -> IO LBS.ByteString+    get :: NC.Manager -> Text -> IO LBS.ByteString     get man url = do-      request <- parseUrl (T.unpack url)-      responseBody <$> httpLbs request man+        request <- NC.parseUrlThrow (T.unpack url)+        NC.responseBody <$> NC.httpLbs request man      handler-      :: HttpException-      -> IO (Either HTTPFailure (ReferencedSchemas schema))+        :: NC.HttpException+        -> IO (Either HTTPFailure (URISchemaMap schema))     handler = pure . Left . HTTPRequestFailure  --------------------------------------------------@@ -85,26 +106,26 @@ --------------------------------------------------  data FilesystemFailure-  = FSParseFailure Text-  | FSReadFailure  IOError-  deriving Show+    = FSParseFailure Text+    | FSReadFailure  IOError+    deriving (Show, Eq)  referencesViaFilesystem'-  :: forall schema. FromJSON schema-  => Spec schema-  -> SchemaWithURI schema-  -> IO (Either FilesystemFailure (ReferencedSchemas schema))-referencesViaFilesystem' spec sw = catch (left FSParseFailure <$> f) handler+    :: forall schema. FromJSON schema+    => FetchInfo schema+    -> SchemaWithURI schema+    -> IO (Either FilesystemFailure (URISchemaMap schema))+referencesViaFilesystem' info sw = catch (left FSParseFailure <$> f) handler   where-    f :: IO (Either Text (ReferencedSchemas schema))-    f = referencesMethodAgnostic readFile' spec sw+    f :: IO (Either Text (URISchemaMap schema))+    f = referencesMethodAgnostic readFile' info sw      readFile' :: Text -> IO LBS.ByteString     readFile' = fmap LBS.fromStrict . BS.readFile . T.unpack      handler-      :: IOError-      -> IO (Either FilesystemFailure (ReferencedSchemas schema))+        :: IOError+        -> IO (Either FilesystemFailure (URISchemaMap schema))     handler = pure . Left . FSReadFailure  --------------------------------------------------@@ -115,59 +136,59 @@ -- schemas is provided by the user. This allows restrictions to be added, -- e.g. rejecting non-local URIs. referencesMethodAgnostic-  :: forall schema. FromJSON schema-  => (Text -> IO LBS.ByteString)-  -> Spec schema-  -> SchemaWithURI schema-  -> IO (Either Text (ReferencedSchemas schema))-referencesMethodAgnostic fetchRef spec sw =-  (fmap.fmap) (ReferencedSchemas (_swSchema sw))-              (foldFunction fetchRef spec mempty sw)+    :: forall schema. FromJSON schema+    => (Text -> IO LBS.ByteString)+    -> FetchInfo schema+    -> SchemaWithURI schema+    -> IO (Either Text (URISchemaMap schema))+referencesMethodAgnostic fetchRef info =+    getRecursiveReferences fetchRef info mempty -foldFunction-  :: forall schema. FromJSON schema-  => (Text -> IO LBS.ByteString)-  -> Spec schema-  -> URISchemaMap schema-  -> SchemaWithURI schema-  -> IO (Either Text (URISchemaMap schema))-foldFunction fetchRef spec@(Spec _ _ getRef) referenced sw =-  foldlM f (Right referenced) (includeSubschemas spec sw)+getRecursiveReferences+    :: forall schema. FromJSON schema+    => (Text -> IO LBS.ByteString)+    -> FetchInfo schema+    -> URISchemaMap schema+    -> SchemaWithURI schema+    -> IO (Either Text (URISchemaMap schema))+getRecursiveReferences fetchRef info referenced sw =+    foldM f (Right referenced) (includeSubschemas info sw)   where     f :: Either Text (URISchemaMap schema)       -> SchemaWithURI schema       -> IO (Either Text (URISchemaMap schema))     f (Left e) _                            = pure (Left e)     f (Right g) (SchemaWithURI schema mUri) =-      case newRef of-        Nothing  -> pure (Right g)-        Just uri -> do-          bts <- fetchRef uri-          case eitherDecode bts of-            Left e     -> pure . Left . T.pack $ e-            Right schm -> foldFunction fetchRef spec (H.insert uri schm g)-                                       (SchemaWithURI schm (Just uri))+        case newRef of+            Nothing  -> pure (Right g)+            Just uri -> do+                bts <- fetchRef uri+                case eitherDecode bts of+                    Left e     -> pure . Left . T.pack $ e+                    Right schm -> getRecursiveReferences+                                      fetchRef info (H.insert uri schm g)+                                      (SchemaWithURI schm (Just uri))       where         newRef :: Maybe Text         newRef-          | Just (Just uri,_) <- resolveReference mUri <$> getRef schema-            = case H.lookup uri g of-                Nothing -> Just uri-                Just _  -> Nothing+          | Just (Just uri,_) <- resolveReference mUri <$> _fiRef info schema+              = case H.lookup uri g of+                    Nothing -> Just uri+                    Just _  -> Nothing           | otherwise = Nothing  -- | Return the schema passed in as an argument, as well as every -- subschema contained within it. includeSubschemas-  :: forall schema.-     Spec schema-  -> SchemaWithURI schema-  -> [SchemaWithURI schema]-includeSubschemas spec@(Spec embedded getId _) (SchemaWithURI schema mUri) =-  SchemaWithURI schema mUri-  : (includeSubschemas spec =<< subSchemas)+    :: forall schema.+       FetchInfo schema+    -> SchemaWithURI schema+    -> [SchemaWithURI schema]+includeSubschemas info (SchemaWithURI schema mUri) =+    SchemaWithURI schema mUri+    : (includeSubschemas info =<< subSchemas)   where     subSchemas :: [SchemaWithURI schema]     subSchemas =-      (\a -> SchemaWithURI a (updateResolutionScope mUri (getId schema)))-        <$> embedded schema+      (\a -> SchemaWithURI a (updateResolutionScope mUri (_fiId info schema)))+         <$> uncurry (<>) (_fiEmbedded info schema)
+ src/Data/JsonSchema/Types.hs view
@@ -0,0 +1,38 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}++module Data.JsonSchema.Types where++import           Prelude+import           Import++import           Data.Validator.Failure (Fail)+import           Data.Validator.Types   (Validator(..))++newtype Spec schema err+    = Spec { _unSpec :: [Validator schema schema err] }++-- | Return a schema's immediate subschemas.+--+-- The first list is subschemas validating the same level of the document,+-- the second list is subschemas validating lower levels (see+-- 'Data.Validator.Types.Fail' for a full explanation).+embedded :: Spec schema a -> schema -> ([schema], [schema])+embedded spec schema =+    let embeds = (\val -> _embedded val schema) <$> _unSpec spec+    in foldl' (\(a,b) (x,y) -> (x <> a, y <> b)) (mempty, mempty) embeds++validate+    :: Spec schema err+    -> schema+    -> Value+    -> [Fail err]+validate spec schema v = _unSpec spec >>= (\val -> _validate val schema v)++-- | A basic schema type that doesn't impose much structure.+--+-- 'Data.JsonSchema.Draft4' doesn't use this, but instead uses the one+-- defined in 'Data.JsonSchema.Draft4.Schema' to make it easier to write+-- draft 4 schemas in Haskell.+newtype Schema+    = Schema { _unSchema :: HashMap Text Value }+    deriving (Eq, Show, FromJSON, ToJSON)
src/Data/Validator/Draft4.hs view
@@ -1,8 +1,551 @@+-- | Turn the validation functions into actual 'Validator's.+--+-- From this point on they know how to parse themselves from JSON+-- and also know how to extract subschemas embedded within themselves. -module Data.Validator.Draft4 (module Export) where+module Data.Validator.Draft4 where -import           Data.Validator.Draft4.Any    as Export-import           Data.Validator.Draft4.Array  as Export-import           Data.Validator.Draft4.Number as Export-import           Data.Validator.Draft4.Object as Export-import           Data.Validator.Draft4.String as Export+import           Prelude+import           Import++import           Data.Aeson.Types             (Parser)+import qualified Data.HashMap.Strict          as HM+import           Data.List.NonEmpty           (NonEmpty)+import qualified Data.List.NonEmpty           as NE+import           Data.Maybe                   (catMaybes, isNothing,+                                               maybe, maybeToList)+import           Data.Scientific              (Scientific)+import           Data.Text                    (Text)++import qualified Data.Validator.Draft4.Any    as AN+import qualified Data.Validator.Draft4.Array  as AR+import qualified Data.Validator.Draft4.Number as NU+import qualified Data.Validator.Draft4.Object as OB+import qualified Data.Validator.Draft4.String as ST+import           Data.Validator.Failure       (Fail(..))+import           Data.Validator.Types         (Validator(..))+import           Data.Validator.Utils         (fromJSONEither)++-- | For internal use.+run :: FromJSON b => (a -> b -> [c]) -> Maybe a -> Value -> [c]+run _ Nothing _  = mempty+run f (Just a) b =+    case fromJSONEither b of+        Left _  -> mempty+        Right c -> f a c++-- | For internal use.+noEmbedded :: a -> ([b], [b])+noEmbedded = const (mempty, mempty)++--------------------------------------------------+-- * Numbers+--------------------------------------------------++newtype MultipleOf+    = MultipleOf { _unMultipleOf :: Scientific }+    deriving (Eq, Show)++instance FromJSON MultipleOf where+    parseJSON = withObject "MultipleOf" $ \o ->+        MultipleOf <$> o .: "multipleOf"++multipleOf :: Validator a (Maybe MultipleOf) ()+multipleOf =+    Validator+        noEmbedded+        (run (fmap maybeToList . NU.multipleOf . _unMultipleOf))++data MaximumContext+    = MaximumContext Bool Scientific+    deriving (Eq, Show)++instance FromJSON MaximumContext where+    parseJSON = withObject "MaximumContext" $ \o -> MaximumContext+        <$> o .:! "exclusiveMaximum" .!= False+        <*> o .: "maximum"++maximumVal :: Validator a (Maybe MaximumContext) NU.MaximumInvalid+maximumVal =+    Validator+        noEmbedded+        (run (\(MaximumContext a b) -> maybeToList . NU.maximumVal a b))++data MinimumContext+    = MinimumContext Bool Scientific+    deriving (Eq, Show)++instance FromJSON MinimumContext where+    parseJSON = withObject "MinimumContext" $ \o -> MinimumContext+        <$> o .:! "exclusiveMinimum" .!= False+        <*> o .: "minimum"++minimumVal :: Validator a (Maybe MinimumContext) NU.MinimumInvalid+minimumVal =+    Validator+        noEmbedded+        (run (\(MinimumContext a b) -> maybeToList . NU.minimumVal a b))++--------------------------------------------------+-- * Strings+--------------------------------------------------++newtype MaxLength+    = MaxLength { _unMaxLength :: Int }+    deriving (Eq, Show)++instance FromJSON MaxLength where+    parseJSON = withObject "MaxLength" $ \o ->+        MaxLength <$> o .: "maxLength"++maxLength :: Validator a (Maybe MaxLength) ()+maxLength =+    Validator+        noEmbedded+        (run (fmap maybeToList . ST.maxLength . _unMaxLength))++newtype MinLength+    = MinLength { _unMinLength :: Int }+    deriving (Eq, Show)++instance FromJSON MinLength where+    parseJSON = withObject "MinLength" $ \o ->+        MinLength <$> o .: "minLength"++minLength :: Validator a (Maybe MinLength) ()+minLength =+    Validator+        noEmbedded+        (run (fmap maybeToList . ST.minLength . _unMinLength))++newtype PatternVal+    = PatternVal { _unPatternVal :: Text }+    deriving (Eq, Show)++instance FromJSON PatternVal where+    parseJSON = withObject "PatternVal" $ \o ->+        PatternVal <$> o .: "pattern"++patternVal :: Validator a (Maybe PatternVal) ()+patternVal =+    Validator+        noEmbedded+        (run (fmap maybeToList . ST.patternVal . _unPatternVal))++--------------------------------------------------+-- * Arrays+--------------------------------------------------++newtype MaxItems+    = MaxItems { _unMaxItems :: Int }+    deriving (Eq, Show)++instance FromJSON MaxItems where+    parseJSON = withObject "MaxItems" $ \o ->+        MaxItems <$> o .: "maxItems"++maxItems :: Validator a (Maybe MaxItems) ()+maxItems =+    Validator+        noEmbedded+        (run (fmap maybeToList . AR.maxItems . _unMaxItems))++newtype MinItems+    = MinItems { _unMinItems :: Int }+    deriving (Eq, Show)++instance FromJSON MinItems where+    parseJSON = withObject "MinItems" $ \o ->+        MinItems <$> o .: "minItems"++minItems :: Validator a (Maybe MinItems) ()+minItems =+    Validator+        noEmbedded+        (run (fmap maybeToList . AR.minItems . _unMinItems))++newtype UniqueItems+    = UniqueItems { _unUniqueItems :: Bool }+    deriving (Eq, Show)++instance FromJSON UniqueItems where+    parseJSON = withObject "UniqueItems" $ \o ->+        UniqueItems <$> o .: "uniqueItems"++uniqueItems :: Validator a (Maybe UniqueItems) ()+uniqueItems =+    Validator+        noEmbedded+        (run (fmap maybeToList . AR.uniqueItems . _unUniqueItems))++data ItemsContext schema =+    ItemsContext+        (Maybe (AR.AdditionalItems schema))+        (AR.Items schema)+    deriving (Eq, Show)++instance FromJSON schema => FromJSON (ItemsContext schema) where+    parseJSON = withObject "ItemsContext" $ \o -> ItemsContext+        <$> o .:! "additionalItems"+        <*> o .: "items"++items+    :: (schema -> Value -> [Fail err])+    -> Validator schema (Maybe (ItemsContext schema)) (AR.ItemsInvalid err)+items f =+    Validator+        (\a -> case a of+                   Nothing -> mempty+                   Just (ItemsContext _ b) ->+                       case b of+                           AR.ItemsObject c -> (mempty, pure c)+                           AR.ItemsArray cs -> (mempty, cs))+        (run (\(ItemsContext a b) -> AR.items f a b))++newtype AdditionalItemsContext schema+    = AdditionalItemsContext+        { _unAdditionalItemsContext :: AR.AdditionalItems schema }+    deriving (Eq, Show)++instance FromJSON schema => FromJSON (AdditionalItemsContext schema) where+    parseJSON = withObject "AdditionalItemsContext" $ \o ->+        AdditionalItemsContext <$> o .: "additionalItems"++-- | Since 'items' will always take care of validating 'additionalItems'+-- as well, the actual validation side of 'additionalItemsEmbedded' is+-- disabled.+additionalItemsEmbedded+    :: Validator+           schema+           (Maybe (AdditionalItemsContext schema))+           err+additionalItemsEmbedded=+    Validator+        (\a -> case a of+                   Just (AdditionalItemsContext (AR.AdditionalObject b)) ->+                       (mempty, pure b)+                   _ -> (mempty, mempty))+        (const (const mempty))++newtype Definitions schema+    = Definitions { _unDefinitions :: HashMap Text schema }+    deriving (Eq, Show)++instance FromJSON schema => FromJSON (Definitions schema) where+    parseJSON = withObject "Definitions" $ \o ->+        Definitions <$> o .: "definitions"++-- | Placing this here since it's similar to @"additionalItems"@.+-- in that its validator doesn't run.+--+-- TODO: Add tests to the language agnostic test suite for both+-- @"additionalItems"@ and this.+definitionsEmbedded+    :: Validator+           schema+           (Maybe (Definitions schema))+           err+definitionsEmbedded =+    Validator+        (\a -> case a of+                 Just (Definitions b) -> (mempty, HM.elems b)+                 Nothing              -> (mempty, mempty))+        (const (const mempty))++--------------------------------------------------+-- * Objects+--------------------------------------------------++newtype MaxProperties+    = MaxProperties { _unMaxProperties :: Int }+    deriving (Eq, Show)++instance FromJSON MaxProperties where+    parseJSON = withObject "MaxProperties" $ \o ->+        MaxProperties <$> o .: "maxProperties"++maxProperties :: Validator a (Maybe MaxProperties) ()+maxProperties =+    Validator+        noEmbedded+        (run (fmap maybeToList . OB.maxProperties . _unMaxProperties))++newtype MinProperties+    = MinProperties { _unMinProperties :: Int }+    deriving (Eq, Show)++instance FromJSON MinProperties where+    parseJSON = withObject "MinProperties" $ \o ->+        MinProperties <$> o .: "minProperties"++minProperties :: Validator a (Maybe MinProperties) ()+minProperties =+    Validator+        noEmbedded+        (run (fmap maybeToList . OB.minProperties . _unMinProperties))++newtype RequiredContext+    = RequiredContext { _unRequiredContext :: OB.Required }+    deriving (Eq, Show)++instance FromJSON RequiredContext where+    parseJSON = withObject "RequiredContext" $ \o ->+        RequiredContext <$> o .: "required"++required :: Validator a (Maybe RequiredContext) ()+required =+    Validator+        noEmbedded+        (run (fmap maybeToList . OB.required . _unRequiredContext))++newtype DependenciesContext schema+    = DependenciesContext+           { _unDependenciesContext :: HashMap Text (OB.Dependency schema) }+    deriving (Eq, Show)++instance FromJSON schema => FromJSON (DependenciesContext schema) where+    parseJSON = withObject "DependenciesContext" $ \o ->+        DependenciesContext <$> o .: "dependencies"++dependencies+    :: (schema -> Value -> [Fail err])+    -> Validator+           schema+           (Maybe (DependenciesContext schema))+           (OB.DependencyInvalid err)+dependencies f =+    Validator+        (maybe mempty ( (\a -> (mempty, a))+                      . catMaybes . fmap checkDependency+                      . HM.elems . _unDependenciesContext+                      ))+        (run (OB.dependencies f . _unDependenciesContext))+  where+    checkDependency :: OB.Dependency schema -> Maybe schema+    checkDependency (OB.PropertyDependency _) = Nothing+    checkDependency (OB.SchemaDependency s)   = Just s++data PropertiesContext schema+    = PropertiesContext+          (Maybe (HashMap Text schema))+          (Maybe (OB.AdditionalProperties schema))+          (HashMap Text schema)+    deriving (Eq, Show)++instance FromJSON schema => FromJSON (PropertiesContext schema) where+    parseJSON = withObject "PropertiesContext" $ \o -> PropertiesContext+        <$> o .:! "patternProperties"+        <*> o .:! "additionalProperties"+        <*> o .: "properties"++properties+    :: (schema -> Value -> [Fail err])+    -> Validator+           schema+           (Maybe (PropertiesContext schema))+           (OB.PropertiesInvalid err)+properties f =+    Validator+        (\a -> case a of+                   Just (PropertiesContext _ _ b) -> (mempty, HM.elems b)+                   Nothing                        -> (mempty, mempty))+        (run (\(PropertiesContext a b c) -> OB.properties f a b c))++-- | The first argument is whether the validator should be run.+-- If @"properties"@ exists it will be parsed to 'False'.+data PatternPropertiesContext schema+    = PatternPropertiesContext+          Bool+          (Maybe (OB.AdditionalProperties schema))+          (HashMap Text schema)+    deriving (Eq, Show)++instance FromJSON schema => FromJSON (PatternPropertiesContext schema) where+    parseJSON = withObject "PatternPropertiesContext" $ \o ->+        PatternPropertiesContext+            <$> shouldRun o+            <*> o .:! "additionalProperties"+            <*> o .: "patternProperties"+      where+        shouldRun :: HashMap Text Value -> Parser Bool+        shouldRun o = do+            a <- o .:! "properties"+            pure $ isNothing (a :: Maybe (HashMap Text schema))++patternProperties+    :: (schema -> Value -> [Fail err])+    -> Validator+           schema+           (Maybe (PatternPropertiesContext schema))+           (OB.PatternPropertiesInvalid err)+patternProperties f =+    Validator+        (\a -> case a of+                   Just (PatternPropertiesContext _ _ b) -> (mempty, HM.elems b)+                   Nothing                               -> (mempty, mempty))+        (run (\(PatternPropertiesContext a b c) -> OB.patternProperties f a b c))++-- | The first argument is whether the validator should be run.+-- If @"properties"@ or @"patternProperties"@ exist it will be parsed+-- to 'False'.+data AdditionalPropertiesContext schema+    = AdditionalPropertiesContext+          Bool+          (OB.AdditionalProperties schema)+    deriving (Eq, Show)++instance FromJSON schema => FromJSON (AdditionalPropertiesContext schema) where+    parseJSON = withObject "AdditionalPropertiesContext" $ \o ->+        AdditionalPropertiesContext+            <$> shouldRun o+            <*> o .: "additionalProperties"+      where+        shouldRun :: HashMap Text Value -> Parser Bool+        shouldRun o = do+            a <- o .:! "properties"+            b <- o .:! "patternProperties"+            pure $ isNothing (a :: Maybe (HashMap Text schema))+                && isNothing (b :: Maybe (HashMap Text schema))++additionalProperties+    :: (schema -> Value -> [Fail err])+    -> Validator+           schema+           (Maybe (AdditionalPropertiesContext schema))+           (OB.AdditionalPropertiesInvalid err)+additionalProperties f =+    Validator+        (\a -> case a of+                   Nothing -> mempty+                   Just (AdditionalPropertiesContext _ b) ->+                       case b of+                           OB.AdditionalPropertiesBool _   -> (mempty, mempty)+                           OB.AdditionalPropertiesObject c -> (mempty, pure c))+        (run (\(AdditionalPropertiesContext a b) -> OB.additionalProperties f a b))++--------------------------------------------------+-- * Any+--------------------------------------------------++newtype Ref+    = Ref { _unRef :: Text }+    deriving (Eq, Show)++instance FromJSON Ref where+    parseJSON = withObject "Ref" $ \o ->+        Ref <$> o .: "$ref"++ref+    :: (FromJSON schema, ToJSON schema)+    => AN.VisitedSchemas+    -> Maybe Text+    -> (Maybe Text -> Maybe schema)+    -> (AN.VisitedSchemas -> Maybe Text -> schema -> Value -> [Fail err])+    -> Validator a (Maybe Ref) (AN.RefInvalid err)+ref visited scope getRef f =+    Validator+        noEmbedded+        (run (AN.ref visited scope getRef f . _unRef))++newtype EnumContext+    = EnumContext { _unEnumContext :: AN.EnumVal }+    deriving (Eq, Show)++instance FromJSON EnumContext where+    parseJSON = withObject "EnumContext" $ \o ->+        EnumContext <$> o .: "enum"++enumVal :: Validator a (Maybe EnumContext) ()+enumVal =+    Validator+        noEmbedded+        (run (fmap maybeToList . AN.enumVal . _unEnumContext))++newtype TypeContext+    = TypeContext { _unTypeContext :: AN.TypeVal }+    deriving (Eq, Show)++instance FromJSON TypeContext where+    parseJSON = withObject "TypeContext" $ \o ->+        TypeContext <$> o .: "type"++typeVal :: Validator a (Maybe TypeContext) ()+typeVal =+    Validator+        noEmbedded+        (run (fmap maybeToList . AN.typeVal . _unTypeContext))++newtype AllOf schema+    = AllOf { _unAllOf :: NonEmpty schema }+    deriving (Eq, Show)++instance FromJSON schema => FromJSON (AllOf schema) where+    parseJSON = withObject "AllOf" $ \o ->+        AllOf <$> o .: "allOf"++allOf+    :: (schema -> Value -> [Fail err])+    -> Validator schema (Maybe (AllOf schema)) err+allOf f =+    Validator+        (\a -> case a of+                   Just (AllOf b) -> (NE.toList b, mempty)+                   Nothing        -> (mempty, mempty))+        (run (AN.allOf f . _unAllOf))++newtype AnyOf schema+    = AnyOf { _unAnyOf :: NonEmpty schema }+    deriving (Eq, Show)++instance FromJSON schema => FromJSON (AnyOf schema) where+    parseJSON = withObject "AnyOf" $ \o ->+        AnyOf <$> o .: "anyOf"++anyOf+    :: ToJSON schema+    => (schema -> Value -> [Fail err])+    -> Validator schema (Maybe (AnyOf schema)) ()+anyOf f =+    Validator+        (\a -> case a of+                 Just (AnyOf b) -> (NE.toList b, mempty)+                 Nothing        -> (mempty, mempty))+        (run (fmap maybeToList . AN.anyOf f . _unAnyOf))++newtype OneOf schema+    = OneOf { _unOneOf :: NonEmpty schema }+    deriving (Eq, Show)++instance FromJSON schema => FromJSON (OneOf schema) where+    parseJSON = withObject "OneOf" $ \o ->+        OneOf <$> o .: "oneOf"++oneOf+    :: ToJSON schema+    => (schema -> Value -> [Fail err])+    -> Validator schema (Maybe (OneOf schema)) ()+oneOf f =+    Validator+        (\a -> case a of+                   Just (OneOf b) -> (NE.toList b, mempty)+                   Nothing        -> (mempty, mempty))+        (run (fmap maybeToList . AN.oneOf f . _unOneOf))++newtype NotVal schema+    = NotVal { _unNotVal :: schema }+    deriving (Eq, Show)++instance FromJSON schema => FromJSON (NotVal schema) where+    parseJSON = withObject "NotVal" $ \o ->+        NotVal <$> o .: "not"++notVal+    :: ToJSON schema+    => (schema -> Value -> [Fail err])+    -> Validator schema (Maybe (NotVal schema)) ()+notVal f =+    Validator+        (\a -> case a of+                   Just (NotVal b) -> (pure b, mempty)+                   Nothing         -> (mempty, mempty))+        (run (fmap maybeToList . AN.notVal f . _unNotVal))
src/Data/Validator/Draft4/Any.hs view
@@ -1,42 +1,67 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}  module Data.Validator.Draft4.Any where +import           Import+-- Hiding is for GHCs before 7.10:+import           Prelude hiding           (any, elem)+ import           Control.Monad import           Data.Aeson.Types         (Parser) import           Data.List.NonEmpty       (NonEmpty)-import qualified Data.List.NonEmpty       as N+import qualified Data.List.NonEmpty       as NE import           Data.Maybe-import           Data.Scientific+import qualified Data.Scientific          as SCI import           Data.Set                 (Set) import qualified Data.Set                 as S -import           Data.Validator.Failure-import           Data.Validator.Utils-import           Data.Validator.Reference (resolveFragment, resolveReference)-import           Import---- For GHCs before 7.10:-import           Prelude hiding           (any, elem)+import           Data.Validator.Failure   (Fail(..))+import qualified Data.Validator.Utils     as UT+import           Data.Validator.Reference (URIBaseAndFragment,+                                           resolveFragment, resolveReference)  -------------------------------------------------- -- * $ref -------------------------------------------------- --- | Will return 'Nothing' if the reference can't be resolved.+data RefInvalid err+    = RefResolution+    | RefLoop+    | RefInvalid err+    deriving (Eq, Show)++newtype VisitedSchemas+    = VisitedSchemas { _unVisited :: [URIBaseAndFragment] }+    deriving (Eq, Show, Monoid)+ ref-  :: forall err schema. (FromJSON schema, ToJSON schema, Show schema)-  => Maybe Text-  -> (Maybe Text -> Maybe schema)-  -> (Maybe Text -> schema -> Value -> [Failure err])-  -> Text-  -> Value-  -> Maybe [Failure err]-ref scope getRef f reference x = do-  let (mUri, mFragment) = resolveReference scope reference-  schema <- getRef mUri-  s      <- resolveFragment mFragment schema-  Just (f mUri s x)+    :: forall err schema. (FromJSON schema, ToJSON schema)+    => VisitedSchemas+    -> Maybe Text+    -> (Maybe Text -> Maybe schema)+    -> (VisitedSchemas -> Maybe Text -> schema -> Value -> [Fail err])+    -> Text+    -> Value+    -> [Fail (RefInvalid err)]+ref visited scope getRef f reference x+    | (mUri, mFragment) `elem` _unVisited visited =+        pure $ Failure RefLoop (toJSON reference) mempty x+    | otherwise =+        case getRef mUri of+            Nothing     -> pure failureDNE+            Just schema ->+                case resolveFragment mFragment schema of+                    Nothing -> pure failureDNE+                    Just s  ->+                        let newVisited = (VisitedSchemas [(mUri, mFragment)]+                                      <> visited)+                        in fmap RefInvalid <$> f newVisited mUri s x+  where+    (mUri, mFragment) = resolveReference scope reference +    failureDNE :: Fail (RefInvalid err)+    failureDNE = Failure RefResolution (toJSON reference) mempty x+ -------------------------------------------------- -- * enum --------------------------------------------------@@ -51,128 +76,144 @@ -- NOTE: We don't enforce the uniqueness constraint in the haskell code, -- but we do in the 'FromJSON' instance. newtype EnumVal-  = EnumVal { _unEnumVal :: NonEmpty Value }-  -- Given a choice, we'd prefer to enforce uniqueness through the type-  -- system over having at least one element. To use a 'Set' though we'd-  -- have to use 'OrdValue' here (there's no 'Ord' instance for plain Values)-  -- and we'd rather not make users mess with 'OrdValue'.-  deriving (Eq, Show)+    = EnumVal { _unEnumVal :: NonEmpty Value }+    -- Given a choice, we'd prefer to enforce uniqueness through the type+    -- system over having at least one element. To use a 'Set' though we'd+    -- have to use 'OrdValue' here (there's no 'Ord' instance for plain Values)+    -- and we'd rather not make users mess with 'OrdValue'.+    deriving (Eq, Show)  instance FromJSON EnumVal where-  parseJSON v = checkUnique . _unNonEmpty' =<< parseJSON v-    where-      checkUnique :: NonEmpty Value -> Parser EnumVal-      checkUnique a-        | allUniqueValues' a = pure (EnumVal a)-        | otherwise = fail "All elements of the Enum validator must be unique."+    parseJSON v = checkUnique . UT._unNonEmpty' =<< parseJSON v+      where+        checkUnique :: NonEmpty Value -> Parser EnumVal+        checkUnique a+            | UT.allUniqueValues' a = pure (EnumVal a)+            | otherwise = fail "All elements of the Enum validator must be unique."  instance ToJSON EnumVal where-  toJSON = toJSON . NonEmpty' . _unEnumVal+    toJSON = toJSON . UT.NonEmpty' . _unEnumVal  instance Arbitrary EnumVal where-  arbitrary = do-    xs <- traverse (const arbitraryValue) =<< (arbitrary :: Gen [()])-    case N.nonEmpty (toUnique xs) of-      Nothing -> EnumVal . pure <$> arbitraryValue-      Just ne -> pure (EnumVal ne)-    where-      toUnique :: [Value] -> [Value]-      toUnique = fmap _unOrdValue . S.toList . S.fromList . fmap OrdValue+    arbitrary = do+        xs <- traverse (const UT.arbitraryValue) =<< (arbitrary :: Gen [()])+        case NE.nonEmpty (toUnique xs) of+            Nothing -> EnumVal . pure <$> UT.arbitraryValue+            Just ne -> pure (EnumVal ne)+      where+        toUnique :: [Value] -> [Value]+        toUnique = fmap UT._unOrdValue . S.toList . S.fromList . fmap UT.OrdValue -enumVal :: EnumVal -> Value -> Maybe (Failure ())+enumVal :: EnumVal -> Value -> Maybe (Fail ()) enumVal (EnumVal vs) x-  | not (allUniqueValues' vs) = Nothing-  | x `elem` vs               = Nothing-  | otherwise                 =-    Just $ Invalid () (toJSON (NonEmpty' vs)) mempty+    | not (UT.allUniqueValues' vs) = Nothing+    | x `elem` vs                  = Nothing+    | otherwise                    = Just $ Failure () (toJSON (UT.NonEmpty' vs))+                                                    mempty x  -------------------------------------------------- -- * type --------------------------------------------------  data TypeVal-  = TypeValString Text-  | TypeValArray (Set Text)-  deriving (Eq, Show)+    = TypeValString Text+    | TypeValArray (Set Text)+    deriving (Eq, Show)  instance FromJSON TypeVal where-  parseJSON v = fmap TypeValString (parseJSON v)-            <|> fmap TypeValArray (parseJSON v)+    parseJSON v = fmap TypeValString (parseJSON v)+              <|> fmap TypeValArray (parseJSON v)  instance ToJSON TypeVal where-  toJSON (TypeValString t) = toJSON t-  toJSON (TypeValArray ts) = toJSON ts+    toJSON (TypeValString t) = toJSON t+    toJSON (TypeValArray ts) = toJSON ts  instance Arbitrary TypeVal where-  arbitrary = oneof [ TypeValString <$> arbitraryText-                    , TypeValArray <$> arbitrarySetOfText-                    ]+    arbitrary = oneof [ TypeValString <$> UT.arbitraryText+                      , TypeValArray <$> UT.arbitrarySetOfText+                      ] -typeVal :: TypeVal -> Value -> Maybe (Failure ())-typeVal (TypeValString t) x = isJsonType x (S.singleton t)-typeVal (TypeValArray ts) x = isJsonType x ts+setFromTypeVal :: TypeVal -> Set Text+setFromTypeVal (TypeValString t) = S.singleton t+setFromTypeVal (TypeValArray ts) = ts -isJsonType :: Value -> Set Text -> Maybe (Failure ())-isJsonType x ts-  | S.null (S.intersection okTypes ts) = Just (Invalid () (toJSON ts) mempty)-  | otherwise                          = Nothing+typeVal :: TypeVal -> Value -> Maybe (Fail ())+typeVal tv x+    | S.null matches = Just (Failure () (toJSON tv) mempty x)+    | otherwise      = Nothing   where+    -- There can be more than one match because a 'Value' can be both a+    -- @"number"@ and an @"integer"@.+    matches :: Set Text+    matches = S.intersection okTypes (setFromTypeVal tv)+     okTypes :: Set Text     okTypes =-      case x of-        Null       -> S.singleton "null"-        (Array _)  -> S.singleton "array"-        (Bool _)   -> S.singleton "boolean"-        (Object _) -> S.singleton "object"-        (String _) -> S.singleton "string"-        (Number y) ->-          case toBoundedInteger y :: Maybe Int of-            Nothing -> S.singleton "number"-            Just _  -> S.fromList ["number", "integer"]+        case x of+            Null       -> S.singleton "null"+            (Array _)  -> S.singleton "array"+            (Bool _)   -> S.singleton "boolean"+            (Object _) -> S.singleton "object"+            (String _) -> S.singleton "string"+            (Number y) ->+                if SCI.isInteger y+                    then S.fromList ["number", "integer"]+                    else S.singleton "number"  ----------------------------------------------------- * other+-- * allOf --------------------------------------------------  allOf-  :: (schema -> Value -> [Failure err])-  -> NonEmpty schema-  -> Value-  -> [Failure err]-allOf f subSchemas x = N.toList subSchemas >>= flip f x+    :: (schema -> Value -> [Fail err])+    -> NonEmpty schema+    -> Value+    -> [Fail err]+allOf f subSchemas x = NE.toList subSchemas >>= flip f x +--------------------------------------------------+-- * anyOf+--------------------------------------------------+ anyOf-  :: ToJSON schema-  => (schema -> Value -> [Failure err])-  -> NonEmpty schema-  -> Value-  -> Maybe (Failure ())+    :: ToJSON schema+    => (schema -> Value -> [Fail err])+    -> NonEmpty schema+    -> Value+    -> Maybe (Fail ()) anyOf f subSchemas x-  | any null (flip f x <$> subSchemas) = Nothing-  | otherwise = Just $ Invalid () (toJSON (NonEmpty' subSchemas)) mempty+    | any null (flip f x <$> subSchemas) = Nothing+    | otherwise = Just $ Failure () (toJSON (UT.NonEmpty' subSchemas))+                                 mempty x +--------------------------------------------------+-- * oneOf+--------------------------------------------------+ oneOf-  :: forall err schema. ToJSON schema-  => (schema -> Value -> [Failure err])-  -> NonEmpty schema-  -> Value-  -> Maybe (Failure ())+    :: forall err schema. ToJSON schema+    => (schema -> Value -> [Fail err])+    -> NonEmpty schema+    -> Value+    -> Maybe (Fail ()) oneOf f subSchemas x-  | length successes == 1 = Nothing-  | otherwise             = Just $ Invalid ()-                                           (toJSON (NonEmpty' subSchemas))-                                           mempty+    | length successes == 1 = Nothing+    | otherwise = Just $ Failure () (toJSON (UT.NonEmpty' subSchemas)) mempty x   where-    successes :: [[Failure err]]-    successes = filter null $ flip f x <$> N.toList subSchemas+    successes :: [[Fail err]]+    successes = filter null $ flip f x <$> NE.toList subSchemas +--------------------------------------------------+-- * not+--------------------------------------------------+ notVal-  :: ToJSON schema-  => (schema -> Value -> [Failure err])-  -> schema-  -> Value-  -> Maybe (Failure ())+    :: ToJSON schema+    => (schema -> Value -> [Fail err])+    -> schema+    -> Value+    -> Maybe (Fail ()) notVal f schema x =-  case f schema x of-    [] -> Just (Invalid () (toJSON schema) mempty)-    _  -> Nothing+    case f schema x of+        [] -> Just (Failure () (toJSON schema) mempty x)+        _  -> Nothing
src/Data/Validator/Draft4/Array.hs view
@@ -1,168 +1,184 @@  module Data.Validator.Draft4.Array where +import           Import+import           Prelude+ import           Control.Monad-import qualified Data.Aeson.Pointer     as P+import qualified Data.Aeson.Pointer     as AP import qualified Data.Text              as T import qualified Data.Vector            as V import           Text.Read              (readMaybe) -import           Data.Validator.Failure+import           Data.Validator.Failure (Fail(..), prependToPath) import           Data.Validator.Utils   (allUniqueValues)-import           Import --- | The spec requires "maxItems" to be non-negative.-maxItems :: Int -> Vector Value -> Maybe (Failure ())+--------------------------------------------------+-- * maxItems+--------------------------------------------------++-- | The spec requires @"maxItems"@ to be non-negative.+maxItems :: Int -> Vector Value -> Maybe (Fail ()) maxItems n xs-  | n < 0           = Nothing-  | V.length xs > n = Just (Invalid () (toJSON n) mempty)-  | otherwise       = Nothing+    | n < 0           = Nothing+    | V.length xs > n = Just (Failure () (toJSON n) mempty (Array xs))+    | otherwise       = Nothing --- | The spec requires "minItems" to be non-negative.-minItems :: Int -> Vector Value -> Maybe (Failure ())+--------------------------------------------------+-- * minItems+--------------------------------------------------++-- | The spec requires @"minItems"@ to be non-negative.+minItems :: Int -> Vector Value -> Maybe (Fail ()) minItems n xs-  | n < 0           = Nothing-  | V.length xs < n = Just (Invalid () (toJSON n) mempty)-  | otherwise       = Nothing+    | n < 0           = Nothing+    | V.length xs < n = Just (Failure () (toJSON n) mempty (Array xs))+    | otherwise       = Nothing -uniqueItems :: Bool -> Vector Value -> Maybe (Failure ())+--------------------------------------------------+-- * uniqueItems+--------------------------------------------------++uniqueItems :: Bool -> Vector Value -> Maybe (Fail ()) uniqueItems True xs-  | allUniqueValues xs = Nothing-  | otherwise          = Just (Invalid () (Bool True) mempty)+   | allUniqueValues xs = Nothing+   | otherwise          = Just (Failure () (Bool True) mempty (Array xs)) uniqueItems False _ = Nothing  -------------------------------------------------- -- * items -------------------------------------------------- -data ItemsInvalid err-  = Items err-  | AdditionalItemsBoolInvalid-  | AdditionalItemsObjectInvalid err-  deriving (Eq, Show)- data Items schema-  = ItemsObject schema-  | ItemsArray [schema]-  deriving (Eq, Show)+    = ItemsObject schema+    | ItemsArray [schema]+    deriving (Eq, Show)  instance FromJSON schema => FromJSON (Items schema) where-  parseJSON v = fmap ItemsObject (parseJSON v)-            <|> fmap ItemsArray (parseJSON v)+    parseJSON v = fmap ItemsObject (parseJSON v)+              <|> fmap ItemsArray (parseJSON v)  instance ToJSON schema => ToJSON (Items schema) where-  toJSON (ItemsObject hm)     = toJSON hm-  toJSON (ItemsArray schemas) = toJSON schemas+    toJSON (ItemsObject hm)     = toJSON hm+    toJSON (ItemsArray schemas) = toJSON schemas  instance Arbitrary schema => Arbitrary (Items schema) where-  arbitrary = oneof [ ItemsObject <$> arbitrary-                    , ItemsArray <$> arbitrary-                    ]+    arbitrary = oneof [ ItemsObject <$> arbitrary+                      , ItemsArray <$> arbitrary+                      ] +data ItemsInvalid err+    = Items err+    | AdditionalItemsBoolInvalid+    | AdditionalItemsObjectInvalid err+    deriving (Eq, Show)+ items-  :: forall err schema.-     (schema -> Value -> [Failure err])-  -> Maybe (AdditionalItems schema)-  -> Items schema-  -> Vector Value-  -> [Failure (ItemsInvalid err)]-items f _ (ItemsObject subSchema) xs = zip [0..] (V.toList xs) >>= g+    :: forall err schema.+       (schema -> Value -> [Fail err])+    -> Maybe (AdditionalItems schema)+    -> Items schema+    -> Vector Value+    -> [Fail (ItemsInvalid err)]+items f _ (ItemsObject subSchema) xs =+    zip [0..] (V.toList xs) >>= g   where-    g :: (Int, Value) -> [Failure (ItemsInvalid err)]-    g (index,x) = modFailure Items-                . addToPath (P.Token (T.pack (show index)))+    g :: (Int, Value) -> [Fail (ItemsInvalid err)]+    g (index,x) = fmap Items+                . prependToPath (AP.Token (T.pack (show index)))               <$> f subSchema x -items f mAdditional (ItemsArray subSchemas) xs = itemFailures-                                              <> additionalItemFailures+items f mAdditional (ItemsArray subSchemas) xs =+    itemFailures <> additionalItemFailures   where     indexedValues :: [(Int, Value)]     indexedValues = zip [0..] (V.toList xs) -    itemFailures :: [Failure (ItemsInvalid err)]+    itemFailures :: [Fail (ItemsInvalid err)]     itemFailures = join (zipWith g subSchemas indexedValues)       where-        g :: schema -> (Int, Value) -> [Failure (ItemsInvalid err)]-        g schema (index,x) = modFailure Items-                           . addToPath (P.Token (T.pack (show index)))+        g :: schema -> (Int, Value) -> [Fail (ItemsInvalid err)]+        g schema (index,x) = fmap Items+                           . prependToPath (AP.Token (T.pack (show index)))                          <$> f schema x -    additionalItemFailures :: [Failure (ItemsInvalid err)]+    additionalItemFailures :: [Fail (ItemsInvalid err)]     additionalItemFailures =-      case mAdditional of-        Nothing  -> mempty-        Just adi -> modFailure correctName-                  . correctIndexes-                <$> additionalItems f adi extras+        case mAdditional of+            Nothing  -> mempty+            Just adi -> fmap correctName+                      . correctIndexes+                    <$> additionalItems f adi extras       where         -- It's not great that we convert back to Vector again.         extras :: Vector Value-        extras =-          V.fromList . fmap snd . drop (length subSchemas) $ indexedValues+        extras = V.fromList . fmap snd+               . drop (length subSchemas) $ indexedValues          -- Since 'additionalItems' only sees part of the array, but starts         -- indexing from zero, we need to modify the paths it reports to         -- represent invalid data so they actually represent the correct         -- offsets.         correctIndexes-          :: Failure (AdditionalItemsInvalid err)-          -> Failure (AdditionalItemsInvalid err)-        correctIndexes (Invalid a b c) = Invalid a b (fixIndex c)+          :: Fail (AdditionalItemsInvalid err)+          -> Fail (AdditionalItemsInvalid err)+        correctIndexes (Failure a b c d) = Failure a b (fixIndex c) d           where-            fixIndex :: P.Pointer -> P.Pointer-            fixIndex (P.Pointer (tok:toks)) =-              case readMaybe . T.unpack . P._unToken $ tok of-                Nothing -> P.Pointer $ tok:toks-                Just n  -> P.Pointer $-                  (P.Token . T.pack . show $ n + length subSchemas):toks-            fixIndex (P.Pointer []) = P.Pointer []+            fixIndex :: AP.Pointer -> AP.Pointer+            fixIndex (AP.Pointer (tok:toks)) =+                case readMaybe . T.unpack . AP._unToken $ tok of+                    Nothing -> AP.Pointer $ tok:toks+                    Just n  -> AP.Pointer $+                        (AP.Token . T.pack . show $ n + length subSchemas):toks+            fixIndex (AP.Pointer []) = AP.Pointer []          correctName :: AdditionalItemsInvalid err -> ItemsInvalid err         correctName AdditionalBoolInvalid = AdditionalItemsBoolInvalid         correctName (AdditionalObjectInvalid err) =-          AdditionalItemsObjectInvalid err+            AdditionalItemsObjectInvalid err  -------------------------------------------------- -- * additionalItems -------------------------------------------------- -data AdditionalItemsInvalid err-  = AdditionalBoolInvalid-  | AdditionalObjectInvalid err-  deriving (Eq, Show)- data AdditionalItems schema-  = AdditionalBool Bool-  | AdditionalObject schema-  deriving (Eq, Show)+    = AdditionalBool Bool+    | AdditionalObject schema+    deriving (Eq, Show)  instance FromJSON schema => FromJSON (AdditionalItems schema) where-  parseJSON v = fmap AdditionalBool (parseJSON v)-            <|> fmap AdditionalObject (parseJSON v)+    parseJSON v = fmap AdditionalBool (parseJSON v)+              <|> fmap AdditionalObject (parseJSON v)  instance ToJSON schema => ToJSON (AdditionalItems schema) where-  toJSON (AdditionalBool b)    = toJSON b-  toJSON (AdditionalObject hm) = toJSON hm+    toJSON (AdditionalBool b)    = toJSON b+    toJSON (AdditionalObject hm) = toJSON hm  instance Arbitrary schema => Arbitrary (AdditionalItems schema) where-  arbitrary = oneof [ AdditionalBool <$> arbitrary-                    , AdditionalObject <$> arbitrary-                    ]+    arbitrary = oneof [ AdditionalBool <$> arbitrary+                      , AdditionalObject <$> arbitrary+                      ] +data AdditionalItemsInvalid err+    = AdditionalBoolInvalid+    | AdditionalObjectInvalid err+    deriving (Eq, Show)+ additionalItems-  :: forall err schema.-     (schema -> Value -> [Failure err])-  -> AdditionalItems schema-  -> Vector Value-  -> [Failure (AdditionalItemsInvalid err)]+    :: forall err schema.+       (schema -> Value -> [Fail err])+    -> AdditionalItems schema+    -> Vector Value+    -> [Fail (AdditionalItemsInvalid err)] additionalItems _ (AdditionalBool b) xs-  | b               = mempty-  | V.length xs > 0 = pure (Invalid AdditionalBoolInvalid (Bool b) mempty)-  | otherwise       = mempty+    | b               = mempty+    | V.length xs > 0 = pure (Failure AdditionalBoolInvalid (Bool b)+                                      mempty (toJSON xs))+    | otherwise       = mempty additionalItems f (AdditionalObject subSchema) xs =-  zip [0..] (V.toList xs) >>= g+    zip [0..] (V.toList xs) >>= g   where-    g :: (Int, Value) -> [Failure (AdditionalItemsInvalid err)]-    g (index,x) = modFailure AdditionalObjectInvalid-                . addToPath (P.Token (T.pack (show index)))+    g :: (Int, Value) -> [Fail (AdditionalItemsInvalid err)]+    g (index,x) = fmap AdditionalObjectInvalid+                . prependToPath (AP.Token (T.pack (show index)))               <$> f subSchema x
src/Data/Validator/Draft4/Number.hs view
@@ -1,51 +1,65 @@  module Data.Validator.Draft4.Number where +import           Import+import           Prelude+ import           Data.Fixed             (mod')-import           Data.Scientific+import           Data.Scientific        (Scientific) -import           Data.Validator.Failure-import           Import+import           Data.Validator.Failure (Fail(..)) --- | The spec requires "multipleOf" to be positive.-multipleOf :: Scientific -> Scientific -> Maybe (Failure ())+--------------------------------------------------+-- * multipleOf+--------------------------------------------------++-- | The spec requires @"multipleOf"@ to be positive.+multipleOf :: Scientific -> Scientific -> Maybe (Fail ()) multipleOf n x-  | n <= 0          = Nothing-  | x `mod'` n /= 0 = Just (Invalid () (toJSON n) mempty)-  | otherwise       = Nothing+    | n <= 0          = Nothing+    | x `mod'` n /= 0 = Just (Failure () (toJSON n) mempty (Number x))+    | otherwise       = Nothing +--------------------------------------------------+-- * maximum+--------------------------------------------------+ data MaximumInvalid-  = Maximum-  | ExclusiveMaximum-  deriving (Eq, Show)+    = Maximum+    | ExclusiveMaximum+    deriving (Eq, Show)  maximumVal-  :: Bool-  -> Scientific-  -> Scientific-  -> Maybe (Failure MaximumInvalid)-maximumVal exclusiveMaximum n x-  | x `greaterThan` n = Just (Invalid err (toJSON n) mempty)-  | otherwise         = Nothing+    :: Bool+    -> Scientific+    -> Scientific+    -> Maybe (Fail MaximumInvalid)+maximumVal exclusive n x+    | x `greaterThan` n = Just (Failure err (toJSON n) mempty (Number x))+    | otherwise         = Nothing   where-    (greaterThan, err) = if exclusiveMaximum-                           then ((>=), ExclusiveMaximum)-                           else ((>), Maximum)+    (greaterThan, err) = if exclusive+                             then ((>=), ExclusiveMaximum)+                             else ((>), Maximum) +--------------------------------------------------+-- * minimum+--------------------------------------------------+ data MinimumInvalid-  = Minimum-  | ExclusiveMinimum-  deriving (Eq, Show)+    = Minimum+    | ExclusiveMinimum+    deriving (Eq, Show)  minimumVal-  :: Bool-  -> Scientific-  -> Scientific-  -> Maybe (Failure MinimumInvalid)-minimumVal exclusiveMinimum n x-  | x `lessThan` n = Just (Invalid err (toJSON n) mempty)-  | otherwise      = Nothing+    :: Bool+    -> Scientific+    -> Scientific+    -> Maybe (Fail MinimumInvalid)+minimumVal exclusive n x+    | x `lessThan` n = Just (Failure err (toJSON n) mempty (Number x))+    | otherwise      = Nothing   where-    (lessThan, err) = if exclusiveMinimum-                        then ((<=), ExclusiveMinimum)-                        else ((<), Minimum)+    (lessThan, err) = if exclusive+                          then ((<=), ExclusiveMinimum)+                          else ((<), Minimum)
src/Data/Validator/Draft4/Object.hs view
@@ -1,11 +1,15 @@ {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE ScopedTypeVariables        #-}  module Data.Validator.Draft4.Object   ( module Data.Validator.Draft4.Object   , module Data.Validator.Draft4.Object.Properties   ) where +import           Import+-- Hiding is for GHCs before 7.10:+import           Prelude                                 hiding (all, concat,+                                                          foldl)+ import           Data.Aeson.Types                        (Parser) import qualified Data.HashMap.Strict                     as H import           Data.Set                                (Set)@@ -13,27 +17,30 @@ import qualified Data.Text                               as T  import           Data.Validator.Draft4.Object.Properties-import           Data.Validator.Failure+import           Data.Validator.Failure                  (Fail(..)) import           Data.Validator.Utils-import           Import --- For GHCs before 7.10:-import           Prelude                                 hiding (all, concat,-                                                          foldl)+--------------------------------------------------+-- * maxProperties+-------------------------------------------------- --- | The spec requires "maxProperties" to be non-negative.-maxProperties :: Int -> HashMap Text Value -> Maybe (Failure ())+-- | The spec requires @"maxProperties"@ to be non-negative.+maxProperties :: Int -> HashMap Text Value -> Maybe (Fail ()) maxProperties n x-  | n < 0        = Nothing-  | H.size x > n = Just (Invalid () (toJSON n) mempty)-  | otherwise    = Nothing+    | n < 0        = Nothing+    | H.size x > n = Just (Failure () (toJSON n) mempty (Object x))+    | otherwise    = Nothing --- | The spec requires "minProperties" to be non-negative.-minProperties :: Int -> HashMap Text Value -> Maybe (Failure ())+--------------------------------------------------+-- * minProperties+--------------------------------------------------++-- | The spec requires @"minProperties"@ to be non-negative.+minProperties :: Int -> HashMap Text Value -> Maybe (Fail ()) minProperties n x-  | n < 0        = Nothing-  | H.size x < n = Just (Invalid () (toJSON n) mempty)-  | otherwise    = Nothing+    | n < 0        = Nothing+    | H.size x < n = Just (Failure () (toJSON n) mempty (Object x))+    | otherwise    = Nothing  -------------------------------------------------- -- * required@@ -48,39 +55,40 @@ -- We don't enfore that 'Required' has at least one element in the -- haskell code, but we do in the 'FromJSON' instance. newtype Required-  = Required { _unRequired :: Set Text }-  deriving (Eq, Show, ToJSON)+    = Required { _unRequired :: Set Text }+    deriving (Eq, Show, ToJSON)  instance FromJSON Required where-  parseJSON v = checkUnique =<< checkSize =<< parseJSON v-    where-      checkSize :: [Text] -> Parser [Text]-      checkSize a-        | null a    = fail "Required validator must not be empty."-        | otherwise = pure a+    parseJSON v = checkUnique =<< checkSize =<< parseJSON v+      where+        checkSize :: [Text] -> Parser [Text]+        checkSize a+            | null a    = fail "Required validator must not be empty."+            | otherwise = pure a -      checkUnique :: [Text] -> Parser Required-      checkUnique a =-        let b = S.fromList a-        -- NOTE: Can use length instead of S.size in GHC 7.10 or later.-        in if length a == S.size b-          then pure (Required b)-          else fail "All elements of the Required validator must be unique."+        checkUnique :: [Text] -> Parser Required+        checkUnique a =+            let b = S.fromList a+            -- NOTE: Can use length instead of S.size in GHC 7.10 or later.+            in if length a == S.size b+                then pure (Required b)+                else fail "All elements of the Required validator must be unique."  instance Arbitrary Required where-  arbitrary = do-    x  <- arbitraryText -- Guarantee at least one element.-    xs <- (fmap.fmap) T.pack arbitrary-    pure . Required . S.fromList $ x:xs+    arbitrary = do+        x  <- arbitraryText -- Guarantee at least one element.+        xs <- (fmap.fmap) T.pack arbitrary+        pure . Required . S.fromList $ x:xs -required :: Required -> HashMap Text Value -> Maybe (Failure ())+required :: Required -> HashMap Text Value -> Maybe (Fail ()) required (Required ts) x-  -- NOTE: When we no longer need to support GHCs before 7.10-  -- we can use null from Prelude throughout the library-  -- instead of specialized versions.-  | S.null ts                  = Nothing-  | H.null (H.difference hm x) = Nothing-  | otherwise                  = Just (Invalid () (toJSON ts) mempty)+    -- NOTE: When we no longer need to support GHCs before 7.10+    -- we can use null from Prelude throughout the library+    -- instead of specialized versions.+    | S.null ts                  = Nothing+    | H.null (H.difference hm x) = Nothing+    | otherwise                  = Just (Failure () (toJSON ts)+                                                 mempty (Object x))   where     hm :: HashMap Text Bool     hm = foldl (\b a -> H.insert a True b) mempty ts@@ -89,29 +97,29 @@ -- * dependencies -------------------------------------------------- -data DependencyInvalid err-  = SchemaDependencyInvalid err-  | PropertyDependencyInvalid-  deriving (Eq, Show)- data Dependency schema-  = SchemaDependency schema-  | PropertyDependency (Set Text)-  deriving (Eq, Show)+    = SchemaDependency schema+    | PropertyDependency (Set Text)+    deriving (Eq, Show)  instance FromJSON schema => FromJSON (Dependency schema) where-  parseJSON v = fmap SchemaDependency (parseJSON v)-            <|> fmap PropertyDependency (parseJSON v)+    parseJSON v = fmap SchemaDependency (parseJSON v)+              <|> fmap PropertyDependency (parseJSON v)  instance ToJSON schema => ToJSON (Dependency schema) where-  toJSON (SchemaDependency schema) = toJSON schema-  toJSON (PropertyDependency ts)   = toJSON ts+    toJSON (SchemaDependency schema) = toJSON schema+    toJSON (PropertyDependency ts)   = toJSON ts  instance Arbitrary schema => Arbitrary (Dependency schema) where-  arbitrary = oneof [ SchemaDependency <$> arbitrary-                    , PropertyDependency <$> arbitrarySetOfText-                    ]+    arbitrary = oneof [ SchemaDependency <$> arbitrary+                      , PropertyDependency <$> arbitrarySetOfText+                      ] +data DependencyInvalid err+    = SchemaDependencyInvalid err+    | PropertyDependencyInvalid+    deriving (Eq, Show)+ -- | From the spec: -- <http://json-schema.org/latest/json-schema-validation.html#anchor70> --@@ -125,23 +133,25 @@ -- > Each element MUST be a string, and elements in the array MUST be unique. -- > This is called a property dependency. dependencies-  :: forall err schema. (schema -> Value -> [Failure err])-  -> HashMap Text (Dependency schema)-  -> HashMap Text Value-  -> [Failure (DependencyInvalid err)]+    :: forall err schema.+       (schema -> Value -> [Fail err])+    -> HashMap Text (Dependency schema)+    -> HashMap Text Value+    -> [Fail (DependencyInvalid err)] dependencies f hm x = concat . fmap (uncurry g) . H.toList $ hm   where-    g :: Text -> Dependency schema -> [Failure (DependencyInvalid err)]+    g :: Text -> Dependency schema -> [Fail (DependencyInvalid err)]     g k (SchemaDependency schema)-      | H.member k x =-        modFailure SchemaDependencyInvalid <$> f schema (Object x)-      | otherwise    = mempty+        | H.member k x =+            fmap SchemaDependencyInvalid <$> f schema (Object x)+        | otherwise    = mempty     g k (PropertyDependency ts)-      | H.member k x && not allPresent =-        pure $ Invalid PropertyDependencyInvalid-                       (toJSON (H.singleton k ts))-                       mempty-      | otherwise = mempty+        | H.member k x && not allPresent =+            pure $ Failure PropertyDependencyInvalid+                           (toJSON (H.singleton k ts))+                           mempty+                           (Object x)+        | otherwise = mempty       where         allPresent :: Bool         allPresent = all (`H.member` x) ts
src/Data/Validator/Draft4/Object/Properties.hs view
@@ -1,190 +1,214 @@-{-# LANGUAGE ScopedTypeVariables #-}  module Data.Validator.Draft4.Object.Properties where +import           Import+import           Prelude+ import           Control.Monad-import           Data.Aeson-import qualified Data.Aeson.Pointer     as P-import qualified Data.HashMap.Strict    as H-import qualified Data.Text              as T+import qualified Data.Aeson.Pointer     as AP+import           Data.Functor           (($>))+import qualified Data.HashMap.Strict    as HM import           Data.Text.Encoding     (encodeUtf8) import qualified Text.Regex.PCRE.Heavy  as RE -import           Data.Validator.Failure-import           Import+import           Data.Validator.Failure (Fail(..), prependToPath) -newtype Remaining = Remaining { _unRemaining :: HashMap Text Value }+-- | For internal use.+newtype Remaining+    = Remaining { _unRemaining :: HashMap Text Value }  -------------------------------------------------- -- * properties --------------------------------------------------  data PropertiesInvalid err-  = PropertiesInvalid err-  | PropPatternInvalid err-  | PropAdditionalInvalid (AdditionalPropertiesInvalid err)-  deriving (Eq, Show)+    = PropertiesInvalid err+    | PropPatternInvalid err+    | PropAdditionalInvalid (AdditionalPropertiesInvalid err)+    deriving (Eq, Show) --- | In order of what's tried: "properties", "patternProperties",--- "additionalProperties".+-- | In order of what's tried: @"properties"@, @"patternProperties"@,+-- @"additionalProperties"@. properties-  :: forall err schema.-     (schema -> Value -> [Failure err])-  -> Maybe (HashMap Text schema)-  -> Maybe (AdditionalProperties schema)-  -> HashMap Text schema-  -> HashMap Text Value-  -> [Failure (PropertiesInvalid err)]+    :: forall err schema.+       (schema -> Value -> [Fail err])+    -> Maybe (HashMap Text schema)+    -> Maybe (AdditionalProperties schema)+    -> HashMap Text schema+    -> HashMap Text Value+    -> [Fail (PropertiesInvalid err)] properties f mPat mAdd propertiesHm x =-     fmap (modFailure PropertiesInvalid) propFailures-  <> fmap (modFailure PropPatternInvalid) patternFailures-  <> fmap (modFailure PropAdditionalInvalid) additionalFailures+       fmap (fmap PropertiesInvalid) propFailures+    <> fmap (fmap PropPatternInvalid) patternFailures+    <> fmap (fmap PropAdditionalInvalid) additionalFailures   where-    propertiesAndUnmatched :: ([Failure err], Remaining)+    propertiesAndUnmatched :: ([Fail err], Remaining)     propertiesAndUnmatched = ( failures-                             , Remaining (H.difference x propertiesHm)+                             , Remaining (HM.difference x propertiesHm)                              )       where-        failures :: [Failure err]-        failures = H.toList (H.intersectionWith f propertiesHm x)-               >>= (\(k,vs) -> fmap (addToPath (P.Token k)) vs)+        failures :: [Fail err]+        failures = HM.toList (HM.intersectionWith f propertiesHm x)+               >>= (\(k,vs) -> fmap (prependToPath (AP.Token k)) vs)      (propFailures, remaining1) = propertiesAndUnmatched -    mPatProp :: Maybe (HashMap Text Value -> ([Failure err], Remaining))+    mPatProp :: Maybe (HashMap Text Value -> ([Fail err], Remaining))     mPatProp = patternAndUnmatched f <$> mPat -    patternFailures :: [Failure err]+    patternFailures :: [Fail err]     patternFailures = case mPatProp of-                        Nothing  -> mempty-                        Just val -> fst (val x)+                          Nothing  -> mempty+                          Just val -> fst (val x)      remaining2 :: Remaining     remaining2 = case mPatProp of-                   Nothing  -> remaining1-                   Just val -> snd . val . _unRemaining $ remaining1+                     Nothing  -> remaining1+                     Just val -> snd . val . _unRemaining $ remaining1 -    additionalFailures :: [Failure (AdditionalPropertiesInvalid err)]-    additionalFailures = case additionalProperties f <$> mAdd of-                           Nothing  -> mempty-                           Just val -> val (_unRemaining remaining2)+    additionalFailures :: [Fail (AdditionalPropertiesInvalid err)]+    additionalFailures =+        case mAdd of+            Nothing -> mempty+            Just a  -> additionalProperties f True a (_unRemaining remaining2)  -------------------------------------------------- -- * patternProperties --------------------------------------------------  data PatternPropertiesInvalid err-  = PPInvalid err-  | PPAdditionalPropertiesInvalid (AdditionalPropertiesInvalid err)-  deriving (Eq, Show)+    = PPInvalid err+    | PPAdditionalPropertiesInvalid (AdditionalPropertiesInvalid err)+    deriving (Eq, Show)  patternProperties-  :: forall err schema.-     (schema -> Value -> [Failure err])-  -> Maybe (AdditionalProperties schema)-  -> HashMap Text schema-  -> HashMap Text Value-  -> [Failure (PatternPropertiesInvalid err)]-patternProperties f mAdd patternPropertiesHm x =-     fmap (modFailure PPInvalid) ppFailures-  <> fmap (modFailure PPAdditionalPropertiesInvalid) addFailures+    :: forall err schema.+       (schema -> Value -> [Fail err])+    -> Bool+    -> Maybe (AdditionalProperties schema)+    -> HashMap Text schema+    -> HashMap Text Value+    -> [Fail (PatternPropertiesInvalid err)]+patternProperties _ False _ _ _ = mempty+patternProperties f _ mAdd patternPropertiesHm x =+       (fmap PPInvalid <$> ppFailures)+    <> (fmap PPAdditionalPropertiesInvalid <$> addFailures)   where-    patternProps :: ([Failure err], Remaining)+    patternProps :: ([Fail err], Remaining)     patternProps = patternAndUnmatched f patternPropertiesHm x      (ppFailures, remaining) = patternProps -    addFailures :: [Failure (AdditionalPropertiesInvalid err)]-    addFailures = case additionalProperties f <$> mAdd of-                    Nothing  -> mempty-                    Just val -> val (_unRemaining remaining)+    addFailures :: [Fail (AdditionalPropertiesInvalid err)]+    addFailures =+        case mAdd of+            Nothing -> mempty+            Just a  -> additionalProperties f True a (_unRemaining remaining)  patternAndUnmatched-  :: forall err schema.-     (schema -> Value -> [Failure err])-  -> HashMap Text schema-  -> HashMap Text Value-  -> ([Failure err], Remaining)+    :: forall err schema.+       (schema -> Value -> [Fail err])+    -> HashMap Text schema+    -> HashMap Text Value+    -> ([Fail err], Remaining) patternAndUnmatched f patPropertiesHm x =-  (H.foldlWithKey' runVals mempty perhapsMatches, remaining)+    (HM.foldlWithKey' runVals mempty perhapsMatches, remaining)   where     perhapsMatches :: HashMap Text (Value, [schema])-    perhapsMatches = H.foldlWithKey' (matchingSchemas patPropertiesHm) mempty x+    perhapsMatches = HM.foldlWithKey' (matchingSchemas patPropertiesHm) mempty x       where         matchingSchemas-          :: HashMap Text schema-          -> HashMap Text (Value, [schema])-          -> Text-          -> Value-          -> HashMap Text (Value, [schema])+            :: HashMap Text schema+            -> HashMap Text (Value, [schema])+            -> Text+            -> Value+            -> HashMap Text (Value, [schema])         matchingSchemas subSchemas acc k v =-          H.insert k (v, H.foldlWithKey' (checkKey k) mempty subSchemas) acc+            HM.insert k (v, HM.foldlWithKey' (checkKey k) mempty subSchemas) acc          checkKey-          :: Text-          -> [schema]-          -> Text-          -> schema-          -> [schema]+            :: Text+            -> [schema]+            -> Text+            -> schema+            -> [schema]         checkKey k acc r subSchema =-          case RE.compileM (encodeUtf8 r) mempty of-            Left _   -> acc-            Right re -> if k RE.=~ re-                          then pure subSchema <> acc-                          else acc+            case RE.compileM (encodeUtf8 r) mempty of+                Left _   -> acc+                Right re -> if k RE.=~ re+                                then pure subSchema <> acc+                                else acc      runVals-      :: [Failure err]-      -> Text-      -> (Value, [schema])-      -> [Failure err]+        :: [Fail err]+        -> Text+        -> (Value, [schema])+        -> [Fail err]     runVals acc k (v,subSchemas) =-      (subSchemas >>= (\schema -> addToPath (P.Token k) <$> f schema v))-      <> acc+        (subSchemas >>= (\schema -> prependToPath (AP.Token k) <$> f schema v))+        <> acc      remaining :: Remaining-    remaining = Remaining . fmap fst . H.filter (null . snd) $ perhapsMatches+    remaining = Remaining . fmap fst . HM.filter (null . snd) $ perhapsMatches  -------------------------------------------------- -- * additionalProperties -------------------------------------------------- -data AdditionalPropertiesInvalid err-  = APBoolInvalid-  | APObjectInvalid err-  deriving (Eq, Show)- data AdditionalProperties schema-  = AdditionalPropertiesBool Bool-  | AdditionalPropertiesObject schema-  deriving (Eq, Show)+    = AdditionalPropertiesBool Bool+    | AdditionalPropertiesObject schema+    deriving (Eq, Show)  instance FromJSON schema => FromJSON (AdditionalProperties schema) where-  parseJSON v = fmap AdditionalPropertiesBool (parseJSON v)-            <|> fmap AdditionalPropertiesObject (parseJSON v)+    parseJSON v = fmap AdditionalPropertiesBool (parseJSON v)+              <|> fmap AdditionalPropertiesObject (parseJSON v)  instance ToJSON schema => ToJSON (AdditionalProperties schema) where-  toJSON (AdditionalPropertiesBool b)    = toJSON b-  toJSON (AdditionalPropertiesObject hm) = toJSON hm+    toJSON (AdditionalPropertiesBool b)    = toJSON b+    toJSON (AdditionalPropertiesObject hm) = toJSON hm  instance Arbitrary schema => Arbitrary (AdditionalProperties schema) where-  arbitrary = oneof [ AdditionalPropertiesBool <$> arbitrary-                    , AdditionalPropertiesObject <$> arbitrary-                    ]+    arbitrary = oneof [ AdditionalPropertiesBool <$> arbitrary+                      , AdditionalPropertiesObject <$> arbitrary+                      ] +data AdditionalPropertiesInvalid err+    = APBoolInvalid+    | APObjectInvalid err+    deriving (Eq, Show)+ additionalProperties-  :: forall err schema.-     (schema -> Value -> [Failure err])-  -> AdditionalProperties schema-  -> HashMap Text Value-  -> [Failure (AdditionalPropertiesInvalid err)]-additionalProperties _ (AdditionalPropertiesBool False) x-  | H.size x > 0 = pure $ Invalid APBoolInvalid (Bool False) mempty-  | otherwise    = mempty-additionalProperties _ (AdditionalPropertiesBool True) _ = mempty-additionalProperties f (AdditionalPropertiesObject schema) x = H.toList x >>= g+    :: forall err schema.+       (schema -> Value -> [Fail err])+    -> Bool+    -> AdditionalProperties schema+    -> HashMap Text Value+    -> [Fail (AdditionalPropertiesInvalid err)]+additionalProperties _ False _ _ = mempty+additionalProperties f _ a x =+    case a of+        AdditionalPropertiesBool b ->+            ($> APBoolInvalid) <$> additionalPropertiesBool b x+        AdditionalPropertiesObject b ->+            fmap APObjectInvalid <$> additionalPropertiesObject f b x++additionalPropertiesBool+    :: Bool+    -> HashMap Text Value+    -> [Fail ()]+additionalPropertiesBool False x+    | HM.size x > 0 = pure $ Failure () (Bool False) mempty (Object x)+    | otherwise    = mempty+additionalPropertiesBool True _ = mempty++additionalPropertiesObject+    :: forall err schema.+       (schema -> Value -> [Fail err])+    -> schema+    -> HashMap Text Value+    -> [Fail err]+additionalPropertiesObject f schema x = HM.toList x >>= g   where-    g :: (Text, Value) -> [Failure (AdditionalPropertiesInvalid err)]-    g (k,v) = modFailure APObjectInvalid-            . addToPath (P.Token k)-          <$> f schema v+    g :: (Text, Value) -> [Fail err]+    g (k,v) = prependToPath (AP.Token k) <$> f schema v
src/Data/Validator/Draft4/String.hs view
@@ -1,32 +1,45 @@  module Data.Validator.Draft4.String where -import           Data.Aeson+import           Import+import           Prelude+ import qualified Data.Text              as T import           Data.Text.Encoding     (encodeUtf8) import qualified Text.Regex.PCRE.Heavy  as RE -import           Data.Validator.Failure-import           Import+import           Data.Validator.Failure (Fail(..)) --- | The spec requires "maxLength" to be non-negative.-maxLength :: Int -> Text -> Maybe (Failure ())+--------------------------------------------------+-- * maxLength+--------------------------------------------------++-- | The spec requires @"maxLength"@ to be non-negative.+maxLength :: Int -> Text -> Maybe (Fail ()) maxLength n x-  | n <= 0         = Nothing-  | T.length x > n = Just (Invalid () (toJSON n) mempty)-  | otherwise      = Nothing+    | n <= 0         = Nothing+    | T.length x > n = Just (Failure () (toJSON n) mempty (String x))+    | otherwise      = Nothing --- | The spec requires "minLength" to be non-negative.-minLength :: Int -> Text -> Maybe (Failure ())+--------------------------------------------------+-- * minLength+--------------------------------------------------++-- | The spec requires @"minLength"@ to be non-negative.+minLength :: Int -> Text -> Maybe (Fail ()) minLength n x-  | n <= 0         = Nothing-  | T.length x < n = Just (Invalid () (toJSON n) mempty)-  | otherwise      = Nothing+    | n <= 0         = Nothing+    | T.length x < n = Just (Failure () (toJSON n) mempty (String x))+    | otherwise      = Nothing -patternVal :: Text -> Text -> Maybe (Failure ())+--------------------------------------------------+-- * pattern+--------------------------------------------------++patternVal :: Text -> Text -> Maybe (Fail ()) patternVal t x =-  case RE.compileM (encodeUtf8 t) mempty of-    Left _   -> Just (Invalid () (toJSON t) mempty)-    Right re -> if x RE.=~ re-                  then Nothing-                  else Just (Invalid () (toJSON t) mempty)+    case RE.compileM (encodeUtf8 t) mempty of+        Left _   -> Just (Failure () (toJSON t) mempty (String x))+        Right re -> if x RE.=~ re+                        then Nothing+                        else Just (Failure () (toJSON t) mempty (String x))
src/Data/Validator/Failure.hs view
@@ -1,42 +1,49 @@-{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE DeriveFunctor #-}  module Data.Validator.Failure where -import qualified Data.Aeson.Pointer as P- import           Import---- For GHCs before 7.10:+-- Hiding is for GHCs before 7.10: import           Prelude            hiding (concat, sequence) +import qualified Data.Aeson.Pointer as AP+ -- | Validators shouldn't know more about the schema they're going to -- be used with than necessary. If a validator throws errors using the -- error sum type of a particular schema, then it can't be used with -- other schemas later that have different error sum types (at least not -- without writing partial functions). ----- Thus validators that can only fail in one way return 'FailureInfo's.--- Validators that can fail in multiple ways return 'ValidationFailure's--- along with an custom error sum type for that particular validator.+-- Because of this we make 'Fail' a higher order type, so each validator+-- can return a sum type describing only the failures that can occur in that+-- validator (or '()' if that validator can only fail in one way). -- -- It's the job of a schema's validate function to unify the errors produced -- by the validators it uses into a single error sum type for that schema.--- The schema's validate function will return a 'ValidationFailure' with+-- The schema's validate function will return a 'Fail' with -- that sum type as its type argument.-data Failure err = Invalid-  { _invalidValidatorsCalled :: !err-  , _invalidFinalValidator   :: !Value-  , _invalidOffendingData    :: !P.Pointer-  } deriving (Eq, Show)--setFailure :: b -> Failure a -> Failure b-setFailure e (Invalid _ a b) = Invalid e a b--modFailure :: (a -> b) -> Failure a -> Failure b-modFailure f v@(Invalid a _ _) = v { _invalidValidatorsCalled = f a }+--+-- The slightly weird naming ('Fail' and 'Failure') is so that we can define+-- a 'type Failure = Fail SchemaErrorType' for each of our schemas, and+-- export it along with 'Fail(..)'. This way the users of the library only+-- use 'Failure', not 'Fail'.+data Fail err = Failure+    { _failureValidatorsCalled :: !err+      -- ^ E.g. @Items UniqueItems@ during draft 4 validation.+    , _failureFinalValidator   :: !Value+      -- ^ The value of the validator that raised the error (e.g. the value of+      -- @"uniqueItems"@ in the above example.+    , _failureOffendingPointer :: !AP.Pointer+      -- ^ A pointer to the part of the data that caused invalidation.+    , _failureOffendingData    :: !Value+      -- ^ The part of the data that caused invalidation. Usually this is+      -- identical to the result of resolving '_invalidOffendingPointer'+      -- against the starting data, but not always (e.g. in the case of+      -- 'additionalItems' where '_invalidOffendingData' will be the items+      -- in the array that were not allowed, instead of the entire array).+    } deriving (Eq, Show, Functor) -addToPath :: P.Token -> Failure a -> Failure a-addToPath tok v@(Invalid _ _ a) = v { _invalidOffendingData = addToken a }-  where-    addToken :: P.Pointer -> P.Pointer-    addToken (P.Pointer ts) = P.Pointer (ts <> pure tok)+prependToPath :: AP.Token -> Fail a -> Fail a+prependToPath tok failure =+    let old = _failureOffendingPointer failure+    in failure { _failureOffendingPointer = AP.Pointer [tok] <> old }
src/Data/Validator/Reference.hs view
@@ -1,56 +1,44 @@- -- | JSON Reference is described here: -- <http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03> -- -- And is extended for JSON Schema here: -- <http://json-schema.org/latest/json-schema-core.html#anchor26>------ Notes on where these functions are used:------   * 'Data.JsonSchema.Fetch.includeSubschemas' uses 'updateResolutionScope'.------   * 'Data.JsonSchema.Fetch.foldFunction' uses 'resolveReference'.------   * 'Data.JsonSchema.Draft4.Internal' uses 'updateResolutionScope'---     throughout.------   * 'Data.Validator.Draft4.Any.ref' uses 'resolveReference' and---     'resolveFragment'.  module Data.Validator.Reference where -import qualified Data.Aeson.Pointer     as P+import           Import+import           Prelude++import qualified Data.Aeson.Pointer     as AP import qualified Data.Text              as T-import           Data.Text.Encoding+import           Data.Text.Encoding     (decodeUtf8, encodeUtf8) import           Network.HTTP.Types.URI (urlDecode) import           System.FilePath        ((</>), dropFileName) -import           Import- type URIBase = Maybe Text type URIBaseAndFragment = (Maybe Text, Maybe Text)  updateResolutionScope :: URIBase -> Maybe Text -> URIBase updateResolutionScope mScope idKeyword-  | Just t <- idKeyword = fst . baseAndFragment $ resolveScopeAgainst mScope t-  | otherwise           = mScope+    | Just t <- idKeyword = fst . baseAndFragment $ resolveScopeAgainst mScope t+    | otherwise           = mScope  resolveReference :: URIBase -> Text -> URIBaseAndFragment resolveReference mScope t = baseAndFragment $ resolveScopeAgainst mScope t  resolveFragment-  :: (FromJSON schema, ToJSON schema, Show schema)-  => Maybe Text-  -> schema-  -> Maybe schema+    :: (FromJSON schema, ToJSON schema)+    => Maybe Text+    -> schema+    -> Maybe schema resolveFragment Nothing schema        = Just schema resolveFragment (Just pointer) schema = do-  let urlDecoded = decodeUtf8 . urlDecode True . encodeUtf8 $ pointer-  p <- either (const Nothing) Just (P.unescape urlDecoded)-  x <- either (const Nothing) Just (P.resolve p (toJSON schema))-  case fromJSON x of-    Error _         -> Nothing-    Success schema' -> Just schema'+    let urlDecoded = decodeUtf8 . urlDecode True . encodeUtf8 $ pointer+    p <- either (const Nothing) Just (AP.unescape urlDecoded)+    x <- either (const Nothing) Just (AP.resolve p (toJSON schema))+    case fromJSON x of+        Error _         -> Nothing+        Success schema' -> Just schema'  -------------------------------------------------- -- * Helpers@@ -73,19 +61,19 @@ resolveScopeAgainst :: Maybe Text -> Text -> Text resolveScopeAgainst Nothing t = t resolveScopeAgainst (Just scope) t-  | isRemoteReference t = t-  | otherwise           = smartAppend+    | isRemoteReference t = t+    | otherwise           = smartAppend   where     -- There shouldn't be a fragment at the end of a scope URI,     -- but just in case a user leaves one in we want to be sure     -- to cut it off before appending.     smartAppend :: Text     smartAppend =-      case baseAndFragment scope of-        (Just base,_) ->-          case T.unpack t of-            -- We want "/foo" and "#/bar" to combine into-            -- "/foo#/bar" not "/foo/#/bar".-            '#':_ -> base <> t-            _     -> T.pack (dropFileName (T.unpack base) </> T.unpack t)-        _ -> t+        case baseAndFragment scope of+            (Just base,_) ->+                case T.unpack t of+                    -- We want "/foo" and "#/bar" to combine into+                    -- "/foo#/bar" not "/foo/#/bar".+                    '#':_ -> base <> t+                    _     -> T.pack (dropFileName (T.unpack base) </> T.unpack t)+            _ -> t
+ src/Data/Validator/Types.hs view
@@ -0,0 +1,27 @@+{-# LANGUAGE DeriveFunctor #-}++module Data.Validator.Types where++import           Prelude++import           Data.Aeson             (Value)+import           Data.Profunctor        (Profunctor(..))++import           Data.Validator.Failure (Fail)++data Validator schema val err = Validator+    { _embedded :: val -> ([schema], [schema])+      -- ^ The first list is embedded schemas that validate the same piece+      -- of data this schema validates (such as schemas embedded in 'allOf').+      -- The second is embedded schemas that validate a subset of that data+      -- (such as the schemas embedded in 'items').+      --+      -- This is done to allow static detection of loops, though this isn't+      -- implemented yet (though the Draft4 code does do live loop detection+      -- during validation).+    , _validate :: val -> Value -> [Fail err]+    } deriving Functor++instance Profunctor (Validator schema) where+    lmap f (Validator a b) = Validator (lmap f a) (lmap f b)+    rmap = fmap
src/Data/Validator/Utils.hs view
@@ -1,18 +1,19 @@  module Data.Validator.Utils where +import           Import+import           Prelude+ import           Control.Arrow-import qualified Data.HashMap.Strict  as H+import qualified Data.HashMap.Strict  as HM import           Data.List.NonEmpty   (NonEmpty)-import qualified Data.List.NonEmpty   as N+import qualified Data.List.NonEmpty   as NE import           Data.Scientific      (Scientific, fromFloatDigits) import           Data.Set             (Set) import qualified Data.Set             as S import qualified Data.Text            as T import qualified Data.Vector          as V -import           Import- -------------------------------------------------- -- * QuickCheck --------------------------------------------------@@ -36,7 +37,7 @@         | otherwise = oneof $             fmap (Array . V.fromList) (traverse (const (f (n `div` 10)))               =<< (arbitrary :: Gen [()]))-          : fmap (Object . H.fromList) (traverse (const (g (n `div` 10)))+          : fmap (Object . HM.fromList) (traverse (const (g (n `div` 10)))               =<< (arbitrary :: Gen [()]))           : nonRecursive @@ -45,14 +46,14 @@      nonRecursive :: [Gen Value]     nonRecursive =-      [ pure Null-      , Bool <$> arbitrary-      , String <$> arbitraryText-      , Number <$> arbitraryScientific-      ]+        [ pure Null+        , Bool <$> arbitrary+        , String <$> arbitraryText+        , Number <$> arbitraryScientific+        ]  arbitraryHashMap :: Arbitrary a => Gen (HashMap Text a)-arbitraryHashMap = H.fromList . fmap (first T.pack) <$> arbitrary+arbitraryHashMap = HM.fromList . fmap (first T.pack) <$> arbitrary  arbitrarySetOfText :: Gen (Set Text) arbitrarySetOfText = S.fromList . fmap T.pack <$> arbitrary@@ -60,21 +61,21 @@ newtype NonEmpty' a = NonEmpty' { _unNonEmpty' :: NonEmpty a }  instance FromJSON a => FromJSON (NonEmpty' a) where-  parseJSON v = do-    xs <- parseJSON v-    case N.nonEmpty xs of-      Nothing -> fail "Must have at least one item."-      Just ne -> pure (NonEmpty' ne)+    parseJSON v = do+        xs <- parseJSON v+        case NE.nonEmpty xs of+            Nothing -> fail "Must have at least one item."+            Just ne -> pure (NonEmpty' ne)  instance ToJSON a => ToJSON (NonEmpty' a) where-  toJSON = toJSON . N.toList . _unNonEmpty'+    toJSON = toJSON . NE.toList . _unNonEmpty'  instance Arbitrary a => Arbitrary (NonEmpty' a) where-  arbitrary = do-    xs <- arbitrary-    case N.nonEmpty xs of-      Nothing -> NonEmpty' . pure <$> arbitrary-      Just ne -> pure (NonEmpty' ne)+    arbitrary = do+        xs <- arbitrary+        case NE.nonEmpty xs of+            Nothing -> NonEmpty' . pure <$> arbitrary+            Just ne -> pure (NonEmpty' ne)  -------------------------------------------------- -- * allUniqueValues@@ -86,7 +87,7 @@ -- NOTE: When we no longer support GHC 7.8 we can generalize -- allUnique to work on any Foldable and remove this function. allUniqueValues' :: NonEmpty Value -> Bool-allUniqueValues' = allUnique . fmap OrdValue . N.toList+allUniqueValues' = allUnique . fmap OrdValue . NE.toList  allUnique :: (Ord a) => [a] -> Bool allUnique xs = S.size (S.fromList xs) == length xs@@ -97,26 +98,36 @@ newtype OrdValue = OrdValue { _unOrdValue :: Value } deriving Eq  instance Ord OrdValue where-  (OrdValue Null) `compare` (OrdValue Null) = EQ-  (OrdValue Null) `compare` _               = LT-  _               `compare` (OrdValue Null) = GT+    (OrdValue Null) `compare` (OrdValue Null) = EQ+    (OrdValue Null) `compare` _               = LT+    _               `compare` (OrdValue Null) = GT -  (OrdValue (Bool x)) `compare` (OrdValue (Bool y)) = x `compare` y-  (OrdValue (Bool _)) `compare` _                   = LT-  _                   `compare` (OrdValue (Bool _)) = GT+    (OrdValue (Bool x)) `compare` (OrdValue (Bool y)) = x `compare` y+    (OrdValue (Bool _)) `compare` _                   = LT+    _                   `compare` (OrdValue (Bool _)) = GT -  (OrdValue (Number x)) `compare` (OrdValue (Number y)) = x `compare` y-  (OrdValue (Number _)) `compare` _                     = LT-  _                     `compare` (OrdValue (Number _)) = GT+    (OrdValue (Number x)) `compare` (OrdValue (Number y)) = x `compare` y+    (OrdValue (Number _)) `compare` _                     = LT+    _                     `compare` (OrdValue (Number _)) = GT -  (OrdValue (String x)) `compare` (OrdValue (String y)) = x `compare` y-  (OrdValue (String _)) `compare` _                     = LT-  _                     `compare` (OrdValue (String _)) = GT+    (OrdValue (String x)) `compare` (OrdValue (String y)) = x `compare` y+    (OrdValue (String _)) `compare` _                     = LT+    _                     `compare` (OrdValue (String _)) = GT -  (OrdValue (Array xs)) `compare` (OrdValue (Array ys)) =-    (OrdValue <$> xs) `compare` (OrdValue <$> ys)-  (OrdValue (Array _))  `compare` _                     = LT-  _                     `compare` (OrdValue (Array _))  = GT+    (OrdValue (Array xs)) `compare` (OrdValue (Array ys)) =+        (OrdValue <$> xs) `compare` (OrdValue <$> ys)+    (OrdValue (Array _))  `compare` _                     = LT+    _                     `compare` (OrdValue (Array _))  = GT -  (OrdValue (Object x)) `compare` (OrdValue (Object y)) =-    H.toList (OrdValue <$> x) `compare` H.toList (OrdValue <$> y)+    (OrdValue (Object x)) `compare` (OrdValue (Object y)) =+        HM.toList (OrdValue <$> x) `compare` HM.toList (OrdValue <$> y)++--------------------------------------------------+-- * other+--------------------------------------------------++fromJSONEither :: FromJSON a => Value -> Either Text a+fromJSONEither a =+    case fromJSON a of+        Error e   -> Left (T.pack e)+        Success b -> Right b
test/Local.hs view
@@ -4,51 +4,119 @@ import           Control.Applicative import           Control.Monad          (unless) import           Data.Aeson-import           Data.List              (isSuffixOf)+import           Data.Foldable          (traverse_)+import qualified Data.List.NonEmpty     as N import           Data.Monoid-import           System.Directory       (getDirectoryContents)+import qualified System.Timeout         as TO import           Test.Tasty             (TestTree, defaultMain, testGroup) import qualified Test.Tasty.HUnit       as HU import           Test.Tasty.QuickCheck  (testProperty) +import qualified Data.JsonSchema.Draft4 as D4+import           Data.JsonSchema.Fetch  (ReferencedSchemas(..))+import qualified Data.JsonSchema.Types  as JT+import           Local.Failure          (correctPaths)+import           Local.Validation       (fetchFromFilesystem,+                                         generalValidation)+import           Local.Reference        (referenceTests)+import           Shared+ -- Examples-import qualified CustomSchema+import qualified AlternateSchema import qualified Full-import qualified PrettyShowFailure import qualified Simple -import           Data.JsonSchema.Draft4-import           Local.Failure          (correctPaths)-import           Local.Filesystem       (fetchFromFilesystem)-import           Local.Reference        (referenceTests)-import           Shared                 (isLocal, readSchemaTests, toTest)- dir :: String dir = "JSON-Schema-Test-Suite/tests/draft4" +supplementDir :: String+supplementDir = "test/supplement"+ main :: IO () main = do-  filenames <- filter isLocal . filter (".json" `isSuffixOf`) <$> getDirectoryContents dir-  ts <- readSchemaTests dir filenames-  defaultMain . testGroup "Tests not requiring an HTTP server" $-      testGroup "Check that examples compile and don't throw errors" exampleTests-    : testGroup "QuickCheck tests" quickCheckTests-    : testGroup "Report the path to invalid data correctly" correctPaths-    : testGroup "Test the referencesViaFilesystem function" fetchFromFilesystem-    : testGroup "Test the Reference module" referenceTests-    : fmap toTest ts +    -- Language agnostic tests+    ts <- readSchemaTests+              dir+              (\a -> not (isHTTPTest a || skipOptional a))++    -- Custom supplements to the language agnostic tests+    supplementTs <- readSchemaTests+                        supplementDir+                        (\a -> not (isHTTPTest a || skipOptional a))++    defaultMain . testGroup "Tests not requiring an HTTP server" $+        [ testGroup+            "Check that examples compile and don't throw errors"+            exampleTests+        , testGroup+            "QuickCheck tests"+            quickCheckTests+        , testGroup+            "Report the path to invalid data correctly"+            correctPaths+        , testGroup+            "Test the Reference module"+            referenceTests+        , testGroup+            "Test the referencesViaFilesystem function"+            fetchFromFilesystem+        , testGroup+            "Supplementary validation tests written in Haskell"+            generalValidation+        , testGroup+            "Supplementary tests written in JSON (using the record based schema)"+            (toTest (fmap timeout . validate) <$> supplementTs)+        , testGroup+            "Supplementary tests written in JSON (using the 'Value' based schema)"+            (toTest (fmap timeout . validateExample) <$> supplementTs)+        , testGroup+            "Language agnostic local tests (using the record based schema)"+            (toTest validate <$> ts)+        , testGroup+            "Language agnostic local tests (using the 'Value' based schema)"+            (toTest validateExample <$> ts)+        ]+  where+    timeout :: HU.Assertion -> HU.Assertion+    timeout f = do+        res <- TO.timeout 3000000 f+        case res of+            Nothing -> HU.assertFailure "timeout expired"+            Just a  -> pure a++    validate :: D4.Schema -> SchemaTestCase -> HU.Assertion+    validate s sc = do+        res <- D4.fetchHTTPAndValidate (D4.SchemaWithURI s Nothing) (_scData sc)+        let failures = case res of+                           Right ()           -> mempty+                           Left (D4.HVData a) -> N.toList a+                           other              -> error ("Local.validate error: "+                                                       <> show other)+        traverse_ (checkPointer (_scData sc)) failures+        assertResult sc failures++    validateExample :: JT.Schema -> SchemaTestCase -> HU.Assertion+    validateExample s sc = do+        res <- AlternateSchema.referencesViaHTTP (D4.SchemaWithURI s Nothing)+        case res of+            Left e          -> error ("Local.validateExample error: " <> show e)+            Right schemaMap -> do+                let failures = AlternateSchema.validate+                                   (ReferencedSchemas s schemaMap)+                                   Nothing s (_scData sc)+                traverse_ (checkPointer (_scData sc)) failures+                assertResult sc failures+ quickCheckTests :: [TestTree] quickCheckTests =-  [testProperty "Invert schemas through JSON without change" invertSchema]+    [testProperty "Invert schemas through JSON without change" invertSchema]   where-    invertSchema :: Schema -> Bool+    invertSchema :: D4.Schema -> Bool     invertSchema a = Just a == decode (encode a)  exampleTests :: [TestTree] exampleTests =-  [ HU.testCase "CustomSchema example" CustomSchema.example-  , HU.testCase "Full example" Full.example-  , HU.testCase "PrettyShowFailure example" PrettyShowFailure.example-  , HU.testCase "Simple example" Simple.example-  ]+    [ HU.testCase "Full example" Full.example+    , HU.testCase "Simple example" Simple.example+    ]
test/Local/Failure.hs view
@@ -1,55 +1,68 @@  module Local.Failure where +import           Prelude+ import           Data.Aeson-import qualified Data.Aeson.Pointer     as P+import qualified Data.Aeson.Pointer          as P import           Data.Monoid-import           Test.Tasty             (TestTree)-import qualified Test.Tasty.HUnit       as HU+import           Test.Tasty                  (TestTree)+import qualified Test.Tasty.HUnit            as HU  import           Data.JsonSchema.Draft4-import qualified Data.Validator.Draft4  as VA+import           Data.JsonSchema.Draft4.Spec (validate)+import qualified Data.Validator.Draft4.Array as AR  correctPaths :: [TestTree] correctPaths =-  [ HU.testCase "Items object" itemsObject-  , HU.testCase "Items array" itemsArray-  ]+    [ HU.testCase "Items object" itemsObject+    , HU.testCase "Items array" itemsArray+    ]  itemsObject :: IO ()-itemsObject = HU.assertEqual "Path to invalid data"-                             (P.Pointer [P.Token "0"])-                             (_invalidOffendingData failure)+itemsObject =+    HU.assertEqual+        "Path to invalid data"+        [Failure (Items UniqueItems) (Bool True) (P.Pointer [P.Token "0"])+                 (toJSON [True, True])+        ]+        failures   where-    [failure] = runValidate (ReferencedSchemas schema mempty)-                            sw (toJSON [[True, True]])+    failures = validate (ReferencedSchemas schema mempty)+                        sw (toJSON [[True, True]])      schema :: Schema     schema = emptySchema-      { _schemaItems = Just (VA.ItemsObject (emptySchema { _schemaUniqueItems = Just True }))-      }+        { _schemaItems = Just $ AR.ItemsObject+            (emptySchema { _schemaUniqueItems = Just True })+        }      sw :: SchemaWithURI Schema     sw = SchemaWithURI-      { _swSchema = schema-      , _swURI    = Nothing-      }+        { _swSchema = schema+        , _swURI    = Nothing+        }  itemsArray :: IO ()-itemsArray = HU.assertEqual "Path to invalid data"-                            (P.Pointer [P.Token "0"])-                            (_invalidOffendingData failure)+itemsArray =+    HU.assertEqual+        "Path to invalid data"+        [Failure (Items UniqueItems) (Bool True) (P.Pointer [P.Token "0"])+                 (toJSON [True, True])+        ]+        failures   where-    [failure] = runValidate (ReferencedSchemas schema mempty)-                            sw (toJSON [[True, True]])+    failures = validate (ReferencedSchemas schema mempty)+                        sw (toJSON [[True, True]])      schema :: Schema     schema = emptySchema-      { _schemaItems = Just (VA.ItemsArray [emptySchema { _schemaUniqueItems = Just True }])-      }+        { _schemaItems = Just $ AR.ItemsArray+            [emptySchema { _schemaUniqueItems = Just True }]+        }      sw :: SchemaWithURI Schema     sw = SchemaWithURI-      { _swSchema = schema-      , _swURI    = Nothing-      }+        { _swSchema = schema+        , _swURI    = Nothing+        }
− test/Local/Filesystem.hs
@@ -1,56 +0,0 @@--module Local.Filesystem where--import           Control.Applicative-import           Data.Aeson-import           Data.Monoid-import           Test.Tasty             (TestTree)-import qualified Test.Tasty.HUnit       as HU-import           Data.Text              (Text)--import           Data.JsonSchema.Draft4--fetchFromFilesystem :: [TestTree]-fetchFromFilesystem =-  [ HU.testCase-      "readFile exceptions are turned into Lefts"-      readFileExceptions--  , HU.testCase-      "Relative reference to local file"-      (resolve "test/Local/schema.json")-  , HU.testCase-      "Chained relative references to local files"-      (resolve "./test/Local/schema-with-ref.json")-  ]--readFileExceptions :: IO ()-readFileExceptions = do-  res <- referencesViaFilesystem (SchemaWithURI schema Nothing)-  case res of-    Left (FSReadFailure _) -> pure ()-    a                      -> error (msg <> show a)-  where-    schema :: Schema-    schema = emptySchema { _schemaRef = Just "does-not-exist.json" }--    msg :: String-    msg = "expected referencesViaFilesystem to return ReadFailure,"-       <> " instead got: "---- * Helpers--resolve :: Text -> IO ()-resolve ref = do-  let schema = emptySchema { _schemaRef = Just ref }-  res <- fetchFilesystemAndValidate (SchemaWithURI schema Nothing) badData-  case res of-    Left (FVData _) -> pure ()-    a               -> error (msg <> show a)-  where-    badData :: Value-    badData = toJSON [True, True]--    msg :: String-    msg = "expected fetchFilesystemAndValidate to return"-       <> " Left (FVData [_]), instead got: "
test/Local/Reference.hs view
@@ -8,44 +8,44 @@  referenceTests :: [TestTree] referenceTests =-  [ HU.testCase "updateResolutionScope test cases" updateResolutionScopeTests-  , HU.testCase "resolveReference test cases" resolveReferenceTests-  ]+    [ HU.testCase "updateResolutionScope test cases" updateResolutionScopeTests+    , HU.testCase "resolveReference test cases" resolveReferenceTests+    ]  updateResolutionScopeTests :: IO () updateResolutionScopeTests = do-  HU.assertEqual-    "case 1 result"-    Nothing-    (updateResolutionScope Nothing Nothing)-  HU.assertEqual-    "case 2 result"-    Nothing-    (updateResolutionScope Nothing (Just "#"))-  HU.assertEqual-    "case 3 result"-    (Just "foo")-    (updateResolutionScope Nothing (Just "foo"))-  HU.assertEqual-    "case 4 result"-    (Just "/./bar") -- TODO: Normalize after updateResolutionScope.-    (updateResolutionScope (Just "/foo") (Just "./bar"))+    HU.assertEqual+        "case 1 result"+        Nothing+        (updateResolutionScope Nothing Nothing)+    HU.assertEqual+        "case 2 result"+        Nothing+        (updateResolutionScope Nothing (Just "#"))+    HU.assertEqual+        "case 3 result"+        (Just "foo")+        (updateResolutionScope Nothing (Just "foo"))+    HU.assertEqual+        "case 4 result"+        (Just "/./bar") -- TODO: Normalize after updateResolutionScope.+        (updateResolutionScope (Just "/foo") (Just "./bar"))  resolveReferenceTests :: IO () resolveReferenceTests = do-  HU.assertEqual-    "case 1 result"-    (Just "/bar", Nothing)-    (resolveReference (Just "/foo") "bar")-  HU.assertEqual-    "case 2 result"-    (Just "/baz", Nothing)-    (resolveReference (Just "/foo/bar") "/baz")-  HU.assertEqual-    "case 3 result"-    (Nothing, Just "/bar")-    (resolveReference Nothing "#/bar")-  HU.assertEqual-    "case 4 result"-    (Just "/foo", Just "/bar")-    (resolveReference (Just "/foo") "#/bar")+    HU.assertEqual+        "case 1 result"+        (Just "/bar", Nothing)+        (resolveReference (Just "/foo") "bar")+    HU.assertEqual+        "case 2 result"+        (Just "/baz", Nothing)+        (resolveReference (Just "/foo/bar") "/baz")+    HU.assertEqual+        "case 3 result"+        (Nothing, Just "/bar")+        (resolveReference Nothing "#/bar")+    HU.assertEqual+        "case 4 result"+        (Just "/foo", Just "/bar")+        (resolveReference (Just "/foo") "#/bar")
+ test/Local/Validation.hs view
@@ -0,0 +1,82 @@++module Local.Validation where++import           Control.Applicative+import           Data.Aeson+import qualified Data.Aeson             as AE+import qualified Data.HashMap.Strict    as HM+import           Data.Monoid+import           Data.Text              (Text)+import           Test.Tasty             (TestTree)+import qualified Test.Tasty.HUnit       as HU++import           Data.JsonSchema.Draft4+import qualified Data.JsonSchema.Types  as JT++import qualified AlternateSchema        as AS++fetchFromFilesystem :: [TestTree]+fetchFromFilesystem =+    [ HU.testCase+        "readFile exceptions are turned into Lefts"+        readFileExceptions++    , HU.testCase+        "Relative reference to local file"+        (resolve "test/Local/schema.json")+    , HU.testCase+        "Chained relative references to local files"+        (resolve "./test/Local/schema-with-ref.json")+    ]++readFileExceptions :: IO ()+readFileExceptions = do+    res <- referencesViaFilesystem (SchemaWithURI schema Nothing)+    case res of+        Left (FSReadFailure _) -> pure ()+        a                      -> error (msg <> show a)+  where+    schema :: Schema+    schema = emptySchema { _schemaRef = Just "does-not-exist.json" }++    msg :: String+    msg = "expected referencesViaFilesystem to return ReadFailure,"+       <> " instead got: "++resolve :: Text -> IO ()+resolve ref = do+    let schema = emptySchema { _schemaRef = Just ref }+    res <- fetchFilesystemAndValidate (SchemaWithURI schema Nothing) badData+    case res of+        Left (FVData _) -> pure ()+        a               -> error (msg <> show a)+  where+    badData :: Value+    badData = toJSON [True, True]++    msg :: String+    msg = "expected fetchFilesystemAndValidate to return"+       <> " Left (FVData [_]), instead got: "++-- | Validation tests that were hard to write in JSON for whatever reason.+generalValidation :: [TestTree]+generalValidation =+    [ HU.testCase+        "Don't parse schemas that have Null in a forbidden location"+        forbidNull+    , HU.testCase+        "The 'Value' based Schema's checkSchema should catch Nulls"+        exampleForbidNull+    ]++forbidNull :: IO ()+forbidNull =+    case fromJSON (Object (HM.singleton "type" Null)) of+        AE.Error _   -> pure ()+        AE.Success a -> HU.assertFailure ("parsed to: " <> show (a :: Schema))++exampleForbidNull :: IO ()+exampleForbidNull =+    case AS.checkSchema (JT.Schema (HM.singleton "type" Null)) of+        [] -> HU.assertFailure "No checkSchema failures"+        _  -> pure ()
test/Remote.hs view
@@ -2,25 +2,63 @@  import           Control.Applicative import           Control.Concurrent.Async       (withAsync)-import           Data.List                      (isSuffixOf)+import           Data.Foldable                  (traverse_)+import qualified Data.List.NonEmpty             as N+import           Data.Monoid import           Network.Wai.Application.Static (defaultFileServerSettings,                                                  staticApp) import           Network.Wai.Handler.Warp       (run)-import           System.Directory               (getDirectoryContents) import           Test.Tasty                     (defaultMain, testGroup)+import qualified Test.Tasty.HUnit               as HU -import           Shared                         (isLocal, readSchemaTests,-                                                 toTest)+import qualified Data.JsonSchema.Draft4         as D4+import           Data.JsonSchema.Fetch          (ReferencedSchemas(..))+import qualified Data.JsonSchema.Types          as JT+import           Shared +-- Examples+import qualified AlternateSchema+ dir :: String dir = "JSON-Schema-Test-Suite/tests/draft4"  main :: IO () main = withAsync serve $ \_ -> do-  filenames <- filter (not . isLocal) . filter (".json" `isSuffixOf`)-                 <$> getDirectoryContents dir-  ts <- readSchemaTests dir filenames-  defaultMain . testGroup "Tests that run an HTTP server" $ toTest <$> ts+    ts <- readSchemaTests dir isHTTPTest+    defaultMain . testGroup "Tests that run an HTTP server" $+        [ testGroup+            "Language agnostic remote tests (using the record based schema)"+            (toTest validate <$> ts)+        , testGroup+            "Language agnostic remote tests (using the 'Value' based schema)"+            (toTest validateExample <$> ts)+        ]+  where+    validate :: D4.Schema -> SchemaTestCase -> HU.Assertion+    validate s sc = do+        res <- D4.fetchHTTPAndValidate (D4.SchemaWithURI s Nothing) (_scData sc)+        let failures = case res of+                           Right ()           -> mempty+                           Left (D4.HVData a) -> N.toList a+                           other              -> error ("Local.validate error: "+                                                       <> show other)+        traverse_ (checkPointer (_scData sc)) failures+        assertResult sc failures +    validateExample :: JT.Schema -> SchemaTestCase -> HU.Assertion+    validateExample s sc = do+        res <- AlternateSchema.referencesViaHTTP (D4.SchemaWithURI s Nothing)+        case res of+            Left e          -> error ("Remote.validateExample error: " <> show e)+            Right schemaMap -> do+                let failures = AlternateSchema.validate+                                   (ReferencedSchemas s schemaMap)+                                   Nothing s (_scData sc)+                traverse_ (checkPointer (_scData sc)) failures+                assertResult sc failures+ serve :: IO ()-serve = run 1234 . staticApp . defaultFileServerSettings $ "JSON-Schema-Test-Suite/remotes"+serve = run 1234+      . staticApp+      . defaultFileServerSettings+      $ "JSON-Schema-Test-Suite/remotes"
test/Shared.hs view
@@ -5,95 +5,165 @@ import           Control.Applicative import           Control.Monad import           Data.Aeson+import qualified Data.Aeson.Pointer     as AP import           Data.Aeson.TH import qualified Data.ByteString.Lazy   as LBS import           Data.Char              (toLower)+import qualified Data.HashMap.Strict    as HM+import           Data.List              (isInfixOf, stripPrefix)+import           Data.Maybe import           Data.Monoid import           Data.Text              (Text) import qualified Data.Text              as T+import           Data.Traversable+import qualified Data.Vector            as V import           GHC.Generics+import qualified System.Directory       as SD import           System.FilePath        ((</>)) import           Test.Tasty             (TestTree) import qualified Test.Tasty.HUnit       as HU  import qualified Data.JsonSchema.Draft4 as D4 -isLocal :: String -> Bool-isLocal file = (file /= "definitions.json")-            && (file /= "ref.json")-            && (file /= "refRemote.json")+-- Recursively return the contents of a directory+-- (or return itself if given a file as an argument).+--+-- Return paths are relative to that directory.+listFiles :: FilePath -> IO [FilePath]+listFiles path = fmap ( fromMaybe "stripPrefix failed"+                      . stripPrefix (path <> "/")+                      ) <$> listFilesFullPath path +-- Recursively return the contents of a directory+-- (or return itself if given a file as an argument).+--+-- All return paths start with the 'FilePath' argument.+listFilesFullPath :: FilePath -> IO [FilePath]+listFilesFullPath path = do+    -- Check if it's a file or directory:+    res <- SD.doesFileExist path+    if res+        then pure [path]+        else do+            fs <- fmap (path </>) <$> SD.listDirectory path+            concat <$> traverse listFilesFullPath fs++checkPointer :: Value -> D4.Failure -> HU.Assertion+checkPointer v failure =+    case AP.resolve (D4._failureOffendingPointer failure) v of+        Left e  -> error ("Couldn't resolve pointer: " <> show e)+        Right a -> assertContains a (D4._failureOffendingData failure)+  where+    -- Some validators, such as 'additionalItems', only return a subset+    -- of the data incated by their '_failureOffendingPointer'.+    -- See the comments on 'Data.Validator.Failure.Failure' for more info.+    assertContains :: Value -> Value -> HU.Assertion+    assertContains x y+        | x == y    = pure ()+        | otherwise =+            case (x,y) of+                (Array xs, Array ys) ->+                    HU.assertBool "Pointer resolution incorrect"+                                  (V.toList ys `isInfixOf` V.toList xs)+                (Object xhm, Object yhm) ->+                    HU.assertBool "Pointer resolution incorrect"+                                  (HM.toList yhm `isInfixOf` HM.toList xhm)+                _ -> HU.assertFailure+                        "Pointer resolution incorrect: result mismatch"++isHTTPTest :: String -> Bool+isHTTPTest file = (file == "definitions.json")+               || (file == "ref.json")+               || (file == "refRemote.json")++-- | We may never support the @"format"@ keywords, and+-- are currently failing the zeroTerminatedFloats test.+skipOptional :: String -> Bool+skipOptional file = (file == "optional/format.json")+                 || (file == "optional/zeroTerminatedFloats.json")+ data SchemaTest = SchemaTest-  { _stDescription :: Text-  , _stSchema      :: D4.Schema-  , _stCases       :: [SchemaTestCase]-  }+    { _stDescription :: Text+    , _stSchema      :: Value+    , _stCases       :: [SchemaTestCase]+    }  data SchemaTestCase = SchemaTestCase-  { _scDescription :: Text-  , _scData        :: Value-  , _scValid       :: Bool-  } deriving Generic+    { _scDescription :: Text+    , _scData        :: Value+    , _scValid       :: Bool+    } deriving Generic  instance FromJSON SchemaTest where-  parseJSON = withObject "SchemaTest" $ \o -> SchemaTest-    <$> o .: "description"-    <*> o .: "schema"-    <*> o .: "tests" -- Perhaps "cases" would have been a more descriptive key.+    parseJSON = withObject "SchemaTest" $ \o -> SchemaTest+        <$> o .: "description"+        <*> o .: "schema"+        <*> o .: "tests" -- Perhaps "cases" would have been+                         -- a more descriptive key.  instance FromJSON SchemaTestCase where-  parseJSON = genericParseJSON defaultOptions { fieldLabelModifier = fmap toLower . drop 3 }+    parseJSON = genericParseJSON+                    defaultOptions+                    { fieldLabelModifier = fmap toLower . drop 3 } -readSchemaTests :: String -> [String] -> IO [SchemaTest]-readSchemaTests dir jsonFiles = concatMapM fileToCases jsonFiles+readSchemaTests+    :: String+    -- ^ The path to a directory.+    -> (String -> Bool)+    -- ^ A function to decide what we're interested in within that directory.+    -> IO [SchemaTest]+readSchemaTests dir filterFunc = do+    files <- filter filterFunc <$> listFiles dir+    concat <$> traverse fileToCases files   where     -- Each file contains an array of SchemaTests, not just one.     fileToCases :: String -> IO [SchemaTest]     fileToCases name = do-      let fullPath = dir </> name-      jsonBS <- LBS.readFile fullPath-      case eitherDecode jsonBS of-        Left e -> fail $ "couldn't parse file '" <> fullPath <> "': " <> e-        Right schemaTests -> pure $ prependFileName name <$> schemaTests+        let fullPath = dir </> name+        jsonBS <- LBS.readFile fullPath+        case eitherDecode jsonBS of+            Left e -> fail $ "couldn't parse file '" <> fullPath <> "': " <> e+            Right schemaTests -> pure $ prependFileName name <$> schemaTests      prependFileName :: String -> SchemaTest -> SchemaTest     prependFileName fileName s = s-      { _stDescription = T.pack fileName <> ": " <> _stDescription s-      }+        { _stDescription = T.pack fileName <> ": " <> _stDescription s+        } -    concatMapM :: (Monad m) => (a -> m [b]) -> [a] -> m [b]-    concatMapM f xs = liftM concat (mapM f xs)+toTest+  :: forall schema. FromJSON schema+  => (schema -> SchemaTestCase -> HU.Assertion)+  -> SchemaTest+  -> TestTree+toTest validate st =+    HU.testCase (T.unpack (_stDescription st)) $+        forM_ (_stCases st) (validate schema)+  where+    schema :: schema+    schema = case fromJSON (_stSchema st) of+                 Error e   -> error ("Couldn't parse schema: " <> show e)+                 Success a -> a -toTest :: SchemaTest -> TestTree-toTest st =-  HU.testCase (T.unpack (_stDescription st)) $ do-    forM_ (_stCases st) $ \sc -> do-      g <- assertRight =<< D4.referencesViaHTTP (D4.SchemaWithURI (_stSchema st) Nothing)-      validate <- assertRight . D4.checkSchema g $ D4.SchemaWithURI (_stSchema st) Nothing-      let res = validate (_scData sc)-      if _scValid sc-        then assertValid   sc res-        else assertInvalid sc res+assertResult :: SchemaTestCase -> [D4.Failure] -> HU.Assertion+assertResult sc failures+    | _scValid sc = assertValid sc failures+    | otherwise   = assertInvalid sc failures -assertValid :: SchemaTestCase -> [D4.Invalid] -> HU.Assertion+assertValid :: SchemaTestCase -> [D4.Failure] -> HU.Assertion assertValid _ [] = pure ()-assertValid sc errs =-  HU.assertFailure $ unlines-    [ "    Failed to validate data"-    , "    Description: "         <> T.unpack (_scDescription sc)-    , "    Data: "                <> show (_scData sc)-    , "    Validation failures: " <> show errs-    ]+assertValid sc failures =+    HU.assertFailure $ unlines+        [ "    Failed to validate data"+        , "    Description: "         <> T.unpack (_scDescription sc)+        , "    Data: "                <> show (_scData sc)+        , "    Validation failures: " <> show failures+        ] -assertInvalid :: SchemaTestCase -> [D4.Invalid] -> HU.Assertion+assertInvalid :: SchemaTestCase -> [D4.Failure] -> HU.Assertion assertInvalid sc [] =-  HU.assertFailure $ unlines-    [ "    Validated invalid data"-    , "    Description: " <> T.unpack (_scDescription sc)-    , "    Data: "        <> show (_scData sc)-    ]+    HU.assertFailure $ unlines+        [ "    Validated invalid data"+        , "    Description: " <> T.unpack (_scDescription sc)+        , "    Data: "        <> show (_scData sc)+        ] assertInvalid _ _ = pure ()--assertRight :: (Show a) => Either a b -> IO b-assertRight (Left e)  = HU.assertFailure (show e) >> fail "assertRight failed"-assertRight (Right b) = pure b
+ test/supplement/recurse.json view
@@ -0,0 +1,17 @@+[+    {+        "description": "recursive ref",+        "schema": {+            "allOf": [+                {"$ref": "#"}+            ]+        },+        "tests": [+            {+                "description": "arbitrary data",+                "data": {},+                "valid": false+            }+        ]+    }+]