packages feed

hjsonschema 0.3.0.0 → 0.4.0.0

raw patch · 12 files changed

+321/−175 lines, 12 filesdep +file-embeddep +hjsonpointerdep +http-typesPVP ok

version bump matches the API change (PVP)

Dependencies added: file-embed, hjsonpointer, http-types

API changes (from Hackage documentation)

- Data.JsonSchema.JsonReference: combineIdAndRef :: Text -> Text -> Text
- Data.JsonSchema.JsonReference: combineIds :: Text -> Text -> Text
- Data.JsonSchema.JsonReference: fetchRef :: Text -> IO (Either Text (HashMap Text Value))
- Data.JsonSchema.JsonReference: jsonPointer :: Text -> Value -> Maybe Value
- Data.JsonSchema.JsonReference: refAndP :: Text -> Maybe (Text, Text)
- Data.JsonSchema.JsonReference: safeGet :: Text -> IO (Either Text ByteString)
- Data.JsonSchema.JsonReference: updateId :: Text -> HashMap Text Value -> Text
+ Data.JsonSchema: isValidSchema :: RawSchema -> Vector ValErr
+ Data.JsonSchema.Reference: combineIdAndRef :: Text -> Text -> Text
+ Data.JsonSchema.Reference: combineIds :: Text -> Text -> Text
+ Data.JsonSchema.Reference: fetchRef :: Text -> IO (Either Text (HashMap Text Value))
+ Data.JsonSchema.Reference: newResolutionScope :: Text -> HashMap Text Value -> Text
+ Data.JsonSchema.Reference: refAndPointer :: Text -> Maybe (Text, Text)
+ Data.JsonSchema.Reference: safeGet :: Text -> IO (Either Text ByteString)

Files

README.md view
@@ -1,12 +1,10 @@ # Intro -An implementation of [JSON Schema](http://json-schema.org/) v4 in haskell.+An implementation of [JSON Schema](http://json-schema.org/) Draft 4 in haskell.  # Status -Still in development. Its handling of the `id` keyword passes tests, but is probably incorrect.--Also lacks documentation and example code.+Still in development. Lacks documentation and solid code to fetch remote schemas.  # Install Tests @@ -23,10 +21,12 @@  # Notes -This uses the [regexpr](https://hackage.haskell.org/package/regexpr-0.5.4) regular expression library fo the "pattern" validator. I have no idea if this is compatible with the ECMA 262 regex dialect, which the [spec](http://json-schema.org/latest/json-schema-validation.html#anchor33) requires.++ This uses the [regexpr](https://hackage.haskell.org/package/regexpr-0.5.4) regular expression library fo the "pattern" validator. I have no idea if this is compatible with the ECMA 262 regex dialect, which the [spec](http://json-schema.org/latest/json-schema-validation.html#anchor33) requires. ++ `draft4.json` is from commit # cc8ec81ce0abe2385ebd6c2a6f2d6deb646f874a [here](https://github.com/json-schema/json-schema).+ # Credits  Thanks to [Julian Berman](https://github.com/Julian) for the fantastic test suite. -Also thanks to Tim Baumann for his [aeson-schema](https://hackage.haskell.org/package/aeson-schema) library. Hjsonschema is based on Aeson-Schema, and some code is directly from there.+Also thanks to Tim Baumann for his [aeson-schema](https://hackage.haskell.org/package/aeson-schema) library. Hjsonschema's test code and its implementation of Graph both come from Aeson-Schema.
+ draft4.json view
@@ -0,0 +1,150 @@+{+    "id": "http://json-schema.org/draft-04/schema#",+    "$schema": "http://json-schema.org/draft-04/schema#",+    "description": "Core schema meta-schema",+    "definitions": {+        "schemaArray": {+            "type": "array",+            "minItems": 1,+            "items": { "$ref": "#" }+        },+        "positiveInteger": {+            "type": "integer",+            "minimum": 0+        },+        "positiveIntegerDefault0": {+            "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]+        },+        "simpleTypes": {+            "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]+        },+        "stringArray": {+            "type": "array",+            "items": { "type": "string" },+            "minItems": 1,+            "uniqueItems": true+        }+    },+    "type": "object",+    "properties": {+        "id": {+            "type": "string",+            "format": "uri"+        },+        "$schema": {+            "type": "string",+            "format": "uri"+        },+        "title": {+            "type": "string"+        },+        "description": {+            "type": "string"+        },+        "default": {},+        "multipleOf": {+            "type": "number",+            "minimum": 0,+            "exclusiveMinimum": true+        },+        "maximum": {+            "type": "number"+        },+        "exclusiveMaximum": {+            "type": "boolean",+            "default": false+        },+        "minimum": {+            "type": "number"+        },+        "exclusiveMinimum": {+            "type": "boolean",+            "default": false+        },+        "maxLength": { "$ref": "#/definitions/positiveInteger" },+        "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },+        "pattern": {+            "type": "string",+            "format": "regex"+        },+        "additionalItems": {+            "anyOf": [+                { "type": "boolean" },+                { "$ref": "#" }+            ],+            "default": {}+        },+        "items": {+            "anyOf": [+                { "$ref": "#" },+                { "$ref": "#/definitions/schemaArray" }+            ],+            "default": {}+        },+        "maxItems": { "$ref": "#/definitions/positiveInteger" },+        "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },+        "uniqueItems": {+            "type": "boolean",+            "default": false+        },+        "maxProperties": { "$ref": "#/definitions/positiveInteger" },+        "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },+        "required": { "$ref": "#/definitions/stringArray" },+        "additionalProperties": {+            "anyOf": [+                { "type": "boolean" },+                { "$ref": "#" }+            ],+            "default": {}+        },+        "definitions": {+            "type": "object",+            "additionalProperties": { "$ref": "#" },+            "default": {}+        },+        "properties": {+            "type": "object",+            "additionalProperties": { "$ref": "#" },+            "default": {}+        },+        "patternProperties": {+            "type": "object",+            "additionalProperties": { "$ref": "#" },+            "default": {}+        },+        "dependencies": {+            "type": "object",+            "additionalProperties": {+                "anyOf": [+                    { "$ref": "#" },+                    { "$ref": "#/definitions/stringArray" }+                ]+            }+        },+        "enum": {+            "type": "array",+            "minItems": 1,+            "uniqueItems": true+        },+        "type": {+            "anyOf": [+                { "$ref": "#/definitions/simpleTypes" },+                {+                    "type": "array",+                    "items": { "$ref": "#/definitions/simpleTypes" },+                    "minItems": 1,+                    "uniqueItems": true+                }+            ]+        },+        "allOf": { "$ref": "#/definitions/schemaArray" },+        "anyOf": { "$ref": "#/definitions/schemaArray" },+        "oneOf": { "$ref": "#/definitions/schemaArray" },+        "not": { "$ref": "#" }+    },+    "dependencies": {+        "exclusiveMaximum": [ "maximum" ],+        "exclusiveMinimum": [ "minimum" ]+    },+    "default": {}+}
hjsonschema.cabal view
@@ -1,6 +1,6 @@ name:                   hjsonschema-version:                0.3.0.0-synopsis:               Haskell implementation of JSON Schema v4.+version:                0.4.0.0+synopsis:               Haskell implementation of JSON Schema Draft 4. homepage:               https://github.com/seagreen/hjsonschema license:                MIT license-file:           MIT-LICENSE.txt@@ -9,31 +9,36 @@ category:               Data build-type:             Simple cabal-version:          >=1.10-extra-source-files:     README.md+extra-source-files:     draft4.json                         JSON-Schema-Test-Suite/tests/draft4/*.json+                        README.md                         tests/Lib.hs  library   hs-source-dirs:       src   exposed-modules:      Data.JsonSchema-                      , Data.JsonSchema.JsonReference+                      , Data.JsonSchema.Reference                       , Data.JsonSchema.Utils                       , Data.JsonSchema.Validators   other-modules:        Data.JsonSchema.Core   default-language:     Haskell2010   default-extensions:   OverloadedStrings+  other-extensions:     TemplateHaskell   ghc-options:          -Wall-  build-depends:        aeson                >= 0.8  && < 0.9-                      , base                 >= 4.7  && < 4.8-                      , bytestring           >= 0.10 && < 0.11-                      , hashable             >= 1.2  && < 1.3-                      , lens                 >= 4.7  && < 4.8-                      , regexpr              >= 0.5  && < 0.6-                      , scientific           >= 0.3  && < 0.4-                      , unordered-containers >= 0.2  && < 0.3-                      , text                 >= 1.2  && < 1.3-                      , vector               >= 0.10 && < 0.11-                      , wreq                 >= 0.3  && < 0.4+  build-depends:        aeson                >= 0.8   && < 0.9+                      , base                 >= 4.7   && < 4.8+                      , bytestring           >= 0.10  && < 0.11+                      , file-embed           >= 0.0.8 && < 0.0.9+                      , hashable             >= 1.2   && < 1.3+                      , hjsonpointer         >= 0.1   && < 0.2+                      , http-types           >= 0.8   && < 0.9+                      , lens                 >= 4.7   && < 4.8+                      , regexpr              >= 0.5   && < 0.6+                      , scientific           >= 0.3   && < 0.4+                      , unordered-containers >= 0.2   && < 0.3+                      , text                 >= 1.2   && < 1.3+                      , vector               >= 0.10  && < 0.11+                      , wreq                 >= 0.3   && < 0.4  test-suite local   type:                 exitcode-stdio-1.0
src/Data/JsonSchema.hs view
@@ -1,21 +1,26 @@+{-# LANGUAGE TemplateHaskell #-}+ module Data.JsonSchema   ( module Data.JsonSchema   , module Data.JsonSchema.Core   ) where  import           Control.Applicative+import           Data.Aeson+import           Data.ByteString.Lazy       (fromStrict)+import           Data.FileEmbed import           Data.Foldable-import qualified Data.HashMap.Strict           as H+import qualified Data.HashMap.Strict        as H import           Data.JsonSchema.Core-import           Data.JsonSchema.JsonReference+import           Data.JsonSchema.Reference import           Data.JsonSchema.Utils import           Data.JsonSchema.Validators import           Data.Maybe-import           Data.Text                     (Text)-import qualified Data.Text                     as T-import           Data.Vector                   (Vector)-import qualified Data.Vector                   as V-import           Prelude                       hiding (foldr)+import           Data.Text                  (Text)+import qualified Data.Text                  as T+import           Data.Vector                (Vector)+import qualified Data.Vector                as V+import           Prelude                    hiding (foldr)  draft4 :: Spec draft4 = Spec $ H.fromList@@ -56,14 +61,14 @@     -- TODO: optimize     includeSubschemas :: RawSchema -> Vector RawSchema     includeSubschemas r =-      let newId = updateId (_rsURI r) (_rsObject r)+      let newId = newResolutionScope (_rsURI r) (_rsObject r)           xs = H.intersectionWith (\(_,f) x -> f newId x) (_unSpec spec) (_rsObject r)           ys = V.concat . H.elems $ xs       in V.cons r . V.concat . V.toList $ includeSubschemas <$> ys      fetch :: Either Text Graph -> RawSchema -> IO (Either Text Graph)     fetch (Right g) r =-      case H.lookup "$ref" (_rsObject r) >>= toTxt >>= refAndP of+      case H.lookup "$ref" (_rsObject r) >>= toTxt >>= refAndPointer of         Nothing     -> return (Right g)         Just (s, _) ->           let t = (_rsURI r `combineIdAndRef` s)@@ -75,3 +80,16 @@                 Left e    -> return (Left e)                 Right obj -> fetchRefs spec (RawSchema t obj) g     fetch leftGraph _ = return leftGraph++-- | Check if a schema is valid or not. Just a helper function+-- built out of the JSON Schema document which describes valid+-- draft 4 schemas and the normal 'compile' and 'validate' functions.+isValidSchema :: RawSchema -> Vector ValErr+isValidSchema r =+  case decode . fromStrict $ $(embedFile "draft4.json") of+    Nothing -> V.singleton "Schema decode failed (this should never happen)"+    Just s  -> do+      let draft4Schema = RawSchema { _rsURI = "", _rsObject = s }+      validate+        (compile draft4 H.empty draft4Schema)+        (Object . _rsObject $ r)
src/Data/JsonSchema/Core.hs view
@@ -3,7 +3,7 @@ import           Data.Aeson import           Data.HashMap.Strict           (HashMap) import qualified Data.HashMap.Strict           as H-import           Data.JsonSchema.JsonReference+import           Data.JsonSchema.Reference import           Data.Maybe import           Data.Text                     (Text) import           Data.Vector                   (Vector)@@ -34,7 +34,7 @@   V.fromList . catMaybes . H.elems $ H.intersectionWith f (_unSpec spec) o   where     f :: (ValidatorGen, a) -> Value -> Maybe Validator-    f (vGen,_) = vGen spec g $ RawSchema (updateId t o) o+    f (vGen,_) = vGen spec g $ RawSchema (newResolutionScope t o) o  validate :: Schema -> Value -> Vector ValErr validate s x = s >>= ($ x)
− src/Data/JsonSchema/JsonReference.hs
@@ -1,94 +0,0 @@--- | There should be a JSON Reference library for haskell.--module Data.JsonSchema.JsonReference where--import           Control.Exception-import           Control.Lens-import           Control.Monad-import           Data.Aeson-import           Data.ByteString.Lazy (ByteString)-import           Data.HashMap.Strict  (HashMap)-import qualified Data.HashMap.Strict  as H-import           Data.Monoid-import           Data.Text            (Text)-import qualified Data.Text            as T-import qualified Data.Vector          as V-import           Network.Wreq-import           Prelude              hiding (foldr)-import           Text.Read            (readMaybe)--combineIdAndRef :: Text -> Text -> Text-combineIdAndRef a b-  | "://" `T.isInfixOf` b              = b-  | T.length a < 1 || T.length b < 1   = a <> b-  | T.last a == '#' && T.head b == '#' = a <> T.tail b-  | otherwise                          = a <> b--combineIds :: Text -> Text -> Text-combineIds a b-  | b == "#" || b == ""                = a-  | "://" `T.isInfixOf` b              = b-  | T.length a < 1 || T.length b < 1   = a <> b-  | T.last a == '#' && T.head b == '#' = a <> T.tail b-  | otherwise                          = a <> b--updateId :: Text -> HashMap Text Value -> Text-updateId t o =-  case H.lookup "id" o of-    Just (String idVal) -> t `combineIds` idVal-    _                   -> t--refAndP :: Text -> Maybe (Text, Text)-refAndP val = getParts $ T.splitOn "#" val-  where-    getParts :: [Text] -> Maybe (Text, Text)-    getParts []    = Just ("","")-    getParts [x]   = Just (x,"")-    getParts [x,y] = Just (x,y)-    getParts _     = Nothing--fetchRef :: Text -> IO (Either Text (HashMap Text Value))-fetchRef t = do-  eResp <- safeGet t-  case eResp of-    Left err -> return (Left err)-    Right b  ->-      case eitherDecode b of-        Right (Object z) -> return (Right z)-        Right v          -> return . Left $-          "fetchRef returned the following instead of an object" <> T.pack (show v)-        Left e           -> return . Left . T.pack $ e--safeGet :: Text -> IO (Either Text ByteString)-safeGet url =-  catch-    (return . Right . (^. responseBody) =<< get (T.unpack url))-    handler-  where-    handler :: SomeException -> IO (Either Text ByteString)-    handler e = return . Left . T.pack . show $ e---- | There should be a JSON Pointer library.------ Spec: http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-07-jsonPointer :: Text -> Value -> Maybe Value-jsonPointer pntr = resolve (T.splitOn "/" pntr)-  where-    resolve :: [Text] -> Value -> Maybe Value-    resolve (referenceToken:ts) a =-      let t = unescape referenceToken-      in case T.length t of-        0 -> resolve ts a-        _ ->-          case a of-            (Object b) -> H.lookup t b >>= resolve ts-            (Array c)  -> do-              n <- readMaybe (T.unpack t)-              when (n < 0 || n + 1 > V.length c) Nothing-              resolve ts (c V.! n)-            _ -> Nothing-    resolve _ a = Just a--    -- TODO: do more things need to be escaped?-    unescape :: Text -> Text-    unescape t = T.replace "%25" "%" $ T.replace "~0" "~" $ T.replace "~1" "/" t
+ src/Data/JsonSchema/Reference.hs view
@@ -0,0 +1,65 @@+module Data.JsonSchema.Reference where++import           Control.Exception+import           Control.Lens+import           Control.Monad+import           Data.Aeson+import           Data.ByteString.Lazy (ByteString)+import           Data.HashMap.Strict  (HashMap)+import qualified Data.HashMap.Strict  as H+import           Data.Monoid+import           Data.Text            (Text)+import qualified Data.Text            as T+import           Network.Wreq+import           Prelude              hiding (foldr)++combineIdAndRef :: Text -> Text -> Text+combineIdAndRef a b+  | "://" `T.isInfixOf` b              = b+  | T.length a < 1 || T.length b < 1   = a <> b+  | T.last a == '#' && T.head b == '#' = a <> T.tail b+  | otherwise                          = a <> b++combineIds :: Text -> Text -> Text+combineIds a b+  | b == "#" || b == ""                = a+  | "://" `T.isInfixOf` b              = b+  | T.length a < 1 || T.length b < 1   = a <> b+  | T.last a == '#' && T.head b == '#' = a <> T.tail b+  | otherwise                          = a <> b++newResolutionScope :: Text -> HashMap Text Value -> Text+newResolutionScope t o =+  case H.lookup "id" o of+    Just (String idKeyword) -> t `combineIds` idKeyword+    _                       -> t++refAndPointer :: Text -> Maybe (Text, Text)+refAndPointer val = getParts $ T.splitOn "#" val+  where+    getParts :: [Text] -> Maybe (Text, Text)+    getParts []    = Just ("","")+    getParts [x]   = Just (x,"")+    getParts [x,y] = Just (x,y)+    getParts _     = Nothing++fetchRef :: Text -> IO (Either Text (HashMap Text Value))+fetchRef t = do+  eResp <- safeGet t+  case eResp of+    Left err -> return (Left err)+    Right b  ->+      case eitherDecode b of+        Right (Object z) -> return (Right z)+        Right v          -> return . Left $+          "fetchRef returned the following instead of an object" <> T.pack (show v)+        Left e           -> return . Left . T.pack $ e++safeGet :: Text -> IO (Either Text ByteString)+safeGet url =+  catch+    (return . Right . (^. responseBody) =<< get (T.unpack url))+    handler+  where+    handler :: SomeException -> IO (Either Text ByteString)+    handler e = return . Left . T.pack . show $ e
src/Data/JsonSchema/Utils.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE OverloadedLists #-}- module Data.JsonSchema.Utils where  import           Data.Aeson
src/Data/JsonSchema/Validators.hs view
@@ -39,16 +39,19 @@ import           Data.Hashable import           Data.HashMap.Strict           (HashMap) import qualified Data.HashMap.Strict           as H+import           Data.JsonPointer import           Data.JsonSchema.Core-import           Data.JsonSchema.JsonReference+import           Data.JsonSchema.Reference import           Data.JsonSchema.Utils import           Data.Maybe import           Data.Monoid import           Data.Text                     (Text) import qualified Data.Text                     as T+import           Data.Text.Encoding import           Data.Traversable import           Data.Vector                   (Vector) import qualified Data.Vector                   as V+import           Network.HTTP.Types.URI import           Text.RegexPR  --------------------------------------------------@@ -513,17 +516,13 @@ -- ignored. ref :: ValidatorGen ref spec g s (String val) = do-  (reference, pointer) <- refAndP (_rsURI s `combineIdAndRef` val)+  (reference, pointer) <- refAndPointer (_rsURI s `combineIdAndRef` val)   r <- RawSchema reference <$> H.lookup reference g-  sc <- pointerToSchema pointer r-  Just $ validate (compile spec g sc)-  where-    pointerToSchema :: Text -> RawSchema -> Maybe RawSchema-    pointerToSchema pntr rawS = do-      mVal <- jsonPointer pntr (Object . _rsObject $ rawS)-      case mVal of-        (Object o) -> Just $ RawSchema (_rsURI rawS) o-        _           -> Nothing+  let p = decodeUtf8 . urlDecode True . encodeUtf8 $ pointer+  case jsonPointer p >>= resolvePointer (Object $ _rsObject r) of+    Right (Object o) ->+      Just $ validate $ compile spec g $ RawSchema (_rsURI r) o+    _                -> Nothing ref _ _ _ _ = Nothing  noVal :: ValidatorGen
tests/Lib.hs view
@@ -14,12 +14,18 @@ import           Data.Monoid import           Data.Text                      (Text) import qualified Data.Text                      as T+import           Data.Vector                    (Vector) import qualified Data.Vector                    as V import           System.FilePath                ((</>))-import           Test.Framework-import           Test.Framework.Providers.HUnit-import qualified Test.HUnit                     as HU+import           Test.Framework                 (Test)+import           Test.Framework.Providers.HUnit (testCase)+import           Test.HUnit                     hiding (Test) +isLocal :: String -> Bool+isLocal file = (file /= "definitions.json")+            && (file /= "ref.json")+            && (file /= "refRemote.json")+ data SchemaTest = SchemaTest   { _stDescription :: Text   , _stSchema      :: RawSchema@@ -34,7 +40,7 @@  instance FromJSON RawSchema where   parseJSON = withObject "Schema" $ \o ->-    return $ RawSchema "" o+    return RawSchema { _rsURI = "", _rsObject = o }  instance FromJSON SchemaTest where   parseJSON = withObject "SchemaTest" $ \o -> SchemaTest@@ -42,11 +48,6 @@     <*> o .: "schema"     <*> o .: "tests" -- I wish this were "cases" -isLocal :: String -> Bool-isLocal file = (file /= "definitions.json")-            && (file /= "ref.json")-            && (file /= "refRemote.json")- readSchemaTests :: String -> [String] -> IO [SchemaTest] readSchemaTests dir jsonFiles = concatMapM fileToCases jsonFiles   where@@ -68,33 +69,37 @@     concatMapM f xs = liftM concat (mapM f xs)  toTest :: SchemaTest -> Test-toTest st = testGroup groupName (mkCase <$> _stCases st)-  where-    groupName :: String-    groupName = T.unpack $ _stDescription st+toTest st =+  testCase (T.unpack $ _stDescription st) $ do+    assertEqual "schema validity errors" V.empty (isValidSchema . _stSchema $ st)+    forM_ (_stCases st) $ \sc -> do+      g <- assertRight =<< fetchRefs draft4 (_stSchema st) H.empty+      let es = validate (compile draft4 g $ _stSchema st) (_scData sc)+      if _scValid sc+        then assertValid   sc es+        else assertInvalid sc es -    mkCase :: SchemaTestCase -> Test-    mkCase sc = testCase caseName assertion-      where-        caseName = T.unpack $ _scDescription sc-        assertion = if _scValid sc-          then assertValid   (_stSchema st) (_scData sc)-          else assertInvalid (_stSchema st) (_scData sc)+assertValid :: SchemaTestCase -> Vector ValErr -> Assertion+assertValid sc es =+  unless (V.length es == 0) $ assertFailure $ unlines+    [ "    Failed to validate data"+    , "    Description: " <> T.unpack (_scDescription sc)+    , "    Data: "        <> show (_scData sc)+    , "    Errors: "      <> show es+    ] -assertValid, assertInvalid :: RawSchema -> Value -> HU.Assertion-assertValid r v = do-  g <- assertRight =<< fetchRefs draft4 r H.empty-  let es = validate (compile draft4 g r) v-  unless (V.length es == 0) $ HU.assertFailure (show es)-assertInvalid r v = do-  g <- assertRight =<< fetchRefs draft4 r H.empty-  let es = validate (compile draft4 g r) v-  when (V.length es == 0) $ HU.assertFailure "expected a validation error"+assertInvalid :: SchemaTestCase -> Vector ValErr -> Assertion+assertInvalid sc es =+  unless (V.length es > 0) $ assertFailure $ unlines+    [ "    Failed to invalidate data"+    , "    Description: " <> T.unpack (_scDescription sc)+    , "    Data: "        <> show (_scData sc)+    ]  assertRight :: (Show a) => Either a b -> IO b assertRight a =   case a of-    Left e  -> HU.assertFailure (show e) >> fail "assertRight failed"+    Left e  -> assertFailure (show e) >> fail "assertRight failed"     Right b -> return b  $(deriveFromJSON defaultOptions { fieldLabelModifier = map toLower . drop 3 } ''SchemaTestCase)
tests/Local.hs view
@@ -8,9 +8,9 @@  main :: IO () main = do-  files <- filter isLocal . filter (".json" `isSuffixOf`)-             <$> getDirectoryContents dir-  ts <- readSchemaTests dir files+  filenames <- filter isLocal . filter (".json" `isSuffixOf`)+                 <$> getDirectoryContents dir+  ts <- readSchemaTests dir filenames   defaultMain (toTest <$> ts)    where
tests/Remote.hs view
@@ -8,9 +8,9 @@  main :: IO () main = do-  files <- filter (not . isLocal) . filter (".json" `isSuffixOf`)-             <$> getDirectoryContents dir-  ts <- readSchemaTests dir files+  filenames <- filter (not . isLocal) . filter (".json" `isSuffixOf`)+                 <$> getDirectoryContents dir+  ts <- readSchemaTests dir filenames   defaultMain (toTest <$> ts)    where