graphqldocs

package
v3.0.0 Latest Latest
Warning

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

Go to latest
Published: Sep 10, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package graphqldocs reads every raw GraphQL document this repository writes out of the source and judges each one against a GitLab schema.

The validating test transport catches a document the moment a test sends it, which covers most of them and cannot cover all of them: a document reachable by no test still ships, and a document reached only on an error branch is exercised by nobody. This package reads them out of the source instead, so the coverage of the check stops depending on the coverage of the tests.

It loads the whole program with go/packages rather than matching the source with a regular expression, because four of this repository's documents are assembled by concatenating a shared fragment constant and only the type checker knows what the assembled value is. Constants are folded during type checking, so a document written as three pieces is judged as the one string GitLab would receive.

A document that lives in a .graphql file rather than a Go constant is read straight off disk, because an embedded variable is not a constant and folds to nothing, so moving a long document into its own file would otherwise drop it out of the inventory without a word.

What it cannot do is check variables: a document read out of the source has no request behind it, so nothing says which variables a handler will send or what they will hold. That half belongs to the test transport, which sees a real request, and to the recorded request inventory it writes.

What it reads, and what it does not

It reads DefaultPatterns, which holds every document this repository writes. It does not read client-go, which builds another 42 of its own for the achievements, work item, security attribute and terraform state services among others. Those reach GitLab through this server too, and the only thing judging them is the test transport, on whichever ones a test happens to drive. A count of documents from here is this repository's, not the server's whole GraphQL surface.

Who calls it

Four commands call it, and what they ask of the inventory splits them in two.

Two of them judge these documents against a schema, deliberately: cmd/audit_graphql_documents renders the result as the text of a standalone gate, and cmd/audit_1to1 folds the same result into the R-PATH dimension, where a document GitLab refuses is one of the three ways a registered action cannot reach the endpoint it names.

The other two want the inventory for a question of their own.

cmd/audit_readonly_graphql asks what operation type each document carries, and used to find them with a walk of its own that read constants and package-level variables and nothing else. Two detectors of the same thing disagree by construction, and this one reads .graphql files that one could not, so it now builds its index from FromPackages and Standalone and judges the text with its own rule. What it cannot resolve, a document with no Document.Object that its own body walk did not record either, it reports rather than skips.

cmd/audit_graphql_shapes pairs each document it finds with the Go struct that decodes the response, and parses those pairs against the schema itself to get a selection set it can walk. It reads this inventory for the other half of that question: a document here that no pairing carries is one handed to something that audit does not follow, and it is named rather than passed over in silence.

Index

Constants

This section is empty.

Variables

View Source
var ErrNoDocuments = errors.New("no GraphQL documents were found, which means this audit is looking at the wrong thing")

ErrNoDocuments is returned when an audit found nothing to judge, which means it is looking at the wrong tree. A clean exit there would be the silence this package exists to remove, so it is an error rather than an empty result.

Functions

func DefaultPatterns

func DefaultPatterns() []string

DefaultPatterns are the packages an audit loads. Every GraphQL document this repository writes lives under them.

It is a function rather than a variable because a caller that appended to a shared slice would change what every later caller audits, and an audit that silently looks at the wrong tree is the failure this package exists to remove.

Types

type Document

type Document struct {
	// Package is the import path of the package that declares it.
	Package string
	// Name is the constant or variable it is declared as, the file name when
	// it is a standalone .graphql document, or "" when it is written inline at
	// the point of use.
	Name string
	// Object is the constant or variable the document is declared as, and nil
	// for the two shapes no declaration names: a standalone .graphql file and a
	// document written inline where it is used.
	//
	// It is here for a caller that reasons about the source through the type
	// checker rather than about the text: cmd/audit_readonly_graphql resolves a
	// handler's call graph and asks what each object it names holds, so the
	// object is the only thing that joins this inventory to that walk. A
	// document with none cannot be attributed to the handler that sends it,
	// which is a fact that audit reports rather than one it can work around.
	Object types.Object
	// Position is where a reader will find it.
	Position token.Position
	// Text is the folded value, with any shared fragment already spliced in.
	Text string
}

Document is one GraphQL document found in the source.

func Collect

func Collect(dir string, patterns []string, overlay map[string][]byte) ([]Document, error)

Collect loads the packages named by patterns, rooted at dir, and returns every GraphQL document they declare.

The load itself, including the refusal of a package that did not type-check, belongs to goprogram.Load. The overlay is passed straight through: it is how a test supplies source that is not on disk, so a fixture package written in the test file itself is type-checked like any other and the folding of a document assembled from a fragment is exercised for real rather than mocked. Production passes nil.

func FromPackages

func FromPackages(loaded []*packages.Package) []Document

FromPackages gathers the documents declared in packages that are already loaded, which is the half of the inventory that lives in Go source.

It is separate from Collect for the caller that has done the load itself: cmd/audit_readonly_graphql type-checks the same tree to build a call graph, and loading it twice would double the seconds a gate costs for an inventory it already holds in memory. The positions come from the packages' own file set, so a caller that mixes these documents with its own walk of the same packages compares positions that mean the same thing.

func Standalone

func Standalone(dir string, patterns []string) ([]Document, error)

Standalone gathers the documents that live in .graphql files rather than in Go constants.

A constant is the only shape this repository uses today, and it is not the only shape it may use tomorrow: moving a long document into its own file and pulling it in with an embed directive is the obvious next step for readability, and an embedded variable is not a constant, so the type checker folds nothing and the document would leave the inventory without a word. Reading the files directly closes that door before anybody walks through it.

Every failure below the root is propagated rather than skipped. A directory this cannot read is a directory whose documents go unjudged, which is the silence the whole command exists to remove; a root that is not there at all is a question about the patterns, and the package loader answers that one.

The walk goes through os.Root, as cmd/format_md_tables does, so a read is scoped to the tree being audited rather than to whatever a symlink in it points at.

func (Document) Label

func (d Document) Label() string

Label names a document for a report line.

type Options

type Options struct {
	// Dir is the repository root to audit.
	Dir string
	// Patterns are the load patterns; empty means [DefaultPatterns].
	Patterns []string
	// SchemaPath names an SDL file to judge the documents against instead of
	// the pinned one. It is how the live re-probe works: cmd/gen_graphql_schema
	// writes today's schema into a temporary directory and this reads it, so a
	// field GitLab narrowed since the pin is reported as a failure rather than
	// waiting for the next re-pin. It is also how a document meant for a
	// particular self-managed release can be checked against that release.
	SchemaPath string
	// Schema is a schema the caller already has, judged in preference to both
	// the pin and SchemaPath. It exists for the live re-probe, which
	// introspects an instance itself rather than writing SDL to a file first,
	// because it also has to refuse an answer too short to be a GitLab schema
	// and report where the pin and that instance disagree, and both of those
	// need the schema as a value rather than as a path.
	Schema *ast.Schema
	// Provenance names what Schema is, for the line a reader of a refusal
	// needs. Required with Schema and ignored without it.
	Provenance string
	// Overlay supplies source that is not on disk, which is how a test hands
	// the audit a fixture package instead of the repository. Production passes
	// nil.
	Overlay map[string][]byte
}

Options is one configured audit: where to look, what to look at, and what to judge it against.

type Refusal

type Refusal struct {
	// Document is the document that was refused.
	Document Document
	// Reasons are the schema's objections, one per line a report prints.
	//
	// A failure that is not a refusal at all, such as a pin that will not
	// load, arrives here as its single message rather than as an empty list,
	// because a finding with nothing under it reads as a document nobody could
	// explain.
	Reasons []string
}

Refusal is one document the schema will not accept.

type Result

type Result struct {
	// Provenance is the one line saying whose opinion judged these documents:
	// a schema pinned on a recorded day, or one fetched today.
	Provenance string
	// Documents are every document the audit read, in the order a reader walks
	// the repository.
	Documents []Document
	// Refusals are the documents the schema would not accept, a subset of
	// Documents in the same order.
	Refusals []Refusal
}

Result is what one audit found.

func Audit

func Audit(opts Options) (Result, error)

Audit reads every document under the configured patterns and judges each one.

A refused document is a finding in the result, not an error: the error return is for the audit failing to run at all, which is the case a caller must not report as a pass.

Jump to

Keyboard shortcuts

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