Documentation
¶
Overview ¶
Package api is `brevis-sql-api`: declared, read-only SQL served over HTTP.
THE FILE IS THE CONTRACT, as it is for the gateway: every endpoint, its parameters and its SQL, refused at load when any of it cannot be honoured. A server that starts on a file it half understood answers a readiness probe and then a caller's first request with something nobody declared.
Index ¶
- Constants
- func Describe(path string, cfg *Config, reg *connections.Registry) ([]byte, error)
- func KeyLine(key, tenant string) (string, error)
- func Refuse(msg string) error
- type After
- type Auth
- type Before
- type ByteSize
- type CacheConfig
- type Config
- type Endpoint
- type HookNames
- type Hooks
- type Limits
- type Listen
- type Manifest
- type ManifestCache
- type ManifestEndpoint
- type ManifestLimits
- type ManifestParam
- type Param
- type Request
- type Result
- type Server
Constants ¶
const ( // EnvLocal is the value that leaves the API open. The engine's word. EnvLocal = "local" // LocalTenant is the tenant every request is, locally and without auth. LocalTenant = "local" )
const DefaultMaxRows = 10000
DefaultMaxRows is the row ceiling when the file names none.
const ManifestVersion = 1
ManifestVersion is the shape `describe` prints. An engine that reads another refuses it by number.
Variables ¶
This section is empty.
Functions ¶
func Describe ¶
Describe prints the manifest of a loaded file. It OPENS NOTHING: the targets come from the registry's declarations, which is why it runs where the API's credentials do not -- in a publish Job, beside the gateway's.
Types ¶
type After ¶ added in v0.6.0
After returns what becomes the answer's `data`, marshalled as JSON.
IT MUST BE A FUNCTION OF ITS RESULT. Its output is what the cache keeps, so a hook that reads the clock or the network answers every later caller with what was true for the first one.
type Auth ¶
type Auth struct {
// Type is `bearer`.
Type string `yaml:"type"`
// KeysFrom names the ENVIRONMENT VARIABLE holding the key list -- one
// `<sha256 of the key, hex>=<tenant>` per line. The file is in git, and
// the variable holds no key either: a hash is what lets an environment
// dump, a CI log or a `kubectl describe` show this variable without
// handing anybody a way in.
KeysFrom string `yaml:"keys_from"`
}
Auth is who may read, and as which tenant.
THE GATEWAY'S RULE, AND ONE MORE. BREVIS_ENV=local leaves the API open and outside it the process refuses to start without a key list -- spelled the way the gateway and the engine spell it, so an operator learns one word. The addition is the TENANT: every key belongs to one, and that tenant is bound into every query as `{{ tenant }}`. A caller never says which tenant it is, so it can never say a different one.
type Before ¶ added in v0.6.0
Before runs before the parameters are read. Nil goes on; Refuse(msg) answers 400 with msg; any other error answers 500, its text kept for the audit line and never sent.
type ByteSize ¶
type ByteSize int64
ByteSize is a number of bytes, written `1GiB`, `512MiB` or a bare number.
type CacheConfig ¶
type CacheConfig struct {
Type string `yaml:"type"`
AddrFrom string `yaml:"addr_from"`
TTL time.Duration `yaml:"ttl"`
MaxEntry ByteSize `yaml:"max_entry"`
}
CacheConfig is where answers are kept, and for how long at most.
type Config ¶
type Config struct {
Name string `yaml:"name"`
Connection string `yaml:"connection"`
// ConnectionID is where the connection reaches, from the registry: part
// of every cache key, so two deployments' answers never meet.
ConnectionID string `yaml:"-"`
// Timezone is the IANA zone `period` resolves in. UTC when absent.
Timezone string `yaml:"timezone"`
Listen Listen `yaml:"listen"`
Endpoints []*Endpoint `yaml:"endpoints"`
// Params are definitions an endpoint's parameter can name with `ref`.
Params map[string]Param `yaml:"params"`
// Limits bound every request; see Limits.
Limits Limits `yaml:"limits"`
// Cache keeps answers until a model they read is rebuilt. Absent, every
// request reads the warehouse.
Cache *CacheConfig `yaml:"cache"`
// CacheKV is where the cache is kept; nil opens Cache.AddrFrom.
CacheKV cache.KV `yaml:"-"`
// Dialect and Open are the connection's, resolved from the registry at
// load: a file naming a connection nobody declared is refused there,
// not at the first request.
Dialect dialect.Dialect `yaml:"-"`
Open func(context.Context) (dialect.Conn, error) `yaml:"-"`
// MaxRows is the file-wide row ceiling.
MaxRows int `yaml:"-"`
// Metrics is where the Prometheus exposition listens.
Metrics struct {
Addr string `yaml:"addr"`
} `yaml:"metrics"`
// Audit receives one JSON line per request. Nil writes none.
Audit io.Writer `yaml:"-"`
// Env is BREVIS_ENV, read by the binary and never from the file: a file
// that could declare itself local would be a file that turns off
// authentication.
Env string `yaml:"-"`
}
Config is one endpoint file, loaded and checked.
type Endpoint ¶
type Endpoint struct {
Name string `yaml:"name"`
Path string `yaml:"path"`
Params map[string]Param `yaml:"params"`
SQL string `yaml:"sql"`
SQLFile string `yaml:"sql_file"`
// Limits are this endpoint's, each at most the file's.
Limits *Limits `yaml:"limits"`
// Tenant is `none` for an endpoint whose data is the same for every
// tenant. Absent, the SQL must use `{{ tenant }}`.
Tenant string `yaml:"tenant"`
// Hook names this endpoint's before and after, registered in the binary.
Hook *HookNames `yaml:"hook"`
// MaxRows is this endpoint's ceiling; zero takes the file's.
MaxRows int `yaml:"-"`
Template *render.Template `yaml:"-"`
// contains filtered or unexported fields
}
Endpoint is one declared GET path.
type HookNames ¶ added in v0.6.0
HookNames is what an endpoint's `hook:` says: a name in this binary's registry for each, both optional.
type Hooks ¶ added in v0.6.0
type Hooks struct {
// contains filtered or unexported fields
}
Hooks is what this binary knows how to run.
COMPILED IN AND NAMED BY THE FILE, as the gateway's are, and for its measurements: a Go function is 93 ns/op against Starlark's 959, nothing is added to the module, and `plugin.Open` returns "not implemented" under the CGO_ENABLED=0 every image here is built with. The cost, said where it is paid: a new hook is a rebuild and a deploy, not a config change.
TWO REGISTRIES, because a before and an after take different arguments: a name found in the wrong one is a hook that cannot be called.
func (*Hooks) MustRegisterAfter ¶ added in v0.6.0
MustRegisterAfter is RegisterAfter for a main with nowhere to return an error.
func (*Hooks) MustRegisterBefore ¶ added in v0.6.0
MustRegisterBefore is RegisterBefore for a main with nowhere to return an error.
func (*Hooks) RegisterAfter ¶ added in v0.6.0
RegisterAfter adds one, on RegisterBefore's terms.
type Limits ¶
type Limits struct {
Timeout time.Duration `yaml:"timeout"`
MaxRows int `yaml:"max_rows"`
MaxBytesBilled ByteSize `yaml:"max_bytes_billed"`
// MaxResponseBytes bounds one answer's JSON. max_rows bounds how many
// rows; this bounds how wide they are, which nothing did.
MaxResponseBytes ByteSize `yaml:"max_response_bytes"`
// MaxInFlight is how many requests the whole API may have waiting on
// the warehouse at once -- each holds a connection. File-wide only.
MaxInFlight int `yaml:"max_in_flight"`
}
Limits bound one request: how long, how many rows, how many bytes billed.
FILE-WIDE, AND AN ENDPOINT MAY ONLY LOWER THEM. The file's numbers are what whoever deploys the API agreed to; an endpoint that could raise its own would make the file's a suggestion.
type Manifest ¶
type Manifest struct {
Version int `json:"version"`
Kind string `json:"kind"`
Name string `json:"name"`
Connection string `json:"connection"`
Dialect string `json:"dialect"`
Cache *ManifestCache `json:"cache,omitempty"`
Endpoints []ManifestEndpoint `json:"endpoints"`
Notes map[string]string `json:"notes,omitempty"`
}
Manifest is an API file as it runs, for the console: what each endpoint takes and reads, never how it reads it.
NO SQL TEXT. It is in the customer's repository, and what an operator acts on is which models an endpoint reads -- a model's page shows the endpoint beside its other consumers. NO VALUE OF ANY VARIABLE: a `*_from` is published as the name it holds. AND ONLY THE COMMENTS THEIR AUTHOR MARKED with `#:`, the gateway's rule: a comment is free text, and a secret ends up in one in ways no pattern recognises.
type ManifestCache ¶
type ManifestCache struct {
Type string `json:"type"`
AddrFrom string `json:"addr_from"`
TTL string `json:"ttl"`
MaxEntry int64 `json:"max_entry"`
}
ManifestCache is the cache's shape, by the variable's name.
type ManifestEndpoint ¶
type ManifestEndpoint struct {
Name string `json:"name"`
Path string `json:"path"`
Tenant string `json:"tenant"`
Params []ManifestParam `json:"params"`
Limits ManifestLimits `json:"limits"`
Reads []string `json:"reads"`
Notes map[string]string `json:"notes,omitempty"`
}
ManifestEndpoint is one endpoint, its parameters resolved.
type ManifestLimits ¶
type ManifestLimits struct {
Timeout string `json:"timeout"`
MaxRows int `json:"max_rows"`
MaxBytesBilled int64 `json:"max_bytes_billed"`
}
ManifestLimits are an endpoint's effective limits.
type ManifestParam ¶
type ManifestParam struct {
Name string `json:"name"`
Type string `json:"type"`
Default *string `json:"default,omitempty"`
Required bool `json:"required,omitempty"`
Values []string `json:"values,omitempty"`
Min *int64 `json:"min,omitempty"`
Max *int64 `json:"max,omitempty"`
MaxLen int `json:"max_len,omitempty"`
}
ManifestParam is one parameter as a caller sees it.
type Param ¶
type Param struct {
Type string `yaml:"type"`
// MaxLen bounds a string, in characters. Required for one.
MaxLen int `yaml:"max_len"`
// Min and Max bound an int.
Min *int64 `yaml:"min"`
Max *int64 `yaml:"max"`
// Values are an enum's, each an identifier: an enum is the one value
// written into the SQL text, and an identifier is what makes that safe.
Values []string `yaml:"values"`
// Default stands in for an omitted value, and is parsed exactly as a
// sent one is -- so a default its own type cannot hold is refused at
// load.
Default *string `yaml:"default"`
// Required refuses a request that omits the parameter.
Required bool `yaml:"required"`
// Ref names a definition in the file's `params:`, and takes nothing
// else.
Ref string `yaml:"ref"`
}
Param is one query-string parameter an endpoint takes.
FIVE TYPES AND EACH ONE NARROW. A parameter is a filter somebody outside sends, and every type is the smallest set of strings that can mean what it says: an int is base-10 digits, a date is YYYY-MM-DD and nothing an instant-parser would also accept, a bool is `true` or `false`, an enum is one of the values the file lists. Anything looser is a second way to spell a value, and the cache key, the audit line and the author all read the first.
type Request ¶ added in v0.6.0
type Request struct {
Endpoint string
Tenant string
Query url.Values
// Header is a copy, without Authorization.
Header http.Header
}
Request is what a before hook sees of one call. Query is the caller's query string, and the only field whose changes are kept: what the hook leaves there is validated exactly as if the caller had sent it, so a hook can translate a value but cannot slip past a type, a tenant or a declaration.
type Result ¶ added in v0.6.0
type Result struct {
Endpoint string
Tenant string
// Params are the values the SQL was rendered with, typed.
Params map[string]any
Columns []string
Rows [][]any
Truncated bool
}
Result is what an after hook sees: the rows, already cut by max_rows.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server answers the endpoints of one file.
func (*Server) Keys ¶
Keys is how many keys and tenants the API accepts; zero and zero means it is open, which only BREVIS_ENV=local allows.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cache holds an endpoint's answers, compressed, until a model they read is rebuilt.
|
Package cache holds an endpoint's answers, compressed, until a model they read is rebuilt. |
|
Package render turns an endpoint's SQL template into a statement and its arguments, and never writes a caller's value into the statement.
|
Package render turns an endpoint's SQL template into a statement and its arguments, and never writes a caller's value into the statement. |