Skip to content

Commit 825c691

Browse files
bit-incarnasclaude
andcommitted
[github-data] fix: switch contributions queries from user(login) to viewer form
GitHub treats `user(login: $login).contributionsCollection` as a third-party query and only returns what's visible on the user's public profile graph -- private contributions get omitted from per-repo / per-day breakdowns even when the authenticated user IS that login and the "Include private contributions on profile" toggle is enabled. Result: every private commit silently dropped out of the activity feed regardless of PAT scope or profile settings. Switch all four contributions queries (main, paginate PRs, paginate issues, paginate reviews) to the `viewer` form which returns the authenticated viewer's complete contributions including granular private-repo data. Drops the now-unused $login variable from each query and the `login` parameter from `fetchContributionsCollection`. The viewer login is still captured via `fetchViewerLogin` for the SyncActivityResult diagnostic. Tests updated to use the `viewer` mock shape; 489/489 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 24dcee6 commit 825c691

7 files changed

Lines changed: 73 additions & 69 deletions

File tree

manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"id": "github-data",
33
"name": "GitHub Data",
4-
"version": "0.0.4",
4+
"version": "0.0.5",
55
"minAppVersion": "1.0.0",
66
"description": "Mirror GitHub state (repos, issues, PRs, releases, Dependabot, Actions, contribution activity) into vault-native markdown. Pull-in only; vault data stays in the vault.",
77
"author": "bit-incarnas",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "obsidian-github-data",
3-
"version": "0.0.4",
3+
"version": "0.0.5",
44
"description": "Mirror GitHub state (repos, issues, PRs, releases, Dependabot, Actions, contribution activity) into vault-native markdown. Pull-in only; vault data stays in the vault.",
55
"main": "main.js",
66
"scripts": {

src/github/graphql.test.ts

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ function mainResponse(overrides: {
3232
};
3333
} = {}) {
3434
return {
35-
user: {
35+
viewer: {
3636
contributionsCollection: {
3737
totalCommitContributions: overrides.totals?.commits ?? 0,
3838
totalIssueContributions: overrides.totals?.issues ?? 0,
@@ -101,7 +101,6 @@ describe("fetchContributionsCollection", () => {
101101
const client = mockClient(async () => response);
102102
const result = await fetchContributionsCollection(
103103
client,
104-
"bit-incarnas",
105104
"2026-04-01T00:00:00Z",
106105
"2026-04-22T00:00:00Z",
107106
);
@@ -110,36 +109,37 @@ describe("fetchContributionsCollection", () => {
110109
expect(result.pullRequestContributions.nodes).toEqual([]);
111110
});
112111

113-
test("passes login + window as variables", async () => {
112+
test("passes window as variables (no login)", async () => {
114113
const graphql = jest.fn(
115114
async (_query: string, _variables?: Record<string, unknown>) =>
116115
mainResponse(),
117116
);
118117
const client = mockClient(graphql);
119118
await fetchContributionsCollection(
120119
client,
121-
"bit-incarnas",
122120
"2026-04-01T00:00:00Z",
123121
"2026-04-22T00:00:00Z",
124122
);
125123
const vars = graphql.mock.calls[0][1];
126124
expect(vars).toEqual({
127-
login: "bit-incarnas",
128125
from: "2026-04-01T00:00:00Z",
129126
to: "2026-04-22T00:00:00Z",
130127
});
128+
// Query should reference viewer, not user(login)
129+
const query = String(graphql.mock.calls[0][0]);
130+
expect(query).toContain("viewer");
131+
expect(query).not.toContain("user(login");
131132
});
132133

133-
test("throws when user is not found", async () => {
134-
const client = mockClient(async () => ({ user: null }));
134+
test("throws when viewer is not returned", async () => {
135+
const client = mockClient(async () => ({ viewer: null }));
135136
await expect(
136137
fetchContributionsCollection(
137138
client,
138-
"ghost",
139139
"2026-04-01T00:00:00Z",
140140
"2026-04-22T00:00:00Z",
141141
),
142-
).rejects.toThrow(/User not found/);
142+
).rejects.toThrow(/Viewer contributions not returned/);
143143
});
144144

145145
test("propagates GraphQL errors", async () => {
@@ -149,7 +149,6 @@ describe("fetchContributionsCollection", () => {
149149
await expect(
150150
fetchContributionsCollection(
151151
client,
152-
"x",
153152
"2026-04-01T00:00:00Z",
154153
"2026-04-22T00:00:00Z",
155154
),
@@ -164,7 +163,6 @@ describe("fetchContributionsCollection", () => {
164163
const client = mockClient(graphql);
165164
await fetchContributionsCollection(
166165
client,
167-
"x",
168166
"2026-04-01T00:00:00Z",
169167
"2026-04-22T00:00:00Z",
170168
);
@@ -209,7 +207,7 @@ describe("fetchContributionsCollection -- pagination", () => {
209207
async (query: string, _variables?: Record<string, unknown>) => {
210208
if (query.includes("PaginatePullRequests")) {
211209
return {
212-
user: {
210+
viewer: {
213211
contributionsCollection: {
214212
pullRequestContributions: {
215213
pageInfo: { endCursor: null, hasNextPage: false },
@@ -232,7 +230,6 @@ describe("fetchContributionsCollection -- pagination", () => {
232230

233231
const result = await fetchContributionsCollection(
234232
client,
235-
"x",
236233
"2026-04-01T00:00:00Z",
237234
"2026-04-22T00:00:00Z",
238235
);
@@ -248,7 +245,7 @@ describe("fetchContributionsCollection -- pagination", () => {
248245
async (query: string, _variables?: Record<string, unknown>) => {
249246
if (query.includes("PaginatePullRequests")) {
250247
return {
251-
user: {
248+
viewer: {
252249
contributionsCollection: {
253250
pullRequestContributions: {
254251
pageInfo: { endCursor: null, hasNextPage: false },
@@ -260,7 +257,7 @@ describe("fetchContributionsCollection -- pagination", () => {
260257
}
261258
if (query.includes("PaginateIssues")) {
262259
return {
263-
user: {
260+
viewer: {
264261
contributionsCollection: {
265262
issueContributions: {
266263
pageInfo: { endCursor: null, hasNextPage: false },
@@ -272,7 +269,7 @@ describe("fetchContributionsCollection -- pagination", () => {
272269
}
273270
if (query.includes("PaginateReviews")) {
274271
return {
275-
user: {
272+
viewer: {
276273
contributionsCollection: {
277274
pullRequestReviewContributions: {
278275
pageInfo: { endCursor: null, hasNextPage: false },
@@ -293,7 +290,6 @@ describe("fetchContributionsCollection -- pagination", () => {
293290

294291
await fetchContributionsCollection(
295292
client,
296-
"x",
297293
"2026-04-01T00:00:00Z",
298294
"2026-04-22T00:00:00Z",
299295
);
@@ -312,7 +308,6 @@ describe("fetchContributionsCollection -- pagination", () => {
312308

313309
await fetchContributionsCollection(
314310
client,
315-
"x",
316311
"2026-04-01T00:00:00Z",
317312
"2026-04-22T00:00:00Z",
318313
{ onWarning: (m) => warnings.push(m) },
@@ -330,7 +325,7 @@ describe("fetchContributionsCollection -- pagination", () => {
330325
if (query.includes("PaginatePullRequests")) {
331326
// Always reports more pages -- runaway loop protection
332327
return {
333-
user: {
328+
viewer: {
334329
contributionsCollection: {
335330
pullRequestContributions: {
336331
pageInfo: { endCursor: "next", hasNextPage: true },
@@ -349,7 +344,6 @@ describe("fetchContributionsCollection -- pagination", () => {
349344

350345
await fetchContributionsCollection(
351346
client,
352-
"x",
353347
"2026-04-01T00:00:00Z",
354348
"2026-04-22T00:00:00Z",
355349
{ onWarning: (m) => warnings.push(m) },
@@ -372,7 +366,6 @@ describe("fetchContributionsCollection -- pagination", () => {
372366

373367
await fetchContributionsCollection(
374368
client,
375-
"x",
376369
"2026-04-01T00:00:00Z",
377370
"2026-04-22T00:00:00Z",
378371
{ onWarning: (m) => warnings.push(m) },

0 commit comments

Comments
 (0)