Documentation
¶
Overview ¶
Package generator orchestrates the defederator code-generation pipeline.
Index ¶
- func BasicTypeModels(schema *ast.Schema, clientImportPath string) gqlgenConfig.TypeMap
- func DescriptionLines(s string) []string
- func Generate(_ context.Context, cfg *defConfig.Config) error
- func GoConstName(s string) string
- func IsIntrospectionOnly(op *ast.OperationDefinition) bool
- func MarshalEnumPlanSpec(plan *federation.Plan) (string, error)
- func MarshalURLPlanSpec(plan *federation.Plan) (string, error)
- func PlanIntrospectionOps(schema *ast.Schema, doc *ast.QueryDocument) (map[string]IntrospectionInfo, error)
- func QuerySourcesFromGoFile(filename string, log io.Writer) ([]*ast.Source, error)
- func RenderFederationTemplate(cfg *config.Config, fragments []*clientgenv2.Fragment, ...) error
- func ResolveIntrospection(schema *ast.Schema, op *ast.OperationDefinition, fragments FragmentsByName, ...) (json.RawMessage, error)
- func StripFederationTypes(sdl string) (string, error)
- func UsedEnumsInOperations(schema *ast.Schema, doc *ast.QueryDocument) map[string]bool
- func WriteExecFile(outDir, pkg string) error
- type EnumDef
- type EnumValueDef
- type FragmentsByName
- type IntrospectionInfo
- type IntrospectionPlan
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BasicTypeModels ¶
func BasicTypeModels(schema *ast.Schema, clientImportPath string) gqlgenConfig.TypeMap
BasicTypeModels returns the gqlgen model bindings that gqlgenc needs in order to resolve user-defined INPUT_OBJECT and ENUM names during response generation. gqlgenc calls SourceGenerator.Type(name) for any field whose type satisfies IsBasicType(), which is true for both kinds; without an entry in cfg.Models, Type() panics.
Input objects are bound to graphql.String — they only appear as operation arguments and are serialized via JSON, so the Go-side representation can be opaque.
Enums are bound to <clientImportPath>.<EnumName> so SourceGenerator.Type returns a named Go type living in the client package itself. The binder's syntheticNamedType fallback handles the fact that the package being generated does not yet exist on disk.
Callers should merge this map into their existing cfg.Models with their own precedence rule — typically "user-specified bindings win". BasicTypeModels does not mutate its inputs.
func DescriptionLines ¶
DescriptionLines splits a GraphQL description into the sequence of payload strings that a generated Go `// …` comment block should contain — one element per line, with each line's leading/trailing whitespace trimmed.
The template emits `// {{ . }}` for each returned element, so each line is individually prefixed with `// ` (matching genqlient's writeDescription). Blank lines inside multi-paragraph descriptions are preserved as empty strings, which render as bare `//` lines and keep paragraph breaks visible in `go doc` output.
Returns nil for an all-whitespace description so callers can `if len(...) > 0` to suppress an empty comment.
func Generate ¶
Generate runs the full defederator code-generation pipeline for cfg.
When cfg.Verbose is true, per-pattern, per-file, per-literal, per-match, and per-operation progress lines are written to stderr. Otherwise the only user-facing output is the final "defederator: wrote …" line emitted by the CLI (see cmd/defederator/main.go).
func GoConstName ¶
GoConstName converts a GraphQL enum value name (typically SCREAMING_SNAKE_CASE) into a Go constant suffix.
The rules: each underscore is dropped, the character after each underscore (and the first character) is upper-cased, and all other characters are lower-cased. So "UNEXPECTED_ERROR" → "UnexpectedError".
This is a behavioural port of genqlient's unexported `goConstName`:
source: github.com/Khan/genqlient v0.8.1, file generate/util.go (line 52)
Defederator generates enums in genqlient's exact format so callers migrating from genqlient need no source changes. If you bump the genqlient version in go.mod, re-check that file: if its goConstName diverges, update this port and the version tag above in lockstep, with a test case demonstrating the new behaviour.
func IsIntrospectionOnly ¶
func IsIntrospectionOnly(op *ast.OperationDefinition) bool
IsIntrospectionOnly reports whether op selects only introspection meta-fields at the operation root: `__schema`, `__type`, or `__typename`. Operations that mix introspection and business fields return false because defederator can't serve them — the business fields need a subgraph call and there's no supergraph-aware planner to merge that with the introspection response.
Returns false for subscriptions: introspection over subscription streams is outside the GraphQL spec.
func MarshalEnumPlanSpec ¶
func MarshalEnumPlanSpec(plan *federation.Plan) (string, error)
MarshalEnumPlanSpec converts a *federation.Plan to a compact JSON string using enum-keyed format. Unlike MarshalURLPlanSpec, the subgraph URLs are NOT embedded in the spec — instead each fetch uses the join__Graph enum name (e.g. "USERS"). Call execengine.Resolve with a URL map at runtime to get a Plan with real URLs. Use this when the supergraph SDL has placeholder URLs.
func MarshalURLPlanSpec ¶
func MarshalURLPlanSpec(plan *federation.Plan) (string, error)
MarshalURLPlanSpec converts a *federation.Plan to a compact JSON string using URL-keyed format. The subgraph URLs come from the resolved *Subgraph pointers in the plan, so no URL map lookup is needed at runtime.
func PlanIntrospectionOps ¶
func PlanIntrospectionOps( schema *ast.Schema, doc *ast.QueryDocument, ) (map[string]IntrospectionInfo, error)
PlanIntrospectionOps partitions doc's operations into introspection ops and federation ops. The returned map is keyed by operation name; ops not present in the map should go through the federation path.
For each introspection op, this also evaluates the response if it's bakeable (no variable dependencies). Returning eagerly here keeps the codegen pipeline straightforward — the caller doesn't need a second pass.
func QuerySourcesFromGoFile ¶
QuerySourcesFromGoFile parses a Go source file and returns one gqlparser ast.Source per embedded `# @genqlient` query string. Each Source has its Name set to "filename:line" so downstream gqlparser errors point at the original Go file. Exported for use by callers outside the generator package (e.g. migrate's operation-pruning helpers).
Per-literal and per-match progress lines are written to log. Pass io.Discard for silence.
func RenderFederationTemplate ¶
func RenderFederationTemplate( cfg *config.Config, fragments []*clientgenv2.Fragment, operations []*clientgenv2.Operation, operationResponses []*clientgenv2.OperationResponse, structSources []*clientgenv2.StructSource, generateCfg *gqlgencConfig.GenerateConfig, client config.PackageConfig, planSpecs map[string]string, urlMode string, enums []*EnumDef, introspectionByName map[string]IntrospectionInfo, ) error
RenderFederationTemplate renders the federation client template. It mirrors clientgenv2.RenderTemplate but uses the federation-specific template. urlMode is "baked" (default) or "enum"; it controls which NewClient signature is generated. See config.Config.URLMode for the full description.
func ResolveIntrospection ¶
func ResolveIntrospection( schema *ast.Schema, op *ast.OperationDefinition, fragments FragmentsByName, variables map[string]any, ) (json.RawMessage, error)
ResolveIntrospection evaluates op's selection set against schema and returns the JSON-encoded response in the shape an operation's generated Response struct expects (the top-level field map only — no "data" envelope).
variables provides values for any query variables the operation references. A nil or empty map is fine if the operation takes no introspection-bearing variables (the bake path); pass actual call arguments at runtime.
Caller must ensure op selects only introspection fields. Mixing in a business field returns an error rather than silently dropping it.
func StripFederationTypes ¶
StripFederationTypes parses a Federation v2 supergraph SDL and returns a clean SDL containing only the user-visible types and fields, without any federation metadata.
func UsedEnumsInOperations ¶
UsedEnumsInOperations returns the set of enum type names referenced by at least one operation in doc, either as the type of a selected response field or as the type of an operation variable. The check follows non-null and list wrappers down to the base named type and matches against schema.Types[name].Kind == ast.Enum.
This drives lazy enum emission: an enum that no operation touches is dead code, and crucially, registering it as a model collides with a user fragment of the same name (gqlgenc's Fragments() rejects "X is duplicated" if a model X already exists when a fragment X is registered). Lazy emission matches genqlient's behaviour for the same input.
Variable types must be included because gqlgenc's OperationArguments resolves each variable's base type through SourceGenerator.Type(name) at codegen time; omitting an enum used in a variable position causes the same "no model configured for GraphQL type X" panic that omitting a response-field enum does.
func WriteExecFile ¶
WriteExecFile reads the embedded execengine source, replaces its package declaration with pkg, and writes the result to <outDir>/federation_exec.go. The written file gives generated code access to the executor without any defederator import.
Types ¶
type EnumDef ¶
type EnumDef struct {
GoName string
GraphQLName string
Description string
Values []EnumValueDef
}
EnumDef describes a single GraphQL enum to emit as a Go named-string type. The template renders one `type T string` declaration plus a `const ( … )` block and a `var AllT = []T{ … }` slice, matching genqlient's output.
func CollectEnums ¶
CollectEnums walks schema and returns one EnumDef per user-defined enum.
Introspection enums (names beginning `__`) are skipped — gqlgen registers them itself during Init.
Results are sorted: enums by GoName, and within each enum the Values are sorted by GraphQLName. Stable ordering makes the generated client.go diff cleanly across runs.
CollectEnums does not mutate schema and has no side effects; callers decide how to use the returned slice.
type EnumValueDef ¶
EnumValueDef is a single enum value: GraphQL wire name and Go constant name.
type FragmentsByName ¶
type FragmentsByName map[string]*ast.FragmentDefinition
FragmentsByName indexes a query document's fragment definitions by name so the resolver can follow `...FragmentName` spreads. Pass an empty map if the query has no fragments.
type IntrospectionInfo ¶
type IntrospectionInfo struct {
// Name is the operation name as written in the source query.
Name string
// Bakeable is true when the response was fully evaluated at codegen time.
// Response is non-empty in that case; otherwise the runtime resolver must
// be called per request with the call site's arguments.
Bakeable bool
// Response holds the JSON-encoded introspection response when Bakeable.
// Empty otherwise.
Response string
// Variables lists the GraphQL variable names (no `$`) that flow into
// introspection field arguments. Used by the runtime emitter to build a
// `map[string]any` from the generated method's typed parameters.
Variables []string
}
IntrospectionInfo describes one introspection operation's emission strategy. The generator builds a map of these keyed by operation name; the template uses Response (bakeable case) or Variables (runtime case) to emit the right method body.
type IntrospectionPlan ¶
IntrospectionPlan summarises how a single introspection-only operation can be served by defederator.
- Bakeable is true when no argument to any introspection field references a query variable. The response can be evaluated at codegen time and embedded as a JSON constant.
- Variables, when Bakeable is false, lists the variable names (without the leading `$`) that the operation passes into introspection field arguments. The runtime resolver substitutes their values per call.
IntrospectionPlan does not validate that op selects only introspection fields; callers should pair it with IsIntrospectionOnly.
func PlanIntrospection ¶
func PlanIntrospection(op *ast.OperationDefinition) IntrospectionPlan
PlanIntrospection classifies op as either bakeable (codegen-time evaluation) or runtime (depends on at least one query variable). It returns the set of variable names that flow into introspection field arguments, deduplicated and in first-encounter order so generated code can iterate deterministically.
The walk descends into inline fragments because they're part of the same operation; fragment spreads are followed through op.Document.Fragments at the caller's discretion (the resolver does this in step 2).