Documentation
¶
Overview ¶
Package api wraps Huma with Soro routing, errors, resources, and middleware.
Index ¶
- Constants
- Variables
- func HTTPError(ctx context.Context, err error) error
- func Register[I, O any](router *Router, operation huma.Operation, ...) error
- func RegisterScoped[I, O any](router *ScopedRouter, operation huma.Operation, ...) error
- func RequestID(ctx context.Context) string
- func WithAudience(policy AudiencePolicy) func(*huma.Operation)
- type API
- func (api *API) Config() Config
- func (api *API) Handler() http.Handler
- func (api *API) Huma() huma.API
- func (api *API) Mux() *http.ServeMux
- func (api *API) OpenAPI() *huma.OpenAPI
- func (api *API) Routes() []Route
- func (api *API) Scope(version, owner, path string) (*ScopedRouter, error)
- func (api *API) Version(name string, configure func(*Router)) error
- type Action
- type Audience
- type AudienceAuthorizer
- type AudiencePolicy
- type CollectionEnvelope
- type CollectionOutput
- type Config
- type ErrorBody
- type ErrorEnvelope
- type Meta
- type Middleware
- type Option
- func WithAudienceAuthorizer(authorizer AudienceAuthorizer) Option
- func WithLogger(logger *slog.Logger) Option
- func WithMiddleware(middleware ...Middleware) Option
- func WithPreTransactionMiddleware(middleware ...Middleware) Option
- func WithRequestIDGenerator(generator func() string) Option
- func WithRequestTransaction(db *database.DB, initialize RequestTransactionInitializer) Option
- type PaginationMeta
- type Registrar
- type RequestTransactionInfo
- type RequestTransactionInitializer
- type Resource
- type ResourceConfig
- type ResourceHook
- type Route
- type Router
- type ScopedRouter
- type StatusError
- type SuccessEnvelope
- type SuccessOutput
Constants ¶
const ( AudienceHeader = "X-Soro-API-Audience" AudienceExtension = "x-soro-audience" AudienceScopesExtension = "x-soro-required-scopes" AudienceClientAudienceExtension = "x-soro-client-audience" )
const RequestIDHeader = "X-Request-ID"
Variables ¶
var ErrFrozen = errors.New("scoped API router is frozen")
Functions ¶
func RegisterScoped ¶
func RegisterScoped[I, O any](router *ScopedRouter, operation huma.Operation, handler func(context.Context, *I) (*O, error)) error
RegisterScoped registers an operation through an engine-owned API boundary. Scoped operations use paths relative to the scope.
func WithAudience ¶
func WithAudience(policy AudiencePolicy) func(*huma.Operation)
WithAudience marks a Huma operation. Soro validates and enforces the policy when the operation is registered.
Types ¶
type API ¶
type API struct {
// contains filtered or unexported fields
}
type Audience ¶
type Audience string
Audience describes who may build a client for an operation. It is additive to normal authentication, authorization, tenant isolation, and auditing.
type AudienceAuthorizer ¶
type AudienceAuthorizer interface {
RequireScopes(context.Context, Audience, []string) error
AuthenticateClient(context.Context, *http.Request, string) (context.Context, error)
}
AudienceAuthorizer bridges Soro's audience policy to application-owned principal scopes and software-client credentials. AuthenticateClient may return a derived context containing the authenticated client identity.
type AudiencePolicy ¶
AudiencePolicy is the complete audience requirement for one operation. ClientAudience is required only for first-party operations.
func FirstParty ¶
func FirstParty(clientAudience string, requiredScope string, additionalScopes ...string) AudiencePolicy
FirstParty requires both normal scoped principal authorization and an independent software-client credential for clientAudience.
func SecondParty ¶
func SecondParty(requiredScope string, additionalScopes ...string) AudiencePolicy
SecondParty requires one or more elevated scopes granted to vetted clients.
func ThirdParty ¶
func ThirdParty(requiredScopes ...string) AudiencePolicy
ThirdParty describes the public developer surface. An empty scope list is explicit public/anonymous access; otherwise normal scope authorization runs.
func (AudiencePolicy) Validate ¶
func (policy AudiencePolicy) Validate() error
type CollectionEnvelope ¶
type CollectionEnvelope[T any] struct { Data []T `json:"data"` Meta PaginationMeta `json:"meta"` }
CollectionEnvelope is the standard body for page-based collection results.
func NewCollectionEnvelope ¶
func NewCollectionEnvelope[T any](ctx context.Context, data []T, page, perPage, total, pages int) CollectionEnvelope[T]
NewCollectionEnvelope creates the canonical collection body for custom operations that use page pagination.
type CollectionOutput ¶
type CollectionOutput[T any] struct { Body CollectionEnvelope[T] }
CollectionOutput can be returned directly from a custom typed Huma operation.
func NewCollectionOutput ¶
func NewCollectionOutput[T any](ctx context.Context, data []T, page, perPage, total, pages int) *CollectionOutput[T]
NewCollectionOutput creates a custom-operation collection output using Soro's canonical request ID from ctx.
type Config ¶
type Config struct {
Title string
Version string
BasePath string
OpenAPIPath string
DocsPath string
SchemasPath string
MaxBodyBytes int64
RequestTransactionTimeout time.Duration
MaxConcurrentRequestTransactions int
}
func DefaultConfig ¶
func DefaultConfig() Config
type ErrorEnvelope ¶
type ErrorEnvelope struct {
Error ErrorBody `json:"error"`
}
type Meta ¶
type Meta struct {
RequestID string `json:"request_id"`
}
Meta is included in every successful JSON response. RequestID is generated by Soro and matches the X-Request-ID response header.
type Option ¶
type Option func(*settings)
func WithAudienceAuthorizer ¶
func WithAudienceAuthorizer(authorizer AudienceAuthorizer) Option
func WithLogger ¶
func WithMiddleware ¶
func WithMiddleware(middleware ...Middleware) Option
func WithPreTransactionMiddleware ¶
func WithPreTransactionMiddleware(middleware ...Middleware) Option
WithPreTransactionMiddleware installs middleware outside the request transaction. Use it for credential authentication that must establish the principal consumed by a request transaction initializer.
func WithRequestIDGenerator ¶
func WithRequestTransaction ¶
func WithRequestTransaction(db *database.DB, initialize RequestTransactionInitializer) Option
WithRequestTransaction wraps every API request in one database transaction and runs initialize immediately after it begins. Responses are withheld until commit succeeds. Without this option, requests retain the existing non-transactional behavior.
type PaginationMeta ¶
type PaginationMeta struct {
RequestID string `json:"request_id"`
Page int `json:"page"`
PerPage int `json:"per_page"`
Total int `json:"total"`
Pages int `json:"pages"`
}
PaginationMeta describes page-based collection results.
type RequestTransactionInfo ¶
type RequestTransactionInfo struct {
Request *http.Request
OperationID string
Owner string
Audience AudiencePolicy
PathValues map[string]string
}
RequestTransactionInfo describes the matched operation before its handler or audience middleware runs. Health, metrics, docs, and unmatched paths are not request-transactional and never reach the initializer.
type RequestTransactionInitializer ¶
type RequestTransactionInitializer func(context.Context, RequestTransactionInfo, database.LocalSettings) (context.Context, error)
RequestTransactionInitializer resolves trusted application context and transaction-local PostgreSQL settings before any API operation runs.
type Resource ¶
type Resource[T, C, U, R any] struct { // contains filtered or unexported fields }
Resource implements the five conventional REST operations for one model.
func NewResource ¶
func NewResource[T, C, U, R any](config ResourceConfig[T, C, U, R]) (*Resource[T, C, U, R], error)
type ResourceConfig ¶
type ResourceConfig[T, C, U, R any] struct { Name string Repository *repository.Repository[T] Serializer serializer.Serializer[T, R] CreateEntity func(context.Context, C) (*T, error) UpdateEntity func(context.Context, *T, U) error Query query.Definition Disabled []Action Authorize ResourceHook[T] // AuthorizeWrite validates the final mapped entity before create or update. // Engine resources with either action enabled must provide it unless they // explicitly opt into global access with AllowUnscoped. AuthorizeWrite ResourceHook[T] Before ResourceHook[T] After ResourceHook[T] Scope func(context.Context, *bun.SelectQuery) *bun.SelectQuery // AllowUnscoped explicitly marks a resource as global. Engine resources // must set this or provide Scope or Authorize. AllowUnscoped bool Audience AudiencePolicy Audiences map[Action]AudiencePolicy ModifyOperation map[Action]func(*huma.Operation) }
ResourceConfig describes a typed REST resource. CreateEntity and UpdateEntity are deliberately explicit so API input can never be reflectively assigned to a persistence model.
type ScopedRouter ¶
type ScopedRouter struct {
// contains filtered or unexported fields
}
ScopedRouter is the engine-facing API registration boundary. It does not expose the underlying mux or Huma API.
func (*ScopedRouter) Freeze ¶
func (router *ScopedRouter) Freeze()
func (*ScopedRouter) Frozen ¶
func (router *ScopedRouter) Frozen() bool
func (*ScopedRouter) Owner ¶
func (router *ScopedRouter) Owner() string
func (*ScopedRouter) Prefix ¶
func (router *ScopedRouter) Prefix() string
type StatusError ¶
type StatusError struct {
ErrorEnvelope
// contains filtered or unexported fields
}
func (*StatusError) ContentType ¶
func (response *StatusError) ContentType(contentType string) string
func (*StatusError) Error ¶
func (response *StatusError) Error() string
func (*StatusError) GetStatus ¶
func (response *StatusError) GetStatus() int
type SuccessEnvelope ¶
SuccessEnvelope is the standard body for one successful result.
type SuccessOutput ¶
type SuccessOutput[T any] struct { Body SuccessEnvelope[T] }
SuccessOutput can be returned directly from a custom typed Huma operation.
func NewSuccessOutput ¶
func NewSuccessOutput[T any](ctx context.Context, data T) *SuccessOutput[T]
NewSuccessOutput creates a custom-operation output using Soro's canonical request ID from ctx.