Documentation
¶
Overview ¶
Package taiga is an HTTP client for the Taiga REST API v1.
Index ¶
- func NotSent(err error) bool
- func RedactSecrets(s string) string
- func RedactTokens(s string) string
- func ToOutput(err error) *output.Error
- type APIError
- type Client
- func (c *Client) BaseURL() string
- func (c *Client) Do(ctx context.Context, r Request) (*Response, error)
- func (c *Client) Download(ctx context.Context, rawURL string, w io.Writer) (int64, error)
- func (c *Client) GetAll(ctx context.Context, path string, q url.Values) ([]json.RawMessage, error)
- func (c *Client) PrepareVersioned(ctx context.Context, path string, patch map[string]any) (map[string]any, map[string]json.RawMessage, error)
- func (c *Client) Upload(ctx context.Context, path string, fields map[string]string, ...) (*Response, error)
- func (c *Client) WriteVersioned(ctx context.Context, method, path string, patch map[string]any, force bool) (*Response, error)
- func (c *Client) WriteVersionedFrom(ctx context.Context, method, path string, patch map[string]any, ...) (*Response, error)
- type ConflictError
- type LocalWriteError
- type NetworkError
- type Option
- type RefusedWriteError
- type Request
- type Response
- type StaticToken
- type Token
- type TokenSource
- type UnreadableBodyError
- type UntrustedURLError
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NotSent ¶ added in v0.3.0
NotSent is a network error raised before the request could leave: the connection was never opened (DNS failure, refused or unreachable). Nothing reached Taiga.
func RedactSecrets ¶ added in v0.3.0
RedactSecrets hides the query of every URL in s, and any token value left outside a URL: signed URLs carry their credential there.
func RedactTokens ¶ added in v0.3.0
RedactTokens hides the value of every token parameter in s, keeping the rest of the text: read output keeps the URLs users wrote, but never a signed link's credential.
Every '=' is examined on its own, so a token inside the value of another parameter (link=https://h/a?token=…, credential=token=…) is found too. The key is the run of key characters right before the '=', compared after undoing HTML entities and percent escapes (%74oken, token, token), in any case, and anything ending in "token" counts (access_token). The value runs to the next delimiter. Then every decoding of the result is checked again: if a key ending in "token" still has a value there, the whole text is replaced. So is a text whose decoding does not settle within maxDecodings layers.
The cost is linear in len(s) times the layers: keys are disjoint runs, and the end of a value is only looked for after a token key, whose value is then skipped.
Example ¶
fmt.Println(RedactTokens("see https://h/a?x=1&token=abc&y=2 and credential=t%6Fken=abc"))
Output: see https://h/a?x=1&token=…&y=2 and credential=t%6Fken=…
Types ¶
type APIError ¶
APIError is a non-2xx response from Taiga.
func (*APIError) IsVersionConflict ¶
IsVersionConflict reports Taiga's optimistic-concurrency rejection: 409 or 412 (precondition failed), or 400 with a "version" key, which is what Taiga 6.7 answers in practice.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
func New ¶
func New(baseURL string, ts TokenSource, opts ...Option) *Client
New builds a client for baseURL (scheme://host, already normalized).
func (*Client) Do ¶
Do sends one API request. GET is retried up to twice on network errors and 5xx. A write whose 2xx answer cannot be read fails with *UnreadableBodyError, never with a network error.
func (*Client) Download ¶ added in v0.3.0
Download streams GET rawURL into w and returns the bytes written. rawURL must have the client's scheme, host and port: it comes from the server and carries a signed token, so it is never followed elsewhere. No Authorization header is sent (the token in the URL is the credential), the fragment is dropped and redirects are not followed. Network errors and 5xx are retried only while nothing has been written to w.
func (*Client) GetAll ¶
GetAll returns every item of a list endpoint, asking Taiga to disable pagination and, if it paginates anyway, following the page number announced in X-Pagination-Next.
func (*Client) PrepareVersioned ¶
func (c *Client) PrepareVersioned(ctx context.Context, path string, patch map[string]any) (map[string]any, map[string]json.RawMessage, error)
PrepareVersioned reads the resource and returns the body that would be sent (for --dry-run).
func (*Client) Upload ¶ added in v0.3.0
func (c *Client) Upload(ctx context.Context, path string, fields map[string]string, fileField, fileName string, r io.Reader, size int64) (*Response, error)
Upload sends one multipart POST with fields and the file read from r, which must yield exactly size bytes. Never retried: the file may be stored even when the answer is lost. The body is streamed with a known Content-Length (Django reads the body by it, so a chunked upload could arrive empty). A 2xx whose body cannot be read is *UnreadableBodyError.
func (*Client) WriteVersioned ¶
func (c *Client) WriteVersioned(ctx context.Context, method, path string, patch map[string]any, force bool) (*Response, error)
WriteVersioned applies patch with optimistic concurrency and one guarded retry.
func (*Client) WriteVersionedFrom ¶ added in v0.2.0
func (c *Client) WriteVersionedFrom(ctx context.Context, method, path string, patch map[string]any, first map[string]json.RawMessage, force bool) (*Response, error)
WriteVersionedFrom writes patch with the version of first, the snapshot the caller used to compute it. On a version conflict it re-reads once and retries only when none of the patched fields changed since first (or force is set), so a merge is never applied over newer data.
type ConflictError ¶
ConflictError means another writer changed the fields we are updating.
func (*ConflictError) Error ¶
func (e *ConflictError) Error() string
type LocalWriteError ¶ added in v0.3.0
type LocalWriteError struct{ Err error }
LocalWriteError is a download whose destination could not be written. Nothing is retried.
func (*LocalWriteError) Error ¶ added in v0.3.0
func (e *LocalWriteError) Error() string
func (*LocalWriteError) Unwrap ¶ added in v0.3.0
func (e *LocalWriteError) Unwrap() error
type NetworkError ¶
NetworkError wraps transport failures.
func (*NetworkError) Error ¶
func (e *NetworkError) Error() string
func (*NetworkError) Unwrap ¶
func (e *NetworkError) Unwrap() error
type RefusedWriteError ¶ added in v0.3.0
type RefusedWriteError struct{ Err error }
RefusedWriteError is a read that failed after Taiga refused the write (a version conflict): nothing was written, so the read's error is the outcome, never an uncertain write.
func (*RefusedWriteError) Error ¶ added in v0.3.0
func (e *RefusedWriteError) Error() string
func (*RefusedWriteError) Unwrap ¶ added in v0.3.0
func (e *RefusedWriteError) Unwrap() error
type UnreadableBodyError ¶ added in v0.2.0
UnreadableBodyError is a write (POST, PATCH, PUT) that Taiga confirmed with a 2xx status whose body could not be read: the change is applied, so it must never look like a network error that is safe to repeat. The client never retries it.
func (*UnreadableBodyError) Error ¶ added in v0.2.0
func (e *UnreadableBodyError) Error() string
func (*UnreadableBodyError) Unwrap ¶ added in v0.2.0
func (e *UnreadableBodyError) Unwrap() error
type UntrustedURLError ¶ added in v0.3.0
type UntrustedURLError struct {
URL, Base string
}
UntrustedURLError is a download URL from the server whose origin is not the client's. Its text never carries the query, which holds the signed token.
func (*UntrustedURLError) Error ¶ added in v0.3.0
func (e *UntrustedURLError) Error() string