From 794dbf030574c88eec81fd6670b2de525593ab22 Mon Sep 17 00:00:00 2001 From: zak <36251971+zrm625@users.noreply.github.com> Date: Sat, 7 Nov 2026 22:14:05 -0500 Subject: [PATCH] fix(github): pull requests load on Enterprise Server again Enterprise Server validates a whole GraphQL document before applying directives, so the listing and summary documents fail there because they name `stack` and `stackEntry`, even under `@include(if: false)`. Enterprise hosts now get documents without those fields, and github.com gets `PullRequestSearchWithStacks` and `PullRequestSummaryWithStacks`, which share one `PullRequestStackFields` fragment with `PullRequestStackMembership`. Co-authored-by: felixlaga Co-Authored-By: Claude Opus 5.5 --- .../source-control-github/graphql.config.ts | 3 +- .../src/server/GitHubPullRequestApi.test.ts | 51 +++-- .../src/server/GitHubPullRequestApi.ts | 71 ++++--- .../src/server/gitHubPullRequestJson.ts | 23 ++- .../src/server/pullRequestBatches.graphql | 114 ++++++----- .../src/server/pullRequestBatches.graphql.ts | 185 +++++++++++------- .../src/server/pullRequestListings.graphql | 32 ++- .../src/server/pullRequestListings.graphql.ts | 102 ++++++---- 8 files changed, 363 insertions(+), 218 deletions(-) diff --git a/packages/source-control-github/graphql.config.ts b/packages/source-control-github/graphql.config.ts index 7244f082e522e..1cbb8433bd41b 100644 --- a/packages/source-control-github/graphql.config.ts +++ b/packages/source-control-github/graphql.config.ts @@ -1,6 +1,7 @@ import { defineConfig } from "@effect/graphql-generator/Config"; -// Public schema: https://docs.github.com/public/fpt/schema.docs.graphql. Gate Enterprise-missing fields with @include. +// Public schema: https://docs.github.com/public/fpt/schema.docs.graphql. Enterprise Server rejects a +// document naming a field it lacks, even under `@include`, so such fields need github.com-only documents. export default defineConfig({ schema: "./schema/github.graphql", documents: ["src/**/*.graphql"], diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts index f46c91fd69570..1ad9bb952c13a 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.test.ts @@ -1054,6 +1054,8 @@ layer("GitHubPullRequestApi.layer", (it) => { mergedAt: null, closedAt: null, commits: { nodes: [{ commit: { statusCheckRollup: { state: "SUCCESS" } } }] }, + stack: number === 7 ? { number: 3, size: 2, baseRefName: "main" } : null, + stackEntry: number === 7 ? { position: 1 } : null, }); mockedExecute.mockReturnValueOnce( Effect.succeed( @@ -1106,6 +1108,10 @@ layer("GitHubPullRequestApi.layer", (it) => { }, ); assert.strictEqual(eight?.headBranch, "feat/8"); + assert.deepStrictEqual( + [seven?.stack, eight?.stack], + [{ number: 3, size: 2, base: "main", position: 1 }, null], + ); expect(mockedExecute).toHaveBeenCalledOnce(); expect(queryAt(0)).toContain( "_0_repository:repository(owner:$_0_owner name:$_0_name){pullRequest(number:$_0_number)", @@ -1114,7 +1120,6 @@ layer("GitHubPullRequestApi.layer", (it) => { _0_owner: "acme", _0_name: "web", _0_number: 7, - _0_stacks: true, _1_number: 8, }); }), @@ -1572,7 +1577,11 @@ layer("GitHubPullRequestApi.layer", (it) => { mockedExecute.mockReturnValue( Effect.succeed( searchPage([ - searchItem(7, "acme/web", "2026-07-03T00:00:00Z"), + { + ...searchItem(7, "acme/web", "2026-07-03T00:00:00Z"), + stack: { number: 3, size: 2, baseRefName: "main" }, + stackEntry: { position: 2 }, + }, searchItem(9, "pingdotgg/t3code", "2026-07-02T00:00:00Z"), {}, ]), @@ -1604,6 +1613,10 @@ layer("GitHubPullRequestApi.layer", (it) => { [0, 0], ], ); + assert.deepStrictEqual( + batch.items.map((item) => item.stack), + [{ number: 3, size: 2, base: "main", position: 2 }, undefined], + ); assert.isFalse(batch.truncated); }), ); @@ -2882,7 +2895,8 @@ layer("GitHubPullRequestApi.layer", (it) => { }), ); - it.effect("names the host on every repository it addresses", () => + // Enterprise Server rejects a document naming a field it lacks, even under `@include(if: false)`. + it.effect("names the host, and no stack field, in every Enterprise read", () => Effect.gen(function* () { mockedExecute.mockImplementation((call) => Effect.succeed( @@ -2892,21 +2906,36 @@ layer("GitHubPullRequestApi.layer", (it) => { ), ); const cli = yield* GitHubPullRequestApi.GitHubPullRequestApi; - - yield* cli.listPullRequests({ + const listing = { cwd: "/w", - repository: "acme/web", host: "github.acme.dev", state: "open", involvement: "all", viewer: "bilal", limit: 10, - }); + } as const; - assert.isAbove(mockedExecute.mock.calls.length, 0); - expect(new Set(mockedExecute.mock.calls.map(([call]) => call.host))).toEqual( - new Set(["github.acme.dev"]), - ); + yield* cli.listPullRequests({ ...listing, repository: "acme/web" }); + yield* cli.searchPullRequests({ ...listing, repositories: ["acme/web"] }); + const summary = yield* cli + .getPullRequestSummary({ ...listing, repository: "acme/web", number: 7 }) + .pipe(Effect.ignore, Effect.forkChild); + yield* TestClock.adjust("10 millis"); + yield* Fiber.join(summary); + + const calls = mockedExecute.mock.calls.map(([call]) => call); + expect( + calls.map((call) => (call.kind === "graphql" ? call.operationName : call.kind)), + ).toEqual([ + "PullRequestSearch", + "PullRequestList", + "PullRequestSearch", + "PullRequestSummary", + ]); + for (const call of calls) { + expect(call.host).toBe("github.acme.dev"); + if (call.kind === "graphql") expect(call.query).not.toMatch(/\bstack/); + } expect(searchOfCall(0)).toContain("repo:acme/web"); }), ); diff --git a/packages/source-control-github/src/server/GitHubPullRequestApi.ts b/packages/source-control-github/src/server/GitHubPullRequestApi.ts index ce38cc726a249..e00074d67a395 100644 --- a/packages/source-control-github/src/server/GitHubPullRequestApi.ts +++ b/packages/source-control-github/src/server/GitHubPullRequestApi.ts @@ -70,6 +70,7 @@ import { isSubjectOfPullRequest, LenientPullRequestList, LenientPullRequestSearch, + LenientPullRequestSearchWithStacks, pageRows, pullRequestLookup, toLabelCandidates, @@ -386,6 +387,8 @@ export interface GitHubPullRequestStat { const STAT_ALIASES_PER_REQUEST = 25; // Cache checks stagger reads across scheduler ticks; allow time to batch. const SUMMARY_BATCH_WINDOW = "10 millis"; +// Only github.com has pull request stacks; other hosts get documents that never name them. +const hasStacks = (host: string) => host.toLowerCase() === "github.com"; export interface GitHubPullRequestSearchBatch { readonly items: ReadonlyArray; @@ -895,7 +898,11 @@ const GRAPHQL_MERGE_METHODS = { const PullRequestGraphQLGroup = GraphQLGroup.merge( PullRequestBatchesGraphQLGroup, - GraphQLGroup.make(LenientPullRequestSearch, LenientPullRequestList), + GraphQLGroup.make( + LenientPullRequestSearch, + LenientPullRequestSearchWithStacks, + LenientPullRequestList, + ), PullRequestReadsGraphQLGroup, PullRequestReviewsGraphQLGroup, PullRequestMutationsGraphQLGroup, @@ -1703,6 +1710,12 @@ export const make = Effect.gen(function* () { RequestResolver.setDelay(SUMMARY_BATCH_WINDOW), RequestResolver.batchN(STAT_ALIASES_PER_REQUEST), ); + const stackedSummaryResolver = GraphQLResolver.aliased(client.PullRequestSummaryWithStacks, { + batchKey: GitHubGraphQlClient.batchKey, + }).pipe( + RequestResolver.setDelay(SUMMARY_BATCH_WINDOW), + RequestResolver.batchN(STAT_ALIASES_PER_REQUEST), + ); const getPullRequestSummary: GitHubPullRequestApi["Service"]["getPullRequestSummary"] = Effect.fn( "GitHubPullRequestApi.getPullRequestSummary", )( @@ -1714,10 +1727,9 @@ export const make = Effect.gen(function* () { operation: "getPullRequestSummary", }); } - const result = yield* GraphQLResolver.request( - { ...lookup, stacks: input.host.toLowerCase() === "github.com" }, - summaryResolver, - ); + const result = hasStacks(input.host) + ? yield* GraphQLResolver.request(lookup, stackedSummaryResolver) + : yield* GraphQLResolver.request(lookup, summaryResolver); const pullRequest = result.repository?.pullRequest; if (pullRequest == null) { return yield* new GitHubRequestPolicy.GitHubApiNotFoundError({ @@ -1830,7 +1842,8 @@ export const make = Effect.gen(function* () { truncated, }; }); - const stacks = input.host.toLowerCase() === "github.com"; + // Stack badges come from their own batched read below, for the rows that stay. + const stacks = hasStacks(input.host); const read = ( continues: boolean, requestedRows = input.limit + 1, @@ -1842,8 +1855,6 @@ export const make = Effect.gen(function* () { q: query, first: pageRows(rows), after, - // Stack badges come from their own batched read below, for the rows that stay. - stacks: false, stats: true, }) .pipe(Effect.map((result) => toPage(toPullRequestRows(result.search)))) @@ -1854,7 +1865,6 @@ export const make = Effect.gen(function* () { states: LIST_STATES[input.state], first: pageRows(rows), after, - stacks: false, stats: true, }) .pipe( @@ -1960,27 +1970,28 @@ export const make = Effect.gen(function* () { operation: "searchPullRequests", }); } - const rows = Math.min(input.limit + 1, PULL_REQUEST_SEARCH_MAX_ROWS); - return yield* client - .PullRequestSearch({ - q: query, - first: rows, - stacks: input.host.toLowerCase() === "github.com", - stats: false, - }) - .pipe( - graphql({ ...input, operation: "searchPullRequests" }), - Effect.map(({ search }) => { - const page = toPullRequestRows(search); - const items = page.rows.flatMap(({ item, repository }) => - repository === null ? [] : [{ ...item, repository }], - ); - return { - items: items.slice(0, input.limit), - truncated: page.rawCount > input.limit || page.hasNextPage, - }; - }), - ); + const variables = { + q: query, + first: Math.min(input.limit + 1, PULL_REQUEST_SEARCH_MAX_ROWS), + stats: false, + }; + return yield* ( + hasStacks(input.host) + ? client.PullRequestSearchWithStacks(variables) + : client.PullRequestSearch(variables) + ).pipe( + graphql({ ...input, operation: "searchPullRequests" }), + Effect.map(({ search }) => { + const page = toPullRequestRows(search); + const items = page.rows.flatMap(({ item, repository }) => + repository === null ? [] : [{ ...item, repository }], + ); + return { + items: items.slice(0, input.limit), + truncated: page.rawCount > input.limit || page.hasNextPage, + }; + }), + ); }), listPullRequestStats: Effect.fn("GitHubPullRequestApi.listPullRequestStats")( diff --git a/packages/source-control-github/src/server/gitHubPullRequestJson.ts b/packages/source-control-github/src/server/gitHubPullRequestJson.ts index f92c35f7afa21..3638fbeb2c84a 100644 --- a/packages/source-control-github/src/server/gitHubPullRequestJson.ts +++ b/packages/source-control-github/src/server/gitHubPullRequestJson.ts @@ -41,15 +41,17 @@ import { dedupeChecks } from "@t3tools/source-control-core/server/pullRequestChe import type { ProviderChangeRequestSummary } from "@t3tools/source-control-core/server/PullRequestProvider"; import type * as GitHubSchema from "../../schema/github.graphql.ts"; -import type { - PullRequestSummaryRepositoryPullRequestPullRequest as SummarizedPullRequest, - PullRequestWatchFingerprintRepositoryPullRequestPullRequest as WatchedPullRequest, +import { + PullRequestStackFields, + type PullRequestSummaryFields, + type PullRequestWatchFingerprintRepositoryPullRequestPullRequest as WatchedPullRequest, } from "./pullRequestBatches.graphql.ts"; import { PageInfo as ListingPageInfo, PullRequestList, PullRequestRow, PullRequestSearch, + PullRequestSearchWithStacks, } from "./pullRequestListings.graphql.ts"; import type { PullRequestActionSummary } from "./pullRequestMutations.graphql.ts"; import type { @@ -790,6 +792,7 @@ const decodeUnknownList = decodeJsonResult(Schema.Array(Schema.Unknown)); const decodeWorkflowRuns = decodeJsonResult(RawWorkflowRunsSchema); const decodeFileEntry = Schema.decodeUnknownExit(RawPullRequestFileSchema); const decodeRow = Schema.decodeUnknownExit(PullRequestRow); +const decodeStack = Schema.decodeUnknownExit(PullRequestStackFields); /** Decode rows individually so one invalid node cannot blank the entire listing. */ const LenientRows = Schema.Struct({ @@ -803,6 +806,12 @@ export const LenientPullRequestSearch = GraphQL.query(PullRequestSearch.name, { result: Schema.Struct({ search: LenientRows }), }); +export const LenientPullRequestSearchWithStacks = GraphQL.query(PullRequestSearchWithStacks.name, { + document: PullRequestSearchWithStacks.document, + variables: PullRequestSearchWithStacks.variables, + result: Schema.Struct({ search: LenientRows }), +}); + export const LenientPullRequestList = GraphQL.query(PullRequestList.name, { document: PullRequestList.document, variables: PullRequestList.variables, @@ -843,7 +852,9 @@ function toRow( const decodedNode = decodeRow(entry); if (!Exit.isSuccess(decodedNode)) return null; const node = decodedNode.value; - const stack = toStackMembership(node); + // Only rows from `PullRequestSearchWithStacks` carry a stack. + const decodedStack = decodeStack(entry); + const stack = Exit.isSuccess(decodedStack) ? toStackMembership(decodedStack.value) : undefined; const reviewRequests = nodesOf(node.reviewRequests).flatMap( (request): ReadonlyArray => { const reviewer = request.requestedReviewer; @@ -940,7 +951,9 @@ export interface GitHubPullRequestSummary { readonly stack?: PullRequestStackMembership | null; } -export function toPullRequestSummary(pr: SummarizedPullRequest): GitHubPullRequestSummary { +export function toPullRequestSummary( + pr: PullRequestSummaryFields & Partial, +): GitHubPullRequestSummary { return { number: pr.number, title: pr.title, diff --git a/packages/source-control-github/src/server/pullRequestBatches.graphql b/packages/source-control-github/src/server/pullRequestBatches.graphql index 24b6925fcf45c..aa116f867f0b8 100644 --- a/packages/source-control-github/src/server/pullRequestBatches.graphql +++ b/packages/source-control-github/src/server/pullRequestBatches.graphql @@ -1,57 +1,74 @@ # One pull request per operation. `GraphQLResolver.aliased` repeats each under an alias per # pull request, so a batch is one request and a missing pull request fails only its own slice. -# `stacks` is false off github.com: Enterprise Server has no pull request stacks. -query PullRequestSummary($owner: String!, $name: String!, $number: Int!, $stacks: Boolean!) { - repository(owner: $owner, name: $name) { - pullRequest(number: $number) { - number - title - url +fragment PullRequestSummaryFields on PullRequest { + number + title + url + state + isDraft + mergeable + reviewDecision + additions + deletions + changedFiles + updatedAt + mergedAt + closedAt + headRefName + baseRefName + author { + login + avatarUrl + ... on User { + name + } + } + latestReviews(first: 20) { + nodes { state - isDraft - mergeable - reviewDecision - additions - deletions - changedFiles - updatedAt - mergedAt - closedAt - headRefName - baseRefName author { login - avatarUrl - ... on User { - name - } } - latestReviews(first: 20) { - nodes { + } + } + commits(last: 1) { + nodes { + commit { + statusCheckRollup { state - author { - login - } - } - } - commits(last: 1) { - nodes { - commit { - statusCheckRollup { - state - } - } } } - stack @include(if: $stacks) { - number - size - baseRefName - } - stackEntry @include(if: $stacks) { - position - } + } + } +} + +# github.com only. Enterprise Server has no pull request stacks, and it rejects a document that +# names these fields even under `@include(if: false)`. +fragment PullRequestStackFields on PullRequest { + stack { + number + size + baseRefName + } + stackEntry { + position + } +} + +query PullRequestSummary($owner: String!, $name: String!, $number: Int!) { + repository(owner: $owner, name: $name) { + pullRequest(number: $number) { + ...PullRequestSummaryFields + } + } +} + +query PullRequestSummaryWithStacks($owner: String!, $name: String!, $number: Int!) { + repository(owner: $owner, name: $name) { + pullRequest(number: $number) { + ...PullRequestSummaryFields + ...PullRequestStackFields } } } @@ -68,14 +85,7 @@ query PullRequestStats($owner: String!, $name: String!, $number: Int!) { query PullRequestStackMembership($owner: String!, $name: String!, $number: Int!) { repository(owner: $owner, name: $name) { pullRequest(number: $number) { - stack { - number - size - baseRefName - } - stackEntry { - position - } + ...PullRequestStackFields } } } diff --git a/packages/source-control-github/src/server/pullRequestBatches.graphql.ts b/packages/source-control-github/src/server/pullRequestBatches.graphql.ts index 04874bae0beb9..2ed78405ba77b 100644 --- a/packages/source-control-github/src/server/pullRequestBatches.graphql.ts +++ b/packages/source-control-github/src/server/pullRequestBatches.graphql.ts @@ -50,53 +50,39 @@ export const LatestReviewsPullRequestReviewConnection = Schema.Struct({ export interface LatestReviewsPullRequestReviewConnection extends Schema.Struct.Type {} /** Represents the rollup for both the check runs and status for a commit. */ -export const PullRequestSummaryRepositoryPullRequestCommitsNodesCommitStatusCheckRollupStatusCheckRollup = Schema.Struct({ +export const PullRequestSummaryFieldsCommitsNodesCommitStatusCheckRollupStatusCheckRollup = Schema.Struct({ /** The combined status for the commit. */ state: GraphQL.enumLiterals(Shared.StatusState.literals) }) -export interface PullRequestSummaryRepositoryPullRequestCommitsNodesCommitStatusCheckRollupStatusCheckRollup extends Schema.Struct.Type {} +export interface PullRequestSummaryFieldsCommitsNodesCommitStatusCheckRollupStatusCheckRollup extends Schema.Struct.Type {} /** Represents a Git commit. */ -export const PullRequestSummaryRepositoryPullRequestCommitsNodesCommitCommit = Schema.Struct({ +export const PullRequestSummaryFieldsCommitsNodesCommitCommit = Schema.Struct({ /** Check and Status rollup information for this commit. */ - statusCheckRollup: Schema.NullOr(PullRequestSummaryRepositoryPullRequestCommitsNodesCommitStatusCheckRollupStatusCheckRollup) + statusCheckRollup: Schema.NullOr(PullRequestSummaryFieldsCommitsNodesCommitStatusCheckRollupStatusCheckRollup) }) -export interface PullRequestSummaryRepositoryPullRequestCommitsNodesCommitCommit extends Schema.Struct.Type {} +export interface PullRequestSummaryFieldsCommitsNodesCommitCommit extends Schema.Struct.Type {} /** Represents a Git commit part of a pull request. */ -export const PullRequestSummaryRepositoryPullRequestCommitsNodesPullRequestCommit = Schema.Struct({ +export const PullRequestSummaryFieldsCommitsNodesPullRequestCommit = Schema.Struct({ /** The Git commit object */ - commit: PullRequestSummaryRepositoryPullRequestCommitsNodesCommitCommit + commit: PullRequestSummaryFieldsCommitsNodesCommitCommit }) -export interface PullRequestSummaryRepositoryPullRequestCommitsNodesPullRequestCommit extends Schema.Struct.Type {} +export interface PullRequestSummaryFieldsCommitsNodesPullRequestCommit extends Schema.Struct.Type {} /** The connection type for PullRequestCommit. */ -export const PullRequestSummaryRepositoryPullRequestCommitsPullRequestCommitConnection = Schema.Struct({ +export const PullRequestSummaryFieldsCommitsPullRequestCommitConnection = Schema.Struct({ /** A list of nodes. */ - nodes: Schema.NullOr(Schema.Array(Schema.NullOr(PullRequestSummaryRepositoryPullRequestCommitsNodesPullRequestCommit))) + nodes: Schema.NullOr(Schema.Array(Schema.NullOr(PullRequestSummaryFieldsCommitsNodesPullRequestCommit))) }) -export interface PullRequestSummaryRepositoryPullRequestCommitsPullRequestCommitConnection extends Schema.Struct.Type {} +export interface PullRequestSummaryFieldsCommitsPullRequestCommitConnection extends Schema.Struct.Type {} -/** A stack of PullRequests */ -export const PullRequestStack = Schema.Struct({ - /** A number uniquely identifying the stack within its repository */ - number: Shared.Int, - /** The total number of pull requests in the stack */ - size: Shared.Int, - /** The branch that the stack's pull requests target */ - baseRefName: Schema.String -}) -export interface PullRequestStack extends Schema.Struct.Type {} - -/** A member of a PullRequestStack */ -export const PullRequestStackEntry = Schema.Struct({ - /** This entry's position in the stack, where 1 is the closest to the base branch, 2 is stacked on top of 1, etc. */ - position: Shared.Int -}) -export interface PullRequestStackEntry extends Schema.Struct.Type {} - -/** A repository pull request. */ -export const PullRequestSummaryRepositoryPullRequestPullRequest = Schema.Struct({ +/** + * `fragment PullRequestSummaryFields on PullRequest` + * + * A repository pull request. + */ +export const PullRequestSummaryFields = Schema.Struct({ /** Identifies the pull request number. */ number: Shared.Int, /** Identifies the pull request title. */ @@ -135,18 +121,45 @@ export const PullRequestSummaryRepositoryPullRequestPullRequest = Schema.Struct( /** A list of latest reviews per user associated with the pull request that are not also pending review. */ latestReviews: Schema.NullOr(LatestReviewsPullRequestReviewConnection), /** A list of commits present in this pull request's head branch not present in the base branch. */ - commits: PullRequestSummaryRepositoryPullRequestCommitsPullRequestCommitConnection, + commits: PullRequestSummaryFieldsCommitsPullRequestCommitConnection +}) +export interface PullRequestSummaryFields extends Schema.Struct.Type {} + +/** A stack of PullRequests */ +export const PullRequestStack = Schema.Struct({ + /** A number uniquely identifying the stack within its repository */ + number: Shared.Int, + /** The total number of pull requests in the stack */ + size: Shared.Int, + /** The branch that the stack's pull requests target */ + baseRefName: Schema.String +}) +export interface PullRequestStack extends Schema.Struct.Type {} + +/** A member of a PullRequestStack */ +export const PullRequestStackEntry = Schema.Struct({ + /** This entry's position in the stack, where 1 is the closest to the base branch, 2 is stacked on top of 1, etc. */ + position: Shared.Int +}) +export interface PullRequestStackEntry extends Schema.Struct.Type {} + +/** + * `fragment PullRequestStackFields on PullRequest` + * + * A repository pull request. + */ +export const PullRequestStackFields = Schema.Struct({ /** The stack this Pull Request belongs to, or null if it is not part of a stack */ - stack: Schema.optionalKey(Schema.NullOr(PullRequestStack)), + stack: Schema.NullOr(PullRequestStack), /** The stack entry for this Pull Request, or null if it is not part of a stack */ - stackEntry: Schema.optionalKey(Schema.NullOr(PullRequestStackEntry)) + stackEntry: Schema.NullOr(PullRequestStackEntry) }) -export interface PullRequestSummaryRepositoryPullRequestPullRequest extends Schema.Struct.Type {} +export interface PullRequestStackFields extends Schema.Struct.Type {} /** A repository contains the content for a project. */ export const PullRequestSummaryRepositoryRepository = Schema.Struct({ /** Returns a single pull request from the current repository by number. */ - pullRequest: Schema.NullOr(PullRequestSummaryRepositoryPullRequestPullRequest) + pullRequest: Schema.NullOr(PullRequestSummaryFields) }) export interface PullRequestSummaryRepositoryRepository extends Schema.Struct.Type {} @@ -156,19 +169,19 @@ export const PullRequestSummary = GraphQL.query("PullRequestSummary", { variables: [ { name: "owner", body: "String!" }, { name: "name", body: "String!" }, - { name: "number", body: "Int!" }, - { name: "stacks", body: "Boolean!" } + { name: "number", body: "Int!" } ], selections: [ - { name: "repository", body: ["(owner:$", "owner", " name:$", "name", "){pullRequest(number:$", "number", "){number title url state isDraft mergeable reviewDecision additions deletions changedFiles updatedAt mergedAt closedAt headRefName baseRefName author{__typename login avatarUrl...on User{name}}latestReviews(first:20){nodes{state author{__typename login}}}commits(last:1){nodes{commit{statusCheckRollup{state}}}}stack@include(if:$", "stacks", "){number size baseRefName}stackEntry@include(if:$", "stacks", "){position}}}"] } + { name: "repository", body: ["(owner:$", "owner", " name:$", "name", "){pullRequest(number:$", "number", "){...PullRequestSummaryFields}}"] } ], - fragments: [] + fragments: [ + { name: "PullRequestSummaryFields", body: ["on PullRequest{number title url state isDraft mergeable reviewDecision additions deletions changedFiles updatedAt mergedAt closedAt headRefName baseRefName author{__typename login avatarUrl...on User{name}}latestReviews(first:20){nodes{state author{__typename login}}}commits(last:1){nodes{commit{statusCheckRollup{state}}}}}"] } + ] }, variables: { owner: Schema.String, name: Schema.String, - number: Shared.Int, - stacks: Schema.Boolean + number: Shared.Int }, result: Schema.Struct({ /** Lookup a given repository by the owner and repository name. */ @@ -180,6 +193,51 @@ export declare namespace PullRequestSummary { export type Result = typeof PullRequestSummary.result.Type } +/** A repository pull request. */ +export const PullRequestSummaryWithStacksRepositoryPullRequestPullRequest = Schema.Struct({ + ...PullRequestSummaryFields.fields, + ...PullRequestStackFields.fields +}) +export interface PullRequestSummaryWithStacksRepositoryPullRequestPullRequest extends Schema.Struct.Type {} + +/** A repository contains the content for a project. */ +export const PullRequestSummaryWithStacksRepositoryRepository = Schema.Struct({ + /** Returns a single pull request from the current repository by number. */ + pullRequest: Schema.NullOr(PullRequestSummaryWithStacksRepositoryPullRequestPullRequest) +}) +export interface PullRequestSummaryWithStacksRepositoryRepository extends Schema.Struct.Type {} + +/** `query PullRequestSummaryWithStacks` */ +export const PullRequestSummaryWithStacks = GraphQL.query("PullRequestSummaryWithStacks", { + document: { + variables: [ + { name: "owner", body: "String!" }, + { name: "name", body: "String!" }, + { name: "number", body: "Int!" } + ], + selections: [ + { name: "repository", body: ["(owner:$", "owner", " name:$", "name", "){pullRequest(number:$", "number", "){...PullRequestSummaryFields...PullRequestStackFields}}"] } + ], + fragments: [ + { name: "PullRequestSummaryFields", body: ["on PullRequest{number title url state isDraft mergeable reviewDecision additions deletions changedFiles updatedAt mergedAt closedAt headRefName baseRefName author{__typename login avatarUrl...on User{name}}latestReviews(first:20){nodes{state author{__typename login}}}commits(last:1){nodes{commit{statusCheckRollup{state}}}}}"] }, + { name: "PullRequestStackFields", body: ["on PullRequest{stack{number size baseRefName}stackEntry{position}}"] } + ] + }, + variables: { + owner: Schema.String, + name: Schema.String, + number: Shared.Int + }, + result: Schema.Struct({ + /** Lookup a given repository by the owner and repository name. */ + repository: Schema.NullOr(PullRequestSummaryWithStacksRepositoryRepository) + }) +}) +export declare namespace PullRequestSummaryWithStacks { + export type Variables = typeof PullRequestSummaryWithStacks.variables.Type + export type Result = typeof PullRequestSummaryWithStacks.result.Type +} + /** A repository pull request. */ export const PullRequestStatsRepositoryPullRequestPullRequest = Schema.Struct({ /** The number of additions in this pull request. */ @@ -224,19 +282,10 @@ export declare namespace PullRequestStats { export type Result = typeof PullRequestStats.result.Type } -/** A repository pull request. */ -export const PullRequestStackMembershipRepositoryPullRequestPullRequest = Schema.Struct({ - /** The stack this Pull Request belongs to, or null if it is not part of a stack */ - stack: Schema.NullOr(PullRequestStack), - /** The stack entry for this Pull Request, or null if it is not part of a stack */ - stackEntry: Schema.NullOr(PullRequestStackEntry) -}) -export interface PullRequestStackMembershipRepositoryPullRequestPullRequest extends Schema.Struct.Type {} - /** A repository contains the content for a project. */ export const PullRequestStackMembershipRepositoryRepository = Schema.Struct({ /** Returns a single pull request from the current repository by number. */ - pullRequest: Schema.NullOr(PullRequestStackMembershipRepositoryPullRequestPullRequest) + pullRequest: Schema.NullOr(PullRequestStackFields) }) export interface PullRequestStackMembershipRepositoryRepository extends Schema.Struct.Type {} @@ -249,9 +298,11 @@ export const PullRequestStackMembership = GraphQL.query("PullRequestStackMembers { name: "number", body: "Int!" } ], selections: [ - { name: "repository", body: ["(owner:$", "owner", " name:$", "name", "){pullRequest(number:$", "number", "){stack{number size baseRefName}stackEntry{position}}}"] } + { name: "repository", body: ["(owner:$", "owner", " name:$", "name", "){pullRequest(number:$", "number", "){...PullRequestStackFields}}"] } ], - fragments: [] + fragments: [ + { name: "PullRequestStackFields", body: ["on PullRequest{stack{number size baseRefName}stackEntry{position}}"] } + ] }, variables: { owner: Schema.String, @@ -335,32 +386,32 @@ export const StatusCheckRollupContextConnection = Schema.Struct({ export interface StatusCheckRollupContextConnection extends Schema.Struct.Type {} /** Represents the rollup for both the check runs and status for a commit. */ -export const PullRequestWatchFingerprintRepositoryPullRequestCommitsNodesCommitStatusCheckRollupStatusCheckRollup = Schema.Struct({ +export const PullRequestCommitsNodesCommitStatusCheckRollupStatusCheckRollup = Schema.Struct({ /** A list of status contexts and check runs for this commit. */ contexts: StatusCheckRollupContextConnection }) -export interface PullRequestWatchFingerprintRepositoryPullRequestCommitsNodesCommitStatusCheckRollupStatusCheckRollup extends Schema.Struct.Type {} +export interface PullRequestCommitsNodesCommitStatusCheckRollupStatusCheckRollup extends Schema.Struct.Type {} /** Represents a Git commit. */ -export const PullRequestWatchFingerprintRepositoryPullRequestCommitsNodesCommitCommit = Schema.Struct({ +export const PullRequestCommitsNodesCommitCommit = Schema.Struct({ /** Check and Status rollup information for this commit. */ - statusCheckRollup: Schema.NullOr(PullRequestWatchFingerprintRepositoryPullRequestCommitsNodesCommitStatusCheckRollupStatusCheckRollup) + statusCheckRollup: Schema.NullOr(PullRequestCommitsNodesCommitStatusCheckRollupStatusCheckRollup) }) -export interface PullRequestWatchFingerprintRepositoryPullRequestCommitsNodesCommitCommit extends Schema.Struct.Type {} +export interface PullRequestCommitsNodesCommitCommit extends Schema.Struct.Type {} /** Represents a Git commit part of a pull request. */ -export const PullRequestWatchFingerprintRepositoryPullRequestCommitsNodesPullRequestCommit = Schema.Struct({ +export const PullRequestCommitsNodesPullRequestCommit = Schema.Struct({ /** The Git commit object */ - commit: PullRequestWatchFingerprintRepositoryPullRequestCommitsNodesCommitCommit + commit: PullRequestCommitsNodesCommitCommit }) -export interface PullRequestWatchFingerprintRepositoryPullRequestCommitsNodesPullRequestCommit extends Schema.Struct.Type {} +export interface PullRequestCommitsNodesPullRequestCommit extends Schema.Struct.Type {} /** The connection type for PullRequestCommit. */ -export const PullRequestWatchFingerprintRepositoryPullRequestCommitsPullRequestCommitConnection = Schema.Struct({ +export const PullRequestCommitsPullRequestCommitConnection = Schema.Struct({ /** A list of nodes. */ - nodes: Schema.NullOr(Schema.Array(Schema.NullOr(PullRequestWatchFingerprintRepositoryPullRequestCommitsNodesPullRequestCommit))) + nodes: Schema.NullOr(Schema.Array(Schema.NullOr(PullRequestCommitsNodesPullRequestCommit))) }) -export interface PullRequestWatchFingerprintRepositoryPullRequestCommitsPullRequestCommitConnection extends Schema.Struct.Type {} +export interface PullRequestCommitsPullRequestCommitConnection extends Schema.Struct.Type {} /** A repository pull request. */ export const PullRequestWatchFingerprintRepositoryPullRequestPullRequest = Schema.Struct({ @@ -377,7 +428,7 @@ export const PullRequestWatchFingerprintRepositoryPullRequestPullRequest = Schem /** The list of all review threads for this pull request. */ reviewThreads: PullRequestReviewThreadConnection, /** A list of commits present in this pull request's head branch not present in the base branch. */ - commits: PullRequestWatchFingerprintRepositoryPullRequestCommitsPullRequestCommitConnection + commits: PullRequestCommitsPullRequestCommitConnection }) export interface PullRequestWatchFingerprintRepositoryPullRequestPullRequest extends Schema.Struct.Type {} @@ -417,4 +468,4 @@ export declare namespace PullRequestWatchFingerprint { } /** Every operation in pullRequestBatches.graphql. */ -export const PullRequestBatchesGraphQLGroup = GraphQLGroup.make(PullRequestSummary, PullRequestStats, PullRequestStackMembership, PullRequestWatchFingerprint) +export const PullRequestBatchesGraphQLGroup = GraphQLGroup.make(PullRequestSummary, PullRequestSummaryWithStacks, PullRequestStats, PullRequestStackMembership, PullRequestWatchFingerprint) diff --git a/packages/source-control-github/src/server/pullRequestListings.graphql b/packages/source-control-github/src/server/pullRequestListings.graphql index b90f4b28609da..46048f5d01f17 100644 --- a/packages/source-control-github/src/server/pullRequestListings.graphql +++ b/packages/source-control-github/src/server/pullRequestListings.graphql @@ -8,16 +8,7 @@ # `additions` and `deletions` are asked for only with `$stats`: measured over twelve repositories # at a hundred rows, a search answers in ~4.0s without them and ~7.1s with them, for two numbers at # the end of a row. The cross-repository search reads them afterwards, in `PullRequestStats`. -# `stack` and `stackEntry` exist on github.com only (`$stacks`). fragment PullRequestRow on PullRequest { - stack @include(if: $stacks) { - number - size - baseRefName - } - stackEntry @include(if: $stacks) { - position - } number title url @@ -84,13 +75,20 @@ fragment PullRequestRow on PullRequest { # Every repository of a host in one read, which is what makes a listing one request rather than # one per repository. `$first` is at most 100: `search` refuses anything larger with # EXCESSIVE_PAGINATION. -query PullRequestSearch( - $q: String! - $first: Int! - $after: String - $stacks: Boolean! - $stats: Boolean! -) { +query PullRequestSearch($q: String!, $first: Int!, $after: String, $stats: Boolean!) { + search(query: $q, type: ISSUE, first: $first, after: $after) { + pageInfo { + hasNextPage + endCursor + } + nodes { + ...PullRequestRow + } + } +} + +# `PullRequestSearch` with each row's stack, for github.com. +query PullRequestSearchWithStacks($q: String!, $first: Int!, $after: String, $stats: Boolean!) { search(query: $q, type: ISSUE, first: $first, after: $after) { pageInfo { hasNextPage @@ -98,6 +96,7 @@ query PullRequestSearch( } nodes { ...PullRequestRow + ...PullRequestStackFields } } } @@ -110,7 +109,6 @@ query PullRequestList( $states: [PullRequestState!] $first: Int! $after: String - $stacks: Boolean! $stats: Boolean! ) { repository(owner: $owner, name: $name) { diff --git a/packages/source-control-github/src/server/pullRequestListings.graphql.ts b/packages/source-control-github/src/server/pullRequestListings.graphql.ts index ad55696b377c8..52ef935a4b9f1 100644 --- a/packages/source-control-github/src/server/pullRequestListings.graphql.ts +++ b/packages/source-control-github/src/server/pullRequestListings.graphql.ts @@ -1,26 +1,9 @@ // Generated by @effect/graphql-generator from pullRequestListings.graphql. Do not edit. import { GraphQL, GraphQLGroup } from "effect/graphql" import * as Schema from "effect/Schema" +import { PullRequestStackFields } from "./pullRequestBatches.graphql.ts" import * as Shared from "../../schema/github.graphql.ts" -/** A stack of PullRequests */ -export const PullRequestStack = Schema.Struct({ - /** A number uniquely identifying the stack within its repository */ - number: Shared.Int, - /** The total number of pull requests in the stack */ - size: Shared.Int, - /** The branch that the stack's pull requests target */ - baseRefName: Schema.String -}) -export interface PullRequestStack extends Schema.Struct.Type {} - -/** A member of a PullRequestStack */ -export const PullRequestStackEntry = Schema.Struct({ - /** This entry's position in the stack, where 1 is the closest to the base branch, 2 is stacked on top of 1, etc. */ - position: Shared.Int -}) -export interface PullRequestStackEntry extends Schema.Struct.Type {} - /** A user is an individual's account on GitHub that owns repositories and can make new content. */ export const AuthorUser = Schema.Struct({ __typename: Schema.Literal("User"), @@ -164,10 +147,6 @@ export interface PullRequestCommitConnection extends Schema.Struct.Type {} /** A repository pull request. */ -export const PullRequest = Schema.Struct({ +export const PullRequestSearchSearchNodesPullRequest = Schema.Struct({ __typename: Schema.Literal("PullRequest"), ...PullRequestRow.fields }) -export interface PullRequest extends Schema.Struct.Type {} +export interface PullRequestSearchSearchNodesPullRequest extends Schema.Struct.Type {} /** The results of a search. */ export const SearchResultItemOther = Schema.Struct({ @@ -241,16 +220,16 @@ export interface SearchResultItemOther extends Schema.Struct.Type {} +export interface PullRequestSearchSearchSearchResultItemConnection extends Schema.Struct.Type {} /** `query PullRequestSearch` */ export const PullRequestSearch = GraphQL.query("PullRequestSearch", { @@ -259,26 +238,24 @@ export const PullRequestSearch = GraphQL.query("PullRequestSearch", { { name: "q", body: "String!" }, { name: "first", body: "Int!" }, { name: "after", body: "String" }, - { name: "stacks", body: "Boolean!" }, { name: "stats", body: "Boolean!" } ], selections: [ { name: "search", body: ["(query:$", "q", " type:ISSUE first:$", "first", " after:$", "after", "){pageInfo{hasNextPage endCursor}nodes{__typename...", "PullRequestRow", "}}"] } ], fragments: [ - { name: "PullRequestRow", body: ["on PullRequest{stack@include(if:$", "stacks", "){number size baseRefName}stackEntry@include(if:$", "stacks", "){position}number title url author{__typename login avatarUrl...on User{name}}headRefName baseRefName state isDraft mergeable reviewDecision latestReviews(first:20){nodes{state author{__typename login}}}additions@include(if:$", "stats", ")deletions@include(if:$", "stats", ")createdAt updatedAt mergedAt repository{nameWithOwner}reviewRequests(first:20){nodes{requestedReviewer{__typename...on User{login}...on Team{slug}}}}labels(first:20){nodes{name color}}commits(last:1){nodes{commit{statusCheckRollup{state}}}}}"] } + { name: "PullRequestRow", body: ["on PullRequest{number title url author{__typename login avatarUrl...on User{name}}headRefName baseRefName state isDraft mergeable reviewDecision latestReviews(first:20){nodes{state author{__typename login}}}additions@include(if:$", "stats", ")deletions@include(if:$", "stats", ")createdAt updatedAt mergedAt repository{nameWithOwner}reviewRequests(first:20){nodes{requestedReviewer{__typename...on User{login}...on Team{slug}}}}labels(first:20){nodes{name color}}commits(last:1){nodes{commit{statusCheckRollup{state}}}}}"] } ] }, variables: { q: Schema.String, first: Shared.Int, after: Schema.optional(Schema.NullOr(Schema.String)), - stacks: Schema.Boolean, stats: Schema.Boolean }, result: Schema.Struct({ /** Perform a search across resources, returning a maximum of 1,000 results. */ - search: SearchResultItemConnection + search: PullRequestSearchSearchSearchResultItemConnection }) }) export declare namespace PullRequestSearch { @@ -286,6 +263,63 @@ export declare namespace PullRequestSearch { export type Result = typeof PullRequestSearch.result.Type } +/** A repository pull request. */ +export const PullRequestSearchWithStacksSearchNodesPullRequest = Schema.Struct({ + __typename: Schema.Literal("PullRequest"), + ...PullRequestRow.fields, + ...PullRequestStackFields.fields +}) +export interface PullRequestSearchWithStacksSearchNodesPullRequest extends Schema.Struct.Type {} + +/** + * A list of results that matched against a search query. Regardless of the number + * of matches, a maximum of 1,000 results will be available across all types, + * potentially split across many pages. + */ +export const PullRequestSearchWithStacksSearchSearchResultItemConnection = Schema.Struct({ + /** Information to aid in pagination. */ + pageInfo: PageInfo, + /** A list of nodes. */ + nodes: Schema.NullOr(Schema.Array(Schema.NullOr(Schema.Union([ + PullRequestSearchWithStacksSearchNodesPullRequest, + SearchResultItemOther + ])))) +}) +export interface PullRequestSearchWithStacksSearchSearchResultItemConnection extends Schema.Struct.Type {} + +/** `query PullRequestSearchWithStacks` */ +export const PullRequestSearchWithStacks = GraphQL.query("PullRequestSearchWithStacks", { + document: { + variables: [ + { name: "q", body: "String!" }, + { name: "first", body: "Int!" }, + { name: "after", body: "String" }, + { name: "stats", body: "Boolean!" } + ], + selections: [ + { name: "search", body: ["(query:$", "q", " type:ISSUE first:$", "first", " after:$", "after", "){pageInfo{hasNextPage endCursor}nodes{__typename...", "PullRequestRow", "...PullRequestStackFields}}"] } + ], + fragments: [ + { name: "PullRequestRow", body: ["on PullRequest{number title url author{__typename login avatarUrl...on User{name}}headRefName baseRefName state isDraft mergeable reviewDecision latestReviews(first:20){nodes{state author{__typename login}}}additions@include(if:$", "stats", ")deletions@include(if:$", "stats", ")createdAt updatedAt mergedAt repository{nameWithOwner}reviewRequests(first:20){nodes{requestedReviewer{__typename...on User{login}...on Team{slug}}}}labels(first:20){nodes{name color}}commits(last:1){nodes{commit{statusCheckRollup{state}}}}}"] }, + { name: "PullRequestStackFields", body: ["on PullRequest{stack{number size baseRefName}stackEntry{position}}"] } + ] + }, + variables: { + q: Schema.String, + first: Shared.Int, + after: Schema.optional(Schema.NullOr(Schema.String)), + stats: Schema.Boolean + }, + result: Schema.Struct({ + /** Perform a search across resources, returning a maximum of 1,000 results. */ + search: PullRequestSearchWithStacksSearchSearchResultItemConnection + }) +}) +export declare namespace PullRequestSearchWithStacks { + export type Variables = typeof PullRequestSearchWithStacks.variables.Type + export type Result = typeof PullRequestSearchWithStacks.result.Type +} + /** The connection type for PullRequest. */ export const PullRequestConnection = Schema.Struct({ /** Information to aid in pagination. */ @@ -311,14 +345,13 @@ export const PullRequestList = GraphQL.query("PullRequestList", { { name: "states", body: "[PullRequestState!]" }, { name: "first", body: "Int!" }, { name: "after", body: "String" }, - { name: "stacks", body: "Boolean!" }, { name: "stats", body: "Boolean!" } ], selections: [ { name: "repository", body: ["(owner:$", "owner", " name:$", "name", "){pullRequests(first:$", "first", " after:$", "after", " states:$", "states", " orderBy:{field:CREATED_AT direction:DESC}){pageInfo{hasNextPage endCursor}nodes{...", "PullRequestRow", "}}}"] } ], fragments: [ - { name: "PullRequestRow", body: ["on PullRequest{stack@include(if:$", "stacks", "){number size baseRefName}stackEntry@include(if:$", "stacks", "){position}number title url author{__typename login avatarUrl...on User{name}}headRefName baseRefName state isDraft mergeable reviewDecision latestReviews(first:20){nodes{state author{__typename login}}}additions@include(if:$", "stats", ")deletions@include(if:$", "stats", ")createdAt updatedAt mergedAt repository{nameWithOwner}reviewRequests(first:20){nodes{requestedReviewer{__typename...on User{login}...on Team{slug}}}}labels(first:20){nodes{name color}}commits(last:1){nodes{commit{statusCheckRollup{state}}}}}"] } + { name: "PullRequestRow", body: ["on PullRequest{number title url author{__typename login avatarUrl...on User{name}}headRefName baseRefName state isDraft mergeable reviewDecision latestReviews(first:20){nodes{state author{__typename login}}}additions@include(if:$", "stats", ")deletions@include(if:$", "stats", ")createdAt updatedAt mergedAt repository{nameWithOwner}reviewRequests(first:20){nodes{requestedReviewer{__typename...on User{login}...on Team{slug}}}}labels(first:20){nodes{name color}}commits(last:1){nodes{commit{statusCheckRollup{state}}}}}"] } ] }, variables: { @@ -327,7 +360,6 @@ export const PullRequestList = GraphQL.query("PullRequestList", { states: Schema.optional(Schema.NullOr(Schema.Array(Shared.PullRequestState))), first: Shared.Int, after: Schema.optional(Schema.NullOr(Schema.String)), - stacks: Schema.Boolean, stats: Schema.Boolean }, result: Schema.Struct({ @@ -341,4 +373,4 @@ export declare namespace PullRequestList { } /** Every operation in pullRequestListings.graphql. */ -export const PullRequestListingsGraphQLGroup = GraphQLGroup.make(PullRequestSearch, PullRequestList) +export const PullRequestListingsGraphQLGroup = GraphQLGroup.make(PullRequestSearch, PullRequestSearchWithStacks, PullRequestList)