Skip to content

Commit

Permalink
Simplify native objects when dealing with unions for optional fields …
Browse files Browse the repository at this point in the history
…[null, TYPE]
  • Loading branch information
siddartha-RE committed Apr 15, 2020
1 parent 3f2df38 commit 7efb0fe
Show file tree
Hide file tree
Showing 12 changed files with 157 additions and 108 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
*.test
*.code-workspace

2 changes: 2 additions & 0 deletions codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ type Codec struct {
schemaCanonical string
typeName *name

wrapDefault func(interface{}) (interface{}, error)

nativeFromTextual func([]byte) (interface{}, []byte, error)
binaryFromNative func([]byte, interface{}) ([]byte, error)
nativeFromBinary func([]byte) (interface{}, []byte, error)
Expand Down
2 changes: 1 addition & 1 deletion examples/ab2t/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"path/filepath"
"sync"

"github.com/linkedin/goavro/v2"
"github.com/realityengines/goavro"
)

func usage() {
Expand Down
72 changes: 39 additions & 33 deletions examples/nested/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
"os"
"reflect"

"github.com/linkedin/goavro/v2"
"github.com/realityengines/goavro"
)

var (
Expand Down Expand Up @@ -42,12 +42,18 @@ func main() {
}

fmt.Printf("user in=%+v\n", user)

fmt.Printf("address in=%+v\n", user.Address)
///Convert Binary From Native
binary, err := codec.BinaryFromNative(nil, user.ToStringMap())
if err != nil {
panic(err)
}
fmt.Println("Textual")
text, err := codec.TextualFromNative(nil, user.ToStringMap())
if err != nil {
panic(err)
}
fmt.Println(string(text))

///Convert Native from Binary
native, _, err := codec.NativeFromBinary(binary)
Expand All @@ -58,6 +64,7 @@ func main() {
//Convert it back tp Native
userOut := StringMapToUser(native.(map[string]interface{}))
fmt.Printf("user out=%+v\n", userOut)
fmt.Printf("address out=%+v\n", userOut.Address)
if ok := reflect.DeepEqual(user, userOut); !ok {
fmt.Fprintf(os.Stderr, "struct Compare Failed ok=%t\n", ok)
os.Exit(1)
Expand Down Expand Up @@ -89,9 +96,9 @@ func (u *User) ToStringMap() map[string]interface{} {
}

if len(u.Errors) > 0 {
datumIn["Errors"] = goavro.Union("array", u.Errors)
datumIn["Errors"] = u.Errors
} else {
datumIn["Errors"] = goavro.Union("null", nil)
datumIn["Errors"] = nil
}

if u.Address != nil {
Expand All @@ -102,16 +109,16 @@ func (u *User) ToStringMap() map[string]interface{} {
"Zip": int(u.Address.Zip),
}
if u.Address.Address2 != "" {
addDatum["Address2"] = goavro.Union("string", u.Address.Address2)
addDatum["Address2"] = u.Address.Address2
} else {
addDatum["Address2"] = goavro.Union("null", nil)
addDatum["Address2"] = nil
}

//important need namespace and record name
datumIn["Address"] = goavro.Union("my.namespace.com.address", addDatum)
datumIn["Address"] = addDatum

} else {
datumIn["Address"] = goavro.Union("null", nil)
datumIn["Address"] = nil
}
return datumIn
}
Expand All @@ -131,38 +138,37 @@ func StringMapToUser(data map[string]interface{}) *User {
ind.LastName = value
}
case "Errors":
if value, ok := v.(map[string]interface{}); ok {
for _, item := range value["array"].([]interface{}) {
if value, ok := v.([]interface{}); ok {
for _, item := range value {
ind.Errors = append(ind.Errors, item.(string))
}
}
case "Address":
if vmap, ok := v.(map[string]interface{}); ok {
//important need namespace and record name
if cookieSMap, ok := vmap["my.namespace.com.address"].(map[string]interface{}); ok {
add := &Address{}
for k, v := range cookieSMap {
switch k {
case "Address1":
if value, ok := v.(string); ok {
add.Address1 = value
}
case "Address2":
if value, ok := v.(string); ok {
add.Address2 = value
}
case "City":
if value, ok := v.(string); ok {
add.City = value
}
case "Zip":
if value, ok := v.(int); ok {
add.Zip = value
}
if cookieSMap, ok := v.(map[string]interface{}); ok {
add := &Address{}
for k, v := range cookieSMap {
switch k {
case "Address1":
if value, ok := v.(string); ok {
add.Address1 = value
}
case "Address2":
if value, ok := v.(string); ok {
add.Address2 = value
}
case "City":
if value, ok := v.(string); ok {
add.City = value
}
case "State":
if value, ok := v.(string); ok {
add.State = value
}
case "Zip":
add.Zip = int(v.(int32))
}
ind.Address = add
}
ind.Address = add
}
}

Expand Down
2 changes: 1 addition & 1 deletion examples/soe/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package main
import (
"fmt"

"github.com/linkedin/goavro"
"github.com/realityengines/goavro"
)

func main() {
Expand Down
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
module github.com/linkedin/goavro/v2
module github.com/realityengines/goavro

go 1.12

require github.com/golang/snappy v0.0.1
require (
github.com/golang/snappy v0.0.1
github.com/linkedin/goavro/v2 v2.9.7 // indirect
)
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/linkedin/goavro v1.0.5 h1:6ds0AI8upkEoafDk0a5r9q1p/xRtMq47jCilZYEqbmg=
github.com/linkedin/goavro v2.1.0+incompatible h1:DV2aUlj2xZiuxQyvag8Dy7zjY69ENjS66bWkSfdpddY=
github.com/linkedin/goavro v2.1.0+incompatible/go.mod h1:bBCwI2eGYpUI/4820s67MElg9tdeLbINjLjiM2xZFYM=
github.com/linkedin/goavro/v2 v2.9.7 h1:Vd++Rb/RKcmNJjM0HP/JJFMEWa21eUBVKPYlKehOGrM=
github.com/linkedin/goavro/v2 v2.9.7/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA=
gopkg.in/linkedin/goavro.v1 v1.0.5 h1:BJa69CDh0awSsLUmZ9+BowBdokpduDZSM9Zk8oKHfN4=
gopkg.in/linkedin/goavro.v1 v1.0.5/go.mod h1:Aw5GdAbizjOEl0kAMHV9iHmA8reZzW/OKuJAl4Hb9F0=
22 changes: 11 additions & 11 deletions logical_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ func TestTimeStampMillisLogicalTypeEncode(t *testing.T) {

func TestTimeStampMillisLogicalTypeUnionEncode(t *testing.T) {
schema := `{"type": ["null", {"type": "long", "logicalType": "timestamp-millis"}]}`
testBinaryEncodeFail(t, schema, Union("string", "test"), "cannot encode binary union: no member schema types support datum: allowed types: [null long.timestamp-millis]")
testBinaryCodecPass(t, schema, Union("long.timestamp-millis", time.Date(2006, 1, 2, 15, 04, 05, 565000000, time.UTC)), []byte("\x02\xfa\x82\xac\xba\x91\x42"))
testBinaryEncodeFail(t, schema, "test", "cannot transform binary timestamp-millis, expected time.Time, received string")
testBinaryCodecPass(t, schema, nil, []byte("\x00"))
testBinaryCodecPass(t, schema, time.Date(2006, 1, 2, 15, 04, 05, 565000000, time.UTC), []byte("\x02\xfa\x82\xac\xba\x91\x42"))
}

func TestTimeStampMicrosLogicalTypeEncode(t *testing.T) {
Expand All @@ -56,8 +57,8 @@ func TestTimeStampMicrosLogicalTypeEncode(t *testing.T) {

func TestTimeStampMicrosLogicalTypeUnionEncode(t *testing.T) {
schema := `{"type": ["null", {"type": "long", "logicalType": "timestamp-micros"}]}`
testBinaryEncodeFail(t, schema, Union("string", "test"), "cannot encode binary union: no member schema types support datum: allowed types: [null long.timestamp-micros]")
testBinaryCodecPass(t, schema, Union("long.timestamp-micros", time.Date(2006, 1, 2, 15, 04, 05, 565283000, time.UTC)), []byte("\x02\xc6\x8d\xf7\xe7\xaf\xd8\x84\x04"))
testBinaryEncodeFail(t, schema, "test", "cannot transform binary timestamp-micros, expected time.Time, received string")
testBinaryCodecPass(t, schema, time.Date(2006, 1, 2, 15, 04, 05, 565283000, time.UTC), []byte("\x02\xc6\x8d\xf7\xe7\xaf\xd8\x84\x04"))
}

func TestTimeMillisLogicalTypeEncode(t *testing.T) {
Expand All @@ -69,8 +70,8 @@ func TestTimeMillisLogicalTypeEncode(t *testing.T) {

func TestTimeMillisLogicalTypeUnionEncode(t *testing.T) {
schema := `{"type": ["null", {"type": "int", "logicalType": "time-millis"}]}`
testBinaryEncodeFail(t, schema, Union("string", "test"), "cannot encode binary union: no member schema types support datum: allowed types: [null int.time-millis]")
testBinaryCodecPass(t, schema, Union("int.time-millis", 66904022*time.Millisecond), []byte("\x02\xac\xff\xe6\x3f"))
testBinaryEncodeFail(t, schema, "test", "cannot transform to binary time-millis, expected time.Duration, received string")
testBinaryCodecPass(t, schema, 66904022*time.Millisecond, []byte("\x02\xac\xff\xe6\x3f"))
}

func TestTimeMicrosLogicalTypeEncode(t *testing.T) {
Expand All @@ -82,8 +83,8 @@ func TestTimeMicrosLogicalTypeEncode(t *testing.T) {

func TestTimeMicrosLogicalTypeUnionEncode(t *testing.T) {
schema := `{"type": ["null", {"type": "long", "logicalType": "time-micros"}]}`
testBinaryEncodeFail(t, schema, Union("string", "test"), "cannot encode binary union: no member schema types support datum: allowed types: [null long.time-micros]")
testBinaryCodecPass(t, schema, Union("long.time-micros", 66904022566*time.Microsecond), []byte("\x02\xcc\xf8\xd2\xbc\xf2\x03"))
testBinaryEncodeFail(t, schema, "test", "cannot transform to binary time-micros, expected time.Duration, received string")
testBinaryCodecPass(t, schema, 66904022566*time.Microsecond, []byte("\x02\xcc\xf8\xd2\xbc\xf2\x03"))
}
func TestDateLogicalTypeEncode(t *testing.T) {
schema := `{"type": "int", "logicalType": "date"}`
Expand Down Expand Up @@ -132,7 +133,7 @@ func ExampleUnion_logicalType() {

// Note the usage of type.logicalType i.e. `long.timestamp-millis` to denote the type in a union. This is due to the single string naming format
// used by goavro. Decimal can be both bytes.decimal or fixed.decimal
bytes, err := codec.BinaryFromNative(nil, map[string]interface{}{"long.timestamp-millis": time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC)})
bytes, err := codec.BinaryFromNative(nil, time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC))
if err != nil {
fmt.Println(err)
}
Expand All @@ -141,7 +142,6 @@ func ExampleUnion_logicalType() {
if err != nil {
fmt.Println(err)
}
out := decoded.(map[string]interface{})
fmt.Printf("%#v\n", out["long.timestamp-millis"].(time.Time).String())
fmt.Printf("%#v\n", decoded.(time.Time).String())
// Output: "2006-01-02 15:04:05 +0000 UTC"
}
6 changes: 5 additions & 1 deletion record.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,11 @@ func makeRecordCodec(st map[string]*Codec, enclosingNamespace string, schemaMap
// NOTE: To support record field default values, union schema
// set to the type name of first member
// TODO: change to schemaCanonical below
defaultValue = Union(fieldCodec.schemaOriginal, defaultValue)
var err error
defaultValue, err = fieldCodec.wrapDefault(defaultValue)
if err != nil {
return nil, err
}
default:
debug("fieldName: %q; type: %q; defaultValue: %T(%#v)\n", fieldName, c.typeName, defaultValue, defaultValue)
}
Expand Down
22 changes: 7 additions & 15 deletions record_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,10 +400,10 @@ func TestRecordFieldUnionDefaultValue(t *testing.T) {
func TestRecordFieldUnionInvalidDefaultValue(t *testing.T) {
testSchemaInvalid(t,
`{"type":"record","name":"r1","fields":[{"name":"f1","type":["null","int"],"default":13}]}`,
"default value ought to encode using field schema")
"default value ought to encode using first union type")
testSchemaInvalid(t,
`{"type":"record","name":"r1","fields":[{"name":"f1","type":["int","null"],"default":null}]}`,
"default value ought to encode using field schema")
"default value ought to encode using first union type")
}

func TestRecordRecursiveRoundTrip(t *testing.T) {
Expand Down Expand Up @@ -509,12 +509,8 @@ func ExampleBinaryFromNative() {
// Convert native Go form to binary Avro data
binary, err := codec.BinaryFromNative(nil, map[string]interface{}{
"next": map[string]interface{}{
"LongList": map[string]interface{}{
"next": map[string]interface{}{
"LongList": map[string]interface{}{
// NOTE: May omit fields when using default value
},
},
"next": map[string]interface{}{
// NOTE: May omit fields when using default value
},
},
})
Expand Down Expand Up @@ -549,7 +545,7 @@ func ExampleNativeFromBinary() {
}

fmt.Printf("%v", native)
// Output: map[next:map[LongList:map[next:map[LongList:map[next:<nil>]]]]]
// Output: map[next:map[next:map[next:<nil>]]]
}

func ExampleNativeFromTextual() {
Expand All @@ -575,7 +571,7 @@ func ExampleNativeFromTextual() {
}

fmt.Printf("%v", native)
// Output: map[next:map[LongList:map[next:map[LongList:map[next:<nil>]]]]]
// Output: map[next:map[next:map[next:<nil>]]]
}

func ExampleTextualFromNative() {
Expand All @@ -595,12 +591,8 @@ func ExampleTextualFromNative() {
// Convert native Go form to text Avro data
text, err := codec.TextualFromNative(nil, map[string]interface{}{
"next": map[string]interface{}{
"LongList": map[string]interface{}{
"next": map[string]interface{}{
"LongList": map[string]interface{}{
// NOTE: May omit fields when using default value
},
},
// NOTE: May omit fields when using default value
},
},
})
Expand Down
Loading

0 comments on commit 7efb0fe

Please sign in to comment.