Documentation
¶
Overview ¶
Package pdk ("Plugin Development Kit") is the public contract an external extension (for example api-cloud) builds against. It holds only interfaces, small value types, and request helpers — no platform logic — and imports only the public api and config packages plus the standard library.
This is the external tier of the two-tier plugin model: external plugins implement pdk.Plugin and receive pdk.Deps (capabilities as public interfaces), never raw repositories or internal service types. It is the surface we promise to keep stable. In-tree plugins use internal/plugin instead, with full access, and are rebuilt with the repo.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func WriteCaptured ¶ added in v0.16.1
func WriteCaptured(w http.ResponseWriter, res *CapturedResponse)
WriteCaptured sends a captured response to the client unchanged. Use it to pass an error response through untouched rather than re-encoding it, which would flatten core's error DTO.
The captured Content-Length is dropped and left for net/http to recompute: a decorator that rewrote the body would otherwise emit a stale length. Other headers describing the captured bytes — Content-Encoding, ETag, Content-Range — cannot be recomputed and are forwarded as-is, so a decorator that reshapes the body must clear them itself.
A captured header replaces, rather than appends to, any value the decorator already set on w, so passing a response through cannot produce a duplicated Content-Type. Set-Cookie is the exception: multiple cookies are legitimate, so captured ones are appended to the decorator's.
Types ¶
type CapturedResponse ¶ added in v0.16.1
type CapturedResponse struct {
// Status is the status code the handler wrote, defaulting to 200 when it
// wrote a body without calling WriteHeader.
Status int
// Header holds the headers the handler set.
Header http.Header
// Body is everything the handler wrote, or nil if it wrote nothing.
Body []byte
}
CapturedResponse is what a core handler wrote, captured in memory by Invoke so a decorator can inspect or reshape it before anything reaches the client.
func Invoke ¶ added in v0.16.1
func Invoke(next http.Handler, r *http.Request) *CapturedResponse
Invoke runs next with r and returns what it wrote, without sending anything to the client. Use it when a decorator needs to read or reshape the core response; a decorator that only observes the outcome should wrap the real http.ResponseWriter instead, which costs nothing.
The captured response is held entirely in memory, so an override on an endpoint that returns a large payload buffers all of it. Only explicitly overridden routes pay this, and the operator chooses those routes.
The writer handed to next implements neither http.Flusher nor http.Hijacker, so a streaming or hijacking handler fails visibly instead of stalling.
type ChainPosition ¶
type ChainPosition int
ChainPosition selects where a plugin's middleware is spliced into the server's request chain. Only two positions are exposed on purpose: a plugin can wrap the whole request or run with the authenticated identity, without the platform's internal middleware order becoming part of the public contract. A plugin cannot insert middleware *between* platform steps (e.g. between auth and the scope enforcer).
const ( // BeforePlatformChain is outermost: it runs before CORS and auth, on every // request, with NO authenticated identity in the context yet. Good for request // IDs, tracing, panic recovery, IP allow-lists. It cannot mark a request as // authenticated — the platform's auth always runs after it and is the only // thing that sets identity, so GO-AUTH-005 still holds. BeforePlatformChain ChainPosition = iota // AfterPlatformChain is innermost: it runs after auth, organization // resolution, and scope enforcement, just before routing. The authenticated // org and identity are in the context and can be read with // middleware.GetOrganizationFromRequest. // Good for per-tenant rate limiting, audit, or request enrichment — still // scoped by the org from context (GO-AUTH-005). AfterPlatformChain )
type Deps ¶
Deps gives an external plugin the platform's capabilities as interfaces grouped by area, using only public types. It never hands over repositories, DB handles, or concrete internal service types — the type system keeps model.* / repository.* from leaking out.
Capabilities are added here as external plugins need them. Each interface is satisfied by shape by the concrete internal service — the methods listed are exactly existing service methods that already speak public types — so exposing one is a plain assignment in the server (see StartPlatformAPIServer), with no adapter code. The assignment itself is the compile-time contract check: if a signature drifts, the server stops building.
type Gateways ¶
type Gateways interface {
// RegisterGateway creates a gateway in an organization (Create).
RegisterGateway(orgID string, id *string, displayName, description string, endpoints []string,
isCritical bool, functionalityType, version, createdBy string, properties map[string]any) (*api.GatewayResponse, error)
// GetGateway returns a single gateway by id within an organization (Read).
GetGateway(gatewayID, orgID string) (*api.GatewayResponse, error)
// UpdateGateway updates a gateway within an organization (Update).
UpdateGateway(gatewayID, orgID, updatedBy string, req *api.GatewayResponse) (*api.GatewayResponse, error)
// DeleteGateway removes a gateway within an organization (Delete).
DeleteGateway(gatewayID, orgID, deletedBy string) error
}
Gateways exposes CRUD access to the platform's gateways, scoped by organization. Every method mirrors an existing GatewayService method verbatim and takes the organization id explicitly — handlers MUST pass the org resolved from the request context, never one from request input (GO-AUTH-005).
type Middleware ¶
Middleware is a standard Go middleware — it wraps one handler with another.
type MiddlewareProvider ¶
type MiddlewareProvider interface {
Middleware() []PositionedMiddleware
}
MiddlewareProvider is an OPTIONAL interface a Plugin may implement to contribute middleware to the request chain. Return an empty slice to add none. Within a position, middleware runs in plugin registration order.
type Plugin ¶
type Plugin interface {
// Name returns a short identifier for the plugin (e.g. "api-cloud").
Name() string
// Init receives the platform capabilities (pdk.Deps). Called once at startup
// before routes are registered; return an error to abort startup.
Init(deps *Deps) error
// RegisterRoutes mounts the plugin's HTTP routes on the shared mux. Only
// called after Init has succeeded. Every route registered here is served
// through the platform's authentication and scope chain.
RegisterRoutes(mux *http.ServeMux)
// OpenAPISpec returns the plugin's OpenAPI 3.x YAML bytes, merged into the
// platform scope registry to enforce per-route scopes. It is mandatory:
// returning empty bytes or bytes the registry loader rejects aborts startup.
//
// The merged registry is what the scope stage consults on each request, keyed
// by the matched route pattern. Declare the scopes each route requires
// (GO-AUTH-007).
OpenAPISpec() []byte
// Shutdown is called during graceful server shutdown.
Shutdown(ctx context.Context) error
}
Plugin is the contract an external extension implements. Every method signature uses only public types, so a Plugin can live in a separate module without importing platform-api's internal/ packages.
type PositionedMiddleware ¶
type PositionedMiddleware struct {
Position ChainPosition
Wrap Middleware
}
PositionedMiddleware pairs a middleware with the position it should occupy in the chain.
type RouteDecorator ¶ added in v0.16.1
RouteDecorator wraps one core handler, receiving it as next.
It shares Go's standard middleware signature but is deliberately a DISTINCT type from Middleware, because the two seams have different contracts: a Middleware runs in the request chain for every request at a ChainPosition, while a RouteDecorator is bound to one route pattern and to the constraints documented on RouteOverride. Being a defined type rather than a bare func type, it makes passing a Middleware where a decorator belongs a compile error instead of a silent mix-up — an ordinary func literal or a func with this signature still assigns to it unchanged.
type RouteOverride ¶ added in v0.16.1
type RouteOverride struct {
// Pattern is an existing core route pattern, matched exactly.
Pattern string
// Wrap decorates the original core handler. It must not be nil.
Wrap RouteDecorator
}
RouteOverride declares that a plugin decorates one existing core route.
Pattern must be an EXISTING core route pattern, matched as an exact string against the patterns core registered (for example "GET /api/v0.9/gateways/{gatewayId}"). A pattern core does not register — a typo, or a version that has moved on — aborts startup rather than silently doing nothing. Overriding a plugin's route, the webhook receiver, or the health endpoint is not supported; only core routes are recorded.
Wrap receives the ORIGINAL core handler as next and returns the handler that is registered under the same pattern on the real mux. Because the pattern is unchanged, path wildcards are resolved by the mux before Wrap runs and r.PathValue still works inside the core handler.
This is an auth-sensitive surface:
- The route's required scopes are unchanged by an override. The scope registry is keyed by OpenAPI path/method, so the original requirement stays in force; a plugin that needs different scopes declares them in its own OpenAPISpec, and must re-declare the core scopes it does not intend to drop (GO-AUTH-007).
- A decorator must scope tenant data by the organization in the request context, never by anything in the request body or query (GO-AUTH-005).
- Rewriting r.URL.Path inside Wrap does not re-route the request; the handler for this pattern is already selected. Do not use an override to redirect traffic to a different endpoint (GO-AUTH-017).
type RouteOverrideProvider ¶ added in v0.16.1
type RouteOverrideProvider interface {
RouteOverrides() []RouteOverride
}
RouteOverrideProvider is an OPTIONAL interface a Plugin may implement to decorate existing core routes. Return an empty slice to decorate none.
Every returned override is validated at startup: a nil Wrap, an empty Pattern, a pattern claimed by another plugin, or a pattern core does not register aborts startup with an error naming the plugin and the pattern.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package middleware is the public tier of the platform's request-context helpers.
|
Package middleware is the public tier of the platform's request-context helpers. |