-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
avro schema validation #105
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ import ( | |
"sync" | ||
"time" | ||
|
||
"github.com/hamba/avro/v2" | ||
"github.com/nats-io/nats.go" | ||
|
||
graphqlParse "github.com/graph-gophers/graphql-go" | ||
|
@@ -314,6 +315,7 @@ type schemaDetails struct { | |
msgDescriptor protoreflect.MessageDescriptor | ||
jsonSchema *jsonschema.Schema | ||
graphQlSchema *graphqlParse.Schema | ||
avroSchema avro.Schema | ||
} | ||
|
||
func (c *Conn) listenToSchemaUpdates(stationName string) error { | ||
|
@@ -425,6 +427,10 @@ func (sd *schemaDetails) handleSchemaUpdateInit(sui SchemaUpdateInit) { | |
if err := sd.compileGraphQl(); err != nil { | ||
log.Println(err.Error()) | ||
} | ||
} else if sd.schemaType == "avro" { | ||
if err := sd.compileAvroSchema(); err != nil { | ||
log.Println(err.Error()) | ||
} | ||
} | ||
} | ||
|
||
|
@@ -480,6 +486,15 @@ func (sd *schemaDetails) compileGraphQl() error { | |
return nil | ||
} | ||
|
||
func (sd *schemaDetails) compileAvroSchema() error { | ||
sch, err := avro.Parse(sd.activeVersion.Content) | ||
if err != nil { | ||
return memphisError(err) | ||
} | ||
sd.avroSchema = sch | ||
return nil | ||
} | ||
|
||
func (sd *schemaDetails) validateMsg(msg any) ([]byte, error) { | ||
switch sd.schemaType { | ||
case "protobuf": | ||
|
@@ -488,6 +503,8 @@ func (sd *schemaDetails) validateMsg(msg any) ([]byte, error) { | |
return sd.validJsonSchemaMsg(msg) | ||
case "graphql": | ||
return sd.validateGraphQlMsg(msg) | ||
case "avro": | ||
return sd.validAvroSchemaMsg(msg) | ||
default: | ||
return nil, memphisError(errors.New("invalid schema type")) | ||
} | ||
|
@@ -614,3 +631,58 @@ func (sd *schemaDetails) validateGraphQlMsg(msg any) ([]byte, error) { | |
} | ||
return msgBytes, nil | ||
} | ||
|
||
func (sd *schemaDetails) validAvroSchemaMsg(msg any) ([]byte, error) { | ||
var ( | ||
msgBytes []byte | ||
err error | ||
message interface{} | ||
) | ||
|
||
if err != nil { | ||
log.Fatal(err) | ||
} | ||
|
||
switch msg.(type) { | ||
case []byte: | ||
msgBytes = msg.([]byte) | ||
if err := json.Unmarshal(msgBytes, &message); err != nil { | ||
err = errors.New("Bad Avro format - " + err.Error()) | ||
return nil, memphisError(err) | ||
} | ||
case map[string]interface{}: | ||
msgBytes, err = json.Marshal(msg) | ||
if err != nil { | ||
return nil, memphisError(err) | ||
} | ||
if err := json.Unmarshal(msgBytes, &message); err != nil { | ||
err = errors.New("Bad Avro format - " + err.Error()) | ||
return nil, memphisError(err) | ||
} | ||
|
||
default: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In protobuf we allow users to send map[string]interface{} in order people won't have to hole the proto file locally, is it possible here as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I've added it now. |
||
msgType := reflect.TypeOf(msg).Kind() | ||
if msgType == reflect.Struct { | ||
msgBytes, err = avro.Marshal(sd.avroSchema, msg) | ||
if err != nil { | ||
return nil, memphisError(err) | ||
} | ||
if err := avro.Unmarshal(sd.avroSchema, msgBytes, &message); err != nil { | ||
return nil, memphisError(err) | ||
} | ||
// Serialize it back after validation and unmarshalling | ||
msgBytes, err = json.Marshal(message) | ||
if err != nil { | ||
return nil, memphisError(err) | ||
} | ||
} else { | ||
return nil, memphisError(errors.New("unsupported message type")) | ||
} | ||
} | ||
|
||
if _, err = avro.Marshal(sd.avroSchema, message); err != nil { | ||
return msgBytes, memphisError(err) | ||
} | ||
|
||
return msgBytes, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"type": "record", | ||
"namespace": "com.example", | ||
"name": "test_schema", | ||
"fields": [ | ||
{ "name": "username", "type": "string", "default": "-2" }, | ||
{ "name": "age", "type": "int" }, | ||
{ "name": "phone", "type": "long" }, | ||
{ "name": "country", "type": "string", "default": "NONE" } | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
myData := []byte(
{"username": "john", "age": 30}
)When json unmarshal the above data,
int
type is converted tofloat64
thus causing schema validation error. So when user is defining avro schema, schema can't have int type. It should be double rather than int.https://github.com/hamba/avro#types-conversions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any workaround to solve this? otherwise we should mention it on the UI as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can think of the below solution. But this solution converts int to int64. Still we need to add note in the UI.
So i reckon leave it as it is and add note in the UI.
https://go.dev/play/p/SkG6qlolz1B
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok totally agree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this thing happening on other SDKs as well or only in Go? if no so let's add a comment about it in the readme file of Go sdk it is enough