graphql

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package graphql exposes graphdb over GraphQL with per-tenant schema isolation.

GenerateSchemaForTenant builds a schema scoped to a single tenant's labels so introspection cannot leak other tenants' metadata; GenerateSchema is the tenant-blind variant for single-tenant or CLI use. (Richer variants add mutations, filtering, aggregation, depth/complexity limits — see the GenerateSchemaWith* functions.) NewGraphQLHandler wraps a schema as an HTTP handler and threads the request context through to resolvers, which read the caller's tenant from it.

Node and edge property values serialize as a JSON string built through the shared storage converter (storage.PropertiesToJSON), so arrays, nested objects, and null round-trip correctly.

Index

Constants

View Source
const (
	TopicNodeCreated = "node.created"
	TopicNodeUpdated = "node.updated"
	TopicNodeDeleted = "node.deleted"
	TopicEdgeCreated = "edge.created"
	TopicEdgeUpdated = "edge.updated"
	TopicEdgeDeleted = "edge.deleted"
)

Topic names for subscriptions

Variables

This section is empty.

Functions

func ExecuteQuery

func ExecuteQuery(ctx context.Context, query string, schema graphql.Schema) *graphql.Result

ExecuteQuery executes a GraphQL query against a schema. The ctx is threaded into graphql.Params so resolvers can read it via p.Context — used by audit A6c-graphql-resolvers (the next PR) to extract the caller's tenant ID.

Audit A6c-graphql-ctx (2026-05-08): pre-fix, this function dropped the request context entirely; resolvers ran with context.Background(), so JWT-derived tenant scoping was invisible to GraphQL.

func ExecuteQueryWithVariables

func ExecuteQueryWithVariables(ctx context.Context, query string, schema graphql.Schema, variables map[string]any) *graphql.Result

ExecuteQueryWithVariables executes a GraphQL query with variables. See ExecuteQuery for the rationale on the ctx parameter.

func ExecuteWithComplexity

func ExecuteWithComplexity(schema graphql.Schema, query string, maxComplexity int, variableValues map[string]any) *graphql.Result

ExecuteWithComplexity executes a GraphQL query with complexity validation

func ExecuteWithDepthLimit

func ExecuteWithDepthLimit(schema graphql.Schema, query string, maxDepth int, variableValues map[string]any) *graphql.Result

ExecuteWithDepthLimit executes a GraphQL query with depth validation

func GenerateSchema

func GenerateSchema(gs *storage.GraphStorage) (graphql.Schema, error)

GenerateSchema generates a GraphQL schema from the storage layer (tenant-blind — discovers labels across every tenant). Used by CLI and single-tenant deployments. API callers should use GenerateSchemaForTenant so introspection (`__schema`) doesn't leak foreign-tenant label names — see audit A9 (#36).

Masking is disabled (deps = nil). CLI-mode builds don't need per-tenant masking — single-tenant deployments have no tenant boundary to mask across.

func GenerateSchemaForTenant

func GenerateSchemaForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)

GenerateSchemaForTenant generates a GraphQL schema scoped to the given tenant's labels. Audit A9 (2026-05-08): closes the introspection metadata leak where a tenant-A caller running `{ __schema { types } }` would see every other tenant's labels.

Resolver closures inside the schema already extract tenantID from p.Context via A6c-graphql-resolvers (#24), so query-result scoping was already correct; this fix is purely about the introspection / type-registry surface.

deps is the per-server masking hookup; nil disables masking. Production callers pass non-nil deps; tests pass nil.

func GenerateSchemaWithAggregation

func GenerateSchemaWithAggregation(gs *storage.GraphStorage) (graphql.Schema, error)

GenerateSchemaWithAggregation generates a GraphQL schema with aggregation support (tenant-blind). API callers should use GenerateSchemaWithAggregationForTenant per audit A9 (#36).

Masking is disabled (deps = nil). Property discovery samples across all tenants, which is correct for this tenant-blind / single-tenant schema.

func GenerateSchemaWithAggregationForTenant

func GenerateSchemaWithAggregationForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)

GenerateSchemaWithAggregationForTenant scopes the schema to one tenant's labels. Audit A9.

deps is the F3 masking hookup; nil disables masking. Property discovery is scoped to the requesting tenant's own nodes so another tenant's property-key names never surface in this tenant's schema introspection (the schema-side counterpart of the A6c resolver-scoping).

func GenerateSchemaWithComplexity

func GenerateSchemaWithComplexity(gs *storage.GraphStorage, config *ComplexityConfig) (graphql.Schema, error)

GenerateSchemaWithComplexity generates a GraphQL schema with complexity analysis

func GenerateSchemaWithCursors

func GenerateSchemaWithCursors(gs *storage.GraphStorage) (graphql.Schema, error)

GenerateSchemaWithCursors generates a GraphQL schema with cursor-based pagination

func GenerateSchemaWithDepthLimit

func GenerateSchemaWithDepthLimit(gs *storage.GraphStorage, maxDepth int) (graphql.Schema, error)

GenerateSchemaWithDepthLimit generates a GraphQL schema with query depth limiting

func GenerateSchemaWithEdges

func GenerateSchemaWithEdges(gs *storage.GraphStorage) (graphql.Schema, error)

GenerateSchemaWithEdges generates a GraphQL schema with edge traversal capabilities (tenant-blind). API callers should use GenerateSchemaWithEdgesForTenant per audit A9 (#36).

Masking is disabled (deps = nil).

func GenerateSchemaWithEdgesForTenant

func GenerateSchemaWithEdgesForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)

GenerateSchemaWithEdgesForTenant scopes the schema's type registry to one tenant's labels. Audit A9 (2026-05-08) — closes the introspection metadata leak.

deps is the F3 masking hookup; nil disables masking.

func GenerateSchemaWithFiltering

func GenerateSchemaWithFiltering(gs *storage.GraphStorage) (graphql.Schema, error)

GenerateSchemaWithFiltering generates a GraphQL schema with filtering support (tenant-blind). API callers should use GenerateSchemaWithFilteringForTenant per audit A9 (#36).

Masking is disabled (deps = nil).

func GenerateSchemaWithFilteringForTenant

func GenerateSchemaWithFilteringForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)

GenerateSchemaWithFilteringForTenant scopes the schema to one tenant's labels. Audit A9.

deps is the F3 masking hookup; nil disables masking.

func GenerateSchemaWithLimits

func GenerateSchemaWithLimits(gs *storage.GraphStorage, config *LimitConfig) (graphql.Schema, error)

GenerateSchemaWithLimits generates a GraphQL schema with filtering and result limits (tenant-blind). API callers should use GenerateSchemaWithLimitsForTenant per audit A9 (#36).

Masking is disabled (deps = nil); use GenerateSchemaWithLimitsForTenant for the production path that needs per-tenant masking.

func GenerateSchemaWithLimitsForTenant

func GenerateSchemaWithLimitsForTenant(gs *storage.GraphStorage, config *LimitConfig, tenantID string, deps *MaskingDeps) (graphql.Schema, error)

GenerateSchemaWithLimitsForTenant scopes the schema's type registry to one tenant's labels. Audit A9 (2026-05-08).

deps is the F3 masking hookup; nil disables masking. The pkg/api server passes the server's PolicyStore + Masker.

func GenerateSchemaWithMutations

func GenerateSchemaWithMutations(gs *storage.GraphStorage) (graphql.Schema, error)

GenerateSchemaWithMutations generates a GraphQL schema with mutation support (tenant-blind). API callers should use GenerateSchemaWithMutationsForTenant per audit A9 (#36).

Masking is disabled (deps = nil).

func GenerateSchemaWithMutationsForTenant

func GenerateSchemaWithMutationsForTenant(gs *storage.GraphStorage, tenantID string, deps *MaskingDeps) (graphql.Schema, error)

GenerateSchemaWithMutationsForTenant scopes the schema to one tenant's labels. Audit A9.

deps is the F3 masking hookup; nil disables masking.

func GenerateSchemaWithSearch

func GenerateSchemaWithSearch(gs *storage.GraphStorage, searchIndex *search.FullTextIndex, deps *MaskingDeps) (graphql.Schema, error)

GenerateSchemaWithSearch creates a GraphQL schema with full-text search.

deps is the F3 masking hookup; nil disables masking on the search-result node properties (the result type contains the matched node's full property bag JSON-encoded; masking flows through there).

func GenerateSchemaWithSorting

func GenerateSchemaWithSorting(gs *storage.GraphStorage) (graphql.Schema, error)

GenerateSchemaWithSorting generates a GraphQL schema with sorting support

func GenerateSchemaWithSubscriptions

func GenerateSchemaWithSubscriptions(gs *storage.GraphStorage, ps *pubsub.PubSub) (graphql.Schema, error)

GenerateSchemaWithSubscriptions creates a GraphQL schema with subscription support

func GetDepthLimit

func GetDepthLimit(schema *graphql.Schema) (int, bool)

GetDepthLimit retrieves the depth limit for a schema

func PublishEdgeCreated

func PublishEdgeCreated(ps *pubsub.PubSub, edge *storage.Edge)

PublishEdgeCreated publishes an edge creation event

func PublishEdgeDeleted

func PublishEdgeDeleted(ps *pubsub.PubSub, edge *storage.Edge)

PublishEdgeDeleted publishes an edge deletion event

func PublishEdgeUpdated

func PublishEdgeUpdated(ps *pubsub.PubSub, edge *storage.Edge)

PublishEdgeUpdated publishes an edge update event

func PublishNodeCreated

func PublishNodeCreated(ps *pubsub.PubSub, node *storage.Node)

PublishNodeCreated publishes a node creation event

func PublishNodeDeleted

func PublishNodeDeleted(ps *pubsub.PubSub, node *storage.Node)

PublishNodeDeleted publishes a node deletion event

func PublishNodeUpdated

func PublishNodeUpdated(ps *pubsub.PubSub, node *storage.Node)

PublishNodeUpdated publishes a node update event

func RegisterDepthLimitedSchema

func RegisterDepthLimitedSchema(wrapper *DepthLimitedSchema)

RegisterDepthLimitedSchema registers a depth-limited schema

func SubscribeToEdgeCreated

func SubscribeToEdgeCreated(ps *pubsub.PubSub, ctx context.Context, edgeType string) (*pubsub.Subscription, error)

SubscribeToEdgeCreated subscribes to edge creation events

func SubscribeToEdgeDeleted

func SubscribeToEdgeDeleted(ps *pubsub.PubSub, ctx context.Context, edgeID uint64) (*pubsub.Subscription, error)

SubscribeToEdgeDeleted subscribes to edge deletion events for a specific edge

func SubscribeToEdgeUpdated

func SubscribeToEdgeUpdated(ps *pubsub.PubSub, ctx context.Context, edgeID uint64) (*pubsub.Subscription, error)

SubscribeToEdgeUpdated subscribes to edge update events for a specific edge

func SubscribeToNodeCreated

func SubscribeToNodeCreated(ps *pubsub.PubSub, ctx context.Context, label string) (*pubsub.Subscription, error)

SubscribeToNodeCreated subscribes to node creation events

func SubscribeToNodeDeleted

func SubscribeToNodeDeleted(ps *pubsub.PubSub, ctx context.Context, nodeID uint64) (*pubsub.Subscription, error)

SubscribeToNodeDeleted subscribes to node deletion events for a specific node

func SubscribeToNodeUpdated

func SubscribeToNodeUpdated(ps *pubsub.PubSub, ctx context.Context, nodeID uint64) (*pubsub.Subscription, error)

SubscribeToNodeUpdated subscribes to node update events for a specific node

func ValidateComplexityConfig

func ValidateComplexityConfig(config *ComplexityConfig) error

ValidateComplexityConfig validates the complexity configuration

func ValidateLimitConfig

func ValidateLimitConfig(config *LimitConfig) error

ValidateLimitConfig validates the limit configuration

func ValidateQueryComplexity

func ValidateQueryComplexity(query string, config *ComplexityConfig, variableValues map[string]any) (int, error)

ValidateQueryComplexity validates a query against the complexity limit

func ValidateQueryDepth

func ValidateQueryDepth(query string, maxDepth int) error

ValidateQueryDepth validates a query against the depth limit

Types

type BatchFunc

type BatchFunc func(ctx context.Context, keys []string) ([]any, []error)

BatchFunc is called with a batch of keys and returns results and errors

type ComplexityConfig

type ComplexityConfig struct {
	MaxComplexity    int // Maximum allowed complexity score
	ListMultiplier   int // Multiplier for list fields (default: 10)
	DefaultListLimit int // Default limit for lists without explicit limit (default: 100)
}

ComplexityConfig defines configuration for query complexity analysis

type DataLoader

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

DataLoader batches and caches requests

func NewDataLoader

func NewDataLoader(batchFn BatchFunc, config *DataLoaderConfig) *DataLoader

NewDataLoader creates a new DataLoader

func NewIncomingEdgesDataLoader

func NewIncomingEdgesDataLoader(gs *storage.GraphStorage) *DataLoader

NewIncomingEdgesDataLoader creates a DataLoader for loading incoming edges

func NewNodeDataLoader

func NewNodeDataLoader(gs *storage.GraphStorage) *DataLoader

NewNodeDataLoader creates a DataLoader for loading nodes by ID

func NewOutgoingEdgesDataLoader

func NewOutgoingEdgesDataLoader(gs *storage.GraphStorage) *DataLoader

NewOutgoingEdgesDataLoader creates a DataLoader for loading outgoing edges

func (*DataLoader) Clear

func (dl *DataLoader) Clear(key string)

Clear removes a key from the cache

func (*DataLoader) ClearAll

func (dl *DataLoader) ClearAll()

ClearAll removes all keys from the cache

func (*DataLoader) Load

func (dl *DataLoader) Load(ctx context.Context, key string) (any, error)

Load loads a value for the given key, batching and caching as configured

func (*DataLoader) Prime

func (dl *DataLoader) Prime(key string, value any)

Prime adds a value to the cache without calling the batch function

type DataLoaderConfig

type DataLoaderConfig struct {
	BatchSize int           // Maximum number of keys to batch together
	Wait      time.Duration // How long to wait before dispatching a batch
}

DataLoaderConfig configures a DataLoader instance

type DataLoaderContext

type DataLoaderContext struct {
	Nodes         *DataLoader
	OutgoingEdges *DataLoader
	IncomingEdges *DataLoader
}

DataLoaderContext holds all DataLoaders for a request

func GenerateSchemaWithDataLoader

func GenerateSchemaWithDataLoader(gs *storage.GraphStorage) (graphql.Schema, *DataLoaderContext)

GenerateSchemaWithDataLoader creates a GraphQL schema with DataLoader integration

type DepthLimitedSchema

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

DepthLimitedSchema wraps a schema with automatic depth validation

type EdgeEvent

type EdgeEvent struct {
	Type     string // "created", "updated", "deleted"
	Edge     *storage.Edge
	EdgeType string // For filtering by edge type
}

EdgeEvent represents an edge change event

type FilterCondition

type FilterCondition struct {
	Field    string
	Operator string
	Value    any
}

FilterCondition represents a single filter condition

type FilterExpression

type FilterExpression struct {
	// For simple conditions
	Conditions []FilterCondition

	// For logical operators
	AND []FilterExpression
	OR  []FilterExpression
	NOT *FilterExpression
}

FilterExpression represents a filter that can be simple or logical

type GraphQLError

type GraphQLError struct {
	Message string `json:"message"`
}

GraphQLError represents a GraphQL error

type GraphQLHandler

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

GraphQLHandler handles GraphQL HTTP requests

func NewGraphQLHandler

func NewGraphQLHandler(schema graphql.Schema) *GraphQLHandler

NewGraphQLHandler creates a new GraphQL HTTP handler

func (*GraphQLHandler) ServeHTTP

func (h *GraphQLHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP handles HTTP requests for GraphQL queries

type GraphQLRequest

type GraphQLRequest struct {
	Query         string         `json:"query"`
	Variables     map[string]any `json:"variables,omitempty"`
	OperationName string         `json:"operationName,omitempty"`
}

GraphQLRequest represents a GraphQL HTTP request

type GraphQLResponse

type GraphQLResponse struct {
	Data   any            `json:"data,omitempty"`
	Errors []GraphQLError `json:"errors,omitempty"`
}

GraphQLResponse represents a GraphQL HTTP response

type LimitConfig

type LimitConfig struct {
	DefaultLimit int // Default limit when no limit specified
	MaxLimit     int // Maximum allowed limit
}

LimitConfig defines limits for query results

type MaskingDeps

type MaskingDeps struct {
	Store  *masking.PolicyStore
	Masker *masking.Masker
}

MaskingDeps bundles the per-tenant masking dependencies that GraphQL resolvers need to apply a tenant's masking policy at response-shaping time. The deps are server-lifecycle and captured in resolver closures (mirroring how *storage.GraphStorage is captured); the actual tenant and policy are resolved per-request via context.

A nil *MaskingDeps means masking is disabled for this schema (e.g., CLI builds, tests, or schema variants that aren't on the production API path). All resolver hook sites tolerate nil deps gracefully.

type NodeEvent

type NodeEvent struct {
	Type  string // "created", "updated", "deleted"
	Node  *storage.Node
	Label string // For filtering by label
}

NodeEvent represents a node change event

type OrderByInput

type OrderByInput struct {
	Field     string
	Direction string
}

OrderByInput represents sorting criteria

Jump to

Keyboard shortcuts

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