Skip to content

Commit

Permalink
ref(integrations): add abstract search_issues method for issue integr…
Browse files Browse the repository at this point in the history
…ations (#76311)
  • Loading branch information
cathteng authored Aug 27, 2024
1 parent 9941c4b commit c24c702
Show file tree
Hide file tree
Showing 12 changed files with 56 additions and 14 deletions.
7 changes: 7 additions & 0 deletions src/sentry/integrations/bitbucket/issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,10 @@ def after_link_issue(self, external_issue, **kwargs):
)
except ApiError as e:
self.raise_error(e)

def search_issues(self, query: str | None, **kwargs) -> dict[str, Any]:
client = self.get_client()
repo = kwargs["repo"]
resp = client.search_issues(repo, query)
assert isinstance(resp, dict)
return resp
2 changes: 1 addition & 1 deletion src/sentry/integrations/bitbucket/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def get(self, request: Request, organization, integration_id, **kwds) -> Respons

full_query = f'title~"{query}"'
try:
resp = installation.get_client().search_issues(repo, full_query)
resp = installation.search_issues(query=full_query, repo=repo)
except ApiError as e:
if "no issue tracker" in str(e):
logger.info(
Expand Down
3 changes: 3 additions & 0 deletions src/sentry/integrations/example/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,9 @@ def extract_source_path_from_source_url(self, repo: Repository, url: str) -> str
def has_repo_access(self, repo: RpcRepository) -> bool:
return False

def search_issues(self, query: str | None, **kwargs):
return []


class ExampleIntegrationProvider(IntegrationProvider):
"""
Expand Down
7 changes: 4 additions & 3 deletions src/sentry/integrations/github/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,9 +296,10 @@ def get_trees_for_org(self, cache_seconds: int = 3600 * 24) -> dict[str, RepoTre

return trees

# TODO(cathy): define in issue ABC
def search_issues(self, query: str) -> Mapping[str, Sequence[Mapping[str, Any]]]:
return self.get_client().search_issues(query)
def search_issues(self, query: str | None, **kwargs) -> dict[str, Any]:
resp = self.get_client().search_issues(query)
assert isinstance(resp, dict)
return resp


class GitHubIntegrationProvider(IntegrationProvider):
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/integrations/github_enterprise/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def extract_branch_from_source_url(self, repo: Repository, url: str) -> str:
def extract_source_path_from_source_url(self, repo: Repository, url: str) -> str:
raise IntegrationFeatureNotImplementedError

def search_issues(self, query):
def search_issues(self, query: str | None, **kwargs):
return self.get_client().search_issues(query)

def has_repo_access(self, repo: RpcRepository) -> bool:
Expand Down
9 changes: 7 additions & 2 deletions src/sentry/integrations/gitlab/integration.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from typing import Any
from urllib.parse import urlparse

from django import forms
Expand Down Expand Up @@ -172,9 +173,13 @@ def search_projects(self, query):
return client.search_projects(group_id, query)

# TODO(cathy): define in issue ABC
def search_issues(self, project_id, query, iids):
def search_issues(self, query: str | None, **kwargs) -> list[dict[str, Any]]:
client = self.get_client()
return client.search_project_issues(project_id, query, iids)
project_id = kwargs["project_id"]
iids = kwargs["iids"]
resp = client.search_project_issues(project_id, query, iids)
assert isinstance(resp, list)
return resp


class InstallationForm(forms.Form):
Expand Down
6 changes: 4 additions & 2 deletions src/sentry/integrations/jira/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,9 +451,11 @@ def update_comment(self, issue_id, user_id, group_note):
issue_id, group_note.data["external_id"], quoted_comment
)

def search_issues(self, query):
def search_issues(self, query: str | None, **kwargs) -> dict[str, Any]:
try:
return self.get_client().search_issues(query)
resp = self.get_client().search_issues(query)
assert isinstance(resp, dict)
return resp
except ApiError as e:
self.raise_error(e)

Expand Down
6 changes: 4 additions & 2 deletions src/sentry/integrations/jira_server/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,9 +572,11 @@ def update_comment(self, issue_id, user_id, group_note):
issue_id, group_note.data["external_id"], quoted_comment
)

def search_issues(self, query):
def search_issues(self, query: str | None, **kwargs) -> dict[str, Any]:
try:
return self.get_client().search_issues(query)
resp = self.get_client().search_issues(query)
assert isinstance(resp, dict)
return resp
except ApiError as e:
self.raise_error(e)

Expand Down
5 changes: 4 additions & 1 deletion src/sentry/integrations/jira_server/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from sentry.api.api_publish_status import ApiPublishStatus
from sentry.api.base import control_silo_endpoint
from sentry.integrations.api.bases.integration import IntegrationEndpoint
from sentry.integrations.jira_server.integration import JiraServerIntegration
from sentry.integrations.models.integration import Integration
from sentry.organizations.services.organization import RpcOrganization
from sentry.shared_integrations.exceptions import ApiError, ApiUnauthorized, IntegrationError
Expand All @@ -23,7 +24,7 @@ class JiraServerSearchEndpoint(IntegrationEndpoint):
}
provider = "jira_server"

def _get_integration(self, organization, integration_id):
def _get_integration(self, organization, integration_id) -> Integration:
return Integration.objects.get(
organizationintegration__organization_id=organization.id,
id=integration_id,
Expand All @@ -38,6 +39,8 @@ def get(
except Integration.DoesNotExist:
return Response(status=404)
installation = integration.get_installation(organization.id)

assert isinstance(installation, JiraServerIntegration), installation
jira_client = installation.get_client()

field = request.GET.get("field")
Expand Down
4 changes: 4 additions & 0 deletions src/sentry/integrations/mixins/issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,10 @@ def get_issue(self, issue_id, **kwargs):
"""
raise NotImplementedError

@abstractmethod
def search_issues(self, query: str | None, **kwargs) -> list[dict[str, Any]] | dict[str, Any]:
raise NotImplementedError

def after_link_issue(self, external_issue, **kwargs):
"""
Takes the external issue that has been linked via `get_issue`.
Expand Down
15 changes: 14 additions & 1 deletion src/sentry/integrations/vsts/issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from sentry.integrations.services.integration import integration_service
from sentry.integrations.source_code_management.issues import SourceCodeIssueIntegration
from sentry.models.activity import Activity
from sentry.shared_integrations.exceptions import ApiError, ApiUnauthorized
from sentry.shared_integrations.exceptions import ApiError, ApiUnauthorized, IntegrationError
from sentry.silo.base import all_silo_function
from sentry.users.services.user import RpcUser
from sentry.users.services.user.service import user_service
Expand Down Expand Up @@ -356,3 +356,16 @@ def create_comment_attribution(self, user_id: int, comment_text: str) -> str:
def update_comment(self, issue_id: int, user_id: int, group_note: str) -> None:
# Azure does not support updating comments.
pass

def search_issues(self, query: str | None, **kwargs) -> dict[str, Any]:
client = self.get_client()

integration = integration_service.get_integration(
integration_id=self.org_integration.integration_id
)
if not integration:
raise IntegrationError("Azure DevOps integration not found")

resp = client.search_issues(query=query, account_name=integration.name)
assert isinstance(resp, dict)
return resp
4 changes: 3 additions & 1 deletion src/sentry/integrations/vsts/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from sentry.hybridcloud.rpc import coerce_id_from
from sentry.integrations.api.bases.integration import IntegrationEndpoint
from sentry.integrations.models.integration import Integration
from sentry.integrations.vsts.integration import VstsIntegration
from sentry.organizations.services.organization import RpcOrganization


Expand Down Expand Up @@ -39,12 +40,13 @@ def get(
return Response({"detail": "query is a required parameter"}, status=400)

installation = integration.get_installation(organization.id)
assert isinstance(installation, VstsIntegration), installation

if field == "externalIssue":
if not query:
return Response([])

resp = installation.get_client().search_issues(integration.name, query)
resp = installation.search_issues(query=query)
return Response(
[
{
Expand Down

0 comments on commit c24c702

Please sign in to comment.