feat: rename HttpCustomizer to HttpTransportCustomizer and make it return http.Transport that is later used (#30)

* feat: rename HttpCustomizer to HttpTransportCustomizer and make it return http.Transport that is later used

* linting
This commit is contained in:
Mateusz Filipowicz
2025-02-19 01:03:21 +01:00
committed by GitHub
parent e25b426a84
commit 7c7ef98c03
6 changed files with 39 additions and 58 deletions

View File

@@ -15,6 +15,9 @@ ij_smart_tabs = false
ij_wrap_on_typing = false ij_wrap_on_typing = false
ij_any_block_comment_add_space = true ij_any_block_comment_add_space = true
[*.go]
indent_style = tab
[*.conf] [*.conf]
ij_continuation_indent_size = 2 ij_continuation_indent_size = 2

View File

@@ -84,14 +84,14 @@ List of available client configuration options is available [here](https://pkg.g
### Customizing HTTP Client ### Customizing HTTP Client
You can customize underlying HTTP client by using `HttpCustomizer` interface: You can customize underlying HTTP client by using `HttpTransportCustomizer` interface:
```go ```go
c, err := unifi.NewClient(&unifi.ClientConfig{ c, err := unifi.NewClient(&unifi.ClientConfig{
... ...
HttpCustomizer: func(transport *http.Transport) error { HttpTransportCustomizer: func(transport *http.Transport) (*http.Transport, error) {
transport.MaxIdleConns = 10 transport.MaxIdleConns = 10
return nil return transport, nil
}, },
}) })
``` ```

View File

@@ -57,29 +57,6 @@ if err != nil {
These helper methods abstract away the boilerplate of manually constructing HTTP requests and processing responses, allowing you to focus on your application's logic while leveraging built-in These helper methods abstract away the boilerplate of manually constructing HTTP requests and processing responses, allowing you to focus on your application's logic while leveraging built-in
validation and error handling provided by the SDK. validation and error handling provided by the SDK.
## Customizing the HTTP Client
While the basic configuration allows simple modifications, you can fully customize the underlying HTTP client for more
control over connection settings, proxy configuration, TLS settings, and connection pooling.
Example:
```go
c, err := unifi.NewClient(&unifi.ClientConfig{
BaseURL: "https://unifi.localdomain",
APIKey: "your-api-key",
HttpCustomizer: func (transport *http.Transport) error {
transport.MaxIdleConns = 20
transport.IdleConnTimeout = 90 * time.Second
// Customize TLS settings or add a proxy configuration
return nil
},
})
if err != nil {
log.Fatalf("Error creating client: %v", err)
}
```
## Interceptors and Middleware ## Interceptors and Middleware
Interceptors provide hooks into the request/response cycle and can be used for logging, metrics collection, or modifying Interceptors provide hooks into the request/response cycle and can be used for logging, metrics collection, or modifying

View File

@@ -58,16 +58,17 @@ if err != nil {
## Customizing the HTTP Client ## Customizing the HTTP Client
You can provide your own HTTP client configuration using the `HttpCustomizer` callback. This is useful if you need to tweak connection settings like timeouts, idle connection settings, or TLS configurations: You can provide your own HTTP client configuration using the `HttpTransportCustomizer` callback. This is useful if you need to tweak connection settings like timeouts, idle connection settings, or TLS configurations:
```go ```go
c, err := unifi.NewClient(&unifi.ClientConfig{ c, err := unifi.NewClient(&unifi.ClientConfig{
BaseURL: "https://unifi.localdomain", BaseURL: "https://unifi.localdomain",
APIKey: "your-api-key", APIKey: "your-api-key",
HttpCustomizer: func(transport *http.Transport) error { HttpTransportCustomizer: func(transport *http.Transport) (*http.Transport, error) {
transport.MaxIdleConns = 10 transport.MaxIdleConns = 10
// Customize TLS settings, proxy, etc. as needed // Customize TLS settings, proxy, etc. as needed
return nil // You can also create new instance of transport and return it, instead of customizing pre-configured
return transport, nil
}, },
}) })
if err != nil { if err != nil {
@@ -132,14 +133,14 @@ import (
) )
// customTransportCustomizer customizes the HTTP transport, e.g., setting idle connection limits and TLS options. // customTransportCustomizer customizes the HTTP transport, e.g., setting idle connection limits and TLS options.
func customTransportCustomizer(transport *http.Transport) error { func customTransportCustomizer(transport *http.Transport) (*http.Transport, error) {
transport.MaxIdleConns = 50 transport.MaxIdleConns = 50
transport.IdleConnTimeout = 120 * time.Second transport.IdleConnTimeout = 120 * time.Second
// Set a custom TLS configuration // Set a custom TLS configuration
transport.TLSClientConfig = &tls.Config{ transport.TLSClientConfig = &tls.Config{
MinVersion: tls.VersionTLS12, MinVersion: tls.VersionTLS12,
} }
return nil return transport, nil
} }
// myErrorHandler implements a custom error handler for HTTP responses. // myErrorHandler implements a custom error handler for HTTP responses.
@@ -175,7 +176,7 @@ func main() {
Timeout: 30 * time.Second, // Maximum duration to wait for a response Timeout: 30 * time.Second, // Maximum duration to wait for a response
VerifySSL: true, // Enable SSL certificate verification VerifySSL: true, // Enable SSL certificate verification
Interceptors: []unifi.ClientInterceptor{&customInterceptor{}}, // Custom interceptors for request/response manipulation Interceptors: []unifi.ClientInterceptor{&customInterceptor{}}, // Custom interceptors for request/response manipulation
HttpCustomizer: customTransportCustomizer, // Function to customize the underlying HTTP transport HttpTransportCustomizer: customTransportCustomizer, // Function to customize the underlying HTTP transport
UserAgent: "MyCustomAgent/1.0", // Custom User-Agent string UserAgent: "MyCustomAgent/1.0", // Custom User-Agent string
ErrorHandler: &myErrorHandler{}, // Custom error handler for processing HTTP response errors ErrorHandler: &myErrorHandler{}, // Custom error handler for processing HTTP response errors
UseLocking: true, // Enable internal locking for safe concurrent request processing UseLocking: true, // Enable internal locking for safe concurrent request processing

View File

@@ -30,9 +30,9 @@ const (
DefaultValidation validationMode = SoftValidation // TODO: change to hard in next major version DefaultValidation validationMode = SoftValidation // TODO: change to hard in next major version
) )
// HttpCustomizer is a function type for customizing the HTTP transport. // HttpTransportCustomizer is a function type for customizing the HTTP transport.
// It receives a pointer to an http.Transport and returns an error if customization fails. // It receives a pointer to an http.Transport and returns an error if customization fails.
type HttpCustomizer func(transport *http.Transport) error type HttpTransportCustomizer func(transport *http.Transport) (*http.Transport, error)
// ResponseErrorHandler defines a method for handling HTTP response errors. // ResponseErrorHandler defines a method for handling HTTP response errors.
// HandleError processes the HTTP response and returns an error if the response indicates failure. // HandleError processes the HTTP response and returns an error if the response indicates failure.
@@ -53,25 +53,25 @@ Fields:
Timeout: The maximum duration to wait for responses; default is no timeout. Timeout: The maximum duration to wait for responses; default is no timeout.
VerifySSL: When false, disables SSL certificate verification. VerifySSL: When false, disables SSL certificate verification.
Interceptors: A slice of ClientInterceptor implementations that can modify requests and responses. Interceptors: A slice of ClientInterceptor implementations that can modify requests and responses.
HttpCustomizer:An optional function to customize the HTTP transport (e.g., for custom TLS settings). HttpTransportCustomizer:An optional function to customize the HTTP transport (e.g., for custom TLS settings).
UserAgent: The User-Agent header string for outgoing HTTP requests. UserAgent: The User-Agent header string for outgoing HTTP requests.
ErrorHandler: A custom handler for processing HTTP response errors. ErrorHandler: A custom handler for processing HTTP response errors.
UseLocking: If true, enables internal locking for concurrent request processing. UseLocking: If true, enables internal locking for concurrent request processing.
ValidationMode:The mode for validating request bodies. Can be "soft", "hard", or "disable". ValidationMode:The mode for validating request bodies. Can be "soft", "hard", or "disable".
*/ */
type ClientConfig struct { type ClientConfig struct {
URL string `validate:"required,http_url"` URL string `validate:"required,http_url"`
APIKey string `validate:"required_without_all=User Password"` APIKey string `validate:"required_without_all=User Password"`
User string `validate:"excluded_with=APIKey,required_with=Password"` User string `validate:"excluded_with=APIKey,required_with=Password"`
Password string `validate:"excluded_with=APIKey,required_with=User"` Password string `validate:"excluded_with=APIKey,required_with=User"`
Timeout time.Duration // How long to wait for replies, default: forever. Timeout time.Duration // How long to wait for replies, default: forever.
VerifySSL bool VerifySSL bool
Interceptors []ClientInterceptor Interceptors []ClientInterceptor
HttpCustomizer HttpCustomizer HttpTransportCustomizer HttpTransportCustomizer
UserAgent string UserAgent string
ErrorHandler ResponseErrorHandler ErrorHandler ResponseErrorHandler
UseLocking bool UseLocking bool
ValidationMode validationMode `validate:"omitempty,oneof=soft hard disable"` ValidationMode validationMode `validate:"omitempty,oneof=soft hard disable"`
} }
// Credentials abstracts authentication credentials. // Credentials abstracts authentication credentials.
@@ -158,8 +158,8 @@ func newClientFromConfig(config *ClientConfig, v *validator) (*client, error) {
Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
TLSClientConfig: &tls.Config{InsecureSkipVerify: !config.VerifySSL}, TLSClientConfig: &tls.Config{InsecureSkipVerify: !config.VerifySSL},
} }
if config.HttpCustomizer != nil { if config.HttpTransportCustomizer != nil {
if err = config.HttpCustomizer(transport); err != nil { if transport, err = config.HttpTransportCustomizer(transport); err != nil {
return nil, fmt.Errorf("failed customizing HTTP transport: %w", err) return nil, fmt.Errorf("failed customizing HTTP transport: %w", err)
} }
} }

View File

@@ -90,9 +90,9 @@ func TestCustomizeHttpClient(t *testing.T) {
_, err := NewClient(&ClientConfig{ _, err := NewClient(&ClientConfig{
URL: localUrl, URL: localUrl,
APIKey: "test-key", APIKey: "test-key",
HttpCustomizer: func(transport *http.Transport) error { HttpTransportCustomizer: func(transport *http.Transport) (*http.Transport, error) {
called = true called = true
return nil return transport, nil
}, },
}) })
@@ -850,16 +850,16 @@ func TestLoginWithAPIKeyDirect(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestHttpCustomizerError(t *testing.T) { func TestHttpTransportCustomizerError(t *testing.T) {
t.Parallel() t.Parallel()
customizer := func(transport *http.Transport) error { customizer := func(transport *http.Transport) (*http.Transport, error) {
return errors.New("customization failed") return nil, errors.New("customization failed")
} }
_, err := NewClient(&ClientConfig{ _, err := NewClient(&ClientConfig{
URL: testUrl, URL: testUrl,
APIKey: "test-key", APIKey: "test-key",
VerifySSL: false, VerifySSL: false,
HttpCustomizer: customizer, HttpTransportCustomizer: customizer,
}) })
require.Error(t, err) require.Error(t, err)
assert.Contains(t, err.Error(), "failed customizing HTTP transport") assert.Contains(t, err.Error(), "failed customizing HTTP transport")