avro 0.4.1.2 → 0.4.2.0
raw patch · 9 files changed
+130/−129 lines, 9 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Data.Avro.Schema: primitiveType :: Text -> Maybe Type
Files
- avro.cabal +1/−8
- src/Data/Avro/Deriving.hs +38/−38
- src/Data/Avro/JSON.hs +6/−2
- src/Data/Avro/Schema.hs +23/−45
- test/Avro/JSONSpec.hs +19/−16
- test/Avro/THUnionSpec.hs +27/−20
- test/data/unions-object-a.json +3/−0
- test/data/unions-object-b.json +3/−0
- test/data/unions.avsc +10/−0
avro.cabal view
@@ -1,13 +1,7 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.31.1.------ see: https://github.com/sol/hpack------ hash: ca29cdadb7a7a9edda357686a62ea84b9122a6bedb7fe04890a5d1fcc8719127- name: avro-version: 0.4.1.2+version: 0.4.2.0 synopsis: Avro serialization support for Haskell description: Avro serialization and deserialization support for Haskell category: Data@@ -88,7 +82,6 @@ hs-source-dirs: src other-extensions: OverloadedStrings- ghc-options: -O2 build-depends: aeson , array
src/Data/Avro/Deriving.hs view
@@ -32,41 +32,41 @@ ) where -import Control.Monad (join)-import Data.Aeson (eitherDecode)-import qualified Data.Aeson as J-import Data.Avro hiding (decode, encode)-import Data.Avro.Schema as S-import qualified Data.Avro.Types as AT-import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import Data.Char (isAlphaNum)+import Control.Monad (join)+import Data.Aeson (eitherDecode)+import qualified Data.Aeson as J+import Data.Avro hiding (decode, encode)+import Data.Avro.Schema as S+import qualified Data.Avro.Types as AT+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.Char (isAlphaNum) import Data.Int-import Data.List.NonEmpty (NonEmpty ((:|)))-import qualified Data.List.NonEmpty as NE-import Data.Map (Map)-import Data.Maybe (fromMaybe)-import Data.Semigroup ((<>))-import qualified Data.Text as Text+import Data.List.NonEmpty (NonEmpty ((:|)))+import qualified Data.List.NonEmpty as NE+import Data.Map (Map)+import Data.Maybe (fromMaybe)+import Data.Semigroup ((<>))+import qualified Data.Text as Text -import GHC.Generics (Generic)+import GHC.Generics (Generic) -import Language.Haskell.TH as TH hiding (notStrict)-import Language.Haskell.TH.Lib as TH hiding (notStrict)-import Language.Haskell.TH.Syntax+import Language.Haskell.TH as TH hiding (notStrict)+import Language.Haskell.TH.Lib as TH hiding (notStrict)+import Language.Haskell.TH.Syntax -import Data.Avro.Deriving.NormSchema-import Data.Avro.EitherN+import Data.Avro.Deriving.NormSchema+import Data.Avro.EitherN -import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.ByteString.Lazy.Char8 as LBSC8-import qualified Data.HashMap.Strict as HM-import qualified Data.Set as S-import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Vector as V+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import qualified Data.ByteString.Lazy.Char8 as LBSC8+import qualified Data.HashMap.Strict as HM+import qualified Data.Set as S+import Data.Text (Text)+import qualified Data.Text as T+import qualified Data.Vector as V import Data.Avro.Decode.Lazy.FromLazyAvro import qualified Data.Avro.Decode.Lazy.LazyValue as LV@@ -110,14 +110,14 @@ { -- | How to build field names for generated data types. The first -- argument is the type name to use as a prefix, rendered -- according to the 'namespaceBehavior' setting.- fieldNameBuilder :: Text -> Field -> T.Text+ fieldNameBuilder :: Text -> Field -> T.Text -- | Determines field representation of generated data types- , fieldRepresentation :: TypeName -> Field -> (FieldStrictness, FieldUnpackedness)+ , fieldRepresentation :: TypeName -> Field -> (FieldStrictness, FieldUnpackedness) -- | Controls how we handle namespaces when defining Haskell type -- and field names.- , namespaceBehavior :: NamespaceBehavior+ , namespaceBehavior :: NamespaceBehavior } deriving Generic -- | Default deriving options@@ -672,10 +672,10 @@ pure $ NewtypeD [] dn [] Nothing ctor ders #else genNewtype dn = do- [ConT eq, ConT sh] <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]+ [ConT eq, ConT sh, ConT gen] <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]] fldType <- [t|ByteString|] let ctor = RecC dn [(mkName ("un" ++ nameBase dn), notStrict, fldType)]- pure $ NewtypeD [] dn [] ctor [eq, sh]+ pure $ NewtypeD [] dn [] ctor [eq, sh, gen] #endif genEnum :: Name -> [Name] -> Q Dec@@ -689,8 +689,8 @@ pure $ DataD [] dn [] Nothing ((\n -> NormalC n []) <$> vs) ders #else genEnum dn vs = do- [ConT eq, ConT sh, ConT or, ConT en] <- sequenceA [[t|Eq|], [t|Show|], [t|Ord|], [t|Enum|], [t|Generic|]]- pure $ DataD [] dn [] ((\n -> NormalC n []) <$> vs) [eq, sh, or, en]+ [ConT eq, ConT sh, ConT or, ConT en, ConT gen] <- sequenceA [[t|Eq|], [t|Show|], [t|Ord|], [t|Enum|], [t|Generic|]]+ pure $ DataD [] dn [] ((\n -> NormalC n []) <$> vs) [eq, sh, or, en, gen] #endif genDataType :: Name -> [VarStrictType] -> Q Dec@@ -704,8 +704,8 @@ pure $ DataD [] dn [] Nothing [RecC dn flds] ders #else genDataType dn flds = do- [ConT eq, ConT sh] <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]- pure $ DataD [] dn [] [RecC dn flds] [eq, sh]+ [ConT eq, ConT sh, ConT gen] <- sequenceA [[t|Eq|], [t|Show|], [t|Generic|]]+ pure $ DataD [] dn [] [RecC dn flds] [eq, sh, gen] #endif notStrict :: Strict
src/Data/Avro/JSON.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} -- | Avro supports a JSON representation of Avro objects alongside the -- Avro binary format. An Avro schema can be used to generate and@@ -97,8 +98,8 @@ | otherwise = let canonicalize name- | Just _ <- Schema.primitiveType name = name- | otherwise = Schema.renderFullname $ Schema.parseFullname name+ | isBuiltIn name = name+ | otherwise = Schema.renderFullname $ Schema.parseFullname name branch = head $ HashMap.keys obj names =@@ -112,6 +113,9 @@ Avro.Error "Invalid JSON representation for union: has to be a JSON object with exactly one field." union _ _ = error "Impossible: function given non-union schema."++ isBuiltIn name = name `elem` [ "null", "boolean", "int", "long", "float"+ , "double", "bytes", "string", "array", "map" ] -- | Convert a 'Aeson.Value' into a type that has an Avro schema. The -- schema is used to validate the JSON and will return an 'Error' if
src/Data/Avro/Schema.hs view
@@ -27,7 +27,6 @@ , validateSchema -- * Lower level utilities , typeName- , primitiveType , buildTypeEnvironment , extractBindings @@ -51,34 +50,33 @@ import qualified Control.Monad.Fail as MF import Control.Monad.State.Strict -import Data.Aeson (FromJSON (..), ToJSON (..), object,- (.!=), (.:), (.:!), (.:?), (.=))-import qualified Data.Aeson as A-import Data.Aeson.Types (Parser, typeMismatch)-import qualified Data.Avro.Types as Ty-import qualified Data.ByteString as B-import qualified Data.ByteString.Base16 as Base16-import qualified Data.Char as Char-import Data.Function (on)+import Data.Aeson (FromJSON (..), ToJSON (..), object, (.!=), (.:), (.:!), (.:?), (.=))+import qualified Data.Aeson as A+import Data.Aeson.Types (Parser, typeMismatch)+import qualified Data.Avro.Types as Ty+import qualified Data.ByteString as B+import qualified Data.ByteString.Base16 as Base16+import qualified Data.Char as Char+import Data.Function (on) import Data.Hashable-import qualified Data.HashMap.Strict as HashMap+import qualified Data.HashMap.Strict as HashMap import Data.Int-import qualified Data.IntMap as IM-import qualified Data.List as L-import Data.List.NonEmpty (NonEmpty (..))-import qualified Data.List.NonEmpty as NE-import Data.Maybe (catMaybes, fromMaybe, isJust)-import Data.Monoid (First (..))+import qualified Data.IntMap as IM+import qualified Data.List as L+import Data.List.NonEmpty (NonEmpty (..))+import qualified Data.List.NonEmpty as NE+import Data.Maybe (catMaybes, fromMaybe, isJust)+import Data.Monoid (First (..)) import Data.Semigroup-import qualified Data.Set as S+import qualified Data.Set as S import Data.String-import Data.Text (Text)-import qualified Data.Text as T-import Data.Text.Encoding as T-import qualified Data.Vector as V-import Prelude as P+import Data.Text (Text)+import qualified Data.Text as T+import Data.Text.Encoding as T+import qualified Data.Vector as V+import Prelude as P -import Text.Show.Functions ()+import Text.Show.Functions () -- |An Avro schema is either -- * A "JSON object in the form `{"type":"typeName" ...`@@ -297,27 +295,6 @@ Union (x:|_) _ -> typeName x _ -> renderFullname $ name bt --- | If the given string is the name of a primitive type, return the--- type, otherwise return 'Nothing'.------ @--- λ> primitiveType "string"--- Just String--- λ> primitiveType "foo"--- Nothing--- @-primitiveType :: Text -> Maybe Type-primitiveType = \case- "null" -> Just Null- "boolean" -> Just Boolean- "int" -> Just Int- "long" -> Just Long- "float" -> Just Float- "double" -> Just Double- "bytes" -> Just Bytes- "string" -> Just String- _ -> Nothing- data Field = Field { fldName :: Text , fldAliases :: [Text] , fldDoc :: Maybe Text@@ -565,6 +542,7 @@ (<>) = mplus instance Monoid (Result a) where mempty = fail "Empty Result"+ mappend = (<>) instance Foldable Result where foldMap _ (Error _) = mempty foldMap f (Success y) = f y
test/Avro/JSONSpec.hs view
@@ -8,6 +8,7 @@ import qualified Data.Aeson as Aeson import qualified Data.ByteString.Lazy as LBS+import qualified Data.Map as Map import Data.Avro.Deriving import Data.Avro.EitherN@@ -69,24 +70,26 @@ let expected = EnumWrapper 37 "blarg" EnumReasonInstead parseJSON enumsExampleJSON `shouldBe` pure expected let unionsExampleA = Unions- { unionsScalars = Left "blarg"- , unionsNullable = Nothing- , unionsRecords = Left $ Foo { fooStuff = "stuff" }- , unionsSameFields = Left $ Foo { fooStuff = "foo stuff" }- , unionsThree = E3_1 37- , unionsFour = E4_2 "foo"- , unionsFive = E5_4 $ Foo { fooStuff = "foo stuff" }+ { unionsScalars = Left "blarg"+ , unionsNullable = Nothing+ , unionsRecords = Left $ Foo { fooStuff = "stuff" }+ , unionsSameFields = Left $ Foo { fooStuff = "foo stuff" }+ , unionsArrayAndMap = Left ["foo"]+ , unionsThree = E3_1 37+ , unionsFour = E4_2 "foo"+ , unionsFive = E5_4 $ Foo { fooStuff = "foo stuff" } } unionsExampleB = Unions- { unionsScalars = Right 37- , unionsNullable = Just 42- , unionsRecords = Right $ Bar { barStuff = "stuff"- , barThings = Foo "things"- }- , unionsSameFields = Right $ NotFoo { notFooStuff = "not foo stuff" }- , unionsThree = E3_3 37- , unionsFour = E4_4 $ Foo { fooStuff = "foo stuff" }- , unionsFive = E5_5 $ NotFoo { notFooStuff = "not foo stuff" }+ { unionsScalars = Right 37+ , unionsNullable = Just 42+ , unionsRecords = Right $ Bar { barStuff = "stuff"+ , barThings = Foo "things"+ }+ , unionsSameFields = Right $ NotFoo { notFooStuff = "not foo stuff" }+ , unionsArrayAndMap = Right $ Map.fromList [("a", 5)]+ , unionsThree = E3_3 37+ , unionsFour = E4_4 $ Foo { fooStuff = "foo stuff" }+ , unionsFive = E5_5 $ NotFoo { notFooStuff = "not foo stuff" } } it "should roundtrip (unions)" $ do forM_ [unionsExampleA, unionsExampleB] $ \ msg ->
test/Avro/THUnionSpec.hs view
@@ -14,6 +14,7 @@ import qualified Data.Avro.Schema as Schema import qualified Data.Avro.Types as Avro import qualified Data.ByteString.Lazy as LBS+import qualified Data.Map as Map import System.Directory (doesFileExist) @@ -26,24 +27,26 @@ spec :: Spec spec = describe "Avro.THUnionSpec: Schema with unions." $ do let objA = Unions- { unionsScalars = Left "foo"- , unionsNullable = Nothing- , unionsRecords = Left $ Foo { fooStuff = "stuff" }- , unionsSameFields = Left $ Foo { fooStuff = "more stuff" }- , unionsThree = E3_1 37- , unionsFour = E4_2 "foo"- , unionsFive = E5_4 $ Foo { fooStuff = "foo stuff" }+ { unionsScalars = Left "foo"+ , unionsNullable = Nothing+ , unionsRecords = Left $ Foo { fooStuff = "stuff" }+ , unionsSameFields = Left $ Foo { fooStuff = "more stuff" }+ , unionsArrayAndMap = Left ["foo"]+ , unionsThree = E3_1 37+ , unionsFour = E4_2 "foo"+ , unionsFive = E5_4 $ Foo { fooStuff = "foo stuff" } } objB = Unions- { unionsScalars = Right 42- , unionsNullable = Just 37- , unionsRecords = Right $ Bar { barStuff = "stuff"- , barThings = Foo { fooStuff = "things" }- }- , unionsSameFields = Right $ NotFoo { notFooStuff = "different from Foo" }- , unionsThree = E3_3 37- , unionsFour = E4_4 $ Foo { fooStuff = "foo stuff" }- , unionsFive = E5_5 $ NotFoo { notFooStuff = "not foo stuff" }+ { unionsScalars = Right 42+ , unionsNullable = Just 37+ , unionsRecords = Right $ Bar { barStuff = "stuff"+ , barThings = Foo { fooStuff = "things" }+ }+ , unionsSameFields = Right $ NotFoo { notFooStuff = "different from Foo" }+ , unionsArrayAndMap = Right $ Map.fromList [("a", 5)]+ , unionsThree = E3_3 37+ , unionsFour = E4_4 $ Foo { fooStuff = "foo stuff" }+ , unionsFive = E5_5 $ NotFoo { notFooStuff = "not foo stuff" } } field name schema def = Schema.Field name [] Nothing (Just Schema.Ascending) schema def@@ -54,10 +57,11 @@ foo = named "haskell.avro.example.Foo" notFoo = named "haskell.avro.example.NotFoo" expectedSchema = record "haskell.avro.example.Unions"- [ field "scalars" (Schema.mkUnion (NE.fromList [Schema.String, Schema.Long])) scalarsDefault- , field "nullable" (Schema.mkUnion (NE.fromList [Schema.Null, Schema.Int])) nullableDefault- , field "records" (Schema.mkUnion (NE.fromList [fooSchema, barSchema])) Nothing- , field "sameFields" (Schema.mkUnion (NE.fromList [foo, notFooSchema])) Nothing+ [ field "scalars" (Schema.mkUnion (NE.fromList [Schema.String, Schema.Long])) scalarsDefault+ , field "nullable" (Schema.mkUnion (NE.fromList [Schema.Null, Schema.Int])) nullableDefault+ , field "records" (Schema.mkUnion (NE.fromList [fooSchema, barSchema])) Nothing+ , field "sameFields" (Schema.mkUnion (NE.fromList [foo, notFooSchema])) Nothing+ , field "arrayAndMap" (Schema.mkUnion (NE.fromList [array, map])) Nothing , field "three" (Schema.mkUnion (NE.fromList [Schema.Int, Schema.String, Schema.Long])) Nothing , field "four" (Schema.mkUnion (NE.fromList [Schema.Int, Schema.String, Schema.Long, foo])) Nothing@@ -72,6 +76,9 @@ , field "things" (named "haskell.avro.example.Foo") Nothing ] notFooSchema = record "haskell.avro.example.NotFoo" [field "stuff" Schema.String Nothing]++ array = Schema.Array { Schema.item = Schema.String }+ map = Schema.Map { Schema.values = Schema.Long } unionsSchemaFile <- runIO $ getFileName "test/data/unions.avsc" >>= LBS.readFile let Just unionsSchemaFromJSON = Aeson.decode unionsSchemaFile
test/data/unions-object-a.json view
@@ -13,6 +13,9 @@ "stuff": "foo stuff" } },+ "arrayAndMap" : {+ "array" : ["foo"]+ }, "three": { "int": 37 }, "four": { "string": "foo" }, "five": {
test/data/unions-object-b.json view
@@ -18,6 +18,9 @@ "stuff": "not foo stuff" } },+ "arrayAndMap" : {+ "map" : { "a" : 5 }+ }, "three": { "long": 37 }, "four": { "haskell.avro.example.Foo": { "stuff" : "foo stuff" }
test/data/unions.avsc view
@@ -44,6 +44,16 @@ } ] },+ { "name" : "arrayAndMap",+ "type" : [+ { "type" : "array",+ "items" : "string"+ },+ { "type" : "map",+ "values" : "long"+ }+ ]+ }, { "name" : "three", "type" : ["int", "string", "long"] }, { "name" : "four", "type" : ["int", "string", "long", "Foo"] }, { "name" : "five", "type" : ["int", "string", "long", "Foo", "NotFoo"] }