@@ -7,18 +7,19 @@ 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 { ResourceDescriptor } from '../integration ' ;
10
+ import type { ProviderAuthenticationSession } from '../authentication/models ' ;
11
11
import { 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' ;
13
19
14
20
const metadata = providersMetadata [ HostingIntegrationId . Bitbucket ] ;
15
21
const authProvider = Object . freeze ( { id : metadata . id , scopes : metadata . scopes } ) ;
16
22
17
- interface BitbucketRepositoryDescriptor extends ResourceDescriptor {
18
- owner : string ;
19
- name : string ;
20
- }
21
-
22
23
export class BitbucketIntegration extends HostingIntegration <
23
24
HostingIntegrationId . Bitbucket ,
24
25
BitbucketRepositoryDescriptor
@@ -136,11 +137,136 @@ export class BitbucketIntegration extends HostingIntegration<
136
137
return Promise . resolve ( undefined ) ;
137
138
}
138
139
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
+
139
243
protected override async searchProviderMyPullRequests (
140
- _session : AuthenticationSession ,
141
- _repos ?: BitbucketRepositoryDescriptor [ ] ,
244
+ session : ProviderAuthenticationSession ,
245
+ repos ?: BitbucketRepositoryDescriptor [ ] ,
142
246
) : 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 ) ) ;
144
270
}
145
271
146
272
protected override async searchProviderMyIssues (
@@ -149,6 +275,14 @@ export class BitbucketIntegration extends HostingIntegration<
149
275
) : Promise < IssueShape [ ] | undefined > {
150
276
return Promise . resolve ( undefined ) ;
151
277
}
278
+
279
+ private fromBitbucketProviderPullRequest (
280
+ remotePullRequest : ProviderPullRequest ,
281
+ // repoDescriptors: BitbucketRemoteRepositoryDescriptor[],
282
+ ) : PullRequest {
283
+ remotePullRequest . graphQLId = remotePullRequest . id ;
284
+ return fromProviderPullRequest ( remotePullRequest , this ) ;
285
+ }
152
286
}
153
287
154
288
const bitbucketCloudDomainRegex = / ^ b i t b u c k e t \. o r g $ / i;
0 commit comments