Skip to content

Commit 6e3a1e1

Browse files
authored
feat(integrations): add option to filter by integration type (#78656)
add query param to filter by `integrationType`: - `messaging` - `project_management` - `source_code_management` - `on_call_scheduling`
1 parent 0215b73 commit 6e3a1e1

File tree

3 files changed

+120
-1
lines changed

3 files changed

+120
-1
lines changed

src/sentry/integrations/api/endpoints/organization_integrations_index.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
OrganizationIntegrationBaseEndpoint,
2222
)
2323
from sentry.integrations.api.serializers.models.integration import OrganizationIntegrationResponse
24+
from sentry.integrations.base import INTEGRATION_TYPE_TO_PROVIDER
2425
from sentry.integrations.models.integration import Integration
2526
from sentry.integrations.models.organization_integration import OrganizationIntegration
2627
from sentry.organizations.services.organization.model import (
@@ -93,6 +94,7 @@ def get(
9394
if provider_key is None:
9495
provider_key = request.GET.get("provider_key", "")
9596
include_config_raw = request.GET.get("includeConfig")
97+
integration_type = request.GET.get("integrationType")
9698

9799
# Include the configurations by default if includeConfig is not present.
98100
# TODO(mgaeta): HACK. We need a consistent way to get booleans from query parameters.
@@ -109,6 +111,18 @@ def get(
109111
if provider_key:
110112
queryset = queryset.filter(integration__provider=provider_key.lower())
111113

114+
if integration_type:
115+
if integration_type not in INTEGRATION_TYPE_TO_PROVIDER:
116+
return Response(
117+
{"detail": "Invalid integration type"},
118+
status=400,
119+
)
120+
provider_slugs = [
121+
provider.value
122+
for provider in INTEGRATION_TYPE_TO_PROVIDER.get(integration_type, [])
123+
]
124+
queryset = queryset.filter(integration__provider__in=provider_slugs)
125+
112126
def on_results(results: Sequence[OrganizationIntegration]) -> Sequence[Mapping[str, Any]]:
113127
if feature_filters:
114128
results = filter_by_features(results, feature_filters)

src/sentry/integrations/base.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,56 @@ class IntegrationFeatures(Enum):
126126
DEPLOYMENT = "deployment"
127127

128128

129+
# Integration Types
130+
MESSAGING = "messaging"
131+
PROJECT_MANAGEMENT = "project_management"
132+
SOURCE_CODE_MANAGEMENT = "source_code_management"
133+
ON_CALL_SCHEDULING = "on_call_scheduling"
134+
135+
136+
class IntegrationProviderSlug(Enum):
137+
SLACK = "slack"
138+
DISCORD = "discord"
139+
MSTeams = "msteams"
140+
JIRA = "jira"
141+
JIRA_SERVER = "jira_server"
142+
AZURE_DEVOPS = "vsts"
143+
GITHUB = "github"
144+
GITHUB_ENTERPRISE = "github_enterprise"
145+
GITLAB = "gitlab"
146+
BITBUCKET = "bitbucket"
147+
PAGERDUTY = "pagerduty"
148+
OPSGENIE = "opsgenie"
149+
150+
151+
INTEGRATION_TYPE_TO_PROVIDER = {
152+
MESSAGING: [
153+
IntegrationProviderSlug.SLACK,
154+
IntegrationProviderSlug.DISCORD,
155+
IntegrationProviderSlug.MSTeams,
156+
],
157+
PROJECT_MANAGEMENT: [
158+
IntegrationProviderSlug.JIRA,
159+
IntegrationProviderSlug.JIRA_SERVER,
160+
IntegrationProviderSlug.GITHUB,
161+
IntegrationProviderSlug.GITHUB_ENTERPRISE,
162+
IntegrationProviderSlug.GITLAB,
163+
IntegrationProviderSlug.AZURE_DEVOPS,
164+
],
165+
SOURCE_CODE_MANAGEMENT: [
166+
IntegrationProviderSlug.GITHUB,
167+
IntegrationProviderSlug.GITHUB_ENTERPRISE,
168+
IntegrationProviderSlug.GITLAB,
169+
IntegrationProviderSlug.BITBUCKET,
170+
IntegrationProviderSlug.AZURE_DEVOPS,
171+
],
172+
ON_CALL_SCHEDULING: [
173+
IntegrationProviderSlug.PAGERDUTY,
174+
IntegrationProviderSlug.OPSGENIE,
175+
],
176+
}
177+
178+
129179
class IntegrationProvider(PipelineProvider, abc.ABC):
130180
"""
131181
An integration provider describes a third party that can be registered within Sentry.

tests/sentry/integrations/api/endpoints/test_organization_integrations.py

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,29 @@ def setUp(self):
1515
name="Example",
1616
external_id="example:1",
1717
)
18+
self.msteams_integration = self.create_integration(
19+
organization=self.organization,
20+
provider="msteams",
21+
name="MS Teams",
22+
external_id="msteams:1",
23+
)
24+
self.opsgenie = self.create_integration(
25+
organization=self.organization,
26+
provider="opsgenie",
27+
name="Opsgenie",
28+
external_id="opsgenie:1",
29+
)
30+
self.slack_integration = self.create_integration(
31+
organization=self.organization,
32+
provider="slack",
33+
name="Slack",
34+
external_id="slack:1",
35+
)
1836

1937
def test_simple(self):
2038
response = self.get_success_response(self.organization.slug)
2139

22-
assert len(response.data) == 1
40+
assert len(response.data) == 4
2341
assert response.data[0]["id"] == str(self.integration.id)
2442
assert "configOrganization" in response.data[0]
2543

@@ -51,3 +69,40 @@ def test_provider_key(self):
5169
self.organization.slug, qs_params={"provider_key": "vercel"}
5270
)
5371
assert response.data == []
72+
73+
def test_integration_type(self):
74+
response = self.get_success_response(
75+
self.organization.slug, qs_params={"integrationType": "messaging"}
76+
)
77+
assert len(response.data) == 2
78+
assert response.data[0]["id"] == str(self.msteams_integration.id)
79+
assert response.data[1]["id"] == str(self.slack_integration.id)
80+
response = self.get_success_response(
81+
self.organization.slug, qs_params={"integrationType": "on_call_scheduling"}
82+
)
83+
assert len(response.data) == 1
84+
assert response.data[0]["id"] == str(self.opsgenie.id)
85+
response = self.get_error_response(
86+
self.organization.slug, qs_params={"integrationType": "third_party"}
87+
)
88+
assert response.data == {"detail": "Invalid integration type"}
89+
assert response.status_code == 400
90+
91+
def test_provider_key_and_integration_type(self):
92+
response = self.get_success_response(
93+
self.organization.slug,
94+
qs_params={"providerKey": "slack", "integrationType": "messaging"},
95+
)
96+
assert len(response.data) == 1
97+
assert response.data[0]["id"] == str(self.slack_integration.id)
98+
response = self.get_success_response(
99+
self.organization.slug,
100+
qs_params={"providerKey": "vercel", "integrationType": "messaging"},
101+
)
102+
assert response.data == []
103+
response = self.get_error_response(
104+
self.organization.slug,
105+
qs_params={"providerKey": "slack", "integrationType": "third_party"},
106+
)
107+
assert response.data == {"detail": "Invalid integration type"}
108+
assert response.status_code == 400

0 commit comments

Comments
 (0)