Documentation
¶
Overview ¶
Package httpx provides HTTP transport utilities for the Spooled SDK.
Index ¶
- func IsAuthenticationError(err error) bool
- func IsNotFoundError(err error) bool
- func IsRateLimitError(err error) bool
- func IsRetryable(err error) bool
- func IsValidationError(err error) bool
- func JSON[T any](resp *Response) (*T, error)
- func JSONArray[T any](resp *Response) ([]T, error)
- func ParseErrorFromResponse(statusCode int, body []byte, headers http.Header) error
- type APIError
- type AuthenticationError
- type AuthorizationError
- type CircuitBreaker
- type CircuitBreakerConfig
- type CircuitBreakerMetrics
- type CircuitBreakerOpenError
- type CircuitState
- type Config
- type ConflictError
- type Logger
- type NetworkError
- type NotFoundError
- type PayloadTooLargeError
- type QuotaInfo
- type RateLimitError
- type Request
- type Response
- type RetryConfig
- type RetryPolicy
- type ServerError
- type TimeoutError
- type TokenRefresher
- func (tr *TokenRefresher) ForceRefresh(ctx context.Context) error
- func (tr *TokenRefresher) GetAccessToken() string
- func (tr *TokenRefresher) GetRefreshToken() string
- func (tr *TokenRefresher) NeedsRefresh() bool
- func (tr *TokenRefresher) Refresh(ctx context.Context) error
- func (tr *TokenRefresher) RefreshIfNeeded(ctx context.Context) error
- func (tr *TokenRefresher) SetAccessToken(token string, expiresIn int)
- func (tr *TokenRefresher) SetRefreshToken(token string)
- type Transport
- type ValidationError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func IsAuthenticationError ¶
IsAuthenticationError returns true if the error is a 401 error.
func IsNotFoundError ¶
IsNotFoundError returns true if the error is a 404 error.
func IsRateLimitError ¶
IsRateLimitError returns true if the error is a 429 error.
func IsRetryable ¶
IsRetryable returns true if the error is retryable.
func IsValidationError ¶
IsValidationError returns true if the error is a 400/422 error.
Types ¶
type APIError ¶
type APIError struct {
StatusCode int `json:"status_code,omitempty"`
Code string `json:"code,omitempty"`
Message string `json:"message,omitempty"`
Details map[string]any `json:"details,omitempty"`
RequestID string `json:"request_id,omitempty"`
RawBody []byte `json:"-"`
Err error `json:"-"`
}
APIError is the base error type for all HTTP errors.
func AsAPIError ¶
AsAPIError extracts the underlying API error.
func (*APIError) IsRetryable ¶
IsRetryable returns true if the error is retryable: 5xx, 408 and 429. Every other 4xx is a deterministic rejection and retrying it cannot succeed.
type AuthenticationError ¶
type AuthenticationError struct{ *APIError }
AuthenticationError represents a 401 error.
func (*AuthenticationError) Unwrap ¶
func (e *AuthenticationError) Unwrap() error
Unwrap returns the underlying API error.
type AuthorizationError ¶
type AuthorizationError struct{ *APIError }
AuthorizationError represents a 403 error.
func (*AuthorizationError) Unwrap ¶
func (e *AuthorizationError) Unwrap() error
Unwrap returns the underlying API error.
type CircuitBreaker ¶
type CircuitBreaker struct {
// contains filtered or unexported fields
}
CircuitBreaker implements the circuit breaker pattern.
func NewCircuitBreaker ¶
func NewCircuitBreaker(cfg CircuitBreakerConfig) *CircuitBreaker
NewCircuitBreaker creates a new circuit breaker.
func (*CircuitBreaker) Allow ¶
func (cb *CircuitBreaker) Allow() bool
Allow checks if a request is allowed through the circuit breaker.
func (*CircuitBreaker) Metrics ¶
func (cb *CircuitBreaker) Metrics() CircuitBreakerMetrics
Metrics returns current circuit breaker metrics.
func (*CircuitBreaker) RecordFailure ¶
func (cb *CircuitBreaker) RecordFailure()
RecordFailure records a failed request.
func (*CircuitBreaker) RecordSuccess ¶
func (cb *CircuitBreaker) RecordSuccess()
RecordSuccess records a successful request.
func (*CircuitBreaker) Reset ¶
func (cb *CircuitBreaker) Reset()
Reset resets the circuit breaker to its initial state.
func (*CircuitBreaker) State ¶
func (cb *CircuitBreaker) State() CircuitState
State returns the current state of the circuit breaker.
type CircuitBreakerConfig ¶
type CircuitBreakerConfig struct {
Enabled bool
FailureThreshold int
SuccessThreshold int
Timeout time.Duration
}
CircuitBreakerConfig configures the circuit breaker.
func DefaultCircuitBreakerConfig ¶
func DefaultCircuitBreakerConfig() CircuitBreakerConfig
DefaultCircuitBreakerConfig returns the default circuit breaker configuration.
type CircuitBreakerMetrics ¶
type CircuitBreakerMetrics struct {
State CircuitState
FailureCount int
SuccessCount int
LastStateChange time.Time
}
CircuitBreakerMetrics holds circuit breaker metrics.
type CircuitBreakerOpenError ¶
type CircuitBreakerOpenError struct{ *APIError }
CircuitBreakerOpenError represents a circuit breaker open error.
func NewCircuitBreakerOpenError ¶
func NewCircuitBreakerOpenError() *CircuitBreakerOpenError
NewCircuitBreakerOpenError creates a new circuit breaker open error.
func (*CircuitBreakerOpenError) IsRetryable ¶
func (e *CircuitBreakerOpenError) IsRetryable() bool
IsRetryable always returns false for circuit breaker errors.
func (*CircuitBreakerOpenError) Unwrap ¶
func (e *CircuitBreakerOpenError) Unwrap() error
Unwrap returns the underlying API error.
type CircuitState ¶
type CircuitState int
CircuitState represents the state of a circuit breaker.
const ( // CircuitClosed - normal operation, requests are allowed CircuitClosed CircuitState = iota // CircuitOpen - circuit is open, requests are blocked CircuitOpen // CircuitHalfOpen - testing if circuit can be closed CircuitHalfOpen )
func (CircuitState) String ¶
func (s CircuitState) String() string
String returns the string representation of a circuit state.
type Config ¶
type Config struct {
BaseURL string
APIKey string
AccessToken string
RefreshToken string
AdminKey string
UserAgent string
Headers map[string]string
Timeout time.Duration
Retry RetryConfig
CircuitBreaker CircuitBreakerConfig
Logger Logger
AutoRefreshToken bool
}
Config holds configuration for the transport.
type ConflictError ¶
type ConflictError struct{ *APIError }
ConflictError represents a 409 error.
func (*ConflictError) Unwrap ¶
func (e *ConflictError) Unwrap() error
Unwrap returns the underlying API error.
type NetworkError ¶
type NetworkError struct{ *APIError }
NetworkError represents a network-level error.
func NewNetworkError ¶
func NewNetworkError(err error) *NetworkError
NewNetworkError creates a new network error.
func (*NetworkError) IsRetryable ¶
func (e *NetworkError) IsRetryable() bool
IsRetryable always returns true for network errors.
func (*NetworkError) Unwrap ¶
func (e *NetworkError) Unwrap() error
Unwrap returns the underlying API error.
type NotFoundError ¶
type NotFoundError struct{ *APIError }
NotFoundError represents a 404 error.
func (*NotFoundError) Unwrap ¶
func (e *NotFoundError) Unwrap() error
Unwrap returns the underlying API error.
type PayloadTooLargeError ¶
type PayloadTooLargeError struct{ *APIError }
PayloadTooLargeError represents a 413 error.
func (*PayloadTooLargeError) Unwrap ¶
func (e *PayloadTooLargeError) Unwrap() error
Unwrap returns the underlying API error.
type QuotaInfo ¶ added in v1.5.0
type QuotaInfo struct {
// Resource is the limited resource, e.g. "active_jobs" or "queues".
Resource string
// Current is the usage that tripped the limit.
Current int64
// Limit is the plan limit for Resource.
Limit int64
// Plan is the organization's plan tier.
Plan string
// UpgradeTo is the plan that lifts the limit, when the API names one.
UpgradeTo string
}
QuotaInfo describes the plan limit behind a 429 QUOTA_EXCEEDED response.
type RateLimitError ¶
type RateLimitError struct {
*APIError
RetryAfter time.Duration
Limit int
Remaining int
Reset time.Time
// Quota is set when the 429 is a plan-limit rejection rather than a rate limit.
Quota *QuotaInfo
// Scope is the limit Limit, Remaining and Reset describe:
// "organization" (the plan's request rate) or "api_key" (the key's own
// per-minute limit). Empty when the header is absent.
Scope string
}
RateLimitError represents a 429 error.
A 429 comes in two kinds. A rate limit (RATE_LIMIT_EXCEEDED, usually with a Retry-After header) clears by itself and is retried. A plan quota (QUOTA_EXCEEDED, Quota != nil) only clears when the customer frees capacity or upgrades, so it is never retried and never counts against the circuit breaker.
RetryAfter comes from the Retry-After header. Limit, Remaining and Reset come from X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds), and Scope from X-RateLimit-Scope; the API sends them on every authenticated response, so they are set on a rate-limit 429 from Spooled Cloud and stay zero only when a server or proxy leaves them out.
func (*RateLimitError) GetRetryAfter ¶
func (e *RateLimitError) GetRetryAfter() int
GetRetryAfter returns the retry-after duration in seconds.
func (*RateLimitError) IsQuotaExceeded ¶ added in v1.5.0
func (e *RateLimitError) IsQuotaExceeded() bool
IsQuotaExceeded reports whether this 429 is a plan-limit rejection (QUOTA_EXCEEDED) instead of a transient rate limit.
func (*RateLimitError) IsRetryable ¶ added in v1.5.0
func (e *RateLimitError) IsRetryable() bool
IsRetryable is false for a plan quota: waiting does not free capacity, so retrying only burns long sleeps and trips the circuit breaker for traffic (worker acks, heartbeats) that is unrelated to the over-quota enqueue.
func (*RateLimitError) Unwrap ¶
func (e *RateLimitError) Unwrap() error
Unwrap returns the underlying API error.
type Request ¶
type Request struct {
Method string
Path string
Body any
// RawBody, when set, is sent verbatim as the request body (skips JSON marshalling of Body).
// Useful for webhook ingestion endpoints that require signature verification over the exact bytes.
RawBody []byte
Query map[string]string
Headers map[string]string
UseAdminKey bool
Idempotent bool // If true, can be retried for POST
// SkipCircuitBreaker sends the request even while the breaker is open and
// keeps its outcome out of the breaker's accounting. It is for worker acks
// (complete, fail, heartbeat): they are lease-fenced and idempotent, and
// dropping a finished job's result because an unrelated call tripped the
// breaker loses work the server would happily accept.
SkipCircuitBreaker bool
}
Request represents an HTTP request to be made.
type RetryConfig ¶
type RetryConfig struct {
MaxRetries int
BaseDelay time.Duration
MaxDelay time.Duration
Factor float64
Jitter bool
}
RetryConfig configures retry behavior.
func DefaultRetryConfig ¶
func DefaultRetryConfig() RetryConfig
DefaultRetryConfig returns the default retry configuration.
type RetryPolicy ¶
type RetryPolicy struct {
MaxRetries int
BaseDelay time.Duration
MaxDelay time.Duration
Factor float64
Jitter bool
}
RetryPolicy implements exponential backoff with optional jitter.
func NewRetryPolicy ¶
func NewRetryPolicy(cfg RetryConfig) *RetryPolicy
NewRetryPolicy creates a new retry policy.
func (*RetryPolicy) Delay ¶
func (p *RetryPolicy) Delay(attempt int) time.Duration
Delay calculates the delay for a given retry attempt (0-indexed).
func (*RetryPolicy) DelayWithJitter ¶
func (p *RetryPolicy) DelayWithJitter(attempt int, jitterFactor float64) time.Duration
DelayWithJitter calculates delay with deterministic jitter for testing.
func (*RetryPolicy) ShouldRetry ¶
func (p *RetryPolicy) ShouldRetry(attempt int) bool
ShouldRetry returns true if we haven't exhausted retry attempts.
type ServerError ¶
type ServerError struct{ *APIError }
ServerError represents a 5xx error.
func (*ServerError) IsRetryable ¶
func (e *ServerError) IsRetryable() bool
IsRetryable always returns true for server errors.
func (*ServerError) Unwrap ¶
func (e *ServerError) Unwrap() error
Unwrap returns the underlying API error.
type TimeoutError ¶
TimeoutError represents a timeout error.
func NewTimeoutError ¶
func NewTimeoutError(timeout time.Duration, err error) *TimeoutError
NewTimeoutError creates a new timeout error.
func (*TimeoutError) IsRetryable ¶
func (e *TimeoutError) IsRetryable() bool
IsRetryable always returns true for timeout errors.
func (*TimeoutError) Unwrap ¶
func (e *TimeoutError) Unwrap() error
Unwrap returns the underlying API error.
type TokenRefresher ¶
type TokenRefresher struct {
// contains filtered or unexported fields
}
TokenRefresher handles automatic token refresh.
func NewTokenRefresher ¶
func NewTokenRefresher(baseURL, apiKey, refreshToken, accessToken string, logger Logger) *TokenRefresher
NewTokenRefresher creates a new token refresher.
func (*TokenRefresher) ForceRefresh ¶
func (tr *TokenRefresher) ForceRefresh(ctx context.Context) error
ForceRefresh performs a token refresh regardless of expiry status. Used when receiving a 401 response.
func (*TokenRefresher) GetAccessToken ¶
func (tr *TokenRefresher) GetAccessToken() string
GetAccessToken returns the current access token.
func (*TokenRefresher) GetRefreshToken ¶ added in v1.3.0
func (tr *TokenRefresher) GetRefreshToken() string
GetRefreshToken returns the current refresh token.
func (*TokenRefresher) NeedsRefresh ¶
func (tr *TokenRefresher) NeedsRefresh() bool
NeedsRefresh returns true if the token needs to be refreshed.
func (*TokenRefresher) Refresh ¶
func (tr *TokenRefresher) Refresh(ctx context.Context) error
Refresh performs a token refresh with single-flight coordination. Only one goroutine will actually perform the refresh; others will wait for it.
func (*TokenRefresher) RefreshIfNeeded ¶
func (tr *TokenRefresher) RefreshIfNeeded(ctx context.Context) error
RefreshIfNeeded refreshes the token if it's expired or about to expire. Uses single-flight to prevent multiple concurrent refreshes.
func (*TokenRefresher) SetAccessToken ¶
func (tr *TokenRefresher) SetAccessToken(token string, expiresIn int)
SetAccessToken updates the access token.
func (*TokenRefresher) SetRefreshToken ¶
func (tr *TokenRefresher) SetRefreshToken(token string)
SetRefreshToken updates the refresh token.
type Transport ¶
type Transport struct {
// contains filtered or unexported fields
}
Transport wraps an http.Client with retry, circuit breaker, and auth handling.
func NewTransport ¶
NewTransport creates a new Transport with the given configuration.
func (*Transport) GetRefreshToken ¶ added in v1.3.0
GetRefreshToken returns the current refresh token, if any.
func (*Transport) SetAccessToken ¶
SetAccessToken updates the access token (used for token refresh).
func (*Transport) SetRefreshToken ¶
SetRefreshToken updates the refresh token.
type ValidationError ¶
type ValidationError struct{ *APIError }
ValidationError represents a 400/422 error.
func (*ValidationError) Unwrap ¶
func (e *ValidationError) Unwrap() error
Unwrap returns the underlying API error.