@@ -7,9 +7,11 @@ 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 { ProviderAuthenticationSession } from '../authentication/models' ;
1011import type { ResourceDescriptor } from '../integration' ;
1112import { HostingIntegration } from '../integration' ;
12- import { providersMetadata } from './models' ;
13+ import type { ProviderPullRequest } from './models' ;
14+ import { fromProviderPullRequest , providersMetadata } from './models' ;
1315
1416const metadata = providersMetadata [ HostingIntegrationId . Bitbucket ] ;
1517const authProvider = Object . freeze ( { id : metadata . id , scopes : metadata . scopes } ) ;
@@ -19,6 +21,19 @@ interface BitbucketRepositoryDescriptor extends ResourceDescriptor {
1921 name : string ;
2022}
2123
24+ interface BitbucketWorkspaceDescriptor extends ResourceDescriptor {
25+ id : string ;
26+ name : string ;
27+ slug : string ;
28+ }
29+
30+ interface BitbucketRemoteRepositoryDescriptor extends ResourceDescriptor {
31+ owner : string ;
32+ name : string ;
33+ cloneUrlHttps ?: string ;
34+ cloneUrlSsh ?: string ;
35+ }
36+
2237export class BitbucketIntegration extends HostingIntegration <
2338 HostingIntegrationId . Bitbucket ,
2439 BitbucketRepositoryDescriptor
@@ -136,11 +151,138 @@ export class BitbucketIntegration extends HostingIntegration<
136151 return Promise . resolve ( undefined ) ;
137152 }
138153
154+ private _accounts : Map < string , Account | undefined > | undefined ;
155+ protected override async getProviderCurrentAccount ( {
156+ accessToken,
157+ } : AuthenticationSession ) : Promise < Account | undefined > {
158+ this . _accounts ??= new Map < string , Account | undefined > ( ) ;
159+
160+ const cachedAccount = this . _accounts . get ( accessToken ) ;
161+ if ( cachedAccount == null ) {
162+ const api = await this . getProvidersApi ( ) ;
163+ const user = await api . getCurrentUser ( this . id , { accessToken : accessToken } ) ;
164+ this . _accounts . set (
165+ accessToken ,
166+ user
167+ ? {
168+ provider : this ,
169+ id : user . id ,
170+ name : user . name ?? undefined ,
171+ email : user . email ?? undefined ,
172+ avatarUrl : user . avatarUrl ?? undefined ,
173+ username : user . username ?? undefined ,
174+ }
175+ : undefined ,
176+ ) ;
177+ }
178+
179+ return this . _accounts . get ( accessToken ) ;
180+ }
181+
182+ private _workspaces : Map < string , BitbucketWorkspaceDescriptor [ ] | undefined > | undefined ;
183+ private async getProviderResourcesForUser (
184+ session : AuthenticationSession ,
185+ force : boolean = false ,
186+ ) : Promise < BitbucketWorkspaceDescriptor [ ] | undefined > {
187+ this . _workspaces ??= new Map < string , BitbucketWorkspaceDescriptor [ ] | undefined > ( ) ;
188+ const { accessToken } = session ;
189+ const cachedResources = this . _workspaces . get ( accessToken ) ;
190+
191+ if ( cachedResources == null || force ) {
192+ const api = await this . getProvidersApi ( ) ;
193+ const account = await this . getProviderCurrentAccount ( session ) ;
194+ if ( account ?. id == null ) return undefined ;
195+
196+ const resources = await api . getBitbucketResourcesForUser ( account . id , { accessToken : accessToken } ) ;
197+ this . _workspaces . set (
198+ accessToken ,
199+ resources != null ? resources . map ( r => ( { ...r , key : r . id } ) ) : undefined ,
200+ ) ;
201+ }
202+
203+ return this . _workspaces . get ( accessToken ) ;
204+ }
205+
206+ private _repositories : Map < string , BitbucketRemoteRepositoryDescriptor [ ] | undefined > | undefined ;
207+ private async getProviderProjectsForResources (
208+ { accessToken } : AuthenticationSession ,
209+ resources : BitbucketWorkspaceDescriptor [ ] ,
210+ force : boolean = false ,
211+ ) : Promise < BitbucketRemoteRepositoryDescriptor [ ] | undefined > {
212+ this . _repositories ??= new Map < string , BitbucketRemoteRepositoryDescriptor [ ] | undefined > ( ) ;
213+ let resourcesWithoutRepositories : BitbucketWorkspaceDescriptor [ ] = [ ] ;
214+ if ( force ) {
215+ resourcesWithoutRepositories = resources ;
216+ } else {
217+ for ( const resource of resources ) {
218+ const resourceKey = `${ accessToken } :${ resource . id } ` ;
219+ const cachedRepositories = this . _repositories . get ( resourceKey ) ;
220+ if ( cachedRepositories == null ) {
221+ resourcesWithoutRepositories . push ( resource ) ;
222+ }
223+ }
224+ }
225+
226+ const cachedRepos = this . _repositories ;
227+ if ( resourcesWithoutRepositories . length > 0 ) {
228+ const api = await this . container . bitbucket ;
229+ if ( api == null ) return undefined ;
230+ await Promise . allSettled (
231+ resourcesWithoutRepositories . map ( async resource => {
232+ const resourceRepos = await api . getRepositoriesForWorkspace ( this , accessToken , resource . slug , {
233+ baseUrl : this . apiBaseUrl ,
234+ } ) ;
235+
236+ if ( resourceRepos == null ) return undefined ;
237+ cachedRepos . set (
238+ `${ accessToken } :${ resource . id } ` ,
239+ resourceRepos . map ( r => ( {
240+ id : `${ r . owner } /${ r . name } ` ,
241+ owner : r . owner ,
242+ name : r . name ,
243+ key : `${ r . owner } /${ r . name } ` ,
244+ } ) ) ,
245+ ) ;
246+ } ) ,
247+ ) ;
248+ }
249+
250+ return resources . reduce < BitbucketRemoteRepositoryDescriptor [ ] > ( ( resultRepos , resource ) => {
251+ const resourceRepos = cachedRepos . get ( `${ accessToken } :${ resource . id } ` ) ;
252+ if ( resourceRepos != null ) {
253+ resultRepos . push ( ...resourceRepos ) ;
254+ }
255+ return resultRepos ;
256+ } , [ ] ) ;
257+ }
258+
139259 protected override async searchProviderMyPullRequests (
140- _session : AuthenticationSession ,
141- _repos ?: BitbucketRepositoryDescriptor [ ] ,
260+ session : ProviderAuthenticationSession ,
261+ requestedRepositories ?: BitbucketRepositoryDescriptor [ ] ,
142262 ) : Promise < PullRequest [ ] | undefined > {
143- return Promise . resolve ( undefined ) ;
263+ const api = await this . getProvidersApi ( ) ;
264+ if ( requestedRepositories != null ) {
265+ // TODO: implement repos version
266+ return undefined ;
267+ }
268+
269+ const user = await this . getProviderCurrentAccount ( session ) ;
270+ if ( user ?. username == null ) return undefined ;
271+
272+ const workspaces = await this . getProviderResourcesForUser ( session ) ;
273+ if ( workspaces == null || workspaces . length === 0 ) return undefined ;
274+
275+ const repos = await this . getProviderProjectsForResources ( session , workspaces ) ;
276+ if ( repos == null || repos . length === 0 ) return undefined ;
277+
278+ const prs = await api . getPullRequestsForRepos (
279+ HostingIntegrationId . Bitbucket ,
280+ repos . map ( repo => ( { namespace : repo . owner , name : repo . name } ) ) ,
281+ {
282+ accessToken : session . accessToken ,
283+ } ,
284+ ) ;
285+ return prs . values . map ( pr => this . fromBitbucketProviderPullRequest ( pr ) ) ;
144286 }
145287
146288 protected override async searchProviderMyIssues (
@@ -149,6 +291,14 @@ export class BitbucketIntegration extends HostingIntegration<
149291 ) : Promise < IssueShape [ ] | undefined > {
150292 return Promise . resolve ( undefined ) ;
151293 }
294+
295+ private fromBitbucketProviderPullRequest (
296+ remotePullRequest : ProviderPullRequest ,
297+ // repoDescriptors: BitbucketRemoteRepositoryDescriptor[],
298+ ) : PullRequest {
299+ remotePullRequest . graphQLId = remotePullRequest . id ;
300+ return fromProviderPullRequest ( remotePullRequest , this ) ;
301+ }
152302}
153303
154304const bitbucketCloudDomainRegex = / ^ b i t b u c k e t \. o r g $ / i;
0 commit comments