Skip to content
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

Add comments and unittests to go-sqlcommenter-core #218

Merged
merged 1 commit into from
Dec 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}
})
}
}