140 lines
3.6 KiB
Go
140 lines
3.6 KiB
Go
package jackbox
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
// Sentinel errors for common failure scenarios
|
|
var (
|
|
// ErrNotAuthenticated indicates the client is not authenticated
|
|
ErrNotAuthenticated = errors.New("not authenticated")
|
|
|
|
// ErrAuthFailed indicates authentication failed
|
|
ErrAuthFailed = errors.New("authentication failed")
|
|
|
|
// ErrConnectionLost indicates the WebSocket connection was lost
|
|
ErrConnectionLost = errors.New("connection lost")
|
|
|
|
// ErrTokenExpired indicates the authentication token has expired
|
|
ErrTokenExpired = errors.New("token expired")
|
|
|
|
// ErrInvalidResponse indicates an unexpected response from the API
|
|
ErrInvalidResponse = errors.New("invalid response from API")
|
|
|
|
// ErrSessionNotFound indicates the specified session does not exist
|
|
ErrSessionNotFound = errors.New("session not found")
|
|
|
|
// ErrNotSubscribed indicates not subscribed to any session
|
|
ErrNotSubscribed = errors.New("not subscribed to any session")
|
|
)
|
|
|
|
// APIError represents an API-related error with context
|
|
type APIError struct {
|
|
Op string // Operation that failed (e.g., "vote", "get_session", "authenticate")
|
|
StatusCode int // HTTP status code
|
|
Message string // Error message from API
|
|
Err error // Underlying error
|
|
}
|
|
|
|
func (e *APIError) Error() string {
|
|
if e.StatusCode > 0 {
|
|
return fmt.Sprintf("API error during %s (HTTP %d): %s", e.Op, e.StatusCode, e.Message)
|
|
}
|
|
if e.Err != nil {
|
|
return fmt.Sprintf("API error during %s: %s (%v)", e.Op, e.Message, e.Err)
|
|
}
|
|
return fmt.Sprintf("API error during %s: %s", e.Op, e.Message)
|
|
}
|
|
|
|
func (e *APIError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// WebSocketError represents a WebSocket-related error with context
|
|
type WebSocketError struct {
|
|
Op string // Operation that failed (e.g., "connect", "subscribe", "send")
|
|
Err error // Underlying error
|
|
}
|
|
|
|
func (e *WebSocketError) Error() string {
|
|
return fmt.Sprintf("WebSocket error during %s: %v", e.Op, e.Err)
|
|
}
|
|
|
|
func (e *WebSocketError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// SessionError represents a session-related error with context
|
|
type SessionError struct {
|
|
Op string // Operation that failed (e.g., "subscribe", "get_active")
|
|
SessionID int // Session ID
|
|
Err error // Underlying error
|
|
}
|
|
|
|
func (e *SessionError) Error() string {
|
|
return fmt.Sprintf("session error during %s for session %d: %v", e.Op, e.SessionID, e.Err)
|
|
}
|
|
|
|
func (e *SessionError) Unwrap() error {
|
|
return e.Err
|
|
}
|
|
|
|
// IsRetryable returns true if the error is transient and the operation should be retried
|
|
func IsRetryable(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
// Check for known retryable errors
|
|
if errors.Is(err, ErrConnectionLost) ||
|
|
errors.Is(err, ErrTokenExpired) {
|
|
return true
|
|
}
|
|
|
|
// Check for API errors with retryable status codes
|
|
var apiErr *APIError
|
|
if errors.As(err, &apiErr) {
|
|
// 5xx errors are typically retryable
|
|
if apiErr.StatusCode >= 500 && apiErr.StatusCode < 600 {
|
|
return true
|
|
}
|
|
// 429 Too Many Requests is retryable
|
|
if apiErr.StatusCode == 429 {
|
|
return true
|
|
}
|
|
}
|
|
|
|
// WebSocket errors are generally retryable
|
|
var wsErr *WebSocketError
|
|
if errors.As(err, &wsErr) {
|
|
return true
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// IsFatal returns true if the error is fatal and reconnection should not be attempted
|
|
func IsFatal(err error) bool {
|
|
if err == nil {
|
|
return false
|
|
}
|
|
|
|
// Check for known fatal errors
|
|
if errors.Is(err, ErrAuthFailed) ||
|
|
errors.Is(err, ErrSessionNotFound) {
|
|
return true
|
|
}
|
|
|
|
// Check for API errors with fatal status codes
|
|
var apiErr *APIError
|
|
if errors.As(err, &apiErr) {
|
|
// 4xx errors (except 429) are typically fatal
|
|
if apiErr.StatusCode >= 400 && apiErr.StatusCode < 500 && apiErr.StatusCode != 429 {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|