diff --git a/acceptance_testing.go b/acceptance_testing.go index e00d3f5..ae31fe6 100644 --- a/acceptance_testing.go +++ b/acceptance_testing.go @@ -19,6 +19,7 @@ import ( "context" "errors" "fmt" + "maps" "math/rand" "reflect" "regexp" @@ -179,11 +180,11 @@ func (d ConfigurableAcceptanceTestDriver) Connector() Connector { } func (d ConfigurableAcceptanceTestDriver) SourceConfig(*testing.T) map[string]string { - return d.Config.SourceConfig + return maps.Clone(d.Config.SourceConfig) } func (d ConfigurableAcceptanceTestDriver) DestinationConfig(*testing.T) map[string]string { - return d.Config.DestinationConfig + return maps.Clone(d.Config.DestinationConfig) } func (d ConfigurableAcceptanceTestDriver) BeforeTest(t *testing.T) { diff --git a/go.mod b/go.mod index b798c15..c4ccc4e 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.20 require ( github.com/Masterminds/sprig/v3 v3.2.3 github.com/conduitio/conduit-connector-protocol v0.5.0 + github.com/goccy/go-json v0.10.2 github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 github.com/jpillora/backoff v1.0.0 diff --git a/go.sum b/go.sum index 85201cb..239132a 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= diff --git a/record.go b/record.go index 87b67c5..2af374e 100644 --- a/record.go +++ b/record.go @@ -18,12 +18,12 @@ package sdk import ( "context" - "encoding/json" "fmt" "strconv" "strings" "github.com/conduitio/conduit-connector-protocol/cpluginv1" + "github.com/goccy/go-json" ) const ( diff --git a/record_formatter.go b/record_formatter.go index 8663744..886da92 100644 --- a/record_formatter.go +++ b/record_formatter.go @@ -16,13 +16,13 @@ package sdk import ( "bytes" - "encoding/json" "fmt" "strings" "text/template" "github.com/Masterminds/sprig/v3" "github.com/conduitio/conduit-connector-sdk/kafkaconnect" + "github.com/goccy/go-json" ) // RecordFormatter is a type that can format a record to bytes. It's used in diff --git a/record_formatter_test.go b/record_formatter_test.go index 94d425d..a717146 100644 --- a/record_formatter_test.go +++ b/record_formatter_test.go @@ -21,6 +21,32 @@ import ( "github.com/matryer/is" ) +var ( + encBytesSink []byte + encErrSink error +) + +func BenchmarkJSONEncoder(b *testing.B) { + rec := Record{ + Position: Position("foo"), + Operation: OperationCreate, + Metadata: Metadata{MetadataConduitSourcePluginName: "example"}, + Key: RawData("bar"), + Payload: Change{ + Before: nil, + After: StructuredData{ + "foo": "bar", + "baz": "qux", + }, + }, + } + + enc := JSONEncoder{} + for i := 0; i < b.N; i++ { + encBytesSink, encErrSink = enc.Encode(rec) + } +} + func TestOpenCDCConverter(t *testing.T) { is := is.New(t) var converter OpenCDCConverter diff --git a/util.go b/util.go index b55d9f5..ace28a5 100644 --- a/util.go +++ b/util.go @@ -16,6 +16,7 @@ package sdk import ( "fmt" + "reflect" "strings" "github.com/mitchellh/mapstructure" @@ -81,6 +82,7 @@ func parseConfig(raw map[string]string, config interface{}) error { WeaklyTypedInput: true, Result: &config, DecodeHook: mapstructure.ComposeDecodeHookFunc( + emptyStringToZeroValueHookFunc(), mapstructure.StringToTimeDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), ), @@ -95,3 +97,15 @@ func parseConfig(raw map[string]string, config interface{}) error { err = decoder.Decode(breakUpConfig(raw)) return err } + +func emptyStringToZeroValueHookFunc() mapstructure.DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || data != "" { + return data, nil + } + return reflect.New(t).Elem().Interface(), nil + } +} diff --git a/util_test.go b/util_test.go index d809bce..87caf82 100644 --- a/util_test.go +++ b/util_test.go @@ -27,11 +27,13 @@ func TestParseConfig_Simple_Struct(t *testing.T) { type Person struct { Name string `json:"person_name"` Age int + Dur time.Duration } input := map[string]string{ "person_name": "meroxa", "age": "91", + "dur": "", // empty value should result in zero value } want := Person{ Name: "meroxa",