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
2021from sentry .integrations .models .integration import Integration
2122from sentry .integrations .pipeline import IntegrationPipeline
2223from sentry .integrations .services .repository import RpcRepository , repository_service
23- from sentry .integrations .source_code_management .repository import RepositoryIntegration
24+ from sentry .integrations .source_code_management .repository import (
25+ RepositoryInfo ,
26+ RepositoryIntegration ,
27+ )
2428from sentry .integrations .tasks .migrate_repo import migrate_repo
2529from sentry .integrations .types import IntegrationProviderSlug
2630from sentry .integrations .utils .atlassian_connect import (
103107scopes = ("issue:write" , "pullrequest" , "webhook" , "repository" )
104108
105109
106- class BitbucketIntegration (RepositoryIntegration , BitbucketIssuesSpec ):
110+ class BitbucketIntegration (RepositoryIntegration [ BitbucketApiClient ] , BitbucketIssuesSpec ):
107111 codeowners_locations = [".bitbucket/CODEOWNERS" ]
108112
109113 @property
110114 def integration_name (self ) -> str :
111115 return IntegrationProviderSlug .BITBUCKET .value
112116
113- def get_client (self ):
117+ def get_client (self ) -> BitbucketApiClient :
114118 return BitbucketApiClient (integration = self .model )
115119
116120 # IntegrationInstallation methods
@@ -120,34 +124,50 @@ def error_message_from_json(self, data):
120124
121125 # RepositoryIntegration methods
122126
127+ def get_repo_external_id (self , repo : Mapping [str , Any ]) -> str :
128+ return str (repo ["uuid" ])
129+
123130 def get_repositories (
124131 self ,
125132 query : str | None = None ,
126133 page_number_limit : int | None = None ,
127134 accessible_only : bool = False ,
128- ) -> list [dict [ str , Any ] ]:
135+ ) -> list [RepositoryInfo ]:
129136 username = self .model .metadata .get ("uuid" , self .username )
130137 if not query :
131138 resp = self .get_client ().get_repos (username )
132139 return [
133- {"identifier" : repo ["full_name" ], "name" : repo ["full_name" ]}
140+ {
141+ "identifier" : repo ["full_name" ],
142+ "name" : repo ["full_name" ],
143+ "external_id" : self .get_repo_external_id (repo ),
144+ }
134145 for repo in resp .get ("values" , [])
135146 ]
136147
148+ client = self .get_client ()
137149 exact_query = f'name="{ query } "'
138150 fuzzy_query = f'name~"{ query } "'
139- exact_search_resp = self .get_client ().search_repositories (username , exact_query )
140- fuzzy_search_resp = self .get_client ().search_repositories (username , fuzzy_query )
141-
142- result : OrderedSet [str ] = OrderedSet ()
143-
144- for j in exact_search_resp .get ("values" , []):
145- result .add (j ["full_name" ])
146-
147- for i in fuzzy_search_resp .get ("values" , []):
148- result .add (i ["full_name" ])
151+ exact_search_resp = client .search_repositories (username , exact_query )
152+ fuzzy_search_resp = client .search_repositories (username , fuzzy_query )
153+
154+ seen : OrderedSet [str ] = OrderedSet ()
155+ repos : list [RepositoryInfo ] = []
156+ for repo in chain (
157+ exact_search_resp .get ("values" , []),
158+ fuzzy_search_resp .get ("values" , []),
159+ ):
160+ if repo ["full_name" ] not in seen :
161+ seen .add (repo ["full_name" ])
162+ repos .append (
163+ {
164+ "identifier" : repo ["full_name" ],
165+ "name" : repo ["full_name" ],
166+ "external_id" : self .get_repo_external_id (repo ),
167+ }
168+ )
149169
150- return [{ "identifier" : full_name , "name" : full_name } for full_name in result ]
170+ return repos
151171
152172 def has_repo_access (self , repo : RpcRepository ) -> bool :
153173 client = self .get_client ()
0 commit comments