@@ -7,18 +7,19 @@ import type { IssueOrPullRequest, IssueOrPullRequestType } from '../../../git/mo
77import type { PullRequest , PullRequestMergeMethod , PullRequestState } from '../../../git/models/pullRequest' ;
88import type { RepositoryMetadata } from '../../../git/models/repositoryMetadata' ;
99import type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider' ;
10- import type { ResourceDescriptor } from '../integration ' ;
10+ import type { ProviderAuthenticationSession } from '../authentication/models ' ;
1111import { HostingIntegration } from '../integration' ;
12- import { providersMetadata } from './models' ;
12+ import type {
13+ BitbucketRemoteRepositoryDescriptor ,
14+ BitbucketRepositoryDescriptor ,
15+ BitbucketWorkspaceDescriptor ,
16+ } from './bitbucket/models' ;
17+ import type { ProviderPullRequest } from './models' ;
18+ import { fromProviderPullRequest , providersMetadata } from './models' ;
1319
1420const metadata = providersMetadata [ HostingIntegrationId . Bitbucket ] ;
1521const authProvider = Object . freeze ( { id : metadata . id , scopes : metadata . scopes } ) ;
1622
17- interface BitbucketRepositoryDescriptor extends ResourceDescriptor {
18- owner : string ;
19- name : string ;
20- }
21-
2223export class BitbucketIntegration extends HostingIntegration <
2324 HostingIntegrationId . Bitbucket ,
2425 BitbucketRepositoryDescriptor
@@ -136,11 +137,136 @@ export class BitbucketIntegration extends HostingIntegration<
136137 return Promise . resolve ( undefined ) ;
137138 }
138139
140+ private _accounts : Map < string , Account | undefined > | undefined ;
141+ protected override async getProviderCurrentAccount ( {
142+ accessToken,
143+ } : AuthenticationSession ) : Promise < Account | undefined > {
144+ this . _accounts ??= new Map < string , Account | undefined > ( ) ;
145+
146+ const cachedAccount = this . _accounts . get ( accessToken ) ;
147+ if ( cachedAccount == null ) {
148+ const api = await this . getProvidersApi ( ) ;
149+ const user = await api . getCurrentUser ( this . id , { accessToken : accessToken } ) ;
150+ this . _accounts . set (
151+ accessToken ,
152+ user
153+ ? {
154+ provider : this ,
155+ id : user . id ,
156+ name : user . name ?? undefined ,
157+ email : user . email ?? undefined ,
158+ avatarUrl : user . avatarUrl ?? undefined ,
159+ username : user . username ?? undefined ,
160+ }
161+ : undefined ,
162+ ) ;
163+ }
164+
165+ return this . _accounts . get ( accessToken ) ;
166+ }
167+
168+ private _workspaces : Map < string , BitbucketWorkspaceDescriptor [ ] | undefined > | undefined ;
169+ private async getProviderResourcesForUser (
170+ session : AuthenticationSession ,
171+ force : boolean = false ,
172+ ) : Promise < BitbucketWorkspaceDescriptor [ ] | undefined > {
173+ this . _workspaces ??= new Map < string , BitbucketWorkspaceDescriptor [ ] | undefined > ( ) ;
174+ const { accessToken } = session ;
175+ const cachedResources = this . _workspaces . get ( accessToken ) ;
176+
177+ if ( cachedResources == null || force ) {
178+ const api = await this . getProvidersApi ( ) ;
179+ const account = await this . getProviderCurrentAccount ( session ) ;
180+ if ( account ?. id == null ) return undefined ;
181+
182+ const resources = await api . getBitbucketResourcesForUser ( account . id , { accessToken : accessToken } ) ;
183+ this . _workspaces . set (
184+ accessToken ,
185+ resources != null ? resources . map ( r => ( { ...r , key : r . id } ) ) : undefined ,
186+ ) ;
187+ }
188+
189+ return this . _workspaces . get ( accessToken ) ;
190+ }
191+
192+ private async getProviderProjectsForResources (
193+ { accessToken } : AuthenticationSession ,
194+ resources : BitbucketWorkspaceDescriptor [ ] ,
195+ force : boolean = false ,
196+ ) : Promise < BitbucketRemoteRepositoryDescriptor [ ] | undefined > {
197+ const repositories = new Map < string , BitbucketRemoteRepositoryDescriptor [ ] | undefined > ( ) ;
198+ let resourcesWithoutRepositories : BitbucketWorkspaceDescriptor [ ] = [ ] ;
199+ if ( force ) {
200+ resourcesWithoutRepositories = resources ;
201+ } else {
202+ for ( const resource of resources ) {
203+ const resourceKey = `${ accessToken } :${ resource . id } ` ;
204+ const cachedRepositories = repositories . get ( resourceKey ) ;
205+ if ( cachedRepositories == null ) {
206+ resourcesWithoutRepositories . push ( resource ) ;
207+ }
208+ }
209+ }
210+
211+ if ( resourcesWithoutRepositories . length > 0 ) {
212+ const api = await this . container . bitbucket ;
213+ if ( api == null ) return undefined ;
214+ await Promise . allSettled (
215+ resourcesWithoutRepositories . map ( async resource => {
216+ const resourceRepos = await api . getRepositoriesForWorkspace ( this , accessToken , resource . slug , {
217+ baseUrl : this . apiBaseUrl ,
218+ } ) ;
219+
220+ if ( resourceRepos == null ) return undefined ;
221+ repositories . set (
222+ `${ accessToken } :${ resource . id } ` ,
223+ resourceRepos . map ( r => ( {
224+ id : `${ r . owner } /${ r . name } ` ,
225+ owner : r . owner ,
226+ name : r . name ,
227+ key : `${ r . owner } /${ r . name } ` ,
228+ } ) ) ,
229+ ) ;
230+ } ) ,
231+ ) ;
232+ }
233+
234+ return resources . reduce < BitbucketRemoteRepositoryDescriptor [ ] > ( ( resultRepos , resource ) => {
235+ const resourceRepos = repositories . get ( `${ accessToken } :${ resource . id } ` ) ;
236+ if ( resourceRepos != null ) {
237+ resultRepos . push ( ...resourceRepos ) ;
238+ }
239+ return resultRepos ;
240+ } , [ ] ) ;
241+ }
242+
139243 protected override async searchProviderMyPullRequests (
140- _session : AuthenticationSession ,
141- _repos ?: BitbucketRepositoryDescriptor [ ] ,
244+ session : ProviderAuthenticationSession ,
245+ repos ?: BitbucketRepositoryDescriptor [ ] ,
142246 ) : Promise < PullRequest [ ] | undefined > {
143- return Promise . resolve ( undefined ) ;
247+ const api = await this . getProvidersApi ( ) ;
248+ if ( repos != null ) {
249+ // TODO: implement repos version
250+ return undefined ;
251+ }
252+
253+ const user = await this . getProviderCurrentAccount ( session ) ;
254+ if ( user ?. username == null ) return undefined ;
255+
256+ const workspaces = await this . getProviderResourcesForUser ( session ) ;
257+ if ( workspaces == null || workspaces . length === 0 ) return undefined ;
258+
259+ const allBitbucketRepos = await this . getProviderProjectsForResources ( session , workspaces ) ;
260+ if ( allBitbucketRepos == null || allBitbucketRepos . length === 0 ) return undefined ;
261+
262+ const prs = await api . getPullRequestsForRepos (
263+ HostingIntegrationId . Bitbucket ,
264+ allBitbucketRepos . map ( repo => ( { namespace : repo . owner , name : repo . name } ) ) ,
265+ {
266+ accessToken : session . accessToken ,
267+ } ,
268+ ) ;
269+ return prs . values . map ( pr => this . fromBitbucketProviderPullRequest ( pr ) ) ;
144270 }
145271
146272 protected override async searchProviderMyIssues (
@@ -149,6 +275,14 @@ export class BitbucketIntegration extends HostingIntegration<
149275 ) : Promise < IssueShape [ ] | undefined > {
150276 return Promise . resolve ( undefined ) ;
151277 }
278+
279+ private fromBitbucketProviderPullRequest (
280+ remotePullRequest : ProviderPullRequest ,
281+ // repoDescriptors: BitbucketRemoteRepositoryDescriptor[],
282+ ) : PullRequest {
283+ remotePullRequest . graphQLId = remotePullRequest . id ;
284+ return fromProviderPullRequest ( remotePullRequest , this ) ;
285+ }
152286}
153287
154288const bitbucketCloudDomainRegex = / ^ b i t b u c k e t \. o r g $ / i;
0 commit comments