Adopt graphql_client codegen for typed GraphQL queries - #196
Draft
leynos wants to merge 7 commits into
Draft
Conversation
added 7 commits
July 9, 2026 11:17
Vendor GitHub's published public schema (72,911 lines, from https://docs.github.com/public/fpt/schema.docs.graphql) at `graphql/schema.docs.graphql` with a README recording provenance and the refresh procedure. Add the `graphql_client` 0.16 dependency (default features only; its optional reqwest transports stay off). Clean-build baseline before any codegen derives: 46 s (`cargo build --all-features` on this machine), recorded for the ExecPlan's build-time tolerance.
Move the issue lookup, review-thread listing, per-thread comment
paging, reviews listing, PR-for-branch lookup, and the
`resolveReviewThread` mutation onto `graphql_client` 0.16 codegen.
Every operation now lives in a named `.graphql` document under
`graphql/`, validated against the vendored GitHub schema at compile
time — a malformed query fails the build rather than a runtime
request.
Typed execution surface on `GraphQLClient`:
- `run_operation` executes a codegen'd operation and returns its
generated `ResponseData`;
- `run_operation_as` and `paginate_operation_as` build the
schema-checked envelope from the generated operation but deserialize
into hand-written domain structs, preserving lenient behaviour the
generated types cannot express (notably the documented client-side
default treating threads missing `isOutdated` as current) and
keeping `serde_path_to_error` paths byte-identical;
- a `CursorVariables` trait plus `paginate_operation` drive typed
cursor pagination with the same `MAX_PAGES` cap and discard-on-error
semantics as `paginate_all`, covered by new red-green rstest units
against scripted loopback stubs.
A shared `src/api/scalars.rs` maps GitHub's custom scalars
(`DateTime`, `URI`, `HTML`); the alias names must match the schema
scalars, so tightly-scoped `expect` attributes cover the
capitalized-acronym lint.
`src/graphql_queries.rs` is deleted. The string-based `run_query`
retains one production consumer: the resolve path's review-comments
paging query selects a `reviewComments` field that does not exist in
GitHub's published schema — a latent production bug exposed by the
codegen migration (only mocked tests kept it green). Its redesign onto
`reviewThreads` follows in a separate commit.
Issue-not-found now surfaces as a semantic `VkError::BadResponse`
("issue #N not found") rather than the previous accidental serde
failure; no test pinned the old text (recorded in the ExecPlan
decision log).
The resolve path's paging query selected `PullRequest.reviewComments`, a field that does not exist in GitHub's published GraphQL schema — `vk resolve` could never locate a thread against the live API, and only fully mocked tests kept the path green. Codegen validation exposed the bug the moment the operation moved to a `.graphql` document. Replace it with `ThreadForCommentQuery`, which pages `repository.pullRequest.reviewThreads` (with each thread's first 100 comments) and scans for the requested comment id, returning the owning thread's GraphQL id. Comments are matched by `fullDatabaseId` — the schema deprecates `databaseId` in favour of the 64-bit-safe field — via a new `BigInt` scalar alias carried as a decimal string. The `resolveReviewThread` mutation also moves to a typed operation. Behavioural guarantees are preserved: not-found mapping, the missing-cursor and non-advancing-cursor aborts, and the mockall fetcher seam with its sequence-based unit tests. Accepted limitation, documented in the module: a comment beyond the first 100 comments of a single thread is not found — the same class of cap as the old flat query's page size. `src/graphql_queries.rs` remnants are gone; every operation now lives in a schema-validated document. The string-based query surface (`run_query`, `fetch_page`, `paginate_all`) is retained deliberately as a fully-tested raw escape hatch for the planned shared-crate extraction (see the ExecPlan decision log).
Delete `run_query`, `fetch_page`, `paginate_all`, the `paginate` free function, and the `Query` newtype now that every operation executes through the typed codegen path. This corrects the previous commit's note that the surface would be retained: the removal was completed with every characterization assertion ported intact (recorded in the ExecPlan decision log). The retry, error-detail, and transcript characterization tests now exercise the shared `run_payload` core directly with identical assertions and scripted servers; the cursor-capture test moved to the typed pagination path, asserting the paginator's cursor lands in `variables.cursor` through the `CursorVariables` impl. The non-object-variables rejection test is retired rather than ported: typed `Variables` structs are objects by construction, so the guarded failure mode cannot exist. `operation_name` stays: the transport still derives request-context strings from it.
Extract the codegen derives, envelope structs, `CursorVariables` impls, and domain-type definitions from `src/review_threads.rs` (505 lines) and `src/reviews.rs` (425 lines) into `src/review_threads/wire.rs` and `src/reviews/wire.rs`, bringing every product source back under the 400-line limit. Public paths are preserved through `pub use wire::…` re-exports, so no consumer changes. Pure refactor: no behaviour or signature changes. `src/review_threads/tests.rs` (655 lines, tests-only) already exceeded the limit at HEAD and is left for a follow-up.
Rewrite the design document's networking section for the codegen era: named operation documents under `graphql/` validated at compile time, `run_operation` and the `_as` decoding escape hatches, `CursorVariables` pagination with the 1000-page cap, the scalars module, and the wire submodule pattern. Update the resolve-threads description to the `reviewThreads`/`fullDatabaseId` lookup, noting the latent `reviewComments` bug the migration exposed and the documented first-100-comments-per-thread limitation. Add the `graphql/` directory to the repository layout. Record PR 3 milestones, artifacts, and an interim retrospective in the ExecPlan.
Record the zero-finding CodeRabbit review and the stacked draft PR; set the plan status to COMPLETE pending review and merges.
Contributor
There was a problem hiding this comment.
Sorry @leynos, your pull request is larger than the review limit of 150000 diff characters
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
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
This branch delivers PR 3, the final phase of the GitHub API
modernisation governed by
ADR 001:
every GraphQL operation is now a named document under
graphql/,
validated against GitHub's vendored public schema at compile time by
graphql_clientcodegen. A malformed query fails the build rather than aruntime request (demonstrated: a deliberate field typo yields
No field named titleTYPO on Issuefromcargo check).Execplan: docs/execplans/adopt-octocrab.md
— PR 3 of its three phases; stacked on #195 (hyper transport), which
stacks on #194 (octocrab REST).
The migration surfaced and fixes a latent production bug: the resolve
thread-lookup query selected
PullRequest.reviewComments, a field thatdoes not exist in GitHub's published schema —
vk resolvecould neverlocate a thread against the live API, and only fully mocked tests kept it
green. The lookup is redesigned onto
reviewThreads, matching commentsby
fullDatabaseId(the schema deprecatesdatabaseId).Review walkthrough
graphql/
— the vendored schema (72,911 lines, provenance and refresh procedure
in its README) and one document per operation group.
src/api/client/mod.rs
—
run_operation(typed execution returning generatedResponseData) andrun_operation_as(schema-checked query,hand-written deserialization target). The
_asescape hatch is theload-bearing design move: it preserves documented lenient behaviour
the generated types cannot express (threads missing
isOutdatedaretreated as current) and keeps
serde_path_to_errorpathsbyte-identical. The string-based surface (
run_query,fetch_page,paginate_all,Query) is removed, with every characterizationassertion ported to the shared
run_payloadcore.and the
CursorVariablestrait — typed cursor pagination, writtentest-first, preserving the 1,000-page cap and discard-on-error
semantics.
— the redesigned thread lookup, its accepted limitation (a comment
beyond the first 100 comments of one thread is not found; the same
class of cap as the old flat query), and the typed
resolveReviewThreadmutation.(src/review_threads/wire.rs,
src/reviews/wire.rs)
— pure refactor bringing every product source back under the
400-line limit; public paths preserved via re-exports.
Validation
make check-fmt/make lint/make test: pass (215 lib tests plus all integration suites and 11 doctests, 0 failed)cargo test --test e2e -- --ignored e2e_pr_42: pass (transcript replay unchanged)graphql/issue.graphqlfailscargo check; revertedmake markdownlint/make nixie: passcoderabbit review --agent: completed, zero findings (cumulative diff frommain)Notes
ReviewThread,ReviewComment,CommentConnection,PageInfo,PullRequestReview,Issue,User)are unchanged; generated types stay module-private.
issue #N not founderrorrather than the previous accidental serde failure (no test pinned the
old text; recorded in the ExecPlan decision log).
src/review_threads/tests.rs(655 lines, tests-only) already exceededthe 400-line limit before this branch and is left for a follow-up.