jsonapi

package
v0.14.0 Latest Latest
Warning

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

Go to latest
Published: Sep 7, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Overview

Package jsonapi is the one transport every Truestamp JSON:API client shares: the authenticated request carrying the tenant header, the response size cap, and the classification of the error envelope into a small set of sentinels a command can errors.Is. The resource packages (internal/beacons, internal/blocks, internal/teams, internal/items, internal/proof) own only their routes and their decoding; each used to carry its own copy of this file, and the copies had started to differ in which statuses mapped to which class.

Index

Constants

View Source
const CodeInvalidKeyset = "invalid_keyset"

CodeInvalidKeyset is the server's code for a page[after] / page[before] value that is not a cursor it issued.

Variables

View Source
var (
	ErrUnauthorized = errors.New("not authenticated")
	ErrForbidden    = errors.New("forbidden")
	ErrNotFound     = errors.New("not found")
	ErrBadRequest   = errors.New("bad request")
	ErrRateLimited  = errors.New("rate limited")
	ErrServer       = errors.New("server error")
)

Class sentinels. An *APIError wraps exactly one of them, so a caller can errors.Is the class while still showing the server's detail text.

Functions

func Do

func Do(ctx context.Context, cfg Config, method, path string, body []byte) ([]byte, error)

Do issues an authenticated request and returns the body on 2xx. Any other status is an *APIError wrapping its class sentinel, with the Retry-After header preserved on 429.

func DoRaw

func DoRaw(ctx context.Context, cfg Config, method, path string, body []byte) (*http.Response, []byte, error)

DoRaw is Do without the status classification: the response (its body already read, capped and closed) comes back for a client with its own error envelope to parse, which proof generation's `meta.code` needs. A missing credential is still an *APIError, because no request is sent.

func Get

func Get(ctx context.Context, cfg Config, path string) ([]byte, error)

Get issues an authenticated GET and returns the body on 2xx.

func Post

func Post(ctx context.Context, cfg Config, path string, payload any) ([]byte, error)

Post marshals payload as the request body and POSTs it.

func SetPageQuery

func SetPageQuery(q url.Values, limit int, after, before string, count bool)

SetPageQuery writes the page parameters every keyset-paged list shares: page[limit] for the page size, page[after] or page[before] to continue from a cursor in either direction, and page[count]=true when the caller wants the server's total. Sort is the caller's, and must be re-sent on every page: this client rebuilds the query rather than following the server's links, so nothing carries over.

func SortByID

func SortByID(oldestFirst bool) string

SortByID is the one ordering every list uses: by id, which for the ULIDs and UUIDv7s these resources carry is insertion time. Newest first unless the caller asked to start from the beginning.

Types

type APIError

type APIError struct {
	Status     int
	Code       string // errors[].code, the server's machine-readable reason, when present
	Pointer    string // errors[].source.pointer, when present
	Detail     string
	RetryAfter string // verbatim Retry-After header on 429
	// Sentinel is the class this error belongs to; Unwrap returns it. A
	// resource package may narrow it to one of its own domain sentinels
	// once it has read the structural discriminators, as teams does for
	// the plan-limit and entitlement rejections on create.
	Sentinel error
}

APIError carries the HTTP status and the preserved `errors[].detail` (falling back to `title`) from the JSON:API error envelope.

func NotFound

func NotFound(detail string) *APIError

NotFound is the error a client returns when a filter-style lookup came back empty and there was no 404 to classify.

func ParseError

func ParseError(status int, body []byte) *APIError

ParseError classifies a non-2xx response. It keeps `errors[].detail` (or `title`) and the `source.pointer` of the first error that carries one: the server can return several errors at once (a free-plan user requesting team_retains trips both the plan-limit and the entitlement rejection), and the pointer is the structural discriminator, so it wins over array position.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) Unwrap

func (e *APIError) Unwrap() error

type Config

type Config struct {
	APIURL string // e.g. https://www.truestamp.com/api/json
	Team   string // optional tenant id; sent verbatim as the `tenant` header
}

Config carries what a request needs beyond the credential, which the process-wide auth.Authorizer installed in cmd/root supplies out of band.

type PageInfo

type PageInfo struct {
	// NextCursor is the page[after] value lifted from links.next; empty on
	// the last page. An unparseable link also reads as empty, which is the
	// safe reading: a bad cursor would otherwise loop.
	NextCursor string
	// PrevCursor is the page[before] value lifted from links.prev; empty on
	// the first page.
	PrevCursor string
	// Total is meta.page.total, present only when the request asked for
	// page[count]=true; zero otherwise.
	Total int
	// Limit is meta.page.limit, the page size the server actually used.
	// Every collection clamps page[limit] to its max_page_size (250 by
	// default) rather than refusing it, so this can be smaller than what
	// was asked for; zero when the server did not report one.
	Limit int
}

PageInfo is what a list response says about the pages around it.

func ParsePage

func ParsePage(body []byte) PageInfo

ParsePage reads links.next, links.prev and meta.page.total from a list body. It tolerates any of them being absent.

Jump to

Keyboard shortcuts

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