Skip to content

Commit 987941b

Browse files
authored
nit(scm): group functions by class they come from in main integration class (#76240)
1 parent bf47823 commit 987941b

File tree

6 files changed

+103
-78
lines changed

6 files changed

+103
-78
lines changed

src/sentry/integrations/bitbucket/integration.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,13 @@ def integration_name(self) -> str:
9090
def get_client(self):
9191
return BitbucketApiClient(integration=self.model)
9292

93-
@property
94-
def username(self):
95-
return self.model.name
93+
# IntegrationInstallation methods
9694

9795
def error_message_from_json(self, data):
9896
return data.get("error", {}).get("message", "unknown error")
9997

98+
# RepositoryIntegration methods
99+
100100
def get_repositories(self, query=None):
101101
username = self.model.metadata.get("uuid", self.username)
102102
if not query:
@@ -156,6 +156,12 @@ def extract_source_path_from_source_url(self, repo: Repository, url: str) -> str
156156
_, _, source_path = url.partition("/")
157157
return source_path
158158

159+
# Bitbucket only methods
160+
161+
@property
162+
def username(self):
163+
return self.model.name
164+
159165

160166
class BitbucketIntegrationProvider(IntegrationProvider):
161167
key = "bitbucket"

src/sentry/integrations/bitbucket_server/integration.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,13 @@ def get_client(self):
249249
identity=self.default_identity,
250250
)
251251

252-
@property
253-
def username(self):
254-
return self.model.name
252+
# IntegrationInstallation methods
255253

256254
def error_message_from_json(self, data):
257255
return data.get("error", {}).get("message", "unknown error")
258256

257+
# RepositoryIntegration methods
258+
259259
def get_repositories(self, query=None):
260260
if not query:
261261
resp = self.get_client().get_repos()
@@ -310,6 +310,12 @@ def extract_branch_from_source_url(self, repo: Repository, url: str) -> str:
310310
def extract_source_path_from_source_url(self, repo: Repository, url: str) -> str:
311311
raise IntegrationFeatureNotImplementedError
312312

313+
# Bitbucket Server only methods
314+
315+
@property
316+
def username(self):
317+
return self.model.name
318+
313319

314320
class BitbucketServerIntegrationProvider(IntegrationProvider):
315321
key = "bitbucket_server"

src/sentry/integrations/github/integration.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ def has_repo_access(self, repo: RpcRepository) -> bool:
268268
return False
269269
return True
270270

271-
# for derive code mappings (TODO: define in an ABC)
271+
# for derive code mappings - TODO(cathy): define in an ABC
272272
def get_trees_for_org(self, cache_seconds: int = 3600 * 24) -> dict[str, RepoTree]:
273273
trees: dict[str, RepoTree] = {}
274274
domain_name = self.model.metadata["domain_name"]
@@ -291,7 +291,7 @@ def get_trees_for_org(self, cache_seconds: int = 3600 * 24) -> dict[str, RepoTre
291291

292292
return trees
293293

294-
# TODO: define in issue ABC
294+
# TODO(cathy): define in issue ABC
295295
def search_issues(self, query: str) -> Mapping[str, Sequence[Mapping[str, Any]]]:
296296
return self.get_client().search_issues(query)
297297

src/sentry/integrations/github_enterprise/integration.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,19 @@ def get_client(self):
154154
org_integration_id=self.org_integration.id,
155155
)
156156

157+
# IntegrationInstallation methods
158+
159+
def message_from_error(self, exc):
160+
if isinstance(exc, ApiError):
161+
message = API_ERRORS.get(exc.code)
162+
if message is None:
163+
message = exc.json.get("message", "unknown error") if exc.json else "unknown error"
164+
return f"Error Communicating with GitHub Enterprise (HTTP {exc.code}): {message}"
165+
else:
166+
return ERR_INTERNAL
167+
168+
# RepositoryIntegration methods
169+
157170
def get_repositories(self, query=None):
158171
if not query:
159172
return [
@@ -197,15 +210,6 @@ def has_repo_access(self, repo: RpcRepository) -> bool:
197210
# TODO: define this, used to migrate repositories
198211
return False
199212

200-
def message_from_error(self, exc):
201-
if isinstance(exc, ApiError):
202-
message = API_ERRORS.get(exc.code)
203-
if message is None:
204-
message = exc.json.get("message", "unknown error") if exc.json else "unknown error"
205-
return f"Error Communicating with GitHub Enterprise (HTTP {exc.code}): {message}"
206-
else:
207-
return ERR_INTERNAL
208-
209213

210214
class InstallationForm(forms.Form):
211215
url = forms.CharField(

src/sentry/integrations/gitlab/integration.py

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,6 @@ def __init__(self, *args, **kwargs):
103103
def integration_name(self) -> str:
104104
return "gitlab"
105105

106-
def get_group_id(self):
107-
return self.model.metadata["group_id"]
108-
109106
def get_client(self):
110107
if self.default_identity is None:
111108
try:
@@ -115,6 +112,22 @@ def get_client(self):
115112

116113
return GitLabApiClient(self)
117114

115+
# IntegrationInstallation methods
116+
def error_message_from_json(self, data):
117+
"""
118+
Extract error messages from gitlab API errors.
119+
Generic errors come in the `error` key while validation errors
120+
are generally in `message`.
121+
122+
See https://docs.gitlab.com/ee/api/#data-validation-and-error-reporting
123+
"""
124+
if "message" in data:
125+
return data["message"]
126+
if "error" in data:
127+
return data["error"]
128+
129+
# RepositoryIntegration methods
130+
118131
def has_repo_access(self, repo: RpcRepository) -> bool:
119132
# TODO: define this, used to migrate repositories
120133
return False
@@ -148,28 +161,21 @@ def extract_source_path_from_source_url(self, repo: Repository, url: str) -> str
148161
_, _, source_path = url.partition("/")
149162
return source_path
150163

164+
# Gitlab only functions
165+
166+
def get_group_id(self):
167+
return self.model.metadata["group_id"]
168+
151169
def search_projects(self, query):
152170
client = self.get_client()
153171
group_id = self.get_group_id()
154172
return client.search_projects(group_id, query)
155173

174+
# TODO(cathy): define in issue ABC
156175
def search_issues(self, project_id, query, iids):
157176
client = self.get_client()
158177
return client.search_project_issues(project_id, query, iids)
159178

160-
def error_message_from_json(self, data):
161-
"""
162-
Extract error messages from gitlab API errors.
163-
Generic errors come in the `error` key while validation errors
164-
are generally in `message`.
165-
166-
See https://docs.gitlab.com/ee/api/#data-validation-and-error-reporting
167-
"""
168-
if "message" in data:
169-
return data["message"]
170-
if "error" in data:
171-
return data["error"]
172-
173179

174180
class InstallationForm(forms.Form):
175181
url = forms.CharField(

src/sentry/integrations/vsts/integration.py

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -131,47 +131,12 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
131131
def integration_name(self) -> str:
132132
return "vsts"
133133

134-
def all_repos_migrated(self) -> bool:
135-
return not self.get_unmigratable_repositories()
136-
137-
def get_repositories(self, query: str | None = None) -> Sequence[Mapping[str, str]]:
138-
try:
139-
repos = self.get_client().get_repos()
140-
except (ApiError, IdentityNotValid) as e:
141-
raise IntegrationError(self.message_from_error(e))
142-
data = []
143-
for repo in repos["value"]:
144-
data.append(
145-
{
146-
"name": "{}/{}".format(repo["project"]["name"], repo["name"]),
147-
"identifier": repo["id"],
148-
}
149-
)
150-
return data
151-
152-
def get_unmigratable_repositories(self) -> list[RpcRepository]:
153-
repos = repository_service.get_repositories(
154-
organization_id=self.organization_id, providers=["visualstudio"]
155-
)
156-
identifiers_to_exclude = {r["identifier"] for r in self.get_repositories()}
157-
return [repo for repo in repos if repo.external_id not in identifiers_to_exclude]
158-
159-
def has_repo_access(self, repo: RpcRepository) -> bool:
160-
client = self.get_client()
161-
try:
162-
# since we don't actually use webhooks for vsts commits,
163-
# just verify repo access
164-
client.get_repo(repo.config["name"], project=repo.config["project"])
165-
except (ApiError, IdentityNotValid):
166-
return False
167-
return True
168-
169134
def get_client(self) -> VstsApiClient:
170135
base_url = self.instance
171136
if SiloMode.get_current_mode() != SiloMode.REGION:
172137
if self.default_identity is None:
173138
self.default_identity = self.get_default_identity()
174-
self.check_domain_name(self.default_identity)
139+
self._check_domain_name(self.default_identity)
175140

176141
if self.org_integration is None:
177142
raise Exception("self.org_integration is not defined")
@@ -184,15 +149,7 @@ def get_client(self) -> VstsApiClient:
184149
identity_id=self.org_integration.default_auth_id,
185150
)
186151

187-
def check_domain_name(self, default_identity: RpcIdentity) -> None:
188-
if re.match("^https://.+/$", self.model.metadata["domain_name"]):
189-
return
190-
191-
base_url = VstsIntegrationProvider.get_base_url(
192-
default_identity.data["access_token"], self.model.external_id
193-
)
194-
self.model.metadata["domain_name"] = base_url
195-
self.model.save()
152+
# IntegrationInstallation methods
196153

197154
def get_organization_config(self) -> Sequence[Mapping[str, Any]]:
198155
client = self.get_client()
@@ -333,6 +290,40 @@ def get_config_data(self) -> Mapping[str, Any]:
333290
config["sync_status_forward"] = sync_status_forward
334291
return config
335292

293+
# RepositoryIntegration methods
294+
295+
def get_repositories(self, query: str | None = None) -> Sequence[Mapping[str, str]]:
296+
try:
297+
repos = self.get_client().get_repos()
298+
except (ApiError, IdentityNotValid) as e:
299+
raise IntegrationError(self.message_from_error(e))
300+
data = []
301+
for repo in repos["value"]:
302+
data.append(
303+
{
304+
"name": "{}/{}".format(repo["project"]["name"], repo["name"]),
305+
"identifier": repo["id"],
306+
}
307+
)
308+
return data
309+
310+
def get_unmigratable_repositories(self) -> list[RpcRepository]:
311+
repos = repository_service.get_repositories(
312+
organization_id=self.organization_id, providers=["visualstudio"]
313+
)
314+
identifiers_to_exclude = {r["identifier"] for r in self.get_repositories()}
315+
return [repo for repo in repos if repo.external_id not in identifiers_to_exclude]
316+
317+
def has_repo_access(self, repo: RpcRepository) -> bool:
318+
client = self.get_client()
319+
try:
320+
# since we don't actually use webhooks for vsts commits,
321+
# just verify repo access
322+
client.get_repo(repo.config["name"], project=repo.config["project"])
323+
except (ApiError, IdentityNotValid):
324+
return False
325+
return True
326+
336327
def source_url_matches(self, url: str) -> bool:
337328
return url.startswith(self.model.metadata["domain_name"])
338329

@@ -362,6 +353,18 @@ def extract_source_path_from_source_url(self, repo: Repository, url: str) -> str
362353
return qs["path"][0].lstrip("/")
363354
return ""
364355

356+
# Azure DevOps only methods
357+
358+
def _check_domain_name(self, default_identity: RpcIdentity) -> None:
359+
if re.match("^https://.+/$", self.model.metadata["domain_name"]):
360+
return
361+
362+
base_url = VstsIntegrationProvider.get_base_url(
363+
default_identity.data["access_token"], self.model.external_id
364+
)
365+
self.model.metadata["domain_name"] = base_url
366+
self.model.save()
367+
365368
@property
366369
def instance(self) -> str:
367370
return self.model.metadata["domain_name"]

0 commit comments

Comments
 (0)