From e4345b38899297331b2588c8e0d5fda6219aa3bd Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Fri, 4 Oct 2024 14:59:27 -0700 Subject: [PATCH 1/5] filter by integration type --- .../organization_integrations_index.py | 9 ++++ src/sentry/integrations/base.py | 47 +++++++++++++++++++ .../test_organization_integrations.py | 37 ++++++++++++++- 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/src/sentry/integrations/api/endpoints/organization_integrations_index.py b/src/sentry/integrations/api/endpoints/organization_integrations_index.py index 93979867c2a713..79970038e3c8a6 100644 --- a/src/sentry/integrations/api/endpoints/organization_integrations_index.py +++ b/src/sentry/integrations/api/endpoints/organization_integrations_index.py @@ -21,6 +21,7 @@ OrganizationIntegrationBaseEndpoint, ) from sentry.integrations.api.serializers.models.integration import OrganizationIntegrationResponse +from sentry.integrations.base import INTEGRATION_TYPE_TO_PROVIDER from sentry.integrations.models.integration import Integration from sentry.integrations.models.organization_integration import OrganizationIntegration from sentry.organizations.services.organization.model import ( @@ -93,6 +94,7 @@ def get( if provider_key is None: provider_key = request.GET.get("provider_key", "") include_config_raw = request.GET.get("includeConfig") + integration_type = request.GET.get("integrationType") # Include the configurations by default if includeConfig is not present. # TODO(mgaeta): HACK. We need a consistent way to get booleans from query parameters. @@ -109,6 +111,13 @@ def get( if provider_key: queryset = queryset.filter(integration__provider=provider_key.lower()) + if integration_type: + provider_slugs = [ + provider.value + for provider in INTEGRATION_TYPE_TO_PROVIDER.get(integration_type, []) + ] + queryset = queryset.filter(integration__provider__in=provider_slugs) + def on_results(results: Sequence[OrganizationIntegration]) -> Sequence[Mapping[str, Any]]: if feature_filters: results = filter_by_features(results, feature_filters) diff --git a/src/sentry/integrations/base.py b/src/sentry/integrations/base.py index abd91547629fae..1f1de210f71e52 100644 --- a/src/sentry/integrations/base.py +++ b/src/sentry/integrations/base.py @@ -125,6 +125,53 @@ class IntegrationFeatures(Enum): DEPLOYMENT = "deployment" +# Integration Types +MESSAGING = "messaging" +PROJECT_MANAGEMENT = "project_management" +SOURCE_CODE_MANAGEMENT = "source_code_management" +ON_CALL_SCHEDULING = "on_call_scheduling" + + +class IntegrationProvider(Enum): + SLACK = "slack" + DISCORD = "discord" + MSTeams = "msteams" + JIRA = "jira" + JIRA_SERVER = "jira_server" + AZURE_DEVOPS = "vsts" + GITHUB = "github" + GITHUB_ENTERPRISE = "github_enterprise" + GITLAB = "gitlab" + BITBUCKET = "bitbucket" + PAGERDUTY = "pagerduty" + OPSGENIE = "opsgenie" + + +INTEGRATION_TYPE_TO_PROVIDER = { + MESSAGING: [ + IntegrationProvider.SLACK, + IntegrationProvider.DISCORD, + IntegrationProvider.MSTeams, + ], + PROJECT_MANAGEMENT: [ + IntegrationProvider.JIRA, + IntegrationProvider.JIRA_SERVER, + IntegrationProvider.AZURE_DEVOPS, + ], + SOURCE_CODE_MANAGEMENT: [ + IntegrationProvider.GITHUB, + IntegrationProvider.GITHUB_ENTERPRISE, + IntegrationProvider.GITLAB, + IntegrationProvider.BITBUCKET, + IntegrationProvider.AZURE_DEVOPS, + ], + ON_CALL_SCHEDULING: [ + IntegrationProvider.PAGERDUTY, + IntegrationProvider.OPSGENIE, + ], +} + + class IntegrationProvider(PipelineProvider, abc.ABC): """ An integration provider describes a third party that can be registered within Sentry. diff --git a/tests/sentry/integrations/api/endpoints/test_organization_integrations.py b/tests/sentry/integrations/api/endpoints/test_organization_integrations.py index c4860d67ba6d40..9607e13bc4e499 100644 --- a/tests/sentry/integrations/api/endpoints/test_organization_integrations.py +++ b/tests/sentry/integrations/api/endpoints/test_organization_integrations.py @@ -15,11 +15,29 @@ def setUp(self): name="Example", external_id="example:1", ) + self.msteams_integration = self.create_integration( + organization=self.organization, + provider="msteams", + name="MS Teams", + external_id="msteams:1", + ) + self.opsgenie = self.create_integration( + organization=self.organization, + provider="opsgenie", + name="Opsgenie", + external_id="opsgenie:1", + ) + self.slack_integration = self.create_integration( + organization=self.organization, + provider="slack", + name="Slack", + external_id="slack:1", + ) def test_simple(self): response = self.get_success_response(self.organization.slug) - assert len(response.data) == 1 + assert len(response.data) == 4 assert response.data[0]["id"] == str(self.integration.id) assert "configOrganization" in response.data[0] @@ -51,3 +69,20 @@ def test_provider_key(self): self.organization.slug, qs_params={"provider_key": "vercel"} ) assert response.data == [] + + def test_integration_type(self): + response = self.get_success_response( + self.organization.slug, qs_params={"integrationType": "messaging"} + ) + assert len(response.data) == 2 + assert response.data[0]["id"] == str(self.msteams_integration.id) + assert response.data[1]["id"] == str(self.slack_integration.id) + response = self.get_success_response( + self.organization.slug, qs_params={"integrationType": "on_call_scheduling"} + ) + assert len(response.data) == 1 + assert response.data[0]["id"] == str(self.opsgenie.id) + response = self.get_success_response( + self.organization.slug, qs_params={"integrationType": "third_party"} + ) + assert response.data == [] From 6dc355bab0f9a9b750f3c4f24750bc93453a0d90 Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Fri, 4 Oct 2024 15:24:46 -0700 Subject: [PATCH 2/5] rename enum --- src/sentry/integrations/base.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sentry/integrations/base.py b/src/sentry/integrations/base.py index 1f1de210f71e52..199d5c4e73cdd2 100644 --- a/src/sentry/integrations/base.py +++ b/src/sentry/integrations/base.py @@ -132,7 +132,7 @@ class IntegrationFeatures(Enum): ON_CALL_SCHEDULING = "on_call_scheduling" -class IntegrationProvider(Enum): +class IntegrationProviderSlug(Enum): SLACK = "slack" DISCORD = "discord" MSTeams = "msteams" @@ -149,25 +149,25 @@ class IntegrationProvider(Enum): INTEGRATION_TYPE_TO_PROVIDER = { MESSAGING: [ - IntegrationProvider.SLACK, - IntegrationProvider.DISCORD, - IntegrationProvider.MSTeams, + IntegrationProviderSlug.SLACK, + IntegrationProviderSlug.DISCORD, + IntegrationProviderSlug.MSTeams, ], PROJECT_MANAGEMENT: [ - IntegrationProvider.JIRA, - IntegrationProvider.JIRA_SERVER, - IntegrationProvider.AZURE_DEVOPS, + IntegrationProviderSlug.JIRA, + IntegrationProviderSlug.JIRA_SERVER, + IntegrationProviderSlug.AZURE_DEVOPS, ], SOURCE_CODE_MANAGEMENT: [ - IntegrationProvider.GITHUB, - IntegrationProvider.GITHUB_ENTERPRISE, - IntegrationProvider.GITLAB, - IntegrationProvider.BITBUCKET, - IntegrationProvider.AZURE_DEVOPS, + IntegrationProviderSlug.GITHUB, + IntegrationProviderSlug.GITHUB_ENTERPRISE, + IntegrationProviderSlug.GITLAB, + IntegrationProviderSlug.BITBUCKET, + IntegrationProviderSlug.AZURE_DEVOPS, ], ON_CALL_SCHEDULING: [ - IntegrationProvider.PAGERDUTY, - IntegrationProvider.OPSGENIE, + IntegrationProviderSlug.PAGERDUTY, + IntegrationProviderSlug.OPSGENIE, ], } From de67747f1435880917d0c22f2d591254e286e74b Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Mon, 7 Oct 2024 15:14:22 -0700 Subject: [PATCH 3/5] validate integration type --- .../api/endpoints/organization_integrations_index.py | 5 +++++ .../api/endpoints/test_organization_integrations.py | 5 +++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sentry/integrations/api/endpoints/organization_integrations_index.py b/src/sentry/integrations/api/endpoints/organization_integrations_index.py index 79970038e3c8a6..78a1ded2c15e57 100644 --- a/src/sentry/integrations/api/endpoints/organization_integrations_index.py +++ b/src/sentry/integrations/api/endpoints/organization_integrations_index.py @@ -112,6 +112,11 @@ def get( queryset = queryset.filter(integration__provider=provider_key.lower()) if integration_type: + if integration_type not in INTEGRATION_TYPE_TO_PROVIDER: + return Response( + {"detail": "Invalid integration type"}, + status=400, + ) provider_slugs = [ provider.value for provider in INTEGRATION_TYPE_TO_PROVIDER.get(integration_type, []) diff --git a/tests/sentry/integrations/api/endpoints/test_organization_integrations.py b/tests/sentry/integrations/api/endpoints/test_organization_integrations.py index 9607e13bc4e499..6e5132db2c3507 100644 --- a/tests/sentry/integrations/api/endpoints/test_organization_integrations.py +++ b/tests/sentry/integrations/api/endpoints/test_organization_integrations.py @@ -82,7 +82,8 @@ def test_integration_type(self): ) assert len(response.data) == 1 assert response.data[0]["id"] == str(self.opsgenie.id) - response = self.get_success_response( + response = self.get_error_response( self.organization.slug, qs_params={"integrationType": "third_party"} ) - assert response.data == [] + assert response.data == {"detail": "Invalid integration type"} + assert response.status_code == 400 From 3ea4f4a410929142d4a28f19a5259cbef02ea321 Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Mon, 7 Oct 2024 16:54:23 -0700 Subject: [PATCH 4/5] more tests yay --- .../test_organization_integrations.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/sentry/integrations/api/endpoints/test_organization_integrations.py b/tests/sentry/integrations/api/endpoints/test_organization_integrations.py index 6e5132db2c3507..6d7ba98a2ba76a 100644 --- a/tests/sentry/integrations/api/endpoints/test_organization_integrations.py +++ b/tests/sentry/integrations/api/endpoints/test_organization_integrations.py @@ -87,3 +87,22 @@ def test_integration_type(self): ) assert response.data == {"detail": "Invalid integration type"} assert response.status_code == 400 + + def test_provider_key_and_integration_type(self): + response = self.get_success_response( + self.organization.slug, + qs_params={"providerKey": "slack", "integrationType": "messaging"}, + ) + assert len(response.data) == 1 + assert response.data[0]["id"] == str(self.slack_integration.id) + response = self.get_success_response( + self.organization.slug, + qs_params={"providerKey": "vercel", "integrationType": "messaging"}, + ) + assert response.data == [] + response = self.get_error_response( + self.organization.slug, + qs_params={"providerKey": "slack", "integrationType": "third_party"}, + ) + assert response.data == {"detail": "Invalid integration type"} + assert response.status_code == 400 From 0802f6536760c61cd3be54c964f7706582034a74 Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Tue, 8 Oct 2024 11:02:57 -0700 Subject: [PATCH 5/5] add missing integrations --- src/sentry/integrations/base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sentry/integrations/base.py b/src/sentry/integrations/base.py index 199d5c4e73cdd2..6e6c6905db95d0 100644 --- a/src/sentry/integrations/base.py +++ b/src/sentry/integrations/base.py @@ -156,6 +156,9 @@ class IntegrationProviderSlug(Enum): PROJECT_MANAGEMENT: [ IntegrationProviderSlug.JIRA, IntegrationProviderSlug.JIRA_SERVER, + IntegrationProviderSlug.GITHUB, + IntegrationProviderSlug.GITHUB_ENTERPRISE, + IntegrationProviderSlug.GITLAB, IntegrationProviderSlug.AZURE_DEVOPS, ], SOURCE_CODE_MANAGEMENT: [