diff --git a/CHANGELOG.md b/CHANGELOG.md index 7fd5965a..fb14c27f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed recurrency limit issue in the Rotation Modal ([#3358](https://github.com/grafana/oncall/pull/3358)) - Added dragging boundary constraints for Rotation Modal and show scroll for the users list ([#3365](https://github.com/grafana/oncall/pull/3365)) +- Delete direct paging integration on team delete by @vadimkerr ([#3367](https://github.com/grafana/oncall/pull/3367)) ### Added diff --git a/engine/apps/alerts/models/alert_receive_channel.py b/engine/apps/alerts/models/alert_receive_channel.py index 73b7f0ed..03c433ca 100644 --- a/engine/apps/alerts/models/alert_receive_channel.py +++ b/engine/apps/alerts/models/alert_receive_channel.py @@ -265,6 +265,7 @@ class AlertReceiveChannel(IntegrationOptionsMixin, MaintainableObject): # Don't allow multiple Direct Paging integrations per team if ( self.integration == AlertReceiveChannel.INTEGRATION_DIRECT_PAGING + and not self.deleted_at and AlertReceiveChannel.objects.filter( organization=self.organization, team=self.team, integration=self.integration ) diff --git a/engine/apps/api/tests/test_alert_receive_channel.py b/engine/apps/api/tests/test_alert_receive_channel.py index 4f0b5a49..e5c1ece0 100644 --- a/engine/apps/api/tests/test_alert_receive_channel.py +++ b/engine/apps/api/tests/test_alert_receive_channel.py @@ -851,6 +851,35 @@ def test_update_alert_receive_channels_direct_paging( assert response.json()["detail"] == AlertReceiveChannel.DuplicateDirectPagingError.DETAIL +@pytest.mark.django_db +def test_delete_alert_receive_channel_direct_paging_duplicate( + make_organization_and_user_with_plugin_token, make_team, make_alert_receive_channel, make_user_auth_headers +): + """Check that it's possible to delete direct paging integration even if there is a duplicate for the team.""" + organization, user, token = make_organization_and_user_with_plugin_token() + integration = make_alert_receive_channel( + organization, integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING, team=None + ) + + # Create a team, add direct paging integration to it, then delete the team. + # There will be 2 direct paging integrations for the team "No team" as a result. + team = make_team(organization) + make_alert_receive_channel(organization, integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING, team=team) + team.delete() + assert ( + organization.alert_receive_channels.filter( + integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING, team=None + ).count() + == 2 + ) + + client = APIClient() + url = reverse("api-internal:alert_receive_channel-detail", kwargs={"pk": integration.public_primary_key}) + response = client.delete(url, **make_user_auth_headers(user, token)) + + assert response.status_code == status.HTTP_204_NO_CONTENT + + @pytest.mark.django_db def test_start_maintenance_integration( make_user_auth_headers, diff --git a/engine/apps/user_management/models/team.py b/engine/apps/user_management/models/team.py index bbf329d8..628e35d5 100644 --- a/engine/apps/user_management/models/team.py +++ b/engine/apps/user_management/models/team.py @@ -95,8 +95,11 @@ class TeamManager(models.Manager["Team"]): for integration in direct_paging_integrations_to_create: metrics_add_integration_to_cache(integration) - # delete excess teams + # delete excess teams and their direct paging integrations team_ids_to_delete = existing_team_ids - grafana_teams.keys() + organization.alert_receive_channels.filter( + team__team_id__in=team_ids_to_delete, integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING + ).delete() organization.teams.filter(team_id__in=team_ids_to_delete).delete() # collect teams diffs to update metrics cache diff --git a/engine/apps/user_management/tests/test_sync.py b/engine/apps/user_management/tests/test_sync.py index ac7eb99d..7153aa5c 100644 --- a/engine/apps/user_management/tests/test_sync.py +++ b/engine/apps/user_management/tests/test_sync.py @@ -101,9 +101,13 @@ def test_sync_users_for_organization_role_none(make_organization, make_user_for_ @pytest.mark.django_db -def test_sync_teams_for_organization(make_organization, make_team): +def test_sync_teams_for_organization(make_organization, make_team, make_alert_receive_channel): organization = make_organization() teams = tuple(make_team(organization, team_id=team_id) for team_id in (1, 2)) + direct_paging_integrations = tuple( + make_alert_receive_channel(organization, integration=AlertReceiveChannel.INTEGRATION_DIRECT_PAGING, team=team) + for team in teams + ) api_teams = tuple( {"id": team_id, "name": "Test", "email": "test@test.test", "avatarUrl": "test.test/test"} for team_id in (2, 3) @@ -113,14 +117,16 @@ def test_sync_teams_for_organization(make_organization, make_team): assert organization.teams.count() == 2 - # check that excess teams are deleted + # check that excess teams and direct paging integrations are deleted assert not organization.teams.filter(pk=teams[0].pk).exists() + assert not organization.alert_receive_channels.filter(pk=direct_paging_integrations[0].pk).exists() # check that existing teams are updated updated_team = organization.teams.filter(pk=teams[1].pk).first() assert updated_team is not None assert updated_team.name == api_teams[0]["name"] assert updated_team.email == api_teams[0]["email"] + assert organization.alert_receive_channels.filter(pk=direct_paging_integrations[1].pk).exists() # check that missing teams are created created_team = organization.teams.filter(team_id=api_teams[1]["id"]).first()