Documentation
¶
Overview ¶
Package platform builds the generated API client with what every request needs: authentication, a User-Agent, request logging, and retries that are safe to make.
Index ¶
- Constants
- Variables
- func AllPages[T any](fetch func(page, size int32) (*http.Response, error)) ([]T, error)
- func AllPagesRaw(fetch func(page, size int32) (*http.Response, error)) ([]json.RawMessage, error)
- func Check(resp *http.Response, body []byte) error
- func CurrentVersion() string
- func Decode(resp *http.Response, err error, target any) (*http.Response, error)
- func Failed(err error, format string, args ...any) error
- func IsStatus(err error, status int) bool
- func MissingTokenHelp() string
- func Read(resp *http.Response, err error) ([]byte, *http.Response, error)
- func ReadDocument(resp *http.Response, err error) (*output.Document, *http.Response, error)
- func SetLimiter(l *RateLimiter)
- func WithTimeout(ctx context.Context, d time.Duration) context.Context
- type APIError
- type Bucket
- type Client
- type Clock
- type RateLimiter
Constants ¶
const PageSize int32 = 100
PageSize is the most items a paged endpoint returns at once.
Variables ¶
var ( // RetryUnit is the base of every wait between attempts: the backoff after a transport // failure, and a 429 without a reset header. Tests shorten it. RetryUnit = time.Second // MaxRateLimitWait bounds the total time spent waiting out 429s for one request. MaxRateLimitWait = 2 * time.Minute )
var DefaultBucket = Bucket{Burst: 100, RefillTokens: 25, RefillInterval: 15 * time.Second}
var ErrNoAccessToken = errors.New("no API access token")
ErrNoAccessToken is reported with the setup help, before any request is made.
var Verbose bool
var Version = "dev"
Version is set at build time for releases, with -ldflags -X, which only works on a variable initialised to a constant. Read it through CurrentVersion.
Functions ¶
func AllPages ¶
AllPages follows nextPage until the last page: a listing cut at the first response would silently leave the rest out.
func AllPagesRaw ¶
AllPagesRaw is AllPages keeping each item as the platform sent it, for commands that print the items themselves with -t json or --jq.
func CurrentVersion ¶
func CurrentVersion() string
CurrentVersion is the release version, or for a binary built with `go install`, the module version its build info carries.
func Failed ¶
Failed reports a failed request as the TypeScript CLI did: the message, the request error, and the platform's problem body pretty-printed, which is what names the violated constraint.
func MissingTokenHelp ¶
func MissingTokenHelp() string
func Read ¶
Read takes a generated client call's result and returns the body, failing on any status outside 2xx.
func ReadDocument ¶
ReadDocument reads a response as an order-preserving document.
func SetLimiter ¶
func SetLimiter(l *RateLimiter)
SetLimiter replaces the shared limiter. Tests use it so that a suite's requests are not paced to the platform's allowance.
Types ¶
type APIError ¶
APIError carries the status and the problem body of a failed request.
func (*APIError) ProblemType ¶
ProblemType is the `type` of an RFC 7807 problem body, if there is one.
type Bucket ¶
The platform limits requests with a token bucket: a burst of 100, refilled by 25 every 15 seconds. A fan-out like `experiment dump` issues far more than that, and every rejected request would retry into the window it just exhausted, so requests are paced to the documented allowance from the first one. The ratelimit-* headers cannot be used instead: they appear only on the 429 itself, and report the burst but not the refill.
func BucketFromEnvironment ¶
func BucketFromEnvironment() Bucket
BucketFromEnvironment reads the STEADYBIT_RATE_LIMIT_* overrides. An invalid value is warned about rather than ignored quietly, since it would change how hard the CLI hits the platform.
type Client ¶
type Client struct {
*api.ClientWithResponses
BaseURL string
// contains filtered or unexported fields
}
func (*Client) Get ¶
Get fetches a path the spec has no operation for, such as the Location of a run.
func (*Client) GetAnonymously ¶ added in v6.1.0
GetAnonymously fetches a path without the access token, as a README showing a badge does: with the token, the platform takes the tenant from it and ignores a wrong one in the URL.
type Clock ¶
type Clock interface {
Now() time.Time
// Sleep waits for d, or until ctx ends, and then returns ctx's error.
Sleep(ctx context.Context, d time.Duration) error
}
Clock is injected so tests can drive the bucket deterministically.
type RateLimiter ¶
type RateLimiter struct {
// contains filtered or unexported fields
}
func Limiter ¶
func Limiter() *RateLimiter
Limiter is built on first use, so that a command that sends nothing never reads, or complains about, the environment.
func NewRateLimiter ¶
func NewRateLimiter(bucket Bucket, clock Clock) *RateLimiter
func (*RateLimiter) DurationFor ¶
func (r *RateLimiter) DurationFor(count int) time.Duration
DurationFor is how long `count` requests take once the burst is spent, which makes the scale of a large dump visible before it starts rather than an hour into it.
func (*RateLimiter) Wait ¶
func (r *RateLimiter) Wait(ctx context.Context) error
Wait blocks until a request may be sent, or until ctx ends. Each caller takes the next token under the lock, even one not refilled yet, and then sleeps until it is due, so concurrent callers cannot spend the same token and one that gives up hands it back.