11from __future__ import annotations
22
33from collections .abc import Mapping , Sequence
4+ from itertools import chain
45from typing import Any
56
67from django .http .request import HttpRequest
2324from sentry .integrations .models .integration import Integration
2425from sentry .integrations .pipeline import IntegrationPipeline
2526from sentry .integrations .services .repository import RpcRepository , repository_service
26- from sentry .integrations .source_code_management .repository import RepositoryIntegration
27+ from sentry .integrations .source_code_management .repository import (
28+ RepositoryInfo ,
29+ RepositoryIntegration ,
30+ )
2731from sentry .integrations .tasks .migrate_repo import migrate_repo
2832from sentry .integrations .types import IntegrationProviderSlug
2933from sentry .integrations .utils .atlassian_connect import (
108112scopes = ("issue:write" , "pullrequest" , "webhook" , "repository" )
109113
110114
111- class BitbucketIntegration (RepositoryIntegration , BitbucketIssuesSpec ):
115+ class BitbucketIntegration (RepositoryIntegration [ BitbucketApiClient ] , BitbucketIssuesSpec ):
112116 codeowners_locations = [".bitbucket/CODEOWNERS" ]
113117
114118 @property
115119 def integration_name (self ) -> str :
116120 return IntegrationProviderSlug .BITBUCKET .value
117121
118- def get_client (self ):
122+ def get_client (self ) -> BitbucketApiClient :
119123 return BitbucketApiClient (integration = self .model )
120124
121125 # IntegrationInstallation methods
@@ -125,34 +129,50 @@ def error_message_from_json(self, data):
125129
126130 # RepositoryIntegration methods
127131
132+ def get_repo_external_id (self , repo : Mapping [str , Any ]) -> str :
133+ return str (repo ["uuid" ])
134+
128135 def get_repositories (
129136 self ,
130137 query : str | None = None ,
131138 page_number_limit : int | None = None ,
132139 accessible_only : bool = False ,
133- ) -> list [dict [ str , Any ] ]:
140+ ) -> list [RepositoryInfo ]:
134141 username = self .model .metadata .get ("uuid" , self .username )
135142 if not query :
136143 resp = self .get_client ().get_repos (username )
137144 return [
138- {"identifier" : repo ["full_name" ], "name" : repo ["full_name" ]}
145+ {
146+ "identifier" : repo ["full_name" ],
147+ "name" : repo ["full_name" ],
148+ "external_id" : self .get_repo_external_id (repo ),
149+ }
139150 for repo in resp .get ("values" , [])
140151 ]
141152
153+ client = self .get_client ()
142154 exact_query = f'name="{ query } "'
143155 fuzzy_query = f'name~"{ query } "'
144- exact_search_resp = self .get_client ().search_repositories (username , exact_query )
145- fuzzy_search_resp = self .get_client ().search_repositories (username , fuzzy_query )
146-
147- result : OrderedSet [str ] = OrderedSet ()
148-
149- for j in exact_search_resp .get ("values" , []):
150- result .add (j ["full_name" ])
151-
152- for i in fuzzy_search_resp .get ("values" , []):
153- result .add (i ["full_name" ])
156+ exact_search_resp = client .search_repositories (username , exact_query )
157+ fuzzy_search_resp = client .search_repositories (username , fuzzy_query )
158+
159+ seen : OrderedSet [str ] = OrderedSet ()
160+ repos : list [RepositoryInfo ] = []
161+ for repo in chain (
162+ exact_search_resp .get ("values" , []),
163+ fuzzy_search_resp .get ("values" , []),
164+ ):
165+ if repo ["full_name" ] not in seen :
166+ seen .add (repo ["full_name" ])
167+ repos .append (
168+ {
169+ "identifier" : repo ["full_name" ],
170+ "name" : repo ["full_name" ],
171+ "external_id" : self .get_repo_external_id (repo ),
172+ }
173+ )
154174
155- return [{ "identifier" : full_name , "name" : full_name } for full_name in result ]
175+ return repos
156176
157177 def has_repo_access (self , repo : RpcRepository ) -> bool :
158178 client = self .get_client ()
0 commit comments