{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE TypeSynonymInstances #-}
module Hedgehog.Gen.JSON
( genJSON
, genJSONValue
, genConstrainedJSON
, genConstrainedJSONValue
, Schema
, readSchema
, Ranges(..)
, NumberRange(..)
, StringRange(..)
, ArrayRange(..)
, ObjectRange(..)
, sensibleRanges
, numberRange
, integerRange
, stringRange
, arrayRange
, objectRange
) where
import Control.Monad.Catch (MonadThrow, throwM)
import qualified Data.Aeson as Aeson
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text as Text
import Hedgehog
import qualified Hedgehog.Gen.JSON.Constrained as Constrained
import Hedgehog.Gen.JSON.JSONSchema (Schema)
import Hedgehog.Gen.JSON.Ranges
import qualified Hedgehog.Gen.JSON.Unconstrained as Unconstrained
import qualified Hedgehog.Range as Range
import Protolude
-- | Reads a JSON Schema from a filepath
readSchema :: (MonadIO m, MonadThrow m) => FilePath -> m Schema
readSchema fp = do
bytes <- liftIO $ BS.readFile fp
case Aeson.eitherDecodeStrict bytes of
Left err -> throwM $ DecodingSchemaException $ Text.pack err
Right schema -> pure schema
-- | Generator for arbitrary unconstrained JSON values encoded as UTF-8
genJSON :: Ranges -> Gen ByteString
genJSON ranges = (LBS.toStrict . Aeson.encode) <$> genJSONValue ranges
-- | Generator for arbitrary unconstrained JSON values
genJSONValue :: Ranges -> Gen Aeson.Value
genJSONValue = Unconstrained.genValue
-- | Generator for arbitrary JSON values constrained by the given JSON Schema and encoded as UTF-8
genConstrainedJSON :: Ranges -> Schema -> Gen ByteString
genConstrainedJSON ranges schema = (LBS.toStrict . Aeson.encode) <$> genConstrainedJSONValue ranges schema
-- | Generator for arbitrary JSON values constrained by the given JSON Schema
genConstrainedJSONValue :: Ranges -> Schema -> Gen Aeson.Value
genConstrainedJSONValue = Constrained.genToplevelValue
-- | Sensible ranges for arbitrary JSON values if you're too lazy to define some
sensibleRanges :: Ranges
sensibleRanges =
Ranges
{ _arrayRange = ArrayRange $ Range.linear 0 5
, _stringRange = StringRange $ Range.linear 0 1000
, _numberRange = NumberRange $ Range.linearFrac (-1000) 1000
, _integerRange = IntegerRange $ Range.linear (-1000) 1000
, _objectRange = ObjectRange $ Range.linear 0 5
}
newtype JSONException =
DecodingSchemaException Text
deriving (Show, Eq)
instance Exception JSONException