Skip to content

Commit 4f9f526

Browse files
Start decoupling ApiClient and DatabricksClient. (#1245)
## What changes are proposed in this pull request? The SDK has two clients which serve very similar purposes: `ApiClient` and `DatabricksClient`. This PR is a first step to pay to merge these two client into a single consistent client. Specifically, it brings some of the functionality from `DatabricksClient` in `ApiClient` in preparation for a change of code generation logic. This PR also slightly changes the way the `ApiClient` default parameter values are set by moving the logic in its constructor. ## How is this tested? Unit and integration tests.
1 parent 536c78c commit 4f9f526

4 files changed

Lines changed: 74 additions & 46 deletions

File tree

NEXT_CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
### Bug Fixes
88

9+
- Default values for `ApiClient` are now properly set in the type constructor rather than in the config constructor.
10+
911
### Documentation
1012

1113
### Internal Changes

client/client.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ func New(cfg *config.Config) (*DatabricksClient, error) {
1515
if err != nil {
1616
return nil, err
1717
}
18-
client, err := cfg.NewApiClient()
18+
clientCfg, err := config.HTTPClientConfigFromConfig(cfg)
1919
if err != nil {
2020
return nil, err
2121
}
2222
return &DatabricksClient{
2323
Config: cfg,
24-
client: client,
24+
client: httpclient.NewApiClient(clientCfg),
2525
}, nil
2626
}
2727

config/api_client.go

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,42 +14,44 @@ import (
1414
"github.com/databricks/databricks-sdk-go/useragent"
1515
)
1616

17-
func (c *Config) NewApiClient() (*httpclient.ApiClient, error) {
18-
if skippable, ok := c.HTTPTransport.(interface {
17+
func HTTPClientConfigFromConfig(cfg *Config) (httpclient.ClientConfig, error) {
18+
if skippable, ok := cfg.HTTPTransport.(interface {
1919
SkipRetryOnIO() bool
2020
}); ok && skippable.SkipRetryOnIO() {
21-
c.Loaders = []Loader{noopLoader{}}
22-
c.Credentials = noopAuth{}
21+
cfg.Loaders = []Loader{noopLoader{}}
22+
cfg.Credentials = noopAuth{}
2323
}
24-
err := c.EnsureResolved()
24+
25+
err := cfg.EnsureResolved()
2526
if err != nil {
26-
return nil, err
27+
return httpclient.ClientConfig{}, err
2728
}
28-
retryTimeout := time.Duration(orDefault(c.RetryTimeoutSeconds, 300)) * time.Second
29-
httpTimeout := time.Duration(orDefault(c.HTTPTimeoutSeconds, 60)) * time.Second
30-
return httpclient.NewApiClient(httpclient.ClientConfig{
31-
RetryTimeout: retryTimeout,
32-
HTTPTimeout: httpTimeout,
33-
RateLimitPerSecond: orDefault(c.RateLimitPerSecond, 15),
34-
DebugHeaders: c.DebugHeaders,
35-
DebugTruncateBytes: c.DebugTruncateBytes,
36-
InsecureSkipVerify: c.InsecureSkipVerify,
37-
Transport: c.HTTPTransport,
38-
AuthVisitor: c.Authenticate,
29+
30+
return httpclient.ClientConfig{
31+
AccountID: cfg.AccountID,
32+
Host: cfg.Host,
33+
RetryTimeout: time.Duration(cfg.RetryTimeoutSeconds) * time.Second,
34+
HTTPTimeout: time.Duration(cfg.HTTPTimeoutSeconds) * time.Second,
35+
RateLimitPerSecond: cfg.RateLimitPerSecond,
36+
DebugHeaders: cfg.DebugHeaders,
37+
DebugTruncateBytes: cfg.DebugTruncateBytes,
38+
InsecureSkipVerify: cfg.InsecureSkipVerify,
39+
Transport: cfg.HTTPTransport,
40+
AuthVisitor: cfg.Authenticate,
3941
Visitors: []httpclient.RequestVisitor{
4042
func(r *http.Request) error {
4143
if r.URL == nil {
4244
return fmt.Errorf("no URL found in request")
4345
}
44-
url, err := url.Parse(c.Host)
46+
url, err := url.Parse(cfg.Host)
4547
if err != nil {
4648
return err
4749
}
4850
r.URL.Host = url.Host
4951
r.URL.Scheme = url.Scheme
5052
return nil
5153
},
52-
authInUserAgentVisitor(c),
54+
authInUserAgentVisitor(cfg),
5355
func(r *http.Request) error {
5456
// Detect if we are running in a CI/CD environment
5557
provider := useragent.CiCdProvider()
@@ -74,7 +76,9 @@ func (c *Config) NewApiClient() (*httpclient.ApiClient, error) {
7476
},
7577
},
7678
TransientErrors: []string{
77-
"REQUEST_LIMIT_EXCEEDED", // This is temporary workaround for SCIM API returning 500. Remove when it's fixed
79+
// This is temporary workaround for SCIM API returning 500.
80+
// TODO: Remove when it's fixed.
81+
"REQUEST_LIMIT_EXCEEDED",
7882
},
7983
ErrorMapper: apierr.GetAPIError,
8084
ErrorRetriable: func(ctx context.Context, err error) bool {
@@ -84,14 +88,7 @@ func (c *Config) NewApiClient() (*httpclient.ApiClient, error) {
8488
}
8589
return false
8690
},
87-
}), nil
88-
}
89-
90-
func orDefault(configured, _default int) int {
91-
if configured == 0 {
92-
return _default
93-
}
94-
return configured
91+
}, nil
9592
}
9693

9794
// noopLoader skips configuration loading
@@ -108,3 +105,12 @@ func (noopAuth) Configure(context.Context, *Config) (credentials.CredentialsProv
108105
visitor := func(r *http.Request) error { return nil }
109106
return credentials.CredentialsProviderFn(visitor), nil
110107
}
108+
109+
// Deprecated: use [HTTPClientConfigFromConfig] with [httpclient.NewApiClient].
110+
func (c *Config) NewApiClient() (*httpclient.ApiClient, error) {
111+
cfg, err := HTTPClientConfigFromConfig(c)
112+
if err != nil {
113+
return nil, err
114+
}
115+
return httpclient.NewApiClient(cfg), nil
116+
}

httpclient/api_client.go

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ type ClientConfig struct {
2828
AuthVisitor RequestVisitor
2929
Visitors []RequestVisitor
3030

31+
AccountID string
32+
Host string
33+
3134
RetryTimeout time.Duration
3235
HTTPTimeout time.Duration
3336
InsecureSkipVerify bool
@@ -42,6 +45,35 @@ type ClientConfig struct {
4245
Transport http.RoundTripper
4346
}
4447

48+
type DoOption struct {
49+
in RequestVisitor
50+
out func(body *common.ResponseWrapper) error
51+
body any
52+
contentType string
53+
isAuthOption bool
54+
queryParams map[string]any
55+
}
56+
57+
type ApiClient struct {
58+
config ClientConfig
59+
rateLimiter *rate.Limiter
60+
httpClient *http.Client
61+
}
62+
63+
// IsAccountClient returns true if the client is configured for Accounts API.
64+
func (apic *ApiClient) IsAccountClient() bool {
65+
if apic.config.AccountID == "" {
66+
return false
67+
}
68+
if strings.HasPrefix(apic.config.Host, "https://accounts.") {
69+
return true
70+
}
71+
if strings.HasPrefix(apic.config.Host, "https://accounts-dod.") {
72+
return true
73+
}
74+
return false
75+
}
76+
4577
var defaultTransport = makeDefaultTransport()
4678

4779
func makeDefaultTransport() *http.Transport {
@@ -73,10 +105,13 @@ func (cfg ClientConfig) httpTransport() http.RoundTripper {
73105
}
74106

75107
func NewApiClient(cfg ClientConfig) *ApiClient {
108+
// Set defaults for config values that are not set.
76109
cfg.HTTPTimeout = time.Duration(orDefault(int(cfg.HTTPTimeout), int(30*time.Second)))
77110
cfg.DebugTruncateBytes = orDefault(cfg.DebugTruncateBytes, 96)
78111
cfg.RetryTimeout = time.Duration(orDefault(int(cfg.RetryTimeout), int(5*time.Minute)))
79-
cfg.HTTPTimeout = time.Duration(orDefault(int(cfg.HTTPTimeout), int(30*time.Second)))
112+
cfg.HTTPTimeout = time.Duration(orDefault(int(cfg.HTTPTimeout), int(60*time.Second)))
113+
cfg.RateLimitPerSecond = orDefault(cfg.RateLimitPerSecond, 15)
114+
80115
if cfg.ErrorMapper == nil {
81116
// default generic error mapper
82117
cfg.ErrorMapper = DefaultErrorMapper
@@ -107,21 +142,6 @@ func NewApiClient(cfg ClientConfig) *ApiClient {
107142
}
108143
}
109144

110-
type ApiClient struct {
111-
config ClientConfig
112-
rateLimiter *rate.Limiter
113-
httpClient *http.Client
114-
}
115-
116-
type DoOption struct {
117-
in RequestVisitor
118-
out func(body *common.ResponseWrapper) error
119-
body any
120-
contentType string
121-
isAuthOption bool
122-
queryParams map[string]any
123-
}
124-
125145
// Do sends an HTTP request against path.
126146
func (c *ApiClient) Do(ctx context.Context, method, path string, opts ...DoOption) error {
127147
var authVisitor RequestVisitor

0 commit comments

Comments
 (0)