@@ -7,9 +7,11 @@ import type { IssueOrPullRequest, IssueOrPullRequestType } from '../../../git/mo
7
7
import type { PullRequest , PullRequestMergeMethod , PullRequestState } from '../../../git/models/pullRequest' ;
8
8
import type { RepositoryMetadata } from '../../../git/models/repositoryMetadata' ;
9
9
import type { IntegrationAuthenticationProviderDescriptor } from '../authentication/integrationAuthenticationProvider' ;
10
+ import type { ProviderAuthenticationSession } from '../authentication/models' ;
10
11
import type { ResourceDescriptor } from '../integration' ;
11
12
import { HostingIntegration } from '../integration' ;
12
- import { providersMetadata } from './models' ;
13
+ import type { ProviderPullRequest } from './models' ;
14
+ import { fromProviderPullRequest , providersMetadata } from './models' ;
13
15
14
16
const metadata = providersMetadata [ HostingIntegrationId . Bitbucket ] ;
15
17
const authProvider = Object . freeze ( { id : metadata . id , scopes : metadata . scopes } ) ;
@@ -19,6 +21,19 @@ interface BitbucketRepositoryDescriptor extends ResourceDescriptor {
19
21
name : string ;
20
22
}
21
23
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
+
22
37
export class BitbucketIntegration extends HostingIntegration <
23
38
HostingIntegrationId . Bitbucket ,
24
39
BitbucketRepositoryDescriptor
@@ -136,11 +151,138 @@ export class BitbucketIntegration extends HostingIntegration<
136
151
return Promise . resolve ( undefined ) ;
137
152
}
138
153
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
+
139
259
protected override async searchProviderMyPullRequests (
140
- _session : AuthenticationSession ,
141
- _repos ?: BitbucketRepositoryDescriptor [ ] ,
260
+ session : ProviderAuthenticationSession ,
261
+ requestedRepositories ?: BitbucketRepositoryDescriptor [ ] ,
142
262
) : 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 ) ) ;
144
286
}
145
287
146
288
protected override async searchProviderMyIssues (
@@ -149,6 +291,14 @@ export class BitbucketIntegration extends HostingIntegration<
149
291
) : Promise < IssueShape [ ] | undefined > {
150
292
return Promise . resolve ( undefined ) ;
151
293
}
294
+
295
+ private fromBitbucketProviderPullRequest (
296
+ remotePullRequest : ProviderPullRequest ,
297
+ // repoDescriptors: BitbucketRemoteRepositoryDescriptor[],
298
+ ) : PullRequest {
299
+ remotePullRequest . graphQLId = remotePullRequest . id ;
300
+ return fromProviderPullRequest ( remotePullRequest , this ) ;
301
+ }
152
302
}
153
303
154
304
const bitbucketCloudDomainRegex = / ^ b i t b u c k e t \. o r g $ / i;
0 commit comments