Skip to content

Commit

Permalink
Search GitHub PR by the entered URL
Browse files Browse the repository at this point in the history
  • Loading branch information
sergeibbb committed Oct 10, 2024
1 parent d6489e1 commit f6e0bdc
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 10 deletions.
7 changes: 6 additions & 1 deletion src/constants.telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,12 @@ export type TelemetryEvents = {
/** Sent when a launchpad operation is taking longer than a set timeout to complete */
'launchpad/operation/slow': {
timeout: number;
operation: 'getMyPullRequests' | 'getCodeSuggestions' | 'getEnrichedItems' | 'getCodeSuggestionCounts';
operation:
| 'getMyPullRequests'
| 'getCodeSuggestions'
| 'getEnrichedItems'
| 'getCodeSuggestionCounts'
| 'getPullRequest';
duration: number;
};

Expand Down
16 changes: 13 additions & 3 deletions src/plus/launchpad/launchpad.ts
Original file line number Diff line number Diff line change
Expand Up @@ -524,11 +524,12 @@ export class LaunchpadCommand extends QuickCommand<State> {

const updateItems = async (
quickpick: QuickPick<LaunchpadItemQuickPickItem | DirectiveQuickPickItem | ConnectMoreIntegrationsItem>,
search?: string,
) => {
quickpick.busy = true;

try {
await updateContextItems(this.container, context, { force: true });
await updateContextItems(this.container, context, { force: true, search: search });

const { items, placeholder } = getItemsAndPlaceholder();
quickpick.placeholder = placeholder;
Expand All @@ -555,7 +556,7 @@ export class LaunchpadCommand extends QuickCommand<State> {
LaunchpadSettingsQuickInputButton,
RefreshQuickInputButton,
],
onDidChangeValue: quickpick => {
onDidChangeValue: async quickpick => {
const hideGroups = Boolean(quickpick.value?.length);

if (groupsHidden !== hideGroups) {
Expand Down Expand Up @@ -591,8 +592,13 @@ export class LaunchpadCommand extends QuickCommand<State> {
// This is a hack because the quickpick doesn't update until you change the items
quickpick.items = [...quickpick.items];
}
// We have found an item that matches to the URL.
// Now it will be displayed as the found item and we exit this function now without sending any requests to API:
return true;
}
}
// Nothing is found above, so let's perform search in the API:
await updateItems(quickpick, value);
}

return true;
Expand Down Expand Up @@ -1329,7 +1335,11 @@ function getIntegrationTitle(integrationId: string): string {
}
}

async function updateContextItems(container: Container, context: Context, options?: { force?: boolean }) {
async function updateContextItems(
container: Container,
context: Context,
options?: { force?: boolean; search?: string },
) {
context.result = await container.launchpad.getCategorizedItems(options);
if (container.telemetry.enabled) {
updateTelemetryContext(context);
Expand Down
52 changes: 46 additions & 6 deletions src/plus/launchpad/launchpadProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ import { showInspectView } from '../../webviews/commitDetails/actions';
import type { ShowWipArgs } from '../../webviews/commitDetails/protocol';
import type { IntegrationResult } from '../integrations/integration';
import type { ConnectionStateChangeEvent } from '../integrations/integrationService';
import type { GitHubRepositoryDescriptor } from '../integrations/providers/github';
import { getPullRequestIdentityValuesFromSearch } from '../integrations/providers/github';
import type { EnrichablePullRequest, ProviderActionablePullRequest } from '../integrations/providers/models';
import {
fromProviderPullRequest,
Expand Down Expand Up @@ -318,6 +320,34 @@ export class LaunchpadProvider implements Disposable {
return { prs: prs, suggestionCounts: suggestionCounts };
}

private async getSearchedPullRequests(search: string) {
const { ownerAndRepo, prNumber } = getPullRequestIdentityValuesFromSearch(search);
let result: TimedResult<SearchedPullRequest[] | undefined> | undefined;

if (prNumber != null) {
if (ownerAndRepo != null) {
// TODO: This needs to be generalized to work outside of GitHub
const integration = await this.container.integrations.get(HostingIntegrationId.GitHub);
const [owner, repo] = ownerAndRepo.split('/', 2);
const descriptor: GitHubRepositoryDescriptor = {
key: ownerAndRepo,
owner: owner,
name: repo,
};
const pr = await withDurationAndSlowEventOnTimeout(
integration?.getPullRequest(descriptor, prNumber),
'getPullRequest',
this.container,
);
if (pr?.value != null) {
result = { value: [{ pullRequest: pr.value, reasons: [] }], duration: pr.duration };
return { prs: result, suggestionCounts: undefined };
}
}
}
return { prs: undefined, suggestionCounts: undefined };
}

private _enrichedItems: CachedLaunchpadPromise<TimedResult<EnrichedItem[]>> | undefined;
@debug<LaunchpadProvider['getEnrichedItems']>({ args: { 0: o => `force=${o?.force}` } })
private async getEnrichedItems(options?: { cancellation?: CancellationToken; force?: boolean }) {
Expand Down Expand Up @@ -612,12 +642,12 @@ export class LaunchpadProvider implements Disposable {
@gate<LaunchpadProvider['getCategorizedItems']>(o => `${o?.force ?? false}`)
@log<LaunchpadProvider['getCategorizedItems']>({ args: { 0: o => `force=${o?.force}`, 1: false } })
async getCategorizedItems(
options?: { force?: boolean },
options?: { force?: boolean; search?: string },
cancellation?: CancellationToken,
): Promise<LaunchpadCategorizedResult> {
const scope = getLogScope();

const fireRefresh = options?.force || this._prs == null;
const fireRefresh = !options?.search && (options?.force || this._prs == null);

const ignoredRepositories = new Set(
(configuration.get('launchpad.ignoredRepositories') ?? []).map(r => r.toLowerCase()),
Expand All @@ -638,7 +668,9 @@ export class LaunchpadProvider implements Disposable {
const [_, enrichedItemsResult, prsWithCountsResult] = await Promise.allSettled([
this.container.git.isDiscoveringRepositories,
this.getEnrichedItems({ force: options?.force, cancellation: cancellation }),
this.getPullRequestsWithSuggestionCounts({ force: options?.force, cancellation: cancellation }),
options?.search
? this.getSearchedPullRequests(options.search)
: this.getPullRequestsWithSuggestionCounts({ force: options?.force, cancellation: cancellation }),
]);

if (cancellation?.isCancellationRequested) throw new CancellationError();
Expand Down Expand Up @@ -752,7 +784,7 @@ export class LaunchpadProvider implements Disposable {
item.suggestedActionCategory,
)!;
// category overrides
if (staleDate != null && item.updatedDate.getTime() < staleDate.getTime()) {
if (!options?.search && staleDate != null && item.updatedDate.getTime() < staleDate.getTime()) {
actionableCategory = 'other';
} else if (codeSuggestionsCount > 0 && item.viewer.isAuthor) {
actionableCategory = 'code-suggestions';
Expand Down Expand Up @@ -788,7 +820,10 @@ export class LaunchpadProvider implements Disposable {
};
return result;
} finally {
this.updateGroupedIds(result?.items ?? []);
if (!options?.search) {
this.updateGroupedIds(result?.items ?? []);
}

if (result != null && fireRefresh) {
this._onDidRefresh.fire(result);
}
Expand Down Expand Up @@ -1045,7 +1080,12 @@ const slowEventTimeout = 1000 * 30; // 30 seconds

function withDurationAndSlowEventOnTimeout<T>(
promise: Promise<T>,
name: 'getMyPullRequests' | 'getCodeSuggestionCounts' | 'getCodeSuggestions' | 'getEnrichedItems',
name:
| 'getMyPullRequests'
| 'getCodeSuggestionCounts'
| 'getCodeSuggestions'
| 'getEnrichedItems'
| 'getPullRequest',
container: Container,
): Promise<TimedResult<T>> {
return timedWithSlowThreshold(promise, {
Expand Down

0 comments on commit f6e0bdc

Please sign in to comment.