Documentation
¶
Overview ¶
Package rest provides an HTTP client with middleware support, retry logic, and optional OpenTelemetry instrumentation.
Index ¶
- Variables
- type Client
- func (c *Client) AddMiddleware(middleware Middleware)
- func (c *Client) GetMiddlewares() []Middleware
- func (c *Client) GetRestClient() *resty.Client
- func (c *Client) GetRestConfig() *Config
- func (c *Client) MakeRequest(ctx context.Context, method string, url string, body string, ...) (*Response, error)
- func (c *Client) MakeRequestWithTrace(ctx context.Context, method string, url string, body string, ...) (*Response, error)
- func (c *Client) SetMiddlewares(middlewares ...Middleware)
- type ClientOption
- type Config
- type ExecutionError
- type LoggingMiddleware
- type Middleware
- type NoOpMiddleware
- type OTelLoggingMiddleware
- type OTelMetricsMiddleware
- type OTelTracingMiddleware
- type RequestInfo
- type ResourceNotFoundError
- type Response
- type ResponseError
- type ServerError
- type TraceInfo
- type UnauthorizedError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrResourceNotFound = errors.New("resource not found") ErrServer = errors.New("server error") ErrResponse = errors.New("response error") )
Sentinel errors for use with errors.Is.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps a resty HTTP client with middleware and OTel support.
func NewClient ¶
func NewClient(options ...ClientOption) *Client
NewClient creates a new REST client with the given options. For custom TLS configuration, use GetRestClient() to access the underlying resty client and call SetTLSClientConfig().
Example ¶
NewClient builds a client from functional options. Fields not set in the provided Config keep their zero values, so start from DefaultRestConfig when you only want to tweak a few fields.
package main
import (
"fmt"
"time"
"github.com/jasoet/pkg/v3/rest"
)
func main() {
cfg := rest.DefaultRestConfig()
cfg.RetryCount = 3
cfg.Timeout = 10 * time.Second
client := rest.NewClient(rest.WithRestConfig(*cfg))
actual := client.GetRestConfig()
fmt.Println("retryCount:", actual.RetryCount)
fmt.Println("retryWaitTime:", actual.RetryWaitTime)
fmt.Println("timeout:", actual.Timeout)
fmt.Println("maxResponseBodyLog:", actual.MaxResponseBodyLog)
}
Output: retryCount: 3 retryWaitTime: 2s timeout: 10s maxResponseBodyLog: 1024
func (*Client) AddMiddleware ¶
func (c *Client) AddMiddleware(middleware Middleware)
AddMiddleware appends a middleware to the chain.
func (*Client) GetMiddlewares ¶
func (c *Client) GetMiddlewares() []Middleware
GetMiddlewares returns a copy of the current middleware chain.
func (*Client) GetRestClient ¶
GetRestClient returns the underlying resty client. Mutations to this client after NewClient returns are not thread-safe for concurrent use with doRequest.
func (*Client) GetRestConfig ¶
GetRestConfig returns a copy of the current REST configuration.
func (*Client) MakeRequest ¶
func (c *Client) MakeRequest(ctx context.Context, method string, url string, body string, headers map[string]string) (*Response, error)
MakeRequest executes an HTTP request without resty trace.
The body parameter is a string; for binary payloads, use GetRestClient() and build the request directly with resty's SetBody(interface{}).
Example ¶
MakeRequest returns the library-owned *rest.Response with status predicates. Non-2xx responses also produce a typed error suitable for errors.As.
package main
import (
"context"
"errors"
"fmt"
"net/http"
"net/http/httptest"
"github.com/jasoet/pkg/v3/rest"
)
func main() {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/missing" {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte(`{"error":"not found"}`))
return
}
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"status":"ok"}`))
}))
defer server.Close()
// Replace the default LoggingMiddleware to keep the example output clean.
client := rest.NewClient(rest.WithMiddlewares(rest.NewNoOpMiddleware()))
ctx := context.Background()
resp, err := client.MakeRequest(ctx, http.MethodGet, server.URL+"/users", "", nil)
fmt.Println("err:", err)
fmt.Println("status:", resp.StatusCode)
fmt.Println("isSuccess:", resp.IsSuccess())
fmt.Println("isError:", resp.IsError())
fmt.Println("body:", resp.Body)
// Non-2xx responses return both the Response and a typed error.
resp, err = client.MakeRequest(ctx, http.MethodGet, server.URL+"/missing", "", nil)
var notFound *rest.ResourceNotFoundError
fmt.Println("notFoundErr:", errors.As(err, ¬Found))
fmt.Println("isNotFound:", resp.IsNotFound())
}
Output: err: <nil> status: 200 isSuccess: true isError: false body: {"status":"ok"} notFoundErr: true isNotFound: true
func (*Client) MakeRequestWithTrace ¶
func (c *Client) MakeRequestWithTrace(ctx context.Context, method string, url string, body string, headers map[string]string) (*Response, error)
MakeRequestWithTrace executes an HTTP request with resty trace enabled.
The body parameter is a string; for binary payloads, use GetRestClient() and build the request directly with resty's SetBody(interface{}).
func (*Client) SetMiddlewares ¶
func (c *Client) SetMiddlewares(middlewares ...Middleware)
SetMiddlewares replaces the entire middleware chain.
type ClientOption ¶
type ClientOption func(*Client)
ClientOption configures a Client during construction.
func WithMiddleware ¶
func WithMiddleware(middleware Middleware) ClientOption
WithMiddleware appends a single middleware to the existing middleware chain. The lock is not held here because option functions run only during NewClient construction.
func WithMiddlewares ¶
func WithMiddlewares(middlewares ...Middleware) ClientOption
WithMiddlewares replaces the entire middleware chain with the provided middlewares. Use WithMiddleware to append instead. The lock is not held here because option functions run only during NewClient construction.
func WithOTelConfig ¶
func WithOTelConfig(cfg *otel.Config) ClientOption
WithOTelConfig sets the OpenTelemetry configuration for the REST client. When set, adds OTel tracing, metrics, and logging middleware automatically.
func WithRestConfig ¶
func WithRestConfig(restConfig Config) ClientOption
WithRestConfig sets the REST client configuration.
type Config ¶
type Config struct {
RetryCount int `yaml:"retryCount" mapstructure:"retryCount"`
RetryWaitTime time.Duration `yaml:"retryWaitTime" mapstructure:"retryWaitTime"`
RetryMaxWaitTime time.Duration `yaml:"retryMaxWaitTime" mapstructure:"retryMaxWaitTime"`
Timeout time.Duration `yaml:"timeout" mapstructure:"timeout"`
// MaxResponseBodyLog limits the number of bytes of response body stored in logs/errors.
// 0 means unlimited. Default is 1024.
MaxResponseBodyLog int `yaml:"maxResponseBodyLog" mapstructure:"maxResponseBodyLog"`
// OpenTelemetry Configuration (optional - nil disables telemetry)
OTelConfig *otel.Config `yaml:"-" mapstructure:"-"` // Not serializable from config files
}
Config holds configuration for the REST client.
func DefaultRestConfig ¶
func DefaultRestConfig() *Config
DefaultRestConfig returns a default REST configuration with sensible defaults.
type ExecutionError ¶
ExecutionError represents an error during request execution (e.g. network failure).
func (*ExecutionError) Error ¶
func (e *ExecutionError) Error() string
func (*ExecutionError) Unwrap ¶
func (e *ExecutionError) Unwrap() error
type LoggingMiddleware ¶
type LoggingMiddleware struct{}
LoggingMiddleware logs HTTP requests and responses
func NewLoggingMiddleware ¶
func NewLoggingMiddleware() *LoggingMiddleware
NewLoggingMiddleware creates a new LoggingMiddleware instance
func (*LoggingMiddleware) AfterRequest ¶
func (m *LoggingMiddleware) AfterRequest(ctx context.Context, info RequestInfo)
AfterRequest logs the completion of the request with timing information
type Middleware ¶
type NoOpMiddleware ¶
type NoOpMiddleware struct{}
NoOpMiddleware is a middleware that does nothing - useful for testing and as a placeholder
func NewNoOpMiddleware ¶
func NewNoOpMiddleware() *NoOpMiddleware
NewNoOpMiddleware creates a new NoOpMiddleware instance
func (*NoOpMiddleware) AfterRequest ¶
func (m *NoOpMiddleware) AfterRequest(ctx context.Context, info RequestInfo)
AfterRequest does nothing
type OTelLoggingMiddleware ¶
type OTelLoggingMiddleware struct {
// contains filtered or unexported fields
}
OTelLoggingMiddleware implements structured logging with trace correlation for HTTP client
func NewOTelLoggingMiddleware ¶
func NewOTelLoggingMiddleware(cfg *pkgotel.Config) *OTelLoggingMiddleware
NewOTelLoggingMiddleware creates a new OpenTelemetry logging middleware
func (*OTelLoggingMiddleware) AfterRequest ¶
func (m *OTelLoggingMiddleware) AfterRequest(ctx context.Context, info RequestInfo)
AfterRequest logs the completion of the request with trace correlation
type OTelMetricsMiddleware ¶
type OTelMetricsMiddleware struct {
// contains filtered or unexported fields
}
OTelMetricsMiddleware implements metrics collection for HTTP client requests
func NewOTelMetricsMiddleware ¶
func NewOTelMetricsMiddleware(cfg *pkgotel.Config) *OTelMetricsMiddleware
NewOTelMetricsMiddleware creates a new OpenTelemetry metrics middleware
func (*OTelMetricsMiddleware) AfterRequest ¶
func (m *OTelMetricsMiddleware) AfterRequest(ctx context.Context, info RequestInfo)
AfterRequest records response metrics
type OTelTracingMiddleware ¶
type OTelTracingMiddleware struct {
// contains filtered or unexported fields
}
OTelTracingMiddleware implements distributed tracing for HTTP client requests
func NewOTelTracingMiddleware ¶
func NewOTelTracingMiddleware(cfg *pkgotel.Config) *OTelTracingMiddleware
NewOTelTracingMiddleware creates a new OpenTelemetry tracing middleware
func (*OTelTracingMiddleware) AfterRequest ¶
func (m *OTelTracingMiddleware) AfterRequest(ctx context.Context, info RequestInfo)
AfterRequest ends the span and records the response status
type RequestInfo ¶
type ResourceNotFoundError ¶
ResourceNotFoundError represents a 404 Not Found response.
func (*ResourceNotFoundError) Error ¶
func (e *ResourceNotFoundError) Error() string
func (*ResourceNotFoundError) Unwrap ¶
func (e *ResourceNotFoundError) Unwrap() error
type Response ¶
Response is the library-owned HTTP response returned by MakeRequest and MakeRequestWithTrace. It decouples callers from the underlying resty types.
func (*Response) IsAuthError ¶
IsAuthError returns true for HTTP 401 (Unauthorized) and 403 (Forbidden). Both indicate an access control failure and both map to UnauthorizedError in handleResponse; use StatusCode to distinguish them.
func (*Response) IsClientError ¶
IsClientError returns true for any HTTP 4xx status code. Note: this overlaps with IsAuthError and IsNotFound; in handleResponse, those are checked first so IsClientError only catches remaining 4xx codes.
func (*Response) IsNotFound ¶
IsNotFound returns true for HTTP 404 (Not Found).
func (*Response) IsServerError ¶
IsServerError returns true for HTTP 5xx status codes.
type ResponseError ¶
ResponseError represents a client-side HTTP error (HTTP 4xx, excluding 401/403/404).
func (*ResponseError) Error ¶
func (e *ResponseError) Error() string
func (*ResponseError) Unwrap ¶
func (e *ResponseError) Unwrap() error
type ServerError ¶
ServerError represents a server-side failure (HTTP 5xx).
func (*ServerError) Error ¶
func (e *ServerError) Error() string
func (*ServerError) Unwrap ¶
func (e *ServerError) Unwrap() error
type TraceInfo ¶
type TraceInfo struct {
DNSLookup time.Duration
TCPConnTime time.Duration
TLSHandshake time.Duration
ServerTime time.Duration
ResponseTime time.Duration
TotalTime time.Duration
}
TraceInfo is the library-owned request trace information, populated by MakeRequestWithTrace. It mirrors the duration fields of the underlying transport trace that consumers actually read.
type UnauthorizedError ¶
type UnauthorizedError struct {
}
UnauthorizedError represents an authentication or authorization failure (HTTP 401/403).
func (*UnauthorizedError) Error ¶
func (e *UnauthorizedError) Error() string
func (*UnauthorizedError) Unwrap ¶
func (e *UnauthorizedError) Unwrap() error