diff --git a/AGENTS.md b/AGENTS.md index cc3e546..d35c732 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -64,7 +64,7 @@ uv run python -m unittest discover -s tests ```sh set -euo pipefail -VERSION=0.1.4 +VERSION=0.1.6 BRANCH="release-v${VERSION}" git fetch origin --tags --prune @@ -116,7 +116,7 @@ rm -rf /tmp/src-py-lib-release-check ```sh set -euo pipefail -VERSION=0.1.4 +VERSION=0.1.6 BRANCH="release-v${VERSION}" GH_REPO="sourcegraph/src-py-lib" @@ -140,7 +140,7 @@ gh pr merge "${BRANCH}" --repo "${GH_REPO}" --squash --delete-branch ```sh set -euo pipefail -VERSION=0.1.4 +VERSION=0.1.6 git fetch origin --tags --prune git switch main @@ -154,7 +154,7 @@ git push origin "v${VERSION}" ```sh set -euo pipefail -VERSION=0.1.4 +VERSION=0.1.6 GH_REPO="sourcegraph/src-py-lib" RUN_ID="$( diff --git a/pyproject.toml b/pyproject.toml index 5488aa3..bc5e3f4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ dev = [ [project] name = "src-py-lib" -version = "0.1.5" +version = "0.1.6" description = "Reusable libraries for Sourcegraph projects" readme = "README.md" requires-python = ">=3.11" diff --git a/src/src_py_lib/clients/sourcegraph.py b/src/src_py_lib/clients/sourcegraph.py index 283153b..2edbb93 100644 --- a/src/src_py_lib/clients/sourcegraph.py +++ b/src/src_py_lib/clients/sourcegraph.py @@ -206,8 +206,29 @@ def __post_init__(self) -> None: require_https=not self.allow_insecure_http, ) - def graphql(self, query: str, variables: Mapping[str, JSONValue] | None = None) -> JSONDict: - return self._client().execute(query, variables) + def graphql( + self, + query: str, + variables: Mapping[str, JSONValue] | None = None, + *, + follow_pages: bool = True, + page_size: int | None = None, + first_variable: str = "first", + after_variable: str = "after", + ) -> JSONDict: + """Execute one Sourcegraph GraphQL operation. + + Set `follow_pages=False` when the caller owns pagination, such as + aliased queries with one cursor per alias. + """ + return self._client().execute( + query, + variables, + follow_pages=follow_pages, + page_size=page_size, + first_variable=first_variable, + after_variable=after_variable, + ) def stream_connection_nodes( self, diff --git a/tests/test_logging_http_clients.py b/tests/test_logging_http_clients.py index 14b0da0..32cbeac 100644 --- a/tests/test_logging_http_clients.py +++ b/tests/test_logging_http_clients.py @@ -1424,6 +1424,35 @@ def test_sourcegraph_client_builds_graphql_request(self) -> None: self.assertEqual(http.calls[0]["url"], "https://sourcegraph.example.com/.api/graphql") self.assertEqual(http.calls[0]["headers"], {"Authorization": "token token"}) + def test_sourcegraph_client_graphql_can_disable_auto_pagination(self) -> None: + http = RecordingHTTP( + [ + { + "data": { + "users": { + "nodes": [{"username": "alice"}], + "pageInfo": {"hasNextPage": True, "endCursor": "cursor-1"}, + } + } + } + ] + ) + client = SourcegraphClient("https://sourcegraph.example.com", "token", http=http) + query = """ +query Users($first: Int!, $after: String) { + users(first: $first, after: $after) { + nodes { username } + pageInfo { hasNextPage endCursor } + } +} +""" + + data = client.graphql(query, page_size=1, follow_pages=False) + + self.assertEqual(json_dict(data.get("users"))["nodes"], [{"username": "alice"}]) + self.assertEqual(len(http.calls), 1) + self.assertEqual(http.calls[0]["json_body"]["variables"], {"first": 1}) + def test_sourcegraph_client_rejects_http_endpoint_by_default(self) -> None: with self.assertRaisesRegex(ValueError, "https:// URL"): SourcegraphClient("http://sourcegraph.example.com", "token") @@ -1536,7 +1565,7 @@ def handler(request: httpx.Request) -> httpx.Response: with src.trace_context(root_context): self.assertEqual( - client.graphql("query Viewer { currentUser { username } }"), + client.graphql("query Viewer { currentUser { username } }", follow_pages=False), {"currentUser": {"username": "alice"}}, ) traces = client.drain_traces() diff --git a/uv.lock b/uv.lock index 688c551..efb7b83 100644 --- a/uv.lock +++ b/uv.lock @@ -254,7 +254,7 @@ wheels = [ [[package]] name = "src-py-lib" -version = "0.1.5" +version = "0.1.6" source = { editable = "." } dependencies = [ { name = "httpx" },