From 75722fffa86c1408214b65e81a97dda08e2c786b Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Fri, 17 May 2024 10:25:53 +0200 Subject: [PATCH 1/4] partner integration --- useragent/user_agent.go | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/useragent/user_agent.go b/useragent/user_agent.go index bd9dc7af5..627c6ce10 100644 --- a/useragent/user_agent.go +++ b/useragent/user_agent.go @@ -52,6 +52,10 @@ func WithUserAgentExtra(key, value string) { extra = extra.With(key, value) } +func WithPartner(partner string) { + WithUserAgentExtra("partner", partner) +} + func getUpstreamUserAgentInfo() []info { product := os.Getenv("DATABRICKS_SDK_UPSTREAM") version := os.Getenv("DATABRICKS_SDK_UPSTREAM_VERSION") @@ -114,19 +118,7 @@ func (d data) With(key, value string) data { if err := matchAlphanumOrSemVer(value); err != nil { panic(err) } - var c data - var found bool - for _, i := range d { - if i.Key == key { - i.Value = value - found = true - } - c = append(c, i) - } - if !found { - c = append(c, info{key, value}) - } - return c + return append(d, info{key, value}) } func (d data) String() string { From ceac2182812008c42c4744bf13c7a008fbecfd7c Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Fri, 17 May 2024 16:43:56 +0200 Subject: [PATCH 2/4] work --- useragent/user_agent_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/useragent/user_agent_test.go b/useragent/user_agent_test.go index fda9a02cc..6caa3d49b 100644 --- a/useragent/user_agent_test.go +++ b/useragent/user_agent_test.go @@ -74,3 +74,11 @@ func TestFromContext_Custom(t *testing.T) { func TestDefaultsAreValid(t *testing.T) { WithProduct(productName, productVersion) } + +func TestMultiplePartners(t *testing.T) { + WithPartner("partner1") + WithPartner("partner2") + userAgent := FromContext(context.Background()) + assert.Contains(t, userAgent, "partner/partner1") + assert.Contains(t, userAgent, "partner/partner2") +} \ No newline at end of file From d19ee8c40473276f44743b02dbd9d74835f6b83d Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Fri, 17 May 2024 16:56:22 +0200 Subject: [PATCH 3/4] fix test --- useragent/user_agent_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/useragent/user_agent_test.go b/useragent/user_agent_test.go index 6caa3d49b..8707dbeea 100644 --- a/useragent/user_agent_test.go +++ b/useragent/user_agent_test.go @@ -49,11 +49,11 @@ func TestFromContext_Custom(t *testing.T) { userAgent2 := FromContext(ctx2) // tests may be developed and run on different versions of different things - expectedFormat := "unit-tests/0.0.1 databricks-sdk-go/%s go/%s os/%s pulumi/3.8.4 terraform/1.3.6 a/e" + expectedFormat := "unit-tests/0.0.1 databricks-sdk-go/%s go/%s os/%s pulumi/3.8.4 terraform/1.3.4 terraform/1.3.5 terraform/1.3.6 a/b a/d a/e" expected := fmt.Sprintf(expectedFormat, version.Version, goVersion(), runtime.GOOS) assert.Equal(t, expected, userAgent) - expectedFormat2 := "unit-tests/0.0.1 databricks-sdk-go/%s go/%s os/%s pulumi/3.8.4 terraform/1.3.6 a/f foo/bar" + expectedFormat2 := "unit-tests/0.0.1 databricks-sdk-go/%s go/%s os/%s pulumi/3.8.4 terraform/1.3.4 terraform/1.3.5 terraform/1.3.6 a/b a/d a/e a/f foo/bar" expected2 := fmt.Sprintf(expectedFormat2, version.Version, goVersion(), runtime.GOOS) assert.Equal(t, expected2, userAgent2) @@ -81,4 +81,4 @@ func TestMultiplePartners(t *testing.T) { userAgent := FromContext(context.Background()) assert.Contains(t, userAgent, "partner/partner1") assert.Contains(t, userAgent, "partner/partner2") -} \ No newline at end of file +} From c0b5eb3410fb44b2d05483812f91a91c1036ce39 Mon Sep 17 00:00:00 2001 From: Miles Yucht Date: Tue, 28 May 2024 13:49:27 +0200 Subject: [PATCH 4/4] docs --- README.md | 20 ++++++++++++++++++++ examples/useragent/main.go | 23 +++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 examples/useragent/main.go diff --git a/README.md b/README.md index e4bcbbc9f..a299507ee 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ The Databricks SDK for Go includes functionality to accelerate development with - [GetByName utility methods](#getbyname-utility-methods) - [Node type and Databricks Runtime selectors](#node-type-and-databricks-runtime-selectors) - [Integration with `io` interfaces for DBFS](#integration-with-io-interfaces-for-dbfs) +- [User Agent Request Attribution](#user-agent-request-attribution) - [Error Handling](#error-handling) - [Logging](#logging) - [Testing](#testing) @@ -531,6 +532,25 @@ buf, err := w.Dbfs.ReadFile(ctx, "/path/to/remote/file") Databricks SDK for Go loosely integrates with [spf13/pflag](https://github.com/spf13/pflag) by implementing [pflag.Value](https://pkg.go.dev/github.com/spf13/pflag#Value) for all enum types. +## User Agent Request Attribution + +The Databricks SDK for Go uses the `User-Agent` header to include request metadata along with each request. By default, this includes the version of the Go SDK, the version of the Go language used by your application, and the underlying operating system. To statically add additional metadata, you can use the `WithPartner()` and `WithProduct()` functions in the `useragent` package. `WithPartner()` can be used by partners to indicate that code using the Databricks SDK for Go should be attributed to a specific partner. Multiple partners can be registered at once. Partner names can contain any number, digit, `.`, `-`, `_` or `+`. + +```go +useragent.WithPartner("partner-abc") +useragent.WithPartner("partner-xyz") +``` + +`WithProduct()` can be used to define the name and version of the product that is built with the Databricks SDK for Go. The product name has the same restrictions as the partner name above, and the product version must be a valid [SemVer](https://semver.org/). Subsequent calls to `WithProduct()` replace the original product with the new user-specified one. + +```go +useragent.WithProduct("databricks-example-product", "1.2.0") +``` + +If both the `DATABRICKS_SDK_UPSTREAM` and `DATABRICKS_SDK_UPSTREAM_VERSION` environment variables are defined, these will also be included in the `User-Agent` header. + +If additional metadata needs to be specified that isn't already supported by the above interfaces, you can use the `WithUserAgentExtra()` function to register arbitrary key-value pairs to include in the user agent. Multiple values associated with the same key are allowed. Keys have the same restrictions as the partner name above. Values must be either as described above or SemVer strings. + ## Error handling The Databricks SDK for Go converts error responses from the Databricks API into the [`apierr.APIError`](https://pkg.go.dev/github.com/databricks/databricks-sdk-go/apierr#APIError) type. This allows you to inspect the error code, message, and details by asserting the error as `apierr.APIError`: diff --git a/examples/useragent/main.go b/examples/useragent/main.go new file mode 100644 index 000000000..7d54d577e --- /dev/null +++ b/examples/useragent/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "context" + + "github.com/databricks/databricks-sdk-go" + "github.com/databricks/databricks-sdk-go/useragent" +) + +// Run this example by running `go run ./examples/useragent` from the root of this repository. +// +// To run this example, ensure that you have a DEFAULT profile configured in your `.databrickscfg`. +func main() { + useragent.WithProduct("databricks-sdk-example", "0.0.1") + useragent.WithPartner("databricks-sdk-example-abc") + useragent.WithPartner("databricks-sdk-example-def") + useragent.WithUserAgentExtra("test-key", "test-value") + w := databricks.Must(databricks.NewWorkspaceClient()) + _, err := w.CurrentUser.Me(context.Background()) + if err != nil { + panic(err) + } +}