diff --git a/apps/dashboard/src/lib/github-cache.ts b/apps/dashboard/src/lib/github-cache.ts index de0e940..4c8a1bf 100644 --- a/apps/dashboard/src/lib/github-cache.ts +++ b/apps/dashboard/src/lib/github-cache.ts @@ -71,6 +71,7 @@ type GetOrRevalidateGitHubResourceOptions = { getNamespaceVersions?: ( namespaceKeys: string[], ) => Promise>; + merge?: (existing: TData, fresh: TData) => TData; now?: () => number; }; @@ -615,6 +616,7 @@ export async function getOrRevalidateGitHubResource({ cacheMode = "legacy", payloadRetentionSeconds = DEFAULT_GITHUB_PAYLOAD_RETENTION_SECONDS, fetcher, + merge, now = Date.now, store, payloadStore, @@ -755,6 +757,14 @@ export async function getOrRevalidateGitHubResource({ return parseCachedPayload(existingEntry.payloadJson); } + const mergedData = + merge && existingEntry + ? merge( + parseCachedPayload(existingEntry.payloadJson), + result.data, + ) + : result.data; + const nextEntry = { cacheKey, userId, @@ -762,7 +772,7 @@ export async function getOrRevalidateGitHubResource({ paramsJson, etag: result.metadata.etag, lastModified: result.metadata.lastModified, - payloadJson: JSON.stringify(result.data), + payloadJson: JSON.stringify(mergedData), fetchedAt: currentTime, freshUntil: currentTime + @@ -780,7 +790,7 @@ export async function getOrRevalidateGitHubResource({ payloadRetentionSeconds, }); - return result.data; + return mergedData; })(); resolvedInFlightCache?.set(cacheKey, task); diff --git a/apps/dashboard/src/lib/github.functions.ts b/apps/dashboard/src/lib/github.functions.ts index 1ee394f..6d29eb0 100644 --- a/apps/dashboard/src/lib/github.functions.ts +++ b/apps/dashboard/src/lib/github.functions.ts @@ -3787,20 +3787,13 @@ async function getMySearchSources( deadlineAt: number, ): Promise { let installations: GitHubAppInstallation[] = []; - let organizations: GitHubOrganization[] = []; try { - const [installationResult, organizationResult] = - await withGitHubOperationTimeout( - "github search source discovery", - getRemainingSearchTimeoutMs(deadlineAt, MY_SEARCH_SOURCE_TIMEOUT_MS), - () => - Promise.all([ - getGitHubAppUserInstallations(context.session.user.id), - getGitHubAuthenticatedOrganizations(context), - ]), - ); + const installationResult = await withGitHubOperationTimeout( + "github search source discovery", + getRemainingSearchTimeoutMs(deadlineAt, MY_SEARCH_SOURCE_TIMEOUT_MS), + () => getGitHubAppUserInstallations(context.session.user.id), + ); installations = installationResult.installations; - organizations = organizationResult; } catch (error) { console.error("[github-search] failed to discover search sources", error); } @@ -3808,13 +3801,6 @@ async function getMySearchSources( const sources: GitHubGraphQLSearchSource[] = []; const excludedOAuthOwners = new Map(); - for (const organization of organizations) { - addExcludedOwnerScope(excludedOAuthOwners, { - login: organization.login, - targetType: "Organization", - }); - } - for (const installation of installations) { const owner = toSearchOwnerScope(installation); if (!owner) { @@ -3832,17 +3818,6 @@ async function getMySearchSources( break; } - if (owner.targetType === "Organization") { - addExcludedOwnerScope(excludedOAuthOwners, owner); - } - if ( - owner.targetType === "User" && - installation.repositorySelection === "all" && - normalizeLogin(owner.login) === normalizeLogin(viewerLogin) - ) { - addExcludedOwnerScope(excludedOAuthOwners, owner); - } - const installationContext = await withGitHubOperationTimeout( `github installation context ${installation.id}`, contextTimeoutMs, @@ -3859,6 +3834,17 @@ async function getMySearchSources( continue; } + if (owner.targetType === "Organization") { + addExcludedOwnerScope(excludedOAuthOwners, owner); + } + if ( + owner.targetType === "User" && + installation.repositorySelection === "all" && + normalizeLogin(owner.login) === normalizeLogin(viewerLogin) + ) { + addExcludedOwnerScope(excludedOAuthOwners, owner); + } + sources.push({ label: `installation:${installation.id}:${owner.login}`, context: installationContext, @@ -3959,6 +3945,7 @@ async function getMyPullsResult({ signalKeys: [githubRevalidationSignalKeys.pullsMine], namespaceKeys: [githubRevalidationSignalKeys.pullsMine], cacheMode: "split", + merge: (existing, fresh) => mergeMyPullsResults([existing, fresh]), fetcher: async () => { const deadlineAt = Date.now() + MY_SEARCH_TOTAL_TIMEOUT_MS; const sources = await getMySearchSources(context, username, deadlineAt); @@ -4130,6 +4117,7 @@ async function getMyIssuesResult({ signalKeys: [githubRevalidationSignalKeys.issuesMine], namespaceKeys: [githubRevalidationSignalKeys.issuesMine], cacheMode: "split", + merge: (existing, fresh) => mergeMyIssuesResults([existing, fresh]), fetcher: async () => { const deadlineAt = Date.now() + MY_SEARCH_TOTAL_TIMEOUT_MS; const sources = await getMySearchSources(context, username, deadlineAt);