diff --git a/CHANGELOG.md b/CHANGELOG.md index e959e80..a8ff068 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Dev +# 1.3.0 + +- Adds GHC 9.2.8 support +- Fix build errors with GHC 9.2.8 +- Remove version bounds on dependency packages in Cabal file +- Adds new "cabal.project" file to project +- Handle Aeson V2.x `KeyMap` <-> `HashMap` data-type changes +- Handle Aeson V2.x `Key` <-> `Text` key-type changes +- Fix broken test-suite after project GHC upgrade +- Test with GHC 9.2.8 +- Update `stack.yaml` to use LTS 20.26 (ghc 9.2.8 package-set) + # 1.2.0 - Widen QuickCheck bounds. diff --git a/cabal.project b/cabal.project new file mode 100644 index 0000000..b764c34 --- /dev/null +++ b/cabal.project @@ -0,0 +1,2 @@ +packages: . + diff --git a/medea.cabal b/medea.cabal index c22233a..dced080 100644 --- a/medea.cabal +++ b/medea.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: medea -version: 1.2.0 +version: 1.3.0 synopsis: A schema language for JSON. description: A reference implementation of a schema language, together with a conformance @@ -18,7 +18,7 @@ maintainer: koz.ross@retro-freedom.nz copyright: Juspay Technologies Pvt Ltd (C) 2020 category: Data build-type: Simple -tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.1 +tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.1 || ==9.2.8 extra-source-files: .hspec CHANGELOG.md @@ -46,9 +46,9 @@ common test-common import: lang-common other-modules: TestM build-depends: - , directory ^>=1.3.3.0 - , filepath ^>=1.4.2.1 - , hspec >=2.7.1 && <2.9.0 + , directory + , filepath + , hspec , medea , mtl @@ -75,24 +75,24 @@ library Data.Medea.ValidJSON build-depends: - , aeson >=1.4.6.0 && <2.0.0.0 - , algebraic-graphs ^>=0.5 - , bytestring ^>=0.10.8.2 - , containers ^>=0.6.0.1 - , deepseq ^>=1.4.4.0 - , free ^>=5.1.3 - , hashable >=1.2.7.0 && <1.4.0.0 - , megaparsec >=8.0.0 && <10.0.0 - , microlens-ghc ^>=0.4.12 - , mtl ^>=2.2.2 - , nonempty-containers ^>=0.3.3.0 - , parser-combinators >=1.1.0 && <2.0.0 - , scientific ^>=0.3.6.2 - , smash ^>=0.1.1.0 - , text ^>=1.2.3.1 - , unordered-containers ^>=0.2.10.0 - , vector ^>=0.12.0.3 - , vector-instances ^>=3.4 + , aeson + , algebraic-graphs + , bytestring + , containers + , deepseq + , free + , hashable + , megaparsec + , microlens-ghc + , mtl + , nonempty-containers + , parser-combinators + , scientific + , smash + , text + , unordered-containers + , vector + , vector-instances hs-source-dirs: src @@ -116,11 +116,11 @@ test-suite quickcheck-validator build-depends: , aeson , bytestring - , hspec-core >=2.7.1 && <2.9.0 - , QuickCheck >=2.13.2 && <2.15.0 - , quickcheck-instances ^>=0.3.22 + , hspec-core + , QuickCheck + , quickcheck-instances , text - , unordered-containers ^>=0.2.10.0 + , unordered-containers , vector hs-source-dirs: test/validator-quickcheck diff --git a/src/Data/Medea.hs b/src/Data/Medea.hs index a19200a..6af77c8 100644 --- a/src/Data/Medea.hs +++ b/src/Data/Medea.hs @@ -77,6 +77,7 @@ import Control.Monad.RWS.Strict (RWST (..), evalRWST) import Control.Monad.Reader (MonadReader, asks) import Control.Monad.State.Strict (MonadState (..), gets) import Data.Aeson (Array, Object, Value (..), decodeStrict) +import qualified Data.Aeson.KeyMap as AKM import qualified Data.ByteString as BS import Data.ByteString (ByteString) import Data.Can (Can (..)) @@ -163,7 +164,7 @@ toValue (ValidatedJSON (_ :< f)) = case f of NumberF n -> Number n StringF s -> String s ArrayF v -> Array . fmap (toValue . coerce) $ v - ObjectF hm -> Object . fmap (toValue . coerce) $ hm + ObjectF hm -> Object . fmap (toValue . coerce) $ AKM.fromHashMapText hm -- | What schema did this validate against? validAgainst :: ValidatedJSON -> SchemaInformation @@ -332,7 +333,7 @@ checkPrim v = do Object obj -> case par of -- Fast path (no object spec) Nothing -> - put (anySet, Nothing) >> (ObjectSchema :<) . ObjectF <$> traverse checkTypes obj + put (anySet, Nothing) >> (ObjectSchema :<) . ObjectF <$> traverse checkTypes (AKM.toHashMapText obj) Just parIdent -> checkObject obj parIdent -- check if the array satisfies the corresponding specification. @@ -361,7 +362,7 @@ checkArray arr parIdent = do -- check if object properties satisfy the corresponding specification. checkObject :: Object -> Identifier -> ValidationM (Cofree ValidJSONF SchemaInformation) checkObject obj parIdent = do - valsAndTypes <- pairPropertySchemaAndVal obj parIdent + valsAndTypes <- pairPropertySchemaAndVal (AKM.toHashMapText obj) parIdent checkedObj <- traverse go valsAndTypes pure (ObjectSchema :< ObjectF checkedObj) where diff --git a/src/Data/Medea/ValidJSON.hs b/src/Data/Medea/ValidJSON.hs index 71b4036..06f6c17 100644 --- a/src/Data/Medea/ValidJSON.hs +++ b/src/Data/Medea/ValidJSON.hs @@ -24,7 +24,7 @@ data ValidJSONF a | StringF {-# UNPACK #-} !Text | ArrayF {-# UNPACK #-} !(Vector a) | ObjectF !(HashMap Text a) - deriving stock (Functor, Typeable, Data) + deriving stock (Functor, Typeable, Data, Eq) instance Foldable ValidJSONF where {-# INLINE foldMap #-} diff --git a/stack.yaml b/stack.yaml index 4752810..8b14d6f 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,12 +1,6 @@ --- -resolver: lts-15.15 +resolver: lts-20.26 packages: - . extra-deps: - - megaparsec-8.0.0@sha256:362f85e243ecbcb550e1de6e5c74ba5a50c09edaa1208c99bc5b9fd242227fc6,3808 - - microlens-ghc-0.4.12@sha256:77337e3222c900011df2bd3dd55d90367ee90c6c8904f4f2826d6dd874945f35,2505 - - microlens-0.4.11.2@sha256:765ec5cdd12a459e65161f0e3cdbce84652bf634d62af3911ba24e4bf8d4d944,4455 - - algebraic-graphs-0.5@sha256:6eeec5ed1687ff7aa916e7bf9f02f51aaabde6f314dc0b7b1a84156974d7da73,8071 - - nonempty-containers-0.3.3.0@sha256:f306bdbb271fb43057e0205293915bbfafc92f9960d89c0ea457625aad752eca,2717 - - nonempty-vector-0.2.0.1@sha256:332f8d48b5de02c1ab4e52c99973d4ca42dcbce21a073ffd1b5b2da1221e113f,1753 - - smash-0.1.1.0@sha256:593381bad038ff93898a8a1422c6e81fc4a2a6fb23b64afa2f758f56607b83dc,1583 + - smash-0.1.3@sha256:e812275cad1fac9d6f8a479f08c1622ae5e694506883c75e26f4c93895bebca8,1351 diff --git a/test/Data/Aeson/Arbitrary.hs b/test/Data/Aeson/Arbitrary.hs index 29eb0ae..b851e86 100644 --- a/test/Data/Aeson/Arbitrary.hs +++ b/test/Data/Aeson/Arbitrary.hs @@ -19,6 +19,7 @@ import Control.Monad (filterM, replicateM) import Control.Monad.Reader (ReaderT, asks, local, runReaderT) import Control.Monad.Trans (lift) import Data.Aeson (Array, Object, Value (..)) +import qualified Data.Aeson.KeyMap as AKM import qualified Data.HashMap.Strict as HM import Data.Text (Text) import qualified Data.Vector as V @@ -93,7 +94,7 @@ makeRandomObject (ObjGenOpts props optionalProps minAdditional maxAdditional) = someOptionalProps <- filterM (\_ -> lift arbitrary) optionalProps let keys = genKeys ++ props ++ someOptionalProps keyVals <- mapM (\x -> (x,) <$> local dec makeRandomValue) keys - pure . HM.fromList $ keyVals + pure . AKM.fromHashMapText . HM.fromList $ keyVals dec :: Word -> Word dec = subtract 1 diff --git a/test/validator-quickcheck/Main.hs b/test/validator-quickcheck/Main.hs index 3367a3d..9564705 100644 --- a/test/validator-quickcheck/Main.hs +++ b/test/validator-quickcheck/Main.hs @@ -15,9 +15,10 @@ import Data.Aeson.Arbitrary isObject, isString, ) +import qualified Data.Aeson.Key as AesonKey +import qualified Data.Aeson.KeyMap as AKM import Data.ByteString.Lazy (toStrict) import Data.Either (isLeft, isRight) -import Data.HashMap.Strict (filterWithKey, lookup) import Data.Medea (Schema, loadSchemaFromFile, validate) import Data.Text (Text) import qualified Data.Vector as V @@ -293,16 +294,16 @@ validationFail gen p scm = property $ forAll gen prop -- Returns true iff the value is an object with the given property and the -- property-value satisfies the predicate. hasProperty :: Text -> (Value -> Bool) -> Object -> Bool -hasProperty propName p obj = maybe False p $ lookup propName obj +hasProperty propName p obj = maybe False p $ AKM.lookup (AesonKey.fromText propName) obj -- Like hasProperty but is also true when the given property is absent. hasOptionalProperty :: Text -> (Value -> Bool) -> Object -> Bool -hasOptionalProperty propName p obj = maybe True p $ lookup propName obj +hasOptionalProperty propName p obj = maybe True p $ AKM.lookup (AesonKey.fromText propName) obj makeMapPred :: ObjGenOpts -> (Value -> Bool) -> Object -> Bool -makeMapPred (ObjGenOpts props optProps _ _) p = all p . filterWithKey (\k _ -> k `notElem` specifiedProps) +makeMapPred (ObjGenOpts props optProps _ _) p = all p . AKM.filterWithKey (\k _ -> k `notElem` specifiedProps) where - specifiedProps = props ++ optProps + specifiedProps = fmap (AesonKey.fromText) $ props ++ optProps testStringVals :: FilePath -> [String] -> Spec testStringVals fp validStrings = do