mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 16:03:20 +00:00
Extract GitHub search result window error pattern to shared constant
Extract the 1000-result window detection pattern to a single source of truth so the classifier and consumer stay synchronized. The pattern is the only signal separating a permanently unreachable page from a transient validation failure, so drift or trimming silently demotes window 422s to generic failures and stops capping the advertised page count (#11485).
This commit is contained in:
@@ -38,6 +38,7 @@ import {
|
||||
__resetLocalGitConfigSignatureCacheForTests,
|
||||
readLocalGitConfigSignature
|
||||
} from './local-git-config-signature'
|
||||
import { GITHUB_SEARCH_RESULT_WINDOW_ERROR_PATTERN } from '../../shared/github-work-items-query-bounds'
|
||||
|
||||
describe('github owner/repo resolution', () => {
|
||||
beforeEach(() => {
|
||||
@@ -797,14 +798,15 @@ describe('gh error classification', () => {
|
||||
})
|
||||
|
||||
// Why: the renderer detects the Search API 1000-result window by matching
|
||||
// /first 1000 search results/i against this message (#11485) — trimming the
|
||||
// raw stderr out of the validation_error copy would silently downgrade every
|
||||
// window 422 to a generic failure. This pins the contract.
|
||||
// GITHUB_SEARCH_RESULT_WINDOW_ERROR_PATTERN against this message (#11485) —
|
||||
// trimming the raw stderr out of the validation_error copy, or drifting the
|
||||
// pattern off GitHub's real wording, would silently downgrade every window
|
||||
// 422 to a generic failure. The stderr stays verbatim so this pins both ends.
|
||||
it('keeps the search-window phrase in validation_error list messages', () => {
|
||||
const stderr =
|
||||
'Command failed: gh api --hostname github.com search/issues\nValidation Failed: Only the first 1000 search results are available (HTTP 422)'
|
||||
const classified = classifyListIssuesError(stderr)
|
||||
expect(classified.type).toBe('validation_error')
|
||||
expect(classified.message).toMatch(/first 1000 search results/i)
|
||||
expect(classified.message).toMatch(GITHUB_SEARCH_RESULT_WINDOW_ERROR_PATTERN)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3211,7 +3211,12 @@ export default function TaskPage(): React.JSX.Element {
|
||||
)
|
||||
|
||||
// Why: see buildSelectedReposKey — array-identity deps re-fire on every
|
||||
// repos:changed even when the selection is unchanged.
|
||||
// repos:changed even when the selection is unchanged. The context part is
|
||||
// resolved as GitHub, but every provider-independent field (projectId,
|
||||
// hostId, projectHostSetupId, repoId) is identical across providers, so the
|
||||
// GitLab effect can key off this too — it passes no gitlabProjectRef, so its
|
||||
// context carries no providerIdentity of its own. Thread a projectRef into
|
||||
// that call and this key needs a GitLab-scoped part.
|
||||
const selectedReposKey = useMemo(
|
||||
() => buildSelectedReposKey(selectedRepos, (r) => getTaskPageRepoSourceContext(r, 'github')),
|
||||
[selectedRepos]
|
||||
@@ -4967,7 +4972,7 @@ export default function TaskPage(): React.JSX.Element {
|
||||
return () => {
|
||||
stale = true
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- selectedReposKey encodes the only selectedRepos fields read above; keying off the array ref would re-run on every parent render.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- selectedReposKey covers every selectedRepos field read above (see its GitHub-scoped-context note); keying off the array ref would re-run on every parent render.
|
||||
}, [taskSource, gitlabView, activeGitlabFilter, gitlabRefreshNonce, selectedReposKey])
|
||||
|
||||
// Why: Todos fetch has its own effect — different trigger (no chip filter) and data path (gl.todos is user-scoped, not repo-scoped).
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
export {
|
||||
GITHUB_SEARCH_RESULT_WINDOW_ERROR_PATTERN,
|
||||
GITHUB_WORK_ITEMS_QUERY_MAX_BYTES,
|
||||
isGitHubWorkItemsQueryTooLarge
|
||||
} from '../../../../shared/github-work-items-query-bounds'
|
||||
|
||||
@@ -53,7 +53,10 @@ import { rightSidebarShowsPullRequestData } from '@/lib/right-sidebar-visibility
|
||||
import { hostedReviewInfoFromGitHubPRInfo } from '../../../../shared/hosted-review-github'
|
||||
import { getHostedReviewCacheKey, linkedReviewHintKey } from './hosted-review-cache-identity'
|
||||
import { getGitHubPRCacheKey, getGitHubRepoCacheKey } from './github-cache-key'
|
||||
import { isGitHubWorkItemsQueryTooLarge } from './github-work-items-query-bounds'
|
||||
import {
|
||||
GITHUB_SEARCH_RESULT_WINDOW_ERROR_PATTERN,
|
||||
isGitHubWorkItemsQueryTooLarge
|
||||
} from './github-work-items-query-bounds'
|
||||
import { classifyGitHubUnavailable } from '../../../../shared/github-api-availability'
|
||||
import { isMacAppDataPath } from '@/lib/passive-macos-app-data-access'
|
||||
import { translate } from '@/i18n/i18n'
|
||||
@@ -2824,7 +2827,8 @@ export const createGitHubSlice: StateCreator<AppState, [], [], GitHubSlice> = (s
|
||||
// Why: only the 1000-result-window 422 may drive the unreachable
|
||||
// clamp; demote other validation errors so they read as failures.
|
||||
errorTypes.push(
|
||||
type === 'validation_error' && !/first 1000 search results/i.test(message)
|
||||
type === 'validation_error' &&
|
||||
!GITHUB_SEARCH_RESULT_WINDOW_ERROR_PATTERN.test(message)
|
||||
? 'unknown'
|
||||
: type
|
||||
)
|
||||
|
||||
@@ -2,6 +2,17 @@ import { isClipboardTextByteLengthOverLimit } from './clipboard-text'
|
||||
|
||||
export const GITHUB_WORK_ITEMS_QUERY_MAX_BYTES = 8 * 1024
|
||||
|
||||
/**
|
||||
* GitHub's Search API only pages through the first 1000 matches; beyond that it
|
||||
* 422s with "Only the first 1000 search results are available". Matching that
|
||||
* free-text wording is the ONLY signal separating a permanently unreachable page
|
||||
* from a transient failure (#11485), so the phrase is pinned here rather than
|
||||
* inlined: if GitHub rewords it, window 422s silently demote to generic
|
||||
* failures and the advertised page count stops being capped.
|
||||
* `gh-utils.test.ts` asserts the classified message still carries this phrase.
|
||||
*/
|
||||
export const GITHUB_SEARCH_RESULT_WINDOW_ERROR_PATTERN = /first 1000 search results/i
|
||||
|
||||
export function isGitHubWorkItemsQueryTooLarge(
|
||||
query: string,
|
||||
maxBytes = GITHUB_WORK_ITEMS_QUERY_MAX_BYTES
|
||||
|
||||
Reference in New Issue
Block a user