From caf1f15ad278690932d18264412fd15d8f1d5ccd Mon Sep 17 00:00:00 2001 From: Aashay Mittal Date: Thu, 2 May 2024 11:39:20 -0700 Subject: [PATCH] feat(goavro) Adding method to return typename associated with Codec **Overview:** In order to identify the kafka topic name from a stream of topics, we need to identify the underlying type associated with the provided Codec data The `Codec.typeName` field already contains this information but is currently package private and cannot be used outside of the package This PR adds the `TypeName` method to `Codec` to expose the underlying type name outside the goavro package scope This also adds the `ShortName` method to `Name` to expose the short name (i.e. record name excluding classpath) as a public method --- codec.go | 6 ++++ codec_test.go | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++ name.go | 6 ++++ name_test.go | 25 ++++++++++++++++ 4 files changed, 120 insertions(+) diff --git a/codec.go b/codec.go index ee5bda1..9406ddb 100644 --- a/codec.go +++ b/codec.go @@ -604,6 +604,12 @@ func (c *Codec) SchemaCRC64Avro() int64 { return int64(c.Rabin) } +// TypeName returns the name of the type described by the +// schema used to create the Codec. +func (c *Codec) TypeName() name { + return *c.typeName +} + // convert a schema data structure to a codec, prefixing with specified // namespace func buildCodec(st map[string]*Codec, enclosingNamespace string, schema interface{}, cb *codecBuilder) (*Codec, error) { diff --git a/codec_test.go b/codec_test.go index f2efe19..9fc9bc6 100644 --- a/codec_test.go +++ b/codec_test.go @@ -145,6 +145,89 @@ func TestCodecRabin(t *testing.T) { } } +func TestTypeName(t* testing.T) { + cases := []struct { + Schema string + expectedFullName string + expectedNamespace string + }{ + { + Schema: `"null"`, + expectedFullName: "null", + expectedNamespace: "", + }, + { + Schema: `"boolean"`, + expectedFullName: "boolean", + expectedNamespace: "", + }, + { + Schema: `"int"`, + expectedFullName: "int", + expectedNamespace: "", + }, + { + Schema: `"long"`, + expectedFullName: "long", + expectedNamespace: "", + }, + { + Schema: `"float"`, + expectedFullName: "float", + expectedNamespace: "", + }, + { + Schema: `[ "int" ]`, + expectedFullName: "union", + expectedNamespace: "", + }, + { + Schema: `[ "int" , {"type":"boolean"} ]`, + expectedFullName: "union", + expectedNamespace: "", + }, + { + Schema: `{"fields":[], "type":"record", "name":"foo"}`, + expectedFullName: "foo", + expectedNamespace: "", + }, + { + Schema: `{"type":"enum", "name":"foo", "symbols":["A1"]}`, + expectedFullName: "foo", + expectedNamespace: "", + }, + { + Schema: `{"name":"foo","type":"fixed","size":15}`, + expectedFullName: "foo", + expectedNamespace: "", + }, + { + Schema: `{"fields":[], "type":"record", "name":"foo", "namespace":"x.y"}`, + expectedFullName: "foo", + expectedNamespace: "x.y", + }, + { + Schema: `{"namespace":"x.y.z", "type":"enum", "name":"foo", "doc":"foo bar", "symbols":["A1", "A2"]}`, + expectedFullName: "foo", + expectedNamespace: "x.y.z", + }, + } + + for _, c := range cases { + codec, err := NewCodec(c.Schema) + if err != nil { + t.Fatalf("CASE: %s; cannot create codec: %s", c.Schema, err) + } + typeName := codec.TypeName() + expected, _ := newName(c.expectedFullName, c.expectedNamespace, "") + if typeName != *expected { + t.Errorf("CASE: %s; GOT: %s; WANT: %s", c.Schema, codec.TypeName(), *expected) + } + } +} + + + func TestSingleObjectEncoding(t *testing.T) { t.Run("int", func(*testing.T) { schema := `"int"` diff --git a/name.go b/name.go index 234823d..91ab6ef 100644 --- a/name.go +++ b/name.go @@ -141,3 +141,9 @@ func (n *name) short() string { } return n.fullName } + +// Shortname returns the name without the prefixed namespace. +// This uses the short method underneath but is visible outside the package. +func (n *name) ShortName() string { + return n.short() +} \ No newline at end of file diff --git a/name_test.go b/name_test.go index afb4483..860197e 100644 --- a/name_test.go +++ b/name_test.go @@ -12,6 +12,7 @@ package goavro // NOTE: part of goavro package because it tests private functionality import ( + "fmt" "testing" ) @@ -98,3 +99,27 @@ func TestNewNameFromSchemaMap(t *testing.T) { t.Errorf("GOT: %q; WANT: %q", got, want) } } + + +func TestShortName(t *testing.T) { + cases := []struct { + name string + namespace string + want string + }{ + {"bar", "", "bar"}, + {"foo", "org.bar", "foo"}, + {"bar.foo", "org", "foo"}, + } + + for _, c := range cases { + n, err := newName(c.name, c.namespace, nullNamespace) + if err != nil { + t.Fatal(err) + } + fmt.Printf("n: %#v fullName: %v shortName: %v\n", n, n.String(), n.ShortName()) + if actual, expected := n.ShortName(), c.want; actual != expected { + t.Errorf("GOT: %#v; WANT: %#v", actual, expected) + } + } +} \ No newline at end of file