Skip to content

Commit 54f124a

Browse files
committed
Adds PR get by id/search to integrations
Only adds support for GitHub
1 parent e3175ea commit 54f124a

File tree

5 files changed

+411
-272
lines changed

5 files changed

+411
-272
lines changed

src/cache.ts

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type Caches = {
1515
issuesOrPrsById: { key: `id:${string}:${string}`; value: IssueOrPullRequest };
1616
issuesOrPrsByIdAndRepo: { key: `id:${string}:${string}:${string}`; value: IssueOrPullRequest };
1717
prByBranch: { key: `branch:${string}:${string}`; value: PullRequest };
18+
prsById: { key: `id:${string}:${string}`; value: PullRequest };
1819
prsBySha: { key: `sha:${string}:${string}`; value: PullRequest };
1920
repoMetadata: { key: `repo:${string}`; value: RepositoryMetadata };
2021
currentAccount: { key: `id:${string}`; value: Account };
@@ -86,6 +87,25 @@ export class CacheProvider implements Disposable {
8687
return item.value as CacheResult<CacheValue<T>>;
8788
}
8889

90+
getCurrentAccount(
91+
integration: IntegrationBase,
92+
cacheable: Cacheable<Account>,
93+
options?: { expiryOverride?: boolean | number },
94+
): CacheResult<Account> {
95+
const { key, etag } = getIntegrationKeyAndEtag(integration);
96+
return this.get('currentAccount', `id:${key}`, etag, cacheable, options);
97+
}
98+
99+
// getEnrichedAutolinks(
100+
// sha: string,
101+
// remoteOrProvider: Integration,
102+
// cacheable: Cacheable<Map<string, EnrichedAutolink>>,
103+
// options?: { force?: boolean },
104+
// ): CacheResult<Map<string, EnrichedAutolink>> {
105+
// const { key, etag } = getRemoteKeyAndEtag(remoteOrProvider);
106+
// return this.get('enrichedAutolinksBySha', `sha:${sha}:${key}`, etag, cacheable, options);
107+
// }
108+
89109
getIssueOrPullRequest(
90110
id: string,
91111
resource: ResourceDescriptor,
@@ -107,15 +127,20 @@ export class CacheProvider implements Disposable {
107127
);
108128
}
109129

110-
// getEnrichedAutolinks(
111-
// sha: string,
112-
// remoteOrProvider: Integration,
113-
// cacheable: Cacheable<Map<string, EnrichedAutolink>>,
114-
// options?: { force?: boolean },
115-
// ): CacheResult<Map<string, EnrichedAutolink>> {
116-
// const { key, etag } = getRemoteKeyAndEtag(remoteOrProvider);
117-
// return this.get('enrichedAutolinksBySha', `sha:${sha}:${key}`, etag, cacheable, options);
118-
// }
130+
getPullRequest(
131+
id: string,
132+
resource: ResourceDescriptor,
133+
integration: IntegrationBase | undefined,
134+
cacheable: Cacheable<PullRequest>,
135+
options?: { expiryOverride?: boolean | number },
136+
): CacheResult<PullRequest> {
137+
const { key, etag } = getResourceKeyAndEtag(resource, integration);
138+
139+
if (resource == null) {
140+
return this.get('prsById', `id:${id}:${key}`, etag, cacheable, options);
141+
}
142+
return this.get('prsById', `id:${id}:${key}:${JSON.stringify(resource)}}`, etag, cacheable, options);
143+
}
119144

120145
getPullRequestForBranch(
121146
branch: string,
@@ -124,11 +149,10 @@ export class CacheProvider implements Disposable {
124149
cacheable: Cacheable<PullRequest>,
125150
options?: { expiryOverride?: boolean | number },
126151
): CacheResult<PullRequest> {
127-
const cache = 'prByBranch';
128152
const { key, etag } = getResourceKeyAndEtag(repo, integration);
129153
// Wrap the cacheable so we can also add the result to the issuesOrPrsById cache
130154
return this.get(
131-
cache,
155+
'prByBranch',
132156
`branch:${branch}:${key}`,
133157
etag,
134158
this.wrapPullRequestCacheable(cacheable, key, etag),
@@ -143,10 +167,15 @@ export class CacheProvider implements Disposable {
143167
cacheable: Cacheable<PullRequest>,
144168
options?: { expiryOverride?: boolean | number },
145169
): CacheResult<PullRequest> {
146-
const cache = 'prsBySha';
147170
const { key, etag } = getResourceKeyAndEtag(repo, integration);
148171
// Wrap the cacheable so we can also add the result to the issuesOrPrsById cache
149-
return this.get(cache, `sha:${sha}:${key}`, etag, this.wrapPullRequestCacheable(cacheable, key, etag), options);
172+
return this.get(
173+
'prsBySha',
174+
`sha:${sha}:${key}`,
175+
etag,
176+
this.wrapPullRequestCacheable(cacheable, key, etag),
177+
options,
178+
);
150179
}
151180

152181
getRepositoryDefaultBranch(
@@ -169,15 +198,6 @@ export class CacheProvider implements Disposable {
169198
return this.get('repoMetadata', `repo:${key}`, etag, cacheable, options);
170199
}
171200

172-
getCurrentAccount(
173-
integration: IntegrationBase,
174-
cacheable: Cacheable<Account>,
175-
options?: { expiryOverride?: boolean | number },
176-
): CacheResult<Account> {
177-
const { key, etag } = getIntegrationKeyAndEtag(integration);
178-
return this.get('currentAccount', `id:${key}`, etag, cacheable, options);
179-
}
180-
181201
private set<T extends Cache>(
182202
cache: T,
183203
key: CacheKey<T>,
@@ -252,12 +272,13 @@ function getExpiresAt<T extends Cache>(cache: T, value: CacheValue<T> | undefine
252272
return now + (updatedAgo > 14 * 24 * 60 * 60 * 1000 ? 12 : 1) * 60 * 60 * 1000;
253273
}
254274
case 'prByBranch':
275+
case 'prsById':
255276
case 'prsBySha': {
256277
if (value == null) return cache === 'prByBranch' ? defaultExpiresAt : 0 /* Never expires */;
257278

258279
// Open prs expire after 1 hour, but closed/merge prs expire after 12 hours unless recently updated and then expire in 1 hour
259280

260-
const pr = value as CacheValue<'prsBySha' | 'prByBranch'>;
281+
const pr = value as CacheValue<'prByBranch' | 'prsById' | 'prsBySha'>;
261282
if (pr.state === 'opened') return defaultExpiresAt;
262283

263284
const updatedAgo = now - (pr.closedDate ?? pr.mergedDate ?? pr.updatedDate).getTime();

src/plus/integrations/integration.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,33 @@ export abstract class IntegrationBase<
407407
session: ProviderAuthenticationSession,
408408
options?: { avatarSize?: number },
409409
): Promise<Account | undefined>;
410+
411+
@debug()
412+
async getPullRequest(resource: T, id: string): Promise<PullRequest | undefined> {
413+
const scope = getLogScope();
414+
415+
const connected = this.maybeConnected ?? (await this.isConnected());
416+
if (!connected) return undefined;
417+
418+
const pr = this.container.cache.getPullRequest(id, resource, this, () => ({
419+
value: (async () => {
420+
try {
421+
const result = await this.getProviderPullRequest?.(this._session!, resource, id);
422+
this.resetRequestExceptionCount();
423+
return result;
424+
} catch (ex) {
425+
return this.handleProviderException<PullRequest | undefined>(ex, scope, undefined);
426+
}
427+
})(),
428+
}));
429+
return pr;
430+
}
431+
432+
protected getProviderPullRequest?(
433+
session: ProviderAuthenticationSession,
434+
resource: T,
435+
id: string,
436+
): Promise<PullRequest | undefined>;
410437
}
411438

412439
export abstract class IssueIntegration<
@@ -1134,4 +1161,45 @@ export abstract class HostingIntegration<
11341161
repos?: T[],
11351162
cancellation?: CancellationToken,
11361163
): Promise<SearchedPullRequest[] | undefined>;
1164+
1165+
async searchPullRequests(
1166+
searchQuery: string,
1167+
repo?: T,
1168+
cancellation?: CancellationToken,
1169+
): Promise<PullRequest[] | undefined>;
1170+
async searchPullRequests(
1171+
searchQuery: string,
1172+
repos?: T[],
1173+
cancellation?: CancellationToken,
1174+
): Promise<PullRequest[] | undefined>;
1175+
@debug()
1176+
async searchPullRequests(
1177+
searchQuery: string,
1178+
repos?: T | T[],
1179+
cancellation?: CancellationToken,
1180+
): Promise<PullRequest[] | undefined> {
1181+
const scope = getLogScope();
1182+
const connected = this.maybeConnected ?? (await this.isConnected());
1183+
if (!connected) return undefined;
1184+
1185+
try {
1186+
const prs = await this.searchProviderPullRequests?.(
1187+
this._session!,
1188+
searchQuery,
1189+
repos != null ? (Array.isArray(repos) ? repos : [repos]) : undefined,
1190+
cancellation,
1191+
);
1192+
this.resetRequestExceptionCount();
1193+
return prs;
1194+
} catch (ex) {
1195+
return this.handleProviderException<PullRequest[] | undefined>(ex, scope, undefined);
1196+
}
1197+
}
1198+
1199+
protected searchProviderPullRequests?(
1200+
session: ProviderAuthenticationSession,
1201+
searchQuery: string,
1202+
repos?: T[],
1203+
cancellation?: CancellationToken,
1204+
): Promise<PullRequest[] | undefined>;
11371205
}

src/plus/integrations/providers/github.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,23 @@ abstract class GitHubIntegrationBase<ID extends SupportedIntegrationIds> extends
9393
);
9494
}
9595

96+
protected override async getProviderPullRequest(
97+
{ accessToken }: AuthenticationSession,
98+
repo: GitHubRepositoryDescriptor,
99+
id: string,
100+
): Promise<PullRequest | undefined> {
101+
return (await this.container.github)?.getPullRequest(
102+
this,
103+
accessToken,
104+
repo.owner,
105+
repo.name,
106+
parseInt(id, 10),
107+
{
108+
baseUrl: this.apiBaseUrl,
109+
},
110+
);
111+
}
112+
96113
protected override async getProviderPullRequestForBranch(
97114
{ accessToken }: AuthenticationSession,
98115
repo: GitHubRepositoryDescriptor,
@@ -179,6 +196,24 @@ abstract class GitHubIntegrationBase<ID extends SupportedIntegrationIds> extends
179196
);
180197
}
181198

199+
protected override async searchProviderPullRequests(
200+
{ accessToken }: AuthenticationSession,
201+
searchQuery: string,
202+
repos?: GitHubRepositoryDescriptor[],
203+
cancellation?: CancellationToken,
204+
): Promise<PullRequest[] | undefined> {
205+
return (await this.container.github)?.searchPullRequests(
206+
this,
207+
accessToken,
208+
{
209+
search: searchQuery,
210+
repos: repos?.map(r => `${r.owner}/${r.name}`),
211+
baseUrl: this.apiBaseUrl,
212+
},
213+
cancellation,
214+
);
215+
}
216+
182217
protected override async mergeProviderPullRequest(
183218
{ accessToken }: AuthenticationSession,
184219
pr: PullRequest | { id: string; headRefSha: string },

0 commit comments

Comments
 (0)