client

package
v1.1.31 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 18, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const DefaultEndpoint = "https://api.threatwinds.com"

DefaultEndpoint is the default ThreatWinds API base URL.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	StatusCode int    `json:"status_code"`
	Method     string `json:"-"`
	Path       string `json:"-"`
	Message    string `json:"message"`
	// ErrorID is the upstream's x-error-id response header exactly as
	// received, unfiltered — this is a client-side type and the field means
	// "what the server said". Whether that value is fit to become *this*
	// process's occurrence id is a separate question, answered at the point of
	// donation by CatcherErrorID rather than here, so that a caller inspecting
	// the field still sees what actually came back.
	ErrorID string `json:"error_id"`
	Body    []byte `json:"-"`
	// contains filtered or unexported fields
}

APIError is returned when an API call results in a 4xx or 5xx response. It implements the error interface.

It is deliberately a plain error rather than a *catcher.SdkError: catcher.Error short-circuits when handed an *SdkError cause and returns it unchanged, discarding the wrapping call's message, args and status override. A caller that wraps this error therefore keeps its own framing, and inherits only the upstream's occurrence id, via CatcherErrorID below. See utils.RemoteError, which is the same decision for the same reason on the generic transport helper.

func (*APIError) CatcherErrorID added in v1.1.29

func (e *APIError) CatcherErrorID() string

CatcherErrorID satisfies catcher.ErrorIDCarrier structurally — the interface is one method, and satisfying it requires declaring that method, not importing the package that declares the interface (see adoptErrorID for why this package imports nothing from the SDK). A caller wrapping this error with catcher.Error or catcher.New therefore builds its own error — its message, its args, its status — that nonetheless inherits this failure's occurrence id, rather than minting a second, unrelated id for one failure. The compile-time assertion that the interface is still the shape this method answers lives in error_test.go, where linking catcher costs nothing.

Without it the id read at client.doOnce went nowhere: APIError has no Unwrap, so catcher's errors.As walk found no carrier, and the id was not in Error()'s text either (it renders status, method, path and message only). catcher minted a fresh UUID, and the two services' log lines for one failure could no longer be correlated by id — the exact failure this interface exists to close.

The guard is applied here rather than when the header is read: ErrorID keeps its meaning of "what the server sent", while only the value that becomes an occurrence id in this process is required to be a canonical UUID. A malformed one is not donated — see adoptErrorID for why a remote-controlled id cannot be taken on trust.

It falls back to the id minted for this failure when the upstream supplied none the guard would accept, so the answer to "which occurrence is this?" is never empty and never changes between two wraps of the same error. The difference between the two cases is not lost — it is exactly what ErrorID reports — but it is not the wrapping caller's problem: either way this failure has one id, and the caller's error gets that one rather than a second.

Nil-safe: errors.As matches on type alone, so a typed-nil *APIError boxed in a non-nil error would otherwise reach this method on a nil receiver. catcher also guards that case, but a method reachable through an interface should not depend on its only caller being careful.

func (*APIError) Error

func (e *APIError) Error() string

Error renders the failure. It is nil-safe, and has to be now that this error is handed to catcher as a cause: catcher.build calls Error() on whatever cause it is given to fill Cause, and errors.As — which is how catcher finds this type — matches on type alone, so a typed-nil *APIError boxed in a non-nil error interface reaches this method on a nil receiver. That is the same trap catcher guards for *SdkError (see ToSdkError) and utils.RemoteError guards for itself, and it renders the same way: "<nil>".

func (*APIError) IsForbidden

func (e *APIError) IsForbidden() bool

func (*APIError) IsNotFound

func (e *APIError) IsNotFound() bool

The status predicates are nil-safe for the same reason Error is: the usual way a caller reaches one is errors.As, which sets its target from a type match and will happily hand back a nil pointer for a typed-nil in the chain. All of them answer false for a nil receiver — "this is not that kind of failure" — rather than panicking inside an `if err.IsNotFound()`.

func (*APIError) IsRateLimited

func (e *APIError) IsRateLimited() bool

func (*APIError) IsUnauthorized

func (e *APIError) IsUnauthorized() bool

func (*APIError) IsValidationError

func (e *APIError) IsValidationError() bool

func (*APIError) RetryAfter

func (e *APIError) RetryAfter() string

RetryAfter returns the Retry-After header value stored on the error, used by the retry logic to determine backoff duration.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the root entry point for the ThreatWinds API SDK.

func New

func New(opts ...Option) (*Client, error)

New creates a configured API client. At least one authentication option (WithAPIKey or WithBearer) must be provided, but not both.

func (*Client) Auth

func (c *Client) Auth() *auth.Client

Auth returns the Auth service client (lazy-initialized).

func (*Client) Billing

func (c *Client) Billing() *billing.Client

Billing returns the Billing service client (lazy-initialized).

func (*Client) Close

func (c *Client) Close()

Close releases idle HTTP connections held by the SDK's internal transport. It is a no-op when the caller supplied their own http.Client via WithHTTPClient. Callers should invoke Close() when they are done with the client to avoid leaking TCP connections in long-running processes.

func (*Client) Compute

func (c *Client) Compute() *compute.Client

Compute returns the Compute service client (lazy-initialized).

type ListOptions

type ListOptions struct {
	Limit int // 0 = server default
	Page  int // 0 = server default
}

ListOptions holds pagination parameters for list endpoints.

type Option

type Option func(*clientConfig)

Option configures a Client via the functional options pattern.

func WithAPIKey

func WithAPIKey(keyID, secret string) Option

WithAPIKey sets API key authentication (key ID + secret).

func WithBearer

func WithBearer(token string) Option

WithBearer sets bearer token authentication.

func WithEndpoint

func WithEndpoint(url string) Option

WithEndpoint sets the API base URL.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient sets a custom HTTP client.

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets the maximum number of retry attempts.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the HTTP request timeout.

type SDKError

type SDKError struct {
	// contains filtered or unexported fields
}

SDKError is returned by New() for configuration errors (not HTTP errors).

It deliberately does not implement catcher.ErrorIDCarrier. It is raised before any request is made — a missing or conflicting authentication option — so there is no upstream, no x-error-id, and nothing to adopt. A caller wrapping it with catcher gets a freshly minted id, which is the correct outcome for a failure that originated in this process: implementing the interface to return "" would be indistinguishable in behaviour and would falsely advertise that this error type participates in cross-service propagation.

func (*SDKError) Error

func (e *SDKError) Error() string

Error is nil-safe for the same reason APIError.Error is: it may be handed to catcher as a cause, and catcher renders any cause it is given.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL