Add the GraphQL SDL and Apollo Federation compiler#20
Open
OmarAlJarrah wants to merge 5 commits into
Open
Conversation
… the IR Add a GraphQL compiler that maps SDL schemas onto the spec-agnostic IR, the GraphQL counterpart to the OpenAPI compiler. It parses at the SDL level so it sees exactly what is written: no introspection built-ins are injected, undefined federation directives never abort the parse, and `extend` occurrences are assembled with per-member provenance. Type mapping: - object and interface types become models (interfaces are Abstract; `implements A & B` populates Implements); - input objects become InputOnly models, and `@oneOf` inputs become key-tagged exclusive unions rather than models of optional fields; - union types become `__typename`-tagged unions; - enums become closed string enums; custom scalars become opaque nil-base scalars; built-in scalars map to primitives, with ID kept as a distinct named string scalar; - non-null `!` maps to Required plus a non-nullable ref and its absence to a nullable ref, per layer, so every `[T!]!` combination is captured; - field arguments become Property.Args at any depth; defaults, `@deprecated`, and descriptions map to the Values channel, Deprecation, and Docs. The three root types become one service with query, mutation, and subscription groups; subscriptions carry server-streaming semantics and a GraphQLBinding. Federation v1 and v2 are both supported, with v2 detected from the `@link` to the federation spec URL. Entities (`@key`), `@shareable`, `@external`, `@requires`, `@provides`, `@override`, `@interfaceObject`, `@composeDirective`, and `@tag` are preserved losslessly under the `federation:` extension namespace; `@inaccessible` additionally lowers to Access/Visibility. All other directive applications are preserved under `graphql:` as ordered argument arrays, and directive definitions under a document-level inventory. Tests: a golden IR snapshot of a full schema; a conformance corpus of one minimal schema per capability row (GraphQL and Federation) with focused assertions and byte-exact goldens; and a round-trip property over every corpus document.
Make the SDL parser an injectable per-instance seam so the parser-panic recovery path is exercisable without any mutable global state, enrich the input-object and directive fixtures to cover every literal kind (list, object, enum symbol, and boolean defaults, plus directive arguments of each kind), and add white-box tests for the pure helpers and defensive branches (naming, identity, provenance, value lowering and JSON rendering, extension assembly, and the load error paths).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
compilers/graphql, a compiler that lowers GraphQL SDL schemas — plainschemas and Apollo Federation v1/v2 subgraphs — into the spec-agnostic IR. It is
the GraphQL counterpart to
compilers/openapiand follows the same shape: aCompilerimplementingcompilers.Compiler, a pointer-derived identity scheme(
ids.go), typed diagnostics with stable slash-namespaced codes (diag.go),per-compile options (
options.go), and no file I/O — bytes arrive incompilers.Source.Parsing uses
github.com/vektah/gqlparser/v2at the SDL level(
parser.ParseSchemas), so the compiler sees exactly what is written: nointrospection built-ins are injected, undefined federation directives never
abort the parse, and
extendoccurrences arrive separately and are assembledwith per-member provenance.
GraphQL coverage
Model; interfaces areAbstract, andimplements A & B(including interfaces implementing interfaces) populatesModel.Implements.InputOnlymodels;@oneOfinputs →Union{Exclusive, WireTagged}with a variant per field (key-tagged, no internal discriminator),never a model of optional fields.
__typename-taggedUnionwith an internal discriminatormapping each member to its type.
Enum; custom scalars → opaque nil-baseScalar(with@specifiedBypreserved); built-in scalars → primitives,with
IDkept as a distinct named string scalar so it is not conflated withString.Property, arguments →Property.Argsat any depth; non-null!maps toRequired+ a non-nullable ref and its absence to a nullable ref,applied per layer so every
[T!]!combination is captured. List wrappers hoistreal
Listnodes. Defaults land on the Values channel as exactBigVals (nofloat64);@deprecated→Deprecation; descriptions →Docs.field becomes an
Operationwith aGraphQLBinding. Query fields are markedside-effect-free; subscriptions carry
StreamingServer+ResponseStream.graphql:namespace, with the directive-definition inventory at documentlevel.
Federation coverage (v1 and v2)
@linkto the federation spec URL (v2); a v1subgraph using federation directives without
@linkis detected as v1.@key,@shareable,@external,@requires,@provides,@override,@interfaceObject,@composeDirective,@extends, and@tagare preservedlosslessly under the
federation:extension namespace;@keymarks entities.@inaccessibleadditionally lowers toAccess = "internal"on types andVisibility.Noneon fields, alongside verbatim preservation.extend type/extend schemaare assembled into their base (a base owned byanother subgraph is synthesized), with the extend occurrences recorded under
graphql:extends.Where GraphQL has no structural home in the IR, information is preserved in
namespaced
Extensionsrather than dropped: the built-inIDscalar carries agraphql:builtin-scalarmarker, and a@oneOf-derived union carries agraphql:oneOfInputmarker so an emitter can recover its input-only nature(
Unionhas noInputOnlyfield). No IR structs were changed.Test plan
surface (
testdata/golden/graphql/social.graphql).matrix that GraphQL/Federation can express — objects, interfaces, unions,
@oneOfinputs, enums, custom scalars, input objects, field arguments, thefour nullability states, recursive types, the operation surface, deprecation,
directives, docs, and federation v1 and v2 — each with a focused
capability-specific assertion plus a byte-exact golden IR snapshot.
round-trips
compile → MarshalJSON → UnmarshalJSON → MarshalJSONbyte-stably.internal/archtestgains acompilers/graphqlrulerestricting its imports to
ir, the compiler contract, and gqlparser.go build ./...,go vet ./...,gofmt -l .,golangci-lint run, andgo test ./...(including-raceon the new package) are all clean.