Skip to content

Commit 05ab22f

Browse files
committed
fix(api): extracts partial GraphQL data, fixes notification sources
1 parent 599709e commit 05ab22f

File tree

2 files changed

+56
-12
lines changed

2 files changed

+56
-12
lines changed

src/app/services/api.ts

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,24 @@ function extractRejectionError(reason: unknown): { statusCode: number | null; me
172172
return { statusCode, message };
173173
}
174174

175+
/**
176+
* Extracts partial data from a GraphqlResponseError (thrown when response contains both data and errors).
177+
* Returns the data if available, null otherwise.
178+
*/
179+
function extractGraphQLPartialData<T>(err: unknown): T | null {
180+
if (
181+
err &&
182+
typeof err === "object" &&
183+
"data" in err &&
184+
err.data &&
185+
typeof err.data === "object" &&
186+
"search" in err.data
187+
) {
188+
return err.data as T;
189+
}
190+
return null;
191+
}
192+
175193
function chunkArray<T>(arr: T[], size: number): T[][] {
176194
const chunks: T[][] = [];
177195
for (let i = 0; i < arr.length; i += size) {
@@ -369,14 +387,28 @@ async function graphqlSearchIssues(
369387
{ q: queryString, cursor }
370388
);
371389
} catch (err) {
372-
const { statusCode, message } = extractRejectionError(err);
390+
// GraphqlResponseError contains partial data — extract valid nodes before recording error
391+
const partial = extractGraphQLPartialData<GraphQLIssueSearchResponse>(err);
392+
if (partial) {
393+
response = partial;
394+
} else {
395+
const { statusCode, message } = extractRejectionError(err);
396+
errors.push({
397+
repo: `search-batch-${chunkIdx + 1}/${chunks.length}`,
398+
statusCode,
399+
message,
400+
retryable: statusCode === null || statusCode >= 500,
401+
});
402+
break;
403+
}
404+
const { message } = extractRejectionError(err);
373405
errors.push({
374406
repo: `search-batch-${chunkIdx + 1}/${chunks.length}`,
375-
statusCode,
407+
statusCode: null,
376408
message,
377-
retryable: statusCode === null || statusCode >= 500,
409+
retryable: true,
378410
});
379-
break;
411+
// Continue processing partial data below — don't break
380412
}
381413

382414
if (response.rateLimit) updateGraphqlRateLimit(response.rateLimit);
@@ -407,7 +439,7 @@ async function graphqlSearchIssues(
407439
const total = response.search.issueCount;
408440
console.warn(`[api] Issue search results capped at 1000 (${total} total)`);
409441
pushNotification(
410-
"search",
442+
"search/issues",
411443
`Issue search results capped at 1,000 of ${total.toLocaleString()} total — some items are hidden`,
412444
"warning"
413445
);
@@ -486,14 +518,26 @@ async function graphqlSearchPRs(
486518
{ q: queryString, cursor }
487519
);
488520
} catch (err) {
489-
const { statusCode, message } = extractRejectionError(err);
521+
const partial = extractGraphQLPartialData<GraphQLPRSearchResponse>(err);
522+
if (partial) {
523+
response = partial;
524+
} else {
525+
const { statusCode, message } = extractRejectionError(err);
526+
errors.push({
527+
repo: `pr-search-batch-${chunkIdx + 1}/${chunks.length}`,
528+
statusCode,
529+
message,
530+
retryable: statusCode === null || statusCode >= 500,
531+
});
532+
break;
533+
}
534+
const { message } = extractRejectionError(err);
490535
errors.push({
491536
repo: `pr-search-batch-${chunkIdx + 1}/${chunks.length}`,
492-
statusCode,
537+
statusCode: null,
493538
message,
494-
retryable: statusCode === null || statusCode >= 500,
539+
retryable: true,
495540
});
496-
break;
497541
}
498542

499543
if (response.rateLimit) updateGraphqlRateLimit(response.rateLimit);
@@ -565,7 +609,7 @@ async function graphqlSearchPRs(
565609
const total = response.search.issueCount;
566610
console.warn(`[api] PR search results capped at 1000 (${total} total)`);
567611
pushNotification(
568-
"search",
612+
"search/prs",
569613
`PR search results capped at 1,000 of ${total.toLocaleString()} total — some items are hidden`,
570614
"warning"
571615
);
@@ -648,7 +692,7 @@ async function graphqlSearchPRs(
648692
} catch (err) {
649693
console.warn("[api] Fork PR statusCheckRollup fallback failed:", err);
650694
pushNotification(
651-
"fork-check-fallback",
695+
"graphql",
652696
"Fork PR check status unavailable — CI status may be missing for some PRs",
653697
"warning"
654698
);

tests/services/api.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ describe("fetchIssues", () => {
358358
expect(octokit.graphql).toHaveBeenCalledTimes(1);
359359
// Warning notification sent
360360
expect(pushNotification).toHaveBeenCalledWith(
361-
"search",
361+
"search/issues",
362362
expect.stringContaining("capped at 1,000"),
363363
"warning"
364364
);

0 commit comments

Comments
 (0)