Skip to content

Commit

Permalink
Add comments and unittests to go-sqlcommenter-core (#218)
Browse files Browse the repository at this point in the history
  • Loading branch information
sjs994 committed Dec 20, 2022
1 parent 0579a4a commit b8c31ec
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 9 deletions.
35 changes: 26 additions & 9 deletions go/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,23 @@ import (
"runtime"
"sort"
"strings"

"go.opentelemetry.io/otel/propagation"
)

// Constants used as key string for tags.
// It is not necessary that all SQLCommenter frameworks/ORMs will contain all these keys i.e.
// it is on best-effort basis.
const (
Route string = "route"
Controller string = "controller"
Action string = "action"
Framework string = "framework"
Driver string = "db_driver"
Traceparent string = "traceparent"
Application string = "application"
Controller = "controller"
Action = "action"
Framework = "framework"
Driver = "db_driver"
Traceparent = "traceparent"
Application = "application"
)

// CommenterConfig contains configurations for SQLCommenter library.
// We can enable and disable certain tags by enabling these configurations.
type CommenterConfig struct {
EnableDBDriver bool
EnableRoute bool
Expand All @@ -46,11 +49,15 @@ type CommenterConfig struct {
EnableApplication bool
}

// StaticTags are few tags that can be set by the application and will be constant
// for every API call.
type StaticTags struct {
Application string
DriverName string
}

// CommenterOptions contains all options regarding SQLCommenter library.
// This includes the configurations as well as any static tags.
type CommenterOptions struct {
Config CommenterConfig
Tags StaticTags
Expand All @@ -60,13 +67,19 @@ func encodeURL(k string) string {
return url.QueryEscape(k)
}

func GetFunctionName(i interface{}) string {
// GetFunctionName returns the name of the function passed.
func GetFunctionName(i any) string {
if i == nil {
return ""
}
return runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
}

// ConvertMapToComment returns a comment string given a map of key-value pairs of tags.
// There are few steps involved here:
// - Sorting the tags by key string
// - url encoding the key value pairs
// - Formatting the key value pairs as "key1=value1,key2=value2" format.
func ConvertMapToComment(tags map[string]string) string {
var sb strings.Builder
i, sz := 0, len(tags)
Expand All @@ -89,6 +102,7 @@ func ConvertMapToComment(tags map[string]string) string {
return sb.String()
}

// ExtractTraceparent extracts the traceparent field using OpenTelemetry library.
func ExtractTraceparent(ctx context.Context) propagation.MapCarrier {
// Serialize the context into carrier
textMapPropogator := propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})
Expand All @@ -97,12 +111,15 @@ func ExtractTraceparent(ctx context.Context) propagation.MapCarrier {
return carrier
}

// RequestTagsProvider adds a basic interface for other libraries like gorilla/mux to implement.
type RequestTagsProvider interface {
Route() string
Action() string
Framework() string
}

// ContextInject injects the tags key-value pairs into context,
// which can be later passed into drivers/ORMs to finally inject them into SQL queries.
func ContextInject(ctx context.Context, h RequestTagsProvider) context.Context {
ctx = context.WithValue(ctx, Route, h.Route())
ctx = context.WithValue(ctx, Action, h.Action())
Expand Down
101 changes: 101 additions & 0 deletions go/core/core_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package core

import (
"context"
"testing"
)

func TestConvertMapToComment(t *testing.T) {
for _, tc := range []struct {
desc string
tagMap map[string]string
want string
}{
{
desc: "nil tagMap",
want: "",
},
{
desc: "no tags",
tagMap: map[string]string{},
want: "",
},
{
desc: "only one tag",
tagMap: map[string]string{
Route: "test-route",
},
want: "route='test-route'",
},
{
desc: "only one tag with url encoding",
tagMap: map[string]string{
Route: "test/route",
},
want: "route='test%2Froute'",
},
{
desc: "multiple tags",
tagMap: map[string]string{
Route: "test/route",
Action: "test-action",
Driver: "sql-pg",
},
want: "action='test-action',db_driver='sql-pg',route='test%2Froute'",
},
} {
t.Run(tc.desc, func(t *testing.T) {
if got, want := ConvertMapToComment(tc.tagMap), tc.want; got != want {
t.Errorf("ConvertMapToComment(%+v) = %q, want = %q", tc.tagMap, got, want)
}
})
}
}

type testRequestProvider struct {
withRoute string
withAction string
withFramework string
}

func (p *testRequestProvider) Route() string { return p.withRoute }
func (p *testRequestProvider) Action() string { return p.withAction }
func (p *testRequestProvider) Framework() string { return p.withFramework }

func TestContextInject(t *testing.T) {
tagsProvider := &testRequestProvider{
withRoute: "test-route",
withAction: "test-action",
withFramework: "test-framework",
}
ctx := context.Background()
gotCtx := ContextInject(ctx, tagsProvider)

for _, tc := range []struct {
desc string
key string
want string
}{
{
desc: "fetch action",
key: Action,
want: "test-action",
},
{
desc: "fetch route",
key: Route,
want: "test-route",
},
{
desc: "fetch framework",
key: Framework,
want: "test-framework",
},
} {
t.Run(tc.desc, func(t *testing.T) {
if got, want := gotCtx.Value(tc.key), tc.want; got != want {
t.Errorf("ContextInject(ctx, tagsProvider) context.Value(%q) = %q, want = %q", tc.key, got, tc.want)
}
})
}
}

0 comments on commit b8c31ec

Please sign in to comment.