Documentation
¶
Overview ¶
Package api is the fleet's HTTP API envelope: one error shape, one type and code vocabulary, one status inference, and the list/pagination shapes that go with them. It depends on nothing outside the standard library, so every library in the fleet can speak it. Framework bindings live in sub-modules (adapters/gin).
The canonical body is the shape AuthKit and OpenRails deploy today:
{"error":{"type":"...","code":"...","message":"...","param":"...","request_id":"...","metadata":{}}}
GinAPI wraps that in a top-level "object":"error" discriminator. That is a COMPATIBILITY DIFFERENCE, not the canonical form: see CompatEnvelope, and compat/DECISION-object-discriminator.md for the evidence and the open decision. Nothing here emits the discriminator on its own.
Index ¶
- Constants
- func AllowedMetadataKeys() []string
- func HasMore(offset, limit int, total int64) bool
- func HasMoreFromLen(offset, resultLen int, total int64) bool
- func MetadataRejections(m map[string]any) map[string]string
- func RegisterMetadataKey(keys ...string)
- func SanitizeMetadata(m map[string]any) map[string]any
- func WriteError(w http.ResponseWriter, err error)
- func WriteJSON(w http.ResponseWriter, status int, v any)
- type Code
- type CompatEnvelope
- type DeletedObject
- type Envelope
- type Error
- func (e *Error) Envelope() Envelope
- func (e *Error) Error() string
- func (e *Error) Is(target error) bool
- func (e *Error) Param() string
- func (e *Error) Unwrap() error
- func (e *Error) WithCause(cause error) *Error
- func (e *Error) WithMetadata(m map[string]any) *Error
- func (e *Error) WithParam(param string) *Error
- func (e *Error) WithRequestID(id string) *Error
- func (e *Error) WithType(t Type) *Error
- type ErrorObject
- type List
- type ListParams
- type Message
- type Type
Constants ¶
const ( DefaultLimit = 20 MaxLimit = 100 )
Offset pagination defaults.
Variables ¶
This section is empty.
Functions ¶
func AllowedMetadataKeys ¶
func AllowedMetadataKeys() []string
AllowedMetadataKeys lists every currently allowlisted key, sorted.
func HasMoreFromLen ¶
HasMoreFromLen reports whether items follow, using the rows actually read — correct when the last page is short.
func MetadataRejections ¶
MetadataRejections reports, per key, why SanitizeMetadata would drop it. It exists so a test can assert a leak is refused instead of hoping it was.
func RegisterMetadataKey ¶
func RegisterMetadataKey(keys ...string)
RegisterMetadataKey allowlists a metadata key for this process. A reserved name is refused: it would shadow an envelope field downstream. Call it from a package initializer, next to the codes the domain defines.
func SanitizeMetadata ¶
SanitizeMetadata returns the subset of m that may go on the wire, or nil.
func WriteError ¶
func WriteError(w http.ResponseWriter, err error)
WriteError writes err as the canonical envelope over net/http.
Types ¶
type Code ¶
type Code string
Code is the stable, machine-readable reason, and the field a client branches on. Transport-level codes are below; each library keeps its own domain catalog (AuthKit's hundreds of auth codes, OpenRails' decline codes) and emits those in the same field.
const ( CodeInvalidParam Code = "invalid_param" CodeMissingParam Code = "missing_param" CodeInvalidFormat Code = "invalid_format" CodeResourceNotFound Code = "resource_not_found" CodeResourceConflict Code = "resource_conflict" CodeAuthenticationRequired Code = "authentication_required" CodeInvalidToken Code = "invalid_token" CodeTokenExpired Code = "token_expired" CodeResourceAccessDenied Code = "resource_access_denied" CodeRateLimitExceeded Code = "rate_limit_exceeded" // CodeModerationRejected is a moderation/policy refusal of submitted // content: 422 under TypeInvalidRequest. The transport category says // "the request was not acted on"; this code says why, and is what a SPA // branches on to show the author a reason instead of a retry. CodeModerationRejected Code = "moderation_rejected" // CodeNotConfigured is an optional capability the operator never wired. CodeNotConfigured Code = "not_configured" // CodeNotImplemented is a capability this build does not have: 501 under // TypeAPI. Same transport handling as any 5xx; the code is what tells the // client to hide the feature rather than retry. CodeNotImplemented Code = "not_implemented" CodePaymentFailed Code = "payment_failed" CodeInternalError Code = "internal_error" )
func CodeForStatus ¶
CodeForStatus is the default code for a status, matching OpenRails' inferErrorTypeAndCode. Root writers do NOT apply it: an absent code means "no machine reason beyond the status", and filling one in is wire-visible — Doujins' client displays code in preference to message, so an invented code replaces a human sentence with a machine string (compat/golden/parsers.json). OpenRails calls it explicitly because its writers have always emitted a code.
type CompatEnvelope ¶
type CompatEnvelope struct {
Object string `json:"object"` // always "error"
Error ErrorObject `json:"error"`
}
CompatEnvelope is Envelope plus GinAPI's top-level discriminator. It exists so a host that must keep the discriminator can, and so both candidate bodies can be shown side by side. Nothing in api emits it by default: whether the discriminator belongs in the canonical envelope is an open owner decision (compat/DECISION-object-discriminator.md).
func WithObject ¶
func WithObject(env Envelope) CompatEnvelope
WithObject renders env in the GinAPI-compatible shape.
type DeletedObject ¶
type DeletedObject struct {
Object string `json:"object"`
ID string `json:"id"`
Deleted bool `json:"deleted"`
}
DeletedObject confirms a deletion: {"object":"gallery","id":"…","deleted":true}.
func NewDeleted ¶
func NewDeleted(objectType, id string) DeletedObject
NewDeleted builds a deletion confirmation.
type Envelope ¶
type Envelope struct {
Error ErrorObject `json:"error"`
}
Envelope is the canonical error body: {"error":{...}}.
func EnvelopeFor ¶
EnvelopeFor derives the wire status and envelope for any error. An error that is not an *Error — and any 500 — is rendered as a bare internal error, so an internal message never reaches the wire. 501, 502 and 503 state a deliberate operational condition and keep theirs.
func NewEnvelope ¶
func NewEnvelope(obj ErrorObject) Envelope
NewEnvelope builds the canonical envelope, defaulting the type and sanitizing metadata. Every path to the wire goes through it.
type Error ¶
type Error struct {
Status int
Type Type
Code Code
Message string
RequestID string
Metadata map[string]any
// contains filtered or unexported fields
}
Error is the transport carrier: a library maps its own sentinel onto one of these and a single writer renders it. It is not a catalog — libraries keep their own.
func (*Error) Is ¶
Is matches any *Error carrying the same Code, so a sentinel, a fresh E() and a wrapped copy are one identity.
func (*Error) WithMetadata ¶
WithMetadata merges public metadata. It sanitizes on the way in, so an unsafe value never reaches the struct, let alone the wire.
func (*Error) WithParam ¶
WithParam names the offending field. It takes a plain string; the pointer the wire needs is this package's problem, not the caller's.
func (*Error) WithRequestID ¶
type ErrorObject ¶
type ErrorObject struct {
Type Type `json:"type"`
Code Code `json:"code,omitempty"`
Message string `json:"message"`
Param *string `json:"param,omitempty"`
RequestID string `json:"request_id,omitempty"`
Metadata map[string]any `json:"metadata,omitempty"`
}
ErrorObject is the error detail carried under the envelope's "error" key. Param is a pointer so "absent" and "empty" stay distinguishable in Go; on the wire omitempty makes it identical to AuthKit's and OpenRails' shape. Callers never build the pointer — the constructors take a plain string.
type List ¶
type List[T any] struct { Object string `json:"object"` // always "list" Data []T `json:"data"` Total int64 `json:"total"` Limit int `json:"limit"` Offset int `json:"offset"` HasMore bool `json:"has_more"` }
List is the offset/limit list body: {"object":"list","data":[...],...}.
type ListParams ¶
ListParams is the offset/limit/sort input a list endpoint reads from the request. Framework bindings fill it; see adapters/gin.
func (*ListParams) Normalize ¶
func (p *ListParams) Normalize(defaultLimit, maxLimit int)
Normalize applies the caller's default and cap and floors the offset.
type Message ¶
type Message struct {
Object string `json:"object"` // always "message"
Message string `json:"message"`
}
Message is a bare human-readable success body.
type Type ¶
type Type string
Type is the transport-level category of a failure: what a client does next. The constants are the ones AuthKit and OpenRails already put on the wire. Type is an open string so a domain may add its own.
Adding a transport type is a wire migration for every parser in the fleet. A failure that only needs explaining gets a Code instead — that is the field a SPA branches on.
const ( // TypeInvalidRequest: fix the input, then retry. 400, and every other // unclassified 4xx — including 404, 409, 415 and 422. Both deployed // writers map them here; the reason lives in Code. TypeInvalidRequest Type = "invalid_request_error" // TypeAuthentication: authenticate or refresh, then retry. 401. TypeAuthentication Type = "authentication_error" // TypeAuthorization: never retry as this principal. 403. TypeAuthorization Type = "authorization_error" // TypeRateLimit: back off and retry later. 429. TypeRateLimit Type = "rate_limit_error" // TypeAPI: the server failed or lacks the capability. 5xx, 501 included. TypeAPI Type = "api_error" // TypeCard is OpenRails' domain extension for 402: collect a new payment // method. It is inferred only for 402, which no other fleet service uses. TypeCard Type = "card_error" )
func TypeForStatus ¶
TypeForStatus is the transport category an HTTP status determines, matching what AuthKit and OpenRails deploy today. 404, 409, 415 and 422 are deliberately TypeInvalidRequest: both writers already map them there, and splitting them out is a wire migration, not a bug fix.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package ginapi writes api envelopes, lists and objects through a gin.Context, binds list parameters from the query string, and carries the locale middleware.
|
Package ginapi writes api envelopes, lists and objects through a gin.Context, binds list parameters from the query string, and carries the locale middleware. |