graphql

package
v1.131.0 Latest Latest
Warning

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

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

Documentation

Overview

Package graphql provides the platform's GraphQL connection kind: a toolkit that reaches a GraphQL endpoint through the platform's auth, persona, audit and export pipeline, the way pkg/toolkits/apigateway reaches an OpenAPI-described REST API and pkg/toolkits/gateway reaches an upstream MCP server.

A GraphQL endpoint is one URL that everything is POSTed to, which is why it is a kind of its own rather than a shape of API connection: there is no route to authorize, no path to rank, and a failure is an errors array inside an HTTP 200. What the kind adds on top of the shared upstream transport is a schema it keeps by introspection, an index of the operations that schema exposes, validation of the document a model wrote before it is sent, and an operation-level policy that reduces a document to the fields it selects.

The toolkit registers three tools however many connections it holds: graphql_discover finds an operation and renders a document that runs it, graphql_query executes one, and graphql_export streams one into an asset.

Index

Constants

View Source
const (
	// Kind is the connection-instance kind discriminator. Operators see
	// this in the admin UI's connection picker.
	Kind = "graphql"

	// DefaultConnectTimeout caps the dial step on each call.
	DefaultConnectTimeout = upstreamauth.DefaultConnectTimeout
	// DefaultCallTimeout caps the total per-call time.
	DefaultCallTimeout = upstreamauth.DefaultCallTimeout
	// DefaultMaxResponseBytes is the upstream read cap: the most the
	// toolkit reads of any one response.
	DefaultMaxResponseBytes = upstreamauth.DefaultMaxResponseBytes

	// DefaultMaxInlineBytes is the inline budget: the most a rendered
	// graphql_query result may hold. It is a model-context budget, set
	// from what a client accepts (#1587, #1606) and matching the API
	// gateway's. A result past it has its data cut, is flagged with
	// data_truncated, and is steered to graphql_export, which streams
	// the whole response into an asset with no context cost.
	DefaultMaxInlineBytes = int64(32 * 1024)

	// DefaultMaxQueryDepth caps the selection depth of a document the
	// toolkit will send. A deeply nested document is how a GraphQL
	// endpoint is made to do unbounded work from one small request, and
	// fifteen is deeper than any document graphql_discover renders.
	DefaultMaxQueryDepth = 15

	// DefaultMaxPages caps a paginated walk when the caller names no
	// limit of their own.
	DefaultMaxPages = 10

	// SchemaValidationStrict refuses a document that does not validate
	// against the stored schema. The default: a document naming a field
	// the connection's schema does not have is a caller working from
	// the wrong schema, and sending it spends a call to learn that.
	SchemaValidationStrict = "strict"
	// SchemaValidationWarn sends the document anyway and reports the
	// violations alongside the upstream's answer. For a deployment
	// whose stored schema is behind the endpoint it serves, or which
	// uses a directive the compatibility-floor introspection query does
	// not record.
	SchemaValidationWarn = "warn"
)
View Source
const (
	AuthModeNone                    = upstreamauth.AuthModeNone
	AuthModeBearer                  = upstreamauth.AuthModeBearer
	AuthModeAPIKey                  = upstreamauth.AuthModeAPIKey
	AuthModeBasic                   = upstreamauth.AuthModeBasic
	AuthModeOAuth                   = upstreamauth.AuthModeOAuth
	AuthModeOAuth2ClientCredentials = upstreamauth.AuthModeOAuth2ClientCredentials
	AuthModeOAuth2AuthorizationCode = upstreamauth.AuthModeOAuth2AuthorizationCode
	AuthModeSignedJWT               = upstreamauth.AuthModeSignedJWT
	AuthModeMTLS                    = upstreamauth.AuthModeMTLS

	CredentialPlacementHeader = upstreamauth.CredentialPlacementHeader
	CredentialPlacementQuery  = upstreamauth.CredentialPlacementQuery

	DefaultAPIKeyHeader = upstreamauth.DefaultAPIKeyHeader

	OAuth2AuthStyleHeader = upstreamauth.OAuth2AuthStyleHeader
	OAuth2AuthStyleParams = upstreamauth.OAuth2AuthStyleParams
)

The credential vocabulary an operator configures on a graphql connection. The values are defined by internal/upstreamauth, which owns the outbound authentication policy for every HTTP-based connection kind; they are aliased here because they are part of this toolkit's public API and of what an operator types into auth_mode.

View Source
const (
	SignedJWTAlgHS256 = upstreamauth.SignedJWTAlgHS256
	SignedJWTAlgRS256 = upstreamauth.SignedJWTAlgRS256
	SignedJWTAlgES256 = upstreamauth.SignedJWTAlgES256

	DefaultSignedJWTTokenLifetime = upstreamauth.DefaultSignedJWTTokenLifetime
	DefaultSignedJWTIssuedAtSkew  = upstreamauth.DefaultSignedJWTIssuedAtSkew
)

The signing algorithms auth_mode=signed_jwt supports, and the defaults an unset lifetime and skew take. Aliased from the shared seam for the same reason as the mode names above.

View Source
const (
	// DiscoverLevelOperations is a ranked list of operations.
	DiscoverLevelOperations = "operations"
	// DiscoverLevelOperation is one operation's arguments, return shape
	// and runnable skeleton.
	DiscoverLevelOperation = "operation"
)

The two depths graphql_discover answers at, reported in DiscoverOutput.Level so a caller can branch on the shape it received rather than on which keys happen to be present.

View Source
const (
	// StoppedByEnd is the upstream saying there is no next page.
	StoppedByEnd = "end"
	// StoppedByMaxPages is the walk's own page bound.
	StoppedByMaxPages = "max_pages"
	// StoppedByError is a page the upstream refused; the errors are on
	// the result.
	StoppedByError = "error"
)

Why a walk stopped, reported as PaginationReport.StoppedBy.

View Source
const (
	// SchemaSourceIntrospection marks a schema read from the endpoint.
	SchemaSourceIntrospection = "introspection"
	// SchemaSourceUpload marks a schema an operator supplied through
	// the admin route, which is the path for an endpoint with
	// introspection disabled.
	SchemaSourceUpload = "upload"
)

Schema provenance values recorded on a stored schema, so an operator reading a connection knows whether what the platform holds came from the endpoint or from them.

View Source
const (
	// ToolDiscover finds an operation and describes it.
	ToolDiscover = "graphql_discover"
	// ToolQuery executes one document.
	ToolQuery = "graphql_query"
	// ToolExport streams one document's result into an asset.
	ToolExport = "graphql_export"
)

Tool names this toolkit registers. Exported so audit code, persona configuration and tests reference the same literals as the registration site.

Variables

View Source
var (
	// ErrConnectionExists is returned when AddConnection is called with
	// a name already registered.
	ErrConnectionExists = errors.New("graphql: connection already exists")
	// ErrConnectionNotFound is returned when an operation is requested
	// against a connection that has not been registered.
	ErrConnectionNotFound = errors.New("graphql: connection not found")
)

Errors the connection registry answers with.

View Source
var ErrNeedsReauth = upstreamauth.ErrNeedsReauth

ErrNeedsReauth is the structured error a tool surfaces when an authorization_code connection's stored refresh token is missing, expired, or definitively rejected by the IdP. Transient failures (network, 5xx, cancellation) do not produce it.

View Source
var ErrSchemaNotFound = errors.New("graphql: no stored schema for connection")

ErrSchemaNotFound reports a connection with no stored schema.

Functions

This section is empty.

Types

type Authenticator

type Authenticator = upstreamauth.Authenticator

Authenticator applies a connection's authentication scheme to an outbound HTTP request. The implementations and every auth mode live in internal/upstreamauth, shared with the platform's other HTTP-based connection kinds; this alias keeps the toolkit's own vocabulary intact at its call sites.

func NewAuthenticator

func NewAuthenticator(c Config) (Authenticator, error)

NewAuthenticator returns the Authenticator implementation for a validated Config.

type Config

type Config struct {
	// EndpointURL is the full URL documents are POSTed to (e.g.
	// "https://datahub.example.com/api/graphql"). Required. Unlike the
	// API gateway's base_url this is the whole address, not a root
	// paths are joined to: a GraphQL endpoint has one address.
	EndpointURL string
	// Description is an optional human-readable description of the
	// connection, surfaced by ListConnections and so by the admin UI
	// and the list_connections tool. Empty falls back to the endpoint.
	Description string
	// ConnectionName is the audit-visible connection identifier and the
	// value passed in a tool's `connection` argument. Populated from
	// the toolkit instance name.
	ConnectionName string

	// AuthMode and the credential fields below carry the shared
	// upstream authentication policy. See internal/upstreamauth.
	AuthMode            string
	Credential          string
	CredentialPlacement string
	APIKeyHeader        string
	APIKeyParam         string
	Username            string
	Password            string
	OAuth2              OAuth2Config
	// SignedJWT carries the assertion parameters used when AuthMode is
	// AuthModeSignedJWT: the platform mints a short-lived JWT per call
	// from an identifier and a signing key issued out of band. Aliased
	// straight from the shared seam rather than mirrored, because
	// nothing in it is this kind's to define.
	SignedJWT SignedJWTConfig

	// ConnectTimeout caps the dial step on each call.
	ConnectTimeout time.Duration
	// CallTimeout caps the total per-call time.
	CallTimeout time.Duration
	// MaxResponseBytes is the upstream read cap.
	MaxResponseBytes int64
	// MaxInlineBytes is the most of a response returned through a tool
	// result. The read cap bounds it, so a connection whose
	// MaxResponseBytes is lower returns at most that.
	MaxInlineBytes int64
	// StaticHeaders are operator-configured headers attached to every
	// outbound request, in addition to whatever AuthMode contributes.
	// This is where an upstream's tenant or folder routing goes (Sage
	// X3's x-xtrem-endpoint, a vendor subscription key). The model
	// never sets or overrides these.
	StaticHeaders map[string]string
	// MTLSClientCertPEM is the PEM client certificate chain presented
	// during the TLS handshake.
	MTLSClientCertPEM string
	// MTLSClientKeyPEM is the PEM private key matching the certificate.
	// Encrypted at rest.
	MTLSClientKeyPEM string
	// TLSCABundlePEM is an optional PEM bundle of root CAs added to the
	// trust store for this connection's outbound requests.
	TLSCABundlePEM string
	// IdentityPassthrough forwards the acting caller's inbound bearer
	// token as the outbound Authorization header instead of applying
	// this connection's shared credential.
	IdentityPassthrough bool

	// SchemaValidation is SchemaValidationStrict (default) or
	// SchemaValidationWarn.
	SchemaValidation string
	// MaxQueryDepth caps the selection depth of a document the toolkit
	// will send. Defaults to DefaultMaxQueryDepth.
	MaxQueryDepth int
	// NamespaceDepth caps how many segments a dotted operation id may
	// have when the schema is walked into operations. Defaults to
	// gqlschema.DefaultNamespaceDepth.
	NamespaceDepth int
	// ReadOnly refuses every mutation document on this connection, for
	// every persona. It is the connection-level counterpart to a
	// persona's `deny` rule on the MUTATION method: an operator who
	// mounts an ERP for reporting sets it once here rather than in
	// every persona.
	ReadOnly bool
}

Config holds the configuration of one GraphQL connection.

func ParseConfig

func ParseConfig(cfg map[string]any) (Config, error)

ParseConfig parses a Config from the generic map a connection is stored as and applies defaults. The returned Config is fully validated.

func (Config) IsOAuthAuthorizationCode

func (c Config) IsOAuthAuthorizationCode() bool

IsOAuthAuthorizationCode reports whether the connection uses the OAuth authorization_code grant. The admin redirect handler and the kind handler gate the one-time browser flow on this rather than on the raw auth_mode string shape.

func (Config) Validate

func (c Config) Validate() error

Validate returns an error when the configuration is missing required fields or holds invalid values. The auth, timeout, static-header, passthrough and TLS rules belong to internal/upstreamauth and are called individually so this kind's own checks stay interleaved in the order an operator reads them.

type DiscoverInput

type DiscoverInput struct {
	Connection  string `json:"connection"`
	Query       string `json:"query,omitempty"`
	OperationID string `json:"operation_id,omitempty"`
	Limit       int    `json:"limit,omitempty"`
	Ranking     string `json:"ranking,omitempty"`
	Depth       int    `json:"depth,omitempty"`
}

DiscoverInput is the parsed argument shape for graphql_discover.

type DiscoverOutput

type DiscoverOutput struct {
	Level      string            `json:"level"`
	Connection string            `json:"connection"`
	Operations []RankedOperation `json:"operations,omitempty"`
	Operation  *OperationDetail  `json:"operation,omitempty"`
	// SchemaHash and SchemaFetchedAt identify the schema version this
	// answer was read from, so a caller comparing two answers can see
	// whether the schema moved underneath them.
	SchemaHash      string `json:"schema_hash,omitempty"`
	SchemaFetchedAt string `json:"schema_fetched_at,omitempty"`
	// MatchedLexical and ShownSemantic report where relevance ended:
	// how many operations contain every token, and how many followed
	// them as neighbors by intent. Absent unless the call carried a
	// query.
	MatchedLexical *int   `json:"matched_lexical,omitempty"`
	ShownSemantic  *int   `json:"shown_semantic,omitempty"`
	Note           string `json:"note,omitempty"`
	Next           string `json:"next,omitempty"`
}

DiscoverOutput is the structured result at both levels. Level names the shape; exactly one of Operations and Operation is populated for it. Next says what argument goes one level deeper.

type Error

type Error struct {
	// Message is the upstream's own text. It is passed through
	// unchanged: for most GraphQL upstreams it is the only diagnosis
	// the caller will get.
	Message string `json:"message"`
	// Path is the response path the error occurred at, when the
	// upstream reports one.
	Path []any `json:"path,omitempty"`
	// Locations are the document positions the upstream blames.
	Locations []ErrorLocation `json:"locations,omitempty"`
	// Extensions is the upstream's own error metadata (an error code,
	// a classification), passed through as sent.
	Extensions map[string]any `json:"extensions,omitempty"`
}

Error is one entry of a GraphQL response's errors array. A GraphQL endpoint reports a failure this way inside an HTTP 200, which is why the platform classifies the outcome from the body rather than from the status line.

type ErrorLocation

type ErrorLocation struct {
	Line   int `json:"line"`
	Column int `json:"column"`
}

ErrorLocation is a line and column in the submitted document.

type ExportAsset

type ExportAsset struct {
	ID             string
	OwnerID        string
	OwnerEmail     string
	Name           string
	Description    string
	ContentType    string
	S3Bucket       string
	S3Key          string
	SizeBytes      int64
	Tags           []string
	Provenance     ExportProvenance
	SessionID      string
	IdempotencyKey string
}

ExportAsset is the row inserted into the portal's assets when a graphql_export call succeeds.

type ExportAssetRef

type ExportAssetRef struct {
	ID        string
	SizeBytes int64
}

ExportAssetRef is what an idempotency-key lookup returns.

type ExportAssetStore

type ExportAssetStore interface {
	InsertExportAsset(ctx context.Context, asset ExportAsset) error
	GetByIdempotencyKey(ctx context.Context, ownerID, key string) (*ExportAssetRef, error)
}

ExportAssetStore is the subset of the portal asset store graphql_export writes through.

type ExportConfig

type ExportConfig struct {
	MaxBytes       int64
	DefaultTimeout time.Duration
	MaxTimeout     time.Duration
}

ExportConfig holds the platform-level limits for graphql_export.

type ExportDeps

type ExportDeps struct {
	AssetStore   ExportAssetStore
	VersionStore ExportVersionStore
	S3Client     ExportS3Client
	ShareCreator ExportShareCreator
	// ResourceLander lands a result in a managed resource by path instead of in
	// a new asset (#1663). nil leaves the asset destination the only one, which
	// is what a deployment with no managed-resource library has.
	ResourceLander toolkit.ResourceLander
	S3Bucket       string
	S3Prefix       string
	BaseURL        string
	Config         ExportConfig
	GetUserContext func(ctx context.Context) *ExportUserContext
}

ExportDeps holds the platform-side dependencies graphql_export needs. A nil AssetStore is export disabled: the tool is not registered, so the model never sees one it could not successfully call.

type ExportProvenance

type ExportProvenance struct {
	ToolCalls []ExportProvenanceCall
	SessionID string
	UserID    string
}

ExportProvenance records the call that produced an asset, so a portal viewer can render where its content came from.

type ExportProvenanceCall

type ExportProvenanceCall struct {
	ToolName   string
	Timestamp  string
	Parameters map[string]any
}

ExportProvenanceCall is one step in the provenance chain.

type ExportS3Client

type ExportS3Client interface {
	// PutObjectStream uploads body to bucket/key, returning the bytes
	// written.
	PutObjectStream(ctx context.Context, bucket, key string, body io.Reader, contentType string) (size int64, err error)
}

ExportS3Client is the object storage graphql_export writes to.

type ExportShareCreator

type ExportShareCreator interface {
	CreatePublicShare(ctx context.Context, assetID, createdBy string) (shareURL string, err error)
}

ExportShareCreator creates a public share link for an exported asset. nil disables public-link creation.

type ExportUserContext

type ExportUserContext struct {
	UserID    string
	UserEmail string
	SessionID string
}

ExportUserContext is the caller's identity, supplied by the platform through a callback so the toolkit does not import the middleware.

type ExportVersion

type ExportVersion struct {
	ID            string
	AssetID       string
	S3Key         string
	S3Bucket      string
	ContentType   string
	SizeBytes     int64
	CreatedBy     string
	ChangeSummary string
}

ExportVersion is the row inserted into the portal's asset versions.

type ExportVersionStore

type ExportVersionStore interface {
	CreateExportVersion(ctx context.Context, version ExportVersion) (int, error)
}

ExportVersionStore is the subset of the portal version store graphql_export writes through.

type MultiConfig

type MultiConfig struct {
	DefaultName string
	Instances   map[string]Config
}

MultiConfig holds parsed per-connection configs plus the aggregate toolkit's default connection name.

func ParseMultiConfig

func ParseMultiConfig(defaultName string, raw map[string]map[string]any) (MultiConfig, error)

ParseMultiConfig validates and returns the parsed config for every instance. Per-instance parse errors are logged and the bad instance is skipped so one misconfigured connection cannot block startup.

type OAuth2Config

type OAuth2Config struct {
	// Grant is the OAuth flow: client_credentials or
	// authorization_code.
	Grant string
	// TokenURL is the upstream's token endpoint.
	TokenURL string
	// ClientID is the platform's registered client id.
	ClientID string
	// ClientSecret is the platform's registered client secret,
	// encrypted at rest.
	ClientSecret string
	// Scopes is an optional list of scopes to request.
	Scopes []string
	// EndpointAuthStyle is "header" (default) or "params".
	EndpointAuthStyle string
	// AuthorizationURL is the upstream's authorization endpoint,
	// required for the authorization_code grant.
	AuthorizationURL string
	// Prompt is an optional OIDC prompt parameter.
	Prompt string
}

OAuth2Config describes the OAuth parameters used when AuthMode is the canonical oauth mode. Mirrors the API gateway's, because both are projections of the same shared seam.

type OAuthKindHandler

type OAuthKindHandler struct {
	// contains filtered or unexported fields
}

OAuthKindHandler adapts this kind onto the platform's unified connection-OAuth flow, so a GraphQL connection using the authorization_code grant is connected, reconnected and revoked through the same admin surface and the same token store as every other kind. Registered at startup in the platform's HTTP wiring.

func NewOAuthKindHandler

func NewOAuthKindHandler(tk *Toolkit) *OAuthKindHandler

NewOAuthKindHandler builds the handler for a toolkit.

func (*OAuthKindHandler) AfterConnect

func (h *OAuthKindHandler) AfterConnect(ctx context.Context, name string, _ map[string]any) error

AfterConnect reads the connection's schema now that a credential exists. A GraphQL endpoint behind an authorization_code grant refuses introspection until the flow completes, so the connection registered with no schema; this is the first moment it can have one.

func (*OAuthKindHandler) ParseOAuthConfig

func (*OAuthKindHandler) ParseOAuthConfig(connConfig map[string]any) (connoauth.Config, error)

ParseOAuthConfig validates a connection's stored config and maps its OAuth settings into a connoauth.Config. It reports an error when the connection is not configured for the authorization_code grant, which the unified handler renders as HTTP 409.

The mapping is delegated to the shared seam's ConnOAuthConfig so the initial code exchange here and the per-call silent refresh in the authenticator read every field through one translator.

type OperationDetail

type OperationDetail struct {
	OperationSummary
	// ArgumentDetails are the operation's arguments with their types,
	// descriptions, defaults and whether they are required.
	ArgumentDetails []gqlschema.Argument `json:"argument_details,omitempty"`
	// InputTypes expands the input-object types the arguments
	// reference, so a caller filling in a filter or a create payload
	// does not need a second lookup.
	InputTypes []gqlschema.InputType `json:"input_types,omitempty"`
	// ReturnShape is the return type's field tree, depth-limited.
	ReturnShape []gqlschema.FieldNode `json:"return_shape,omitempty"`
	// Skeleton is a document that validates against this connection's
	// schema and calls this operation, with every argument bound to a
	// variable. It is the load-bearing part of this level: it is the
	// difference between a correct selection on the first try and
	// several calls spent on validation errors.
	Skeleton string `json:"skeleton"`
	// Variables is a JSON object carrying one entry per required
	// argument, ready to edit and pass as graphql_query's variables.
	// An optional argument is declared in the skeleton but absent here,
	// so leaving it out sends the schema's own default; add its key to
	// set it.
	Variables string `json:"variables"`
}

OperationDetail is one operation at the operation level: everything needed to write the call, plus a document that already makes it.

type OperationSummary

type OperationSummary struct {
	// OperationID is the kind-prefixed dotted id
	// ("query:masterData.product.query"). It is what operation_id
	// takes.
	OperationID string `json:"operation_id"`
	// Kind is QUERY or MUTATION. It is also the method a persona rule
	// names for this operation.
	Kind string `json:"kind"`
	// Path is the path a persona rule names this operation under: the
	// dotted id with dots as slashes.
	Path string `json:"path"`
	// Summary is the descriptions the schema carries along the
	// operation's path.
	Summary string `json:"summary,omitempty"`
	// ReturnType is the rendered type the operation returns.
	ReturnType string `json:"return_type,omitempty"`
	// Arguments are the operation's argument names, rendered
	// "name: Type" so a caller can see what it takes without a second
	// call.
	Arguments []string `json:"arguments,omitempty"`
	// Deprecated reports the schema marking this operation
	// @deprecated.
	Deprecated bool `json:"deprecated,omitempty"`
}

OperationSummary is the slim per-operation view a ranked list returns. It carries what a caller needs to choose an operation and to ask for it by id, and nothing more: the full argument and return detail is one call away behind operation_id.

type PaginateInput

type PaginateInput struct {
	// Items is the dotted path, inside the response's data object, of
	// the array merged across pages
	// ("masterData.product.query.edges", "searchAcrossEntities.results").
	// Required.
	Items string `json:"items"`
	// CursorVariable is the document variable the next page's cursor is
	// bound to ("after", "scrollId"). Required: the cursor goes back as
	// a variable, so the document itself is unchanged between pages.
	CursorVariable string `json:"cursor_variable"`
	// NextCursorPath is the dotted path, inside data, holding the next
	// page's cursor. Defaults to the Relay pageInfo.endCursor beside
	// the items array.
	NextCursorPath string `json:"next_cursor_path,omitempty"`
	// HasNextPath is the dotted path, inside data, of a boolean saying
	// whether another page exists. Defaults to the Relay
	// pageInfo.hasNextPage beside the items array. An upstream that
	// signals the end by a null cursor alone needs no such field: the
	// walk stops on an absent cursor either way.
	HasNextPath string `json:"has_next_path,omitempty"`
	// MaxPages bounds the walk. Defaults to DefaultMaxPages.
	MaxPages int `json:"max_pages,omitempty"`
}

PaginateInput is the walk a caller asks for. It names the array to merge and the variable the next page's cursor is fed back into; everything else has a Relay default.

type PaginationReport

type PaginationReport struct {
	// PagesFetched is how many pages were read.
	PagesFetched int `json:"pages_fetched"`
	// ItemsMerged is the length of the merged array.
	ItemsMerged int `json:"items_merged"`
	// StoppedBy is why the walk ended.
	StoppedBy string `json:"stopped_by"`
	// NextCursor is the cursor the next page would have used, present
	// when the walk stopped at its page bound rather than at the end.
	NextCursor string `json:"next_cursor,omitempty"`
}

PaginationReport says what a walk did.

type QueryInput

type QueryInput struct {
	Connection     string          `json:"connection"`
	Query          string          `json:"query"`
	Variables      json.RawMessage `json:"variables,omitempty"`
	OperationName  string          `json:"operation_name,omitempty"`
	TimeoutSeconds int             `json:"timeout_seconds,omitempty"`
	Paginate       *PaginateInput  `json:"paginate,omitempty"`
}

QueryInput is the parsed argument shape for graphql_query.

Variables is raw because the schema admits two forms: the object a GraphQL client sends, and a string holding that object's JSON, which is what a client that stringifies structured arguments sends. Both reach the same upstream request (#1548).

type QueryOutput

type QueryOutput struct {
	Connection string `json:"connection"`
	// Kind is QUERY or MUTATION: the operation kind that executed, and
	// the method the persona rules authorized it under.
	Kind string `json:"kind"`
	// OperationName is the operation that ran, when the document named
	// one.
	OperationName string `json:"operation_name,omitempty"`
	// Operations are the dotted operation ids this document invoked,
	// the same ids graphql_discover reports and persona rules name.
	Operations []string `json:"operations,omitempty"`
	// Status is the HTTP status the endpoint answered with. It is
	// almost always 200, including for a failure: what decides the
	// outcome is upstream_error.
	Status int `json:"status"`
	// Data is the GraphQL data object, as sent. Present even alongside
	// errors: a partial result is preserved rather than discarded.
	Data json.RawMessage `json:"data,omitempty"`
	// Errors is the response's errors array, passed through unchanged.
	Errors []Error `json:"errors,omitempty"`
	// Extensions is the response's extensions object, passed through.
	Extensions map[string]any `json:"extensions,omitempty"`
	// UpstreamError reports a call that failed however it was
	// transported: a non-2xx, or a 200 carrying errors. It is what the
	// platform's audit, call catalog and metrics classify the call on.
	UpstreamError bool `json:"upstream_error"`
	// ValidationWarnings are the schema violations found in a
	// connection whose schema_validation is warn. Empty under strict,
	// where a violation refuses the call instead.
	ValidationWarnings []string `json:"validation_warnings,omitempty"`
	// DataBytes is the size of the data read.
	DataBytes int `json:"data_bytes"`
	// DataTruncated reports data withheld because the rendered result
	// would exceed this connection's max_inline_bytes. A cut JSON
	// document cannot be parsed, so the data is omitted rather than
	// halved; export_arguments carries the call that streams it whole.
	DataTruncated bool `json:"data_truncated,omitempty"`
	// ExportArguments is the graphql_export call that writes this same
	// result to an asset, present when the data did not fit inline.
	ExportArguments map[string]any `json:"export_arguments,omitempty"`
	// Pagination reports a walk that ran.
	Pagination *PaginationReport `json:"pagination,omitempty"`
	// Note explains anything that changed the answer.
	Note string `json:"note,omitempty"`
}

QueryOutput is what a caller gets back from one document.

type RankedConnectionOperation

type RankedConnectionOperation struct {
	Connection string
	Operation  gqlschema.Operation
	Score      float64
}

RankedConnectionOperation is one operation matched by SearchOperations, tagged with the connection it belongs to and its score under the ranking that produced it. The score is comparable within a connection; across connections it is best-effort, because an indexed connection scores by cosine while one that fell back to lexical scores positionally.

type RankedOperation

type RankedOperation struct {
	OperationSummary
	Score        *float64 `json:"score,omitempty"`
	LexicalMatch *bool    `json:"lexical_match,omitempty"`
}

RankedOperation is one operation in a list, plus what put it there when a query ranked it. Score and LexicalMatch are pointers because both are absent from an unranked list and a zero score is a real score.

type RankingMode

type RankingMode string

RankingMode selects how graphql_discover scores an operation against the caller's query. The three modes and the arithmetic behind them are the platform's, shared with the API gateway through internal/opranking, so a caller's experience of relevance does not depend on which kind their connection is.

const (
	// RankingLexical is the substring AND filter: every token of the
	// query must appear in the operation's text. Deterministic and
	// needs no embedding provider.
	RankingLexical RankingMode = RankingMode(opranking.ModeLexical)
	// RankingSemantic ranks by embedding cosine similarity alone.
	RankingSemantic RankingMode = RankingMode(opranking.ModeSemantic)
	// RankingHybrid blends the lexical signal with the cosine. It is
	// what an omitted ranking resolves to whenever the connection has
	// an embedding index.
	RankingHybrid RankingMode = RankingMode(opranking.ModeHybrid)
)

RankingMode values exposed on the graphql_discover schema.

func ParseRankingMode

func ParseRankingMode(s string) (RankingMode, error)

ParseRankingMode resolves the caller's ranking argument. An empty value is resolved by the caller, which upgrades it to hybrid when the connection has an index.

type RoutePolicy

type RoutePolicy interface {
	Allow(ctx context.Context, connection, method, path, template string) (allowed bool, reason string)
}

RoutePolicy gates a GraphQL call by (connection, method, path) on top of the platform's tool and connection authorization. The method is the operation kind (QUERY or MUTATION) and the path is the dotted operation id with dots as slashes, which puts a GraphQL operation into the same space a persona's api_routes rules already evaluate.

The template argument is always equal to path here: a GraphQL operation id carries no parameters to substitute, so the two forms an HTTP route has collapse to one.

type SchemaInfo

type SchemaInfo struct {
	Connection     string    `json:"connection"`
	Hash           string    `json:"schema_hash,omitempty"`
	Source         string    `json:"source,omitempty"`
	FetchedAt      time.Time `json:"fetched_at,omitzero"`
	OperationCount int       `json:"operation_count"`
	Error          string    `json:"error,omitempty"`
}

SchemaInfo is what an operator surface reports about a connection's schema: which version the platform holds, where it came from, when, how many operations it exposes, and — when there is none — why.

type SchemaStore

type SchemaStore interface {
	// GetSchema returns the stored schema for a connection, or an error
	// wrapping ErrSchemaNotFound when there is none.
	GetSchema(ctx context.Context, connection string) (StoredSchema, error)
	// PutSchema writes a connection's schema, replacing any previous
	// one.
	PutSchema(ctx context.Context, s StoredSchema) error
	// DeleteSchema removes a connection's schema, called when the
	// connection is deleted.
	DeleteSchema(ctx context.Context, connection string) error
}

SchemaStore persists a connection's schema. A deployment without one (no database) still works: every connection introspects at registration and holds its schema in memory for the process's life. What the store buys is a restart that does not re-read every endpoint, and an operation index that survives one.

type SignedJWTConfig

type SignedJWTConfig = upstreamauth.SignedJWTConfig

SignedJWTConfig describes the assertion the platform mints when AuthMode is AuthModeSignedJWT. Defined by internal/upstreamauth, which owns the mode for every HTTP-based connection kind; aliased here because it is part of this toolkit's public API.

type StoredSchema

type StoredSchema struct {
	// Connection is the connection name the schema belongs to.
	Connection string
	// Hash is the sha256 of the SDL, hex-encoded. An operation index
	// and its embeddings are keyed on it.
	Hash string
	// SDL is the schema text.
	SDL string
	// Source is SchemaSourceIntrospection or SchemaSourceUpload.
	Source string
	// FetchedAt is when the schema was read or uploaded.
	FetchedAt time.Time
}

StoredSchema is one connection's schema as it is kept between restarts: the SDL, the hash that identifies it, where it came from, and when it was read. FetchedAt is what a strict-mode refusal names, so a caller working from a newer schema than the platform holds can see that is what happened.

type Toolkit

type Toolkit struct {
	// contains filtered or unexported fields
}

Toolkit is the graphql toolkit. One Toolkit manages every registered GraphQL connection, each addressing a different endpoint. Connections are added at startup from the platform's merged YAML+DB config, or at runtime by the admin REST handler when an operator saves one through the portal.

func NewMulti

func NewMulti(cfg MultiConfig) *Toolkit

NewMulti creates the toolkit from every configured instance. Per-connection materialization failures (an authenticator that cannot be built) are logged and skipped so one bad connection cannot block platform startup. Endpoint failures happen at call time and are surfaced through the tool's response, not at startup.

func (*Toolkit) AddConnection

func (t *Toolkit) AddConnection(name string, config map[string]any) error

AddConnection registers a connection at runtime from the generic config map the admin API stores. Satisfies toolkit.ConnectionManager.

func (*Toolkit) Close

func (t *Toolkit) Close() error

Close releases every connection's idle transport.

func (*Toolkit) ConnOAuthStore

func (t *Toolkit) ConnOAuthStore() connoauth.Store

ConnOAuthStore returns the wired OAuth token store, or nil.

func (*Toolkit) Connection

func (t *Toolkit) Connection() string

Connection returns the default connection name for audit logging.

func (*Toolkit) HasConnection

func (t *Toolkit) HasConnection(name string) bool

HasConnection reports whether a connection is registered.

func (*Toolkit) HydrateSchemas

func (t *Toolkit) HydrateSchemas(ctx context.Context)

HydrateSchemas brings every registered connection's schema up: the stored one when there is one, a fresh introspection otherwise. The platform calls it once after wiring the schema store, so a restart does not re-read every endpoint and a first start reads them all.

Connections are hydrated concurrently. They are independent network reads against different endpoints, and doing them in sequence would make startup cost the sum of every upstream's latency rather than the slowest one's.

Failures are recorded on the connection and logged, never returned: an endpoint that is down at startup must not stop the platform serving every other connection. The caller bounds the whole pass with the context it passes.

func (*Toolkit) IndexItems

func (t *Toolkit) IndexItems(name string) (schemaHash string, items map[string]string, ok bool)

IndexItems returns the text each of a connection's operations is embedded from, keyed by operation id, together with the schema hash those operations belong to. It is what the platform's index-jobs consumer reads: the toolkit owns what an operation's indexable text is, and the consumer owns where the resulting vectors are written.

func (*Toolkit) Kind

func (*Toolkit) Kind() string

Kind returns the connection-instance kind discriminator.

func (*Toolkit) ListConnections

func (t *Toolkit) ListConnections() []toolkit.ConnectionDetail

ListConnections enumerates the registered connections for list_connections and the admin UI. OperationCount is how many operations the current schema exposes, so an operator can see at a glance whether a connection's schema was read.

func (*Toolkit) Name

func (t *Toolkit) Name() string

Name returns the toolkit instance name.

func (*Toolkit) Operations

func (t *Toolkit) Operations(name string) (ops []gqlschema.Operation, schemaHash string, ok bool)

Operations returns a connection's current operation index together with the schema hash it belongs to. The index-jobs source reads it to build the text it embeds.

func (*Toolkit) RefreshSchema

func (t *Toolkit) RefreshSchema(ctx context.Context, name string) error

RefreshSchema reads a connection's schema from its endpoint by introspection, stores it, and rebuilds the operation index. It is what the admin refresh action calls, and what a connection falls back to when nothing is stored.

An endpoint with introspection disabled fails here with a message saying so; that message is recorded on the connection and reported by SchemaInfo, so the state is a named cause rather than an operation index that is silently empty.

func (*Toolkit) RegisterTools

func (t *Toolkit) RegisterTools(s *mcp.Server)

RegisterTools registers this toolkit's tools with the MCP server.

func (*Toolkit) ReloadConnection

func (t *Toolkit) ReloadConnection(name string) error

ReloadConnection drops and rebuilds a connection so a config change takes effect, then brings its schema back up. In-flight OAuth refresh tokens persist through the unified connoauth store.

func (*Toolkit) ReloadVectors

func (t *Toolkit) ReloadVectors(ctx context.Context, name string)

ReloadVectors re-reads a connection's persisted embeddings. The index-jobs consumer calls it after a successful pass so a connection that was ranking lexically starts ranking semantically without a restart.

func (*Toolkit) RemoveConnection

func (t *Toolkit) RemoveConnection(name string) error

RemoveConnection drops a connection and closes its idle transports. The stored schema is dropped with it: a connection that is gone should not leave an operation index behind for a later connection of the same name to inherit.

func (*Toolkit) SchemaInfo

func (t *Toolkit) SchemaInfo(name string) (SchemaInfo, error)

SchemaInfo reports what the platform holds for one connection.

func (*Toolkit) SchemaInfos

func (t *Toolkit) SchemaInfos() []SchemaInfo

SchemaInfos reports every registered connection's schema state, for the admin surface and for the index-jobs source's gap detection.

func (*Toolkit) SearchOperations

func (t *Toolkit) SearchOperations(ctx context.Context, query string, perConnLimit int) []RankedConnectionOperation

SearchOperations ranks operations across every connection on this toolkit against a free-form query, returning up to perConnLimit per connection. It is the federation seam behind the universal search tool's endpoints group: the same ranking graphql_discover exposes, aggregated across connections instead of scoped to one.

Per-connection route policy is applied first, so a federated search never surfaces an operation a scoped graphql_discover call would have hidden.

func (*Toolkit) SetAuthEvents

func (t *Toolkit) SetAuthEvents(w *authevents.Writer)

SetAuthEvents wires the audit-event writer into the toolkit and into every already-materialized authenticator, so an outbound token refresh emits its lifecycle event.

func (*Toolkit) SetConnOAuthStore

func (t *Toolkit) SetConnOAuthStore(s connoauth.Store)

SetConnOAuthStore wires the unified OAuth token store, required for the authorization_code grant, and re-threads it through every already-materialized authenticator.

func (*Toolkit) SetEmbeddingProvider

func (t *Toolkit) SetEmbeddingProvider(p embedding.Provider)

SetEmbeddingProvider wires the provider that embeds a caller's query for semantic and hybrid ranking. Operation vectors are never computed here: they are written by the platform's index-jobs consumer and read through the VectorReader.

func (*Toolkit) SetExportDeps

func (t *Toolkit) SetExportDeps(deps ExportDeps)

SetExportDeps wires the platform-side dependencies for graphql_export.

func (*Toolkit) SetMemBudget

func (t *Toolkit) SetMemBudget(b *membudget.Budget)

SetMemBudget wires the shared in-flight memory budget the buffered tools reserve against before allocating a response buffer. Passing nil leaves the buffered path bounded only by the per-connection read cap.

func (*Toolkit) SetMetrics

func (t *Toolkit) SetMetrics(m *observability.Metrics)

SetMetrics wires the observability recorder and retroactively instruments every already-registered connection's HTTP client, so connections added before metrics were enabled still emit outbound observations. Called once at startup before any listener accepts requests; apigwmetrics.Instrument is idempotent against the same (connection, metrics) pair, so a second call with the same recorder is a no-op rather than a double wrap.

func (*Toolkit) SetQueryProvider

func (*Toolkit) SetQueryProvider(query.Provider)

SetQueryProvider satisfies registry.Toolkit. See SetSemanticProvider.

func (*Toolkit) SetRoutePolicy

func (t *Toolkit) SetRoutePolicy(p RoutePolicy)

SetRoutePolicy wires the per-operation authorization check.

func (*Toolkit) SetSchema

func (t *Toolkit) SetSchema(ctx context.Context, name string, payload []byte) error

SetSchema installs a schema an operator supplied, accepting either SDL or an introspection result. It is the path for an endpoint that disables introspection, where the platform cannot read the schema for itself.

func (*Toolkit) SetSchemaStore

func (t *Toolkit) SetSchemaStore(s SchemaStore)

SetSchemaStore wires the store a connection's schema is kept in between restarts. Passing nil leaves schemas in memory only.

func (*Toolkit) SetSemanticProvider

func (*Toolkit) SetSemanticProvider(semantic.Provider)

SetSemanticProvider satisfies registry.Toolkit. A GraphQL endpoint carries its own schema and is not a table catalog, so there is nothing for the semantic layer to enrich here.

func (*Toolkit) SetVectorReader

func (t *Toolkit) SetVectorReader(r VectorReader)

SetVectorReader wires the reader of persisted operation embeddings.

func (*Toolkit) Tools

func (t *Toolkit) Tools() []string

Tools returns the tool names this toolkit registers. graphql_export is present only when the platform wired the export dependencies, so the list matches what a client can actually call.

type VectorReader

type VectorReader interface {
	// LoadVectors returns the persisted vectors for one connection's
	// schema version, keyed by operation id. An empty map means the
	// schema has not been indexed yet; ranking falls back to lexical
	// and says so.
	LoadVectors(ctx context.Context, connection, schemaHash string) (map[string][]float32, error)
}

VectorReader loads the operation embeddings written by the platform's index-jobs consumer. The toolkit only reads them: a connection never embeds its own operations on a request path, the same way the API gateway's connections never embed their catalog.

Jump to

Keyboard shortcuts

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