module Data.JsonSchema
( module Data.JsonSchema.Core
, module Data.JsonSchema
, module Data.JsonSchema.Draft4
) where
import Control.Monad.Except (MonadError, MonadIO, join, throwError)
import qualified Data.ByteString.Lazy as LBS
import Data.Foldable
import qualified Data.HashMap.Strict as H
import Data.String
import Data.JsonSchema.Core
import Data.JsonSchema.Draft4
import qualified Data.JsonSchema.Helpers as HE
import Data.JsonSchema.Reference
import Import
import Prelude hiding (concat, sequence)
fetchReferencedSchemas :: Spec err -> SchemaCache -> RawSchema -> IO (Either Text SchemaGraph)
fetchReferencedSchemas = fetchReferencedSchemas' HE.defaultFetch
fetchReferencedSchemas'
:: forall m e t err. (MonadIO m, Functor m, MonadError t e, Traversable e, IsString t)
=> (Text -> m (e LBS.ByteString))
-> Spec err
-> SchemaCache
-> RawSchema
-> m (e SchemaGraph)
fetchReferencedSchemas' fetchRef spec cache rawSchema = do
let startingCache = case _rsURI rawSchema of
Nothing -> cache
Just uri -> H.insert uri (_rsData rawSchema) cache
fmap (SchemaGraph rawSchema) <$> foldlM fetch (return startingCache) (includeSubschemas rawSchema)
where
includeSubschemas :: RawSchema -> [RawSchema]
includeSubschemas r =
let scope = _rsURI r `newResolutionScope` _rsData r
xs = H.intersectionWith (\(ValSpec f _) x -> f scope x) (_unSpec spec) (_rsData r)
in r : (concat . concat . H.elems $ fmap includeSubschemas <$> xs)
fetch :: e SchemaCache -> RawSchema -> m (e SchemaCache)
fetch eg r = join <$> sequence (run <$> eg)
where
run :: SchemaCache -> m (e SchemaCache)
run g = do
let scope = newResolutionScope (_rsURI r) (_rsData r)
case resolveReference scope <$> (H.lookup "$ref" (_rsData r) >>= HE.toTxt) of
Just (Just uri,_) ->
if not (isRemoteReference uri) || H.member uri g
then return (return g)
else fetchRef uri >>= decodeResponse g uri
_ -> return (return g)
decodeResponse :: SchemaCache -> Text -> e LBS.ByteString -> m (e SchemaCache)
decodeResponse g uri eBts = join <$> sequence (runDecode g uri <$> eBts)
runDecode :: SchemaCache -> Text -> LBS.ByteString -> m (e SchemaCache)
runDecode g uri bts =
case eitherDecode bts of
Left e -> return $ throwError (fromString e)
Right obj -> fmap _cachedSchemas <$> fetchReferencedSchemas' fetchRef spec g (RawSchema (Just uri) obj)